diff options
Diffstat (limited to 'crates')
26 files changed, 632 insertions, 272 deletions
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs index da47187e4..4964ddc7d 100644 --- a/crates/assists/src/handlers/fill_match_arms.rs +++ b/crates/assists/src/handlers/fill_match_arms.rs | |||
@@ -8,7 +8,7 @@ use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | |||
8 | use test_utils::mark; | 8 | use test_utils::mark; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | utils::{render_snippet, Cursor}, | 11 | utils::{does_pat_match_variant, render_snippet, Cursor}, |
12 | AssistContext, AssistId, AssistKind, Assists, | 12 | AssistContext, AssistId, AssistKind, Assists, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -147,25 +147,6 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { | |||
147 | }) | 147 | }) |
148 | } | 148 | } |
149 | 149 | ||
150 | fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { | ||
151 | let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text()); | ||
152 | |||
153 | let pat_head = match pat { | ||
154 | Pat::IdentPat(bind_pat) => { | ||
155 | if let Some(p) = bind_pat.pat() { | ||
156 | first_node_text(&p) | ||
157 | } else { | ||
158 | return false; | ||
159 | } | ||
160 | } | ||
161 | pat => first_node_text(pat), | ||
162 | }; | ||
163 | |||
164 | let var_head = first_node_text(var); | ||
165 | |||
166 | pat_head == var_head | ||
167 | } | ||
168 | |||
169 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { | 150 | fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { |
170 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { | 151 | sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
171 | Some(Adt::Enum(e)) => Some(e), | 152 | Some(Adt::Enum(e)) => Some(e), |
@@ -291,6 +272,34 @@ mod tests { | |||
291 | } | 272 | } |
292 | 273 | ||
293 | #[test] | 274 | #[test] |
275 | fn partial_fill_option() { | ||
276 | check_assist( | ||
277 | fill_match_arms, | ||
278 | r#" | ||
279 | enum Option<T> { Some(T), None } | ||
280 | use Option::*; | ||
281 | |||
282 | fn main() { | ||
283 | match None$0 { | ||
284 | None => {} | ||
285 | } | ||
286 | } | ||
287 | "#, | ||
288 | r#" | ||
289 | enum Option<T> { Some(T), None } | ||
290 | use Option::*; | ||
291 | |||
292 | fn main() { | ||
293 | match None { | ||
294 | None => {} | ||
295 | Some(${0:_}) => {} | ||
296 | } | ||
297 | } | ||
298 | "#, | ||
299 | ); | ||
300 | } | ||
301 | |||
302 | #[test] | ||
294 | fn partial_fill_or_pat() { | 303 | fn partial_fill_or_pat() { |
295 | check_assist( | 304 | check_assist( |
296 | fill_match_arms, | 305 | fill_match_arms, |
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs index aee3397ab..aee880625 100644 --- a/crates/assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -10,7 +10,10 @@ use syntax::{ | |||
10 | AstNode, | 10 | AstNode, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; | 13 | use crate::{ |
14 | utils::{does_pat_match_variant, unwrap_trivial_block}, | ||
15 | AssistContext, AssistId, AssistKind, Assists, | ||
16 | }; | ||
14 | 17 | ||
15 | // Assist: replace_if_let_with_match | 18 | // Assist: replace_if_let_with_match |
16 | // | 19 | // |
@@ -66,7 +69,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
66 | .sema | 69 | .sema |
67 | .type_of_pat(&pat) | 70 | .type_of_pat(&pat) |
68 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) | 71 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) |
69 | .map(|it| it.sad_pattern()) | 72 | .map(|it| { |
73 | if does_pat_match_variant(&pat, &it.sad_pattern()) { | ||
74 | it.happy_pattern() | ||
75 | } else { | ||
76 | it.sad_pattern() | ||
77 | } | ||
78 | }) | ||
70 | .unwrap_or_else(|| make::wildcard_pat().into()); | 79 | .unwrap_or_else(|| make::wildcard_pat().into()); |
71 | let else_expr = unwrap_trivial_block(else_block); | 80 | let else_expr = unwrap_trivial_block(else_block); |
72 | make::match_arm(vec![pattern], else_expr) | 81 | make::match_arm(vec![pattern], else_expr) |
@@ -279,6 +288,36 @@ fn foo(x: Option<i32>) { | |||
279 | } | 288 | } |
280 | 289 | ||
281 | #[test] | 290 | #[test] |
291 | fn special_case_inverted_option() { | ||
292 | check_assist( | ||
293 | replace_if_let_with_match, | ||
294 | r#" | ||
295 | enum Option<T> { Some(T), None } | ||
296 | use Option::*; | ||
297 | |||
298 | fn foo(x: Option<i32>) { | ||
299 | $0if let None = x { | ||
300 | println!("none") | ||
301 | } else { | ||
302 | println!("some") | ||
303 | } | ||
304 | } | ||
305 | "#, | ||
306 | r#" | ||
307 | enum Option<T> { Some(T), None } | ||
308 | use Option::*; | ||
309 | |||
310 | fn foo(x: Option<i32>) { | ||
311 | match x { | ||
312 | None => println!("none"), | ||
313 | Some(_) => println!("some"), | ||
314 | } | ||
315 | } | ||
316 | "#, | ||
317 | ); | ||
318 | } | ||
319 | |||
320 | #[test] | ||
282 | fn special_case_result() { | 321 | fn special_case_result() { |
283 | check_assist( | 322 | check_assist( |
284 | replace_if_let_with_match, | 323 | replace_if_let_with_match, |
@@ -309,6 +348,36 @@ fn foo(x: Result<i32, ()>) { | |||
309 | } | 348 | } |
310 | 349 | ||
311 | #[test] | 350 | #[test] |
351 | fn special_case_inverted_result() { | ||
352 | check_assist( | ||
353 | replace_if_let_with_match, | ||
354 | r#" | ||
355 | enum Result<T, E> { Ok(T), Err(E) } | ||
356 | use Result::*; | ||
357 | |||
358 | fn foo(x: Result<i32, ()>) { | ||
359 | $0if let Err(x) = x { | ||
360 | println!("{}", x) | ||
361 | } else { | ||
362 | println!("ok") | ||
363 | } | ||
364 | } | ||
365 | "#, | ||
366 | r#" | ||
367 | enum Result<T, E> { Ok(T), Err(E) } | ||
368 | use Result::*; | ||
369 | |||
370 | fn foo(x: Result<i32, ()>) { | ||
371 | match x { | ||
372 | Err(x) => println!("{}", x), | ||
373 | Ok(_) => println!("ok"), | ||
374 | } | ||
375 | } | ||
376 | "#, | ||
377 | ); | ||
378 | } | ||
379 | |||
380 | #[test] | ||
312 | fn nested_indent() { | 381 | fn nested_indent() { |
313 | check_assist( | 382 | check_assist( |
314 | replace_if_let_with_match, | 383 | replace_if_let_with_match, |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index 44c35bafa..4e762e18b 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -248,3 +248,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
248 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { | 248 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { |
249 | [Direction::Next, Direction::Prev].iter().copied() | 249 | [Direction::Next, Direction::Prev].iter().copied() |
250 | } | 250 | } |
251 | |||
252 | pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool { | ||
253 | let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text()); | ||
254 | |||
255 | let pat_head = match pat { | ||
256 | ast::Pat::IdentPat(bind_pat) => { | ||
257 | if let Some(p) = bind_pat.pat() { | ||
258 | first_node_text(&p) | ||
259 | } else { | ||
260 | return pat.syntax().text() == var.syntax().text(); | ||
261 | } | ||
262 | } | ||
263 | pat => first_node_text(pat), | ||
264 | }; | ||
265 | |||
266 | let var_head = first_node_text(var); | ||
267 | |||
268 | pat_head == var_head | ||
269 | } | ||
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs index eeb952ec3..8ec4ac65e 100644 --- a/crates/completion/src/item.rs +++ b/crates/completion/src/item.rs | |||
@@ -10,7 +10,7 @@ use ide_db::{ | |||
10 | }, | 10 | }, |
11 | SymbolKind, | 11 | SymbolKind, |
12 | }; | 12 | }; |
13 | use stdx::{assert_never, impl_from}; | 13 | use stdx::{impl_from, never}; |
14 | use syntax::{algo, TextRange}; | 14 | use syntax::{algo, TextRange}; |
15 | use text_edit::TextEdit; | 15 | use text_edit::TextEdit; |
16 | 16 | ||
@@ -404,7 +404,7 @@ impl Builder { | |||
404 | pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { | 404 | pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { |
405 | self.detail = detail.map(Into::into); | 405 | self.detail = detail.map(Into::into); |
406 | if let Some(detail) = &self.detail { | 406 | if let Some(detail) = &self.detail { |
407 | if assert_never!(detail.contains('\n'), "multiline detail:\n{}", detail) { | 407 | if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { |
408 | self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string()); | 408 | self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string()); |
409 | } | 409 | } |
410 | } | 410 | } |
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index a87c80b8a..aef7e1f6c 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -2,9 +2,9 @@ | |||
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; | 4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; |
5 | use hir_expand::{db::AstDatabase, AstId, HirFileId}; | 5 | use hir_expand::{db::AstDatabase, HirFileId}; |
6 | use la_arena::ArenaMap; | 6 | use la_arena::ArenaMap; |
7 | use syntax::{ast, SmolStr}; | 7 | use syntax::SmolStr; |
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | adt::{EnumData, StructData}, | 10 | adt::{EnumData, StructData}, |
@@ -16,9 +16,10 @@ use crate::{ | |||
16 | item_tree::ItemTree, | 16 | item_tree::ItemTree, |
17 | lang_item::{LangItemTarget, LangItems}, | 17 | lang_item::{LangItemTarget, LangItems}, |
18 | nameres::DefMap, | 18 | nameres::DefMap, |
19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 19 | AttrDefId, BlockId, BlockLoc, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, |
20 | GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, | 20 | FunctionLoc, GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, |
21 | StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, | 21 | StaticLoc, StructId, StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, |
22 | UnionLoc, VariantId, | ||
22 | }; | 23 | }; |
23 | 24 | ||
24 | #[salsa::query_group(InternDatabaseStorage)] | 25 | #[salsa::query_group(InternDatabaseStorage)] |
@@ -41,6 +42,8 @@ pub trait InternDatabase: SourceDatabase { | |||
41 | fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; | 42 | fn intern_type_alias(&self, loc: TypeAliasLoc) -> TypeAliasId; |
42 | #[salsa::interned] | 43 | #[salsa::interned] |
43 | fn intern_impl(&self, loc: ImplLoc) -> ImplId; | 44 | fn intern_impl(&self, loc: ImplLoc) -> ImplId; |
45 | #[salsa::interned] | ||
46 | fn intern_block(&self, loc: BlockLoc) -> BlockId; | ||
44 | } | 47 | } |
45 | 48 | ||
46 | #[salsa::query_group(DefDatabaseStorage)] | 49 | #[salsa::query_group(DefDatabaseStorage)] |
@@ -56,7 +59,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
56 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; | 59 | fn crate_def_map_query(&self, krate: CrateId) -> Arc<DefMap>; |
57 | 60 | ||
58 | #[salsa::invoke(DefMap::block_def_map_query)] | 61 | #[salsa::invoke(DefMap::block_def_map_query)] |
59 | fn block_def_map(&self, krate: CrateId, block: AstId<ast::BlockExpr>) -> Arc<DefMap>; | 62 | fn block_def_map(&self, block: BlockId) -> Arc<DefMap>; |
60 | 63 | ||
61 | #[salsa::invoke(StructData::struct_data_query)] | 64 | #[salsa::invoke(StructData::struct_data_query)] |
62 | fn struct_data(&self, id: StructId) -> Arc<StructData>; | 65 | fn struct_data(&self, id: StructId) -> Arc<StructData>; |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index cf09ebd3f..42b50b5b7 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -74,12 +74,16 @@ use stdx::impl_from; | |||
74 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 74 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
75 | pub struct ModuleId { | 75 | pub struct ModuleId { |
76 | krate: CrateId, | 76 | krate: CrateId, |
77 | block: Option<BlockId>, | ||
77 | pub local_id: LocalModuleId, | 78 | pub local_id: LocalModuleId, |
78 | } | 79 | } |
79 | 80 | ||
80 | impl ModuleId { | 81 | impl ModuleId { |
81 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { | 82 | pub fn def_map(&self, db: &dyn db::DefDatabase) -> Arc<DefMap> { |
82 | db.crate_def_map(self.krate) | 83 | match self.block { |
84 | Some(block) => db.block_def_map(block), | ||
85 | None => db.crate_def_map(self.krate), | ||
86 | } | ||
83 | } | 87 | } |
84 | 88 | ||
85 | pub fn krate(&self) -> CrateId { | 89 | pub fn krate(&self) -> CrateId { |
@@ -230,6 +234,15 @@ pub struct ImplId(salsa::InternId); | |||
230 | type ImplLoc = ItemLoc<Impl>; | 234 | type ImplLoc = ItemLoc<Impl>; |
231 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); | 235 | impl_intern!(ImplId, ImplLoc, intern_impl, lookup_intern_impl); |
232 | 236 | ||
237 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] | ||
238 | pub struct BlockId(salsa::InternId); | ||
239 | #[derive(Debug, Hash, PartialEq, Eq, Clone)] | ||
240 | pub struct BlockLoc { | ||
241 | ast_id: AstId<ast::BlockExpr>, | ||
242 | module: ModuleId, | ||
243 | } | ||
244 | impl_intern!(BlockId, BlockLoc, intern_block, lookup_intern_block); | ||
245 | |||
233 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 246 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
234 | pub struct TypeParamId { | 247 | pub struct TypeParamId { |
235 | pub parent: GenericDefId, | 248 | pub parent: GenericDefId, |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 4fbbecb38..199771e9a 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -62,7 +62,7 @@ use la_arena::Arena; | |||
62 | use profile::Count; | 62 | use profile::Count; |
63 | use rustc_hash::FxHashMap; | 63 | use rustc_hash::FxHashMap; |
64 | use stdx::format_to; | 64 | use stdx::format_to; |
65 | use syntax::{ast, AstNode}; | 65 | use syntax::ast; |
66 | 66 | ||
67 | use crate::{ | 67 | use crate::{ |
68 | db::DefDatabase, | 68 | db::DefDatabase, |
@@ -70,14 +70,14 @@ use crate::{ | |||
70 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, | 70 | nameres::{diagnostics::DefDiagnostic, path_resolution::ResolveMode}, |
71 | path::ModPath, | 71 | path::ModPath, |
72 | per_ns::PerNs, | 72 | per_ns::PerNs, |
73 | AstId, LocalModuleId, ModuleDefId, ModuleId, | 73 | AstId, BlockId, BlockLoc, LocalModuleId, ModuleDefId, ModuleId, |
74 | }; | 74 | }; |
75 | 75 | ||
76 | /// Contains all top-level defs from a macro-expanded crate | 76 | /// Contains all top-level defs from a macro-expanded crate |
77 | #[derive(Debug, PartialEq, Eq)] | 77 | #[derive(Debug, PartialEq, Eq)] |
78 | pub struct DefMap { | 78 | pub struct DefMap { |
79 | _c: Count<Self>, | 79 | _c: Count<Self>, |
80 | parent: Option<Arc<DefMap>>, | 80 | block: Option<BlockInfo>, |
81 | root: LocalModuleId, | 81 | root: LocalModuleId, |
82 | modules: Arena<ModuleData>, | 82 | modules: Arena<ModuleData>, |
83 | krate: CrateId, | 83 | krate: CrateId, |
@@ -91,6 +91,13 @@ pub struct DefMap { | |||
91 | diagnostics: Vec<DefDiagnostic>, | 91 | diagnostics: Vec<DefDiagnostic>, |
92 | } | 92 | } |
93 | 93 | ||
94 | #[derive(Debug, PartialEq, Eq)] | ||
95 | struct BlockInfo { | ||
96 | block: BlockId, | ||
97 | parent: Arc<DefMap>, | ||
98 | parent_module: LocalModuleId, | ||
99 | } | ||
100 | |||
94 | impl std::ops::Index<LocalModuleId> for DefMap { | 101 | impl std::ops::Index<LocalModuleId> for DefMap { |
95 | type Output = ModuleData; | 102 | type Output = ModuleData; |
96 | fn index(&self, id: LocalModuleId) -> &ModuleData { | 103 | fn index(&self, id: LocalModuleId) -> &ModuleData { |
@@ -190,15 +197,12 @@ impl DefMap { | |||
190 | Arc::new(def_map) | 197 | Arc::new(def_map) |
191 | } | 198 | } |
192 | 199 | ||
193 | pub(crate) fn block_def_map_query( | 200 | pub(crate) fn block_def_map_query(db: &dyn DefDatabase, block_id: BlockId) -> Arc<DefMap> { |
194 | db: &dyn DefDatabase, | 201 | let block: BlockLoc = db.lookup_intern_block(block_id); |
195 | krate: CrateId, | 202 | let item_tree = db.item_tree(block.ast_id.file_id); |
196 | block: AstId<ast::BlockExpr>, | 203 | let block_items = item_tree.inner_items_of_block(block.ast_id.value); |
197 | ) -> Arc<DefMap> { | ||
198 | let item_tree = db.item_tree(block.file_id); | ||
199 | let block_items = item_tree.inner_items_of_block(block.value); | ||
200 | 204 | ||
201 | let parent = parent_def_map(db, krate, block); | 205 | let parent = block.module.def_map(db); |
202 | 206 | ||
203 | if block_items.is_empty() { | 207 | if block_items.is_empty() { |
204 | // If there are no inner items, nothing new is brought into scope, so we can just return | 208 | // If there are no inner items, nothing new is brought into scope, so we can just return |
@@ -206,10 +210,13 @@ impl DefMap { | |||
206 | return parent; | 210 | return parent; |
207 | } | 211 | } |
208 | 212 | ||
209 | let mut def_map = DefMap::empty(krate, parent.edition); | 213 | let block_info = |
210 | def_map.parent = Some(parent); | 214 | BlockInfo { block: block_id, parent, parent_module: block.module.local_id }; |
215 | |||
216 | let mut def_map = DefMap::empty(block.module.krate, block_info.parent.edition); | ||
217 | def_map.block = Some(block_info); | ||
211 | 218 | ||
212 | let def_map = collector::collect_defs(db, def_map, Some(block.value)); | 219 | let def_map = collector::collect_defs(db, def_map, Some(block.ast_id.value)); |
213 | Arc::new(def_map) | 220 | Arc::new(def_map) |
214 | } | 221 | } |
215 | 222 | ||
@@ -218,7 +225,7 @@ impl DefMap { | |||
218 | let root = modules.alloc(ModuleData::default()); | 225 | let root = modules.alloc(ModuleData::default()); |
219 | DefMap { | 226 | DefMap { |
220 | _c: Count::new(), | 227 | _c: Count::new(), |
221 | parent: None, | 228 | block: None, |
222 | krate, | 229 | krate, |
223 | edition, | 230 | edition, |
224 | extern_prelude: FxHashMap::default(), | 231 | extern_prelude: FxHashMap::default(), |
@@ -266,7 +273,8 @@ impl DefMap { | |||
266 | } | 273 | } |
267 | 274 | ||
268 | pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { | 275 | pub fn module_id(&self, local_id: LocalModuleId) -> ModuleId { |
269 | ModuleId { krate: self.krate, local_id } | 276 | let block = self.block.as_ref().map(|b| b.block); |
277 | ModuleId { krate: self.krate, local_id, block } | ||
270 | } | 278 | } |
271 | 279 | ||
272 | pub(crate) fn resolve_path( | 280 | pub(crate) fn resolve_path( |
@@ -286,9 +294,9 @@ impl DefMap { | |||
286 | pub fn dump(&self) -> String { | 294 | pub fn dump(&self) -> String { |
287 | let mut buf = String::new(); | 295 | let mut buf = String::new(); |
288 | let mut current_map = self; | 296 | let mut current_map = self; |
289 | while let Some(parent) = ¤t_map.parent { | 297 | while let Some(block) = ¤t_map.block { |
290 | go(&mut buf, current_map, "block scope", current_map.root); | 298 | go(&mut buf, current_map, "block scope", current_map.root); |
291 | current_map = &**parent; | 299 | current_map = &*block.parent; |
292 | } | 300 | } |
293 | go(&mut buf, current_map, "crate", current_map.root); | 301 | go(&mut buf, current_map, "crate", current_map.root); |
294 | return buf; | 302 | return buf; |
@@ -342,35 +350,6 @@ impl ModuleData { | |||
342 | } | 350 | } |
343 | } | 351 | } |
344 | 352 | ||
345 | fn parent_def_map( | ||
346 | db: &dyn DefDatabase, | ||
347 | krate: CrateId, | ||
348 | block: AstId<ast::BlockExpr>, | ||
349 | ) -> Arc<DefMap> { | ||
350 | // FIXME: store this info in the item tree instead of reparsing here | ||
351 | let ast_id_map = db.ast_id_map(block.file_id); | ||
352 | let block_ptr = ast_id_map.get(block.value); | ||
353 | let root = match db.parse_or_expand(block.file_id) { | ||
354 | Some(it) => it, | ||
355 | None => { | ||
356 | return Arc::new(DefMap::empty(krate, Edition::Edition2018)); | ||
357 | } | ||
358 | }; | ||
359 | let ast = block_ptr.to_node(&root); | ||
360 | |||
361 | for ancestor in ast.syntax().ancestors().skip(1) { | ||
362 | if let Some(block_expr) = ast::BlockExpr::cast(ancestor) { | ||
363 | let ancestor_id = ast_id_map.ast_id(&block_expr); | ||
364 | let ast_id = InFile::new(block.file_id, ancestor_id); | ||
365 | let parent_map = db.block_def_map(krate, ast_id); | ||
366 | return parent_map; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | // No enclosing block scope, so the parent is the crate-level DefMap. | ||
371 | db.crate_def_map(krate) | ||
372 | } | ||
373 | |||
374 | #[derive(Debug, Clone, PartialEq, Eq)] | 353 | #[derive(Debug, Clone, PartialEq, Eq)] |
375 | pub enum ModuleSource { | 354 | pub enum ModuleSource { |
376 | SourceFile(ast::SourceFile), | 355 | SourceFile(ast::SourceFile), |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index c1eded5f2..419e465ed 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -10,8 +10,6 @@ | |||
10 | //! | 10 | //! |
11 | //! `ReachedFixedPoint` signals about this. | 11 | //! `ReachedFixedPoint` signals about this. |
12 | 12 | ||
13 | use std::iter::successors; | ||
14 | |||
15 | use base_db::Edition; | 13 | use base_db::Edition; |
16 | use hir_expand::name; | 14 | use hir_expand::name; |
17 | use hir_expand::name::Name; | 15 | use hir_expand::name::Name; |
@@ -131,8 +129,8 @@ impl DefMap { | |||
131 | result.krate = result.krate.or(new.krate); | 129 | result.krate = result.krate.or(new.krate); |
132 | result.segment_index = result.segment_index.min(new.segment_index); | 130 | result.segment_index = result.segment_index.min(new.segment_index); |
133 | 131 | ||
134 | match ¤t_map.parent { | 132 | match ¤t_map.block { |
135 | Some(map) => current_map = map, | 133 | Some(block) => current_map = &block.parent, |
136 | None => return result, | 134 | None => return result, |
137 | } | 135 | } |
138 | } | 136 | } |
@@ -193,14 +191,35 @@ impl DefMap { | |||
193 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) | 191 | self.resolve_name_in_module(db, original_module, &segment, prefer_module) |
194 | } | 192 | } |
195 | PathKind::Super(lvl) => { | 193 | PathKind::Super(lvl) => { |
196 | let m = successors(Some(original_module), |m| self.modules[*m].parent) | 194 | let mut module = original_module; |
197 | .nth(lvl as usize); | 195 | for i in 0..lvl { |
198 | if let Some(local_id) = m { | 196 | match self.modules[module].parent { |
199 | PerNs::types(self.module_id(local_id).into(), Visibility::Public) | 197 | Some(it) => module = it, |
200 | } else { | 198 | None => match &self.block { |
201 | log::debug!("super path in root module"); | 199 | Some(block) => { |
202 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | 200 | // Look up remaining path in parent `DefMap` |
201 | let new_path = ModPath { | ||
202 | kind: PathKind::Super(lvl - i), | ||
203 | segments: path.segments.clone(), | ||
204 | }; | ||
205 | log::debug!("`super` path: {} -> {} in parent map", path, new_path); | ||
206 | return block.parent.resolve_path_fp_with_macro( | ||
207 | db, | ||
208 | mode, | ||
209 | block.parent_module, | ||
210 | &new_path, | ||
211 | shadow, | ||
212 | ); | ||
213 | } | ||
214 | None => { | ||
215 | log::debug!("super path in root module"); | ||
216 | return ResolvePathResult::empty(ReachedFixedPoint::Yes); | ||
217 | } | ||
218 | }, | ||
219 | } | ||
203 | } | 220 | } |
221 | |||
222 | PerNs::types(self.module_id(module).into(), Visibility::Public) | ||
204 | } | 223 | } |
205 | PathKind::Abs => { | 224 | PathKind::Abs => { |
206 | // 2018-style absolute path -- only extern prelude | 225 | // 2018-style absolute path -- only extern prelude |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 73e3a4702..b36d0b59b 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -8,12 +8,12 @@ mod block; | |||
8 | 8 | ||
9 | use std::sync::Arc; | 9 | use std::sync::Arc; |
10 | 10 | ||
11 | use base_db::{fixture::WithFixture, SourceDatabase}; | 11 | use base_db::{fixture::WithFixture, FilePosition, SourceDatabase}; |
12 | use expect_test::{expect, Expect}; | 12 | use expect_test::{expect, Expect}; |
13 | use hir_expand::db::AstDatabase; | 13 | use syntax::AstNode; |
14 | use test_utils::mark; | 14 | use test_utils::mark; |
15 | 15 | ||
16 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; | 16 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB, Lookup}; |
17 | 17 | ||
18 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | 18 | fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { |
19 | let db = TestDB::with_files(ra_fixture); | 19 | let db = TestDB::with_files(ra_fixture); |
@@ -23,14 +23,58 @@ fn compute_crate_def_map(ra_fixture: &str) -> Arc<DefMap> { | |||
23 | 23 | ||
24 | fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> { | 24 | fn compute_block_def_map(ra_fixture: &str) -> Arc<DefMap> { |
25 | let (db, position) = TestDB::with_position(ra_fixture); | 25 | let (db, position) = TestDB::with_position(ra_fixture); |
26 | |||
27 | // FIXME: perhaps we should make this use body lowering tests instead? | ||
28 | |||
26 | let module = db.module_for_file(position.file_id); | 29 | let module = db.module_for_file(position.file_id); |
27 | let ast_map = db.ast_id_map(position.file_id.into()); | 30 | let mut def_map = db.crate_def_map(module.krate); |
28 | let ast = db.parse(position.file_id); | 31 | while let Some(new_def_map) = descend_def_map_at_position(&db, position, def_map.clone()) { |
29 | let block: ast::BlockExpr = | 32 | def_map = new_def_map; |
30 | syntax::algo::find_node_at_offset(&ast.syntax_node(), position.offset).unwrap(); | 33 | } |
31 | let block_id = ast_map.ast_id(&block); | 34 | |
35 | // FIXME: select the right module, not the root | ||
36 | |||
37 | def_map | ||
38 | } | ||
39 | |||
40 | fn descend_def_map_at_position( | ||
41 | db: &dyn DefDatabase, | ||
42 | position: FilePosition, | ||
43 | def_map: Arc<DefMap>, | ||
44 | ) -> Option<Arc<DefMap>> { | ||
45 | for (local_id, module_data) in def_map.modules() { | ||
46 | let mod_def = module_data.origin.definition_source(db); | ||
47 | let ast_map = db.ast_id_map(mod_def.file_id); | ||
48 | let item_tree = db.item_tree(mod_def.file_id); | ||
49 | let root = db.parse_or_expand(mod_def.file_id).unwrap(); | ||
50 | for item in module_data.scope.declarations() { | ||
51 | match item { | ||
52 | ModuleDefId::FunctionId(it) => { | ||
53 | // Technically blocks can be inside any type (due to arrays and const generics), | ||
54 | // and also in const/static initializers. For tests we only really care about | ||
55 | // functions though. | ||
56 | |||
57 | let ast = ast_map.get(item_tree[it.lookup(db).id.value].ast_id).to_node(&root); | ||
58 | |||
59 | if ast.syntax().text_range().contains(position.offset) { | ||
60 | // Cursor inside function, descend into its body's DefMap. | ||
61 | // Note that we don't handle block *expressions* inside function bodies. | ||
62 | let ast_map = db.ast_id_map(position.file_id.into()); | ||
63 | let ast_id = ast_map.ast_id(&ast.body().unwrap()); | ||
64 | let block = BlockLoc { | ||
65 | ast_id: InFile::new(position.file_id.into(), ast_id), | ||
66 | module: def_map.module_id(local_id), | ||
67 | }; | ||
68 | let block_id = db.intern_block(block); | ||
69 | return Some(db.block_def_map(block_id)); | ||
70 | } | ||
71 | } | ||
72 | _ => continue, | ||
73 | } | ||
74 | } | ||
75 | } | ||
32 | 76 | ||
33 | db.block_def_map(module.krate, InFile::new(position.file_id.into(), block_id)) | 77 | None |
34 | } | 78 | } |
35 | 79 | ||
36 | fn check(ra_fixture: &str, expect: Expect) { | 80 | fn check(ra_fixture: &str, expect: Expect) { |
diff --git a/crates/hir_def/src/nameres/tests/block.rs b/crates/hir_def/src/nameres/tests/block.rs index 01d6326a7..470ca593e 100644 --- a/crates/hir_def/src/nameres/tests/block.rs +++ b/crates/hir_def/src/nameres/tests/block.rs | |||
@@ -95,3 +95,29 @@ fn outer() { | |||
95 | "#]], | 95 | "#]], |
96 | ); | 96 | ); |
97 | } | 97 | } |
98 | |||
99 | #[test] | ||
100 | fn super_imports() { | ||
101 | check_at( | ||
102 | r#" | ||
103 | mod module { | ||
104 | fn f() { | ||
105 | use super::Struct; | ||
106 | $0 | ||
107 | } | ||
108 | } | ||
109 | |||
110 | struct Struct {} | ||
111 | "#, | ||
112 | expect![[r#" | ||
113 | block scope | ||
114 | Struct: t | ||
115 | crate | ||
116 | Struct: t | ||
117 | module: t | ||
118 | |||
119 | crate::module | ||
120 | f: v | ||
121 | "#]], | ||
122 | ); | ||
123 | } | ||
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index f178dd744..23d885218 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -435,13 +435,16 @@ impl TryToNav for hir::TypeParam { | |||
435 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { | 435 | fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { |
436 | let src = self.source(db)?; | 436 | let src = self.source(db)?; |
437 | let full_range = match &src.value { | 437 | let full_range = match &src.value { |
438 | Either::Left(it) => it.syntax().text_range(), | 438 | Either::Left(it) => it |
439 | .name() | ||
440 | .map_or_else(|| it.syntax().text_range(), |name| name.syntax().text_range()), | ||
439 | Either::Right(it) => it.syntax().text_range(), | 441 | Either::Right(it) => it.syntax().text_range(), |
440 | }; | 442 | }; |
441 | let focus_range = match &src.value { | 443 | let focus_range = match &src.value { |
442 | Either::Left(_) => None, | 444 | Either::Left(it) => it.name(), |
443 | Either::Right(it) => it.name().map(|it| it.syntax().text_range()), | 445 | Either::Right(it) => it.name(), |
444 | }; | 446 | } |
447 | .map(|it| it.syntax().text_range()); | ||
445 | Some(NavigationTarget { | 448 | Some(NavigationTarget { |
446 | file_id: src.file_id.original_file(db), | 449 | file_id: src.file_id.original_file(db), |
447 | name: self.name(db).to_string().into(), | 450 | name: self.name(db).to_string().into(), |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 3a4f4d80b..40d9487eb 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -1098,4 +1098,20 @@ fn foo<const FOO$0: usize>() -> usize { | |||
1098 | "#]], | 1098 | "#]], |
1099 | ); | 1099 | ); |
1100 | } | 1100 | } |
1101 | |||
1102 | #[test] | ||
1103 | fn test_find_self_ty_in_trait_def() { | ||
1104 | check( | ||
1105 | r#" | ||
1106 | trait Foo { | ||
1107 | fn f() -> Self$0; | ||
1108 | } | ||
1109 | "#, | ||
1110 | expect![[r#" | ||
1111 | Self TypeParam FileId(0) 6..9 6..9 Other | ||
1112 | |||
1113 | FileId(0) 26..30 Other | ||
1114 | "#]], | ||
1115 | ); | ||
1116 | } | ||
1101 | } | 1117 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index c25bcce50..99a558532 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -9,7 +9,7 @@ use ide_db::{ | |||
9 | search::FileReference, | 9 | search::FileReference, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use stdx::assert_never; | 12 | use stdx::never; |
13 | use syntax::{ | 13 | use syntax::{ |
14 | ast::{self, NameOwner}, | 14 | ast::{self, NameOwner}, |
15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, | 15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, |
@@ -285,7 +285,7 @@ fn rename_mod( | |||
285 | } | 285 | } |
286 | 286 | ||
287 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { | 287 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { |
288 | if assert_never!(local.is_self(sema.db)) { | 288 | if never!(local.is_self(sema.db)) { |
289 | bail!("rename_to_self invoked on self"); | 289 | bail!("rename_to_self invoked on self"); |
290 | } | 290 | } |
291 | 291 | ||
@@ -388,7 +388,7 @@ fn rename_self_to_param( | |||
388 | let (file_id, self_param) = match local.source(sema.db) { | 388 | let (file_id, self_param) = match local.source(sema.db) { |
389 | InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param), | 389 | InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param), |
390 | _ => { | 390 | _ => { |
391 | assert_never!(true, "rename_self_to_param invoked on a non-self local"); | 391 | never!(true, "rename_self_to_param invoked on a non-self local"); |
392 | bail!("rename_self_to_param invoked on a non-self local"); | 392 | bail!("rename_self_to_param invoked on a non-self local"); |
393 | } | 393 | } |
394 | }; | 394 | }; |
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs index b1f87731b..f76bac151 100644 --- a/crates/ide_db/src/source_change.rs +++ b/crates/ide_db/src/source_change.rs | |||
@@ -10,7 +10,7 @@ use std::{ | |||
10 | 10 | ||
11 | use base_db::{AnchoredPathBuf, FileId}; | 11 | use base_db::{AnchoredPathBuf, FileId}; |
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use stdx::assert_never; | 13 | use stdx::never; |
14 | use text_edit::TextEdit; | 14 | use text_edit::TextEdit; |
15 | 15 | ||
16 | #[derive(Default, Debug, Clone)] | 16 | #[derive(Default, Debug, Clone)] |
@@ -40,10 +40,7 @@ impl SourceChange { | |||
40 | pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) { | 40 | pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) { |
41 | match self.source_file_edits.entry(file_id) { | 41 | match self.source_file_edits.entry(file_id) { |
42 | Entry::Occupied(mut entry) => { | 42 | Entry::Occupied(mut entry) => { |
43 | assert_never!( | 43 | never!(entry.get_mut().union(edit).is_err(), "overlapping edits for same file"); |
44 | entry.get_mut().union(edit).is_err(), | ||
45 | "overlapping edits for same file" | ||
46 | ); | ||
47 | } | 44 | } |
48 | Entry::Vacant(entry) => { | 45 | Entry::Vacant(entry) => { |
49 | entry.insert(edit); | 46 | entry.insert(edit); |
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/mbe/Cargo.toml b/crates/mbe/Cargo.toml index af80e2be3..43bc10490 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml | |||
@@ -17,6 +17,5 @@ log = "0.4.8" | |||
17 | syntax = { path = "../syntax", version = "0.0.0" } | 17 | syntax = { path = "../syntax", version = "0.0.0" } |
18 | parser = { path = "../parser", version = "0.0.0" } | 18 | parser = { path = "../parser", version = "0.0.0" } |
19 | tt = { path = "../tt", version = "0.0.0" } | 19 | tt = { path = "../tt", version = "0.0.0" } |
20 | |||
21 | [dev-dependencies] | ||
22 | test_utils = { path = "../test_utils" } | 20 | test_utils = { path = "../test_utils" } |
21 | |||
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 19543d777..35cde5f10 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -14,6 +14,7 @@ mod tests; | |||
14 | 14 | ||
15 | use std::fmt; | 15 | use std::fmt; |
16 | 16 | ||
17 | use test_utils::mark; | ||
17 | pub use tt::{Delimiter, DelimiterKind, Punct}; | 18 | pub use tt::{Delimiter, DelimiterKind, Punct}; |
18 | 19 | ||
19 | use crate::{ | 20 | use crate::{ |
@@ -76,6 +77,14 @@ pub struct MacroRules { | |||
76 | shift: Shift, | 77 | shift: Shift, |
77 | } | 78 | } |
78 | 79 | ||
80 | /// For Macro 2.0 | ||
81 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
82 | pub struct MacroDef { | ||
83 | rules: Vec<Rule>, | ||
84 | /// Highest id of the token we have in TokenMap | ||
85 | shift: Shift, | ||
86 | } | ||
87 | |||
79 | #[derive(Clone, Debug, PartialEq, Eq)] | 88 | #[derive(Clone, Debug, PartialEq, Eq)] |
80 | struct Rule { | 89 | struct Rule { |
81 | lhs: MetaTemplate, | 90 | lhs: MetaTemplate, |
@@ -179,7 +188,7 @@ impl MacroRules { | |||
179 | let mut src = TtIter::new(tt); | 188 | let mut src = TtIter::new(tt); |
180 | let mut rules = Vec::new(); | 189 | let mut rules = Vec::new(); |
181 | while src.len() > 0 { | 190 | while src.len() > 0 { |
182 | let rule = Rule::parse(&mut src)?; | 191 | let rule = Rule::parse(&mut src, true)?; |
183 | rules.push(rule); | 192 | rules.push(rule); |
184 | if let Err(()) = src.expect_char(';') { | 193 | if let Err(()) = src.expect_char(';') { |
185 | if src.len() > 0 { | 194 | if src.len() > 0 { |
@@ -200,7 +209,58 @@ impl MacroRules { | |||
200 | // apply shift | 209 | // apply shift |
201 | let mut tt = tt.clone(); | 210 | let mut tt = tt.clone(); |
202 | self.shift.shift_all(&mut tt); | 211 | self.shift.shift_all(&mut tt); |
203 | mbe_expander::expand(self, &tt) | 212 | mbe_expander::expand_rules(&self.rules, &tt) |
213 | } | ||
214 | |||
215 | pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { | ||
216 | self.shift.shift(id) | ||
217 | } | ||
218 | |||
219 | pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, Origin) { | ||
220 | match self.shift.unshift(id) { | ||
221 | Some(id) => (id, Origin::Call), | ||
222 | None => (id, Origin::Def), | ||
223 | } | ||
224 | } | ||
225 | } | ||
226 | |||
227 | impl MacroDef { | ||
228 | pub fn parse(tt: &tt::Subtree) -> Result<MacroDef, ParseError> { | ||
229 | let mut src = TtIter::new(tt); | ||
230 | let mut rules = Vec::new(); | ||
231 | |||
232 | if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { | ||
233 | mark::hit!(parse_macro_def_rules); | ||
234 | while src.len() > 0 { | ||
235 | let rule = Rule::parse(&mut src, true)?; | ||
236 | rules.push(rule); | ||
237 | if let Err(()) = src.expect_char(';') { | ||
238 | if src.len() > 0 { | ||
239 | return Err(ParseError::Expected("expected `;`".to_string())); | ||
240 | } | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | } else { | ||
245 | mark::hit!(parse_macro_def_simple); | ||
246 | let rule = Rule::parse(&mut src, false)?; | ||
247 | if src.len() != 0 { | ||
248 | return Err(ParseError::Expected("remain tokens in macro def".to_string())); | ||
249 | } | ||
250 | rules.push(rule); | ||
251 | } | ||
252 | for rule in rules.iter() { | ||
253 | validate(&rule.lhs)?; | ||
254 | } | ||
255 | |||
256 | Ok(MacroDef { rules, shift: Shift::new(tt) }) | ||
257 | } | ||
258 | |||
259 | pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> { | ||
260 | // apply shift | ||
261 | let mut tt = tt.clone(); | ||
262 | self.shift.shift_all(&mut tt); | ||
263 | mbe_expander::expand_rules(&self.rules, &tt) | ||
204 | } | 264 | } |
205 | 265 | ||
206 | pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { | 266 | pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { |
@@ -216,12 +276,14 @@ impl MacroRules { | |||
216 | } | 276 | } |
217 | 277 | ||
218 | impl Rule { | 278 | impl Rule { |
219 | fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { | 279 | fn parse(src: &mut TtIter, expect_arrow: bool) -> Result<Rule, ParseError> { |
220 | let lhs = src | 280 | let lhs = src |
221 | .expect_subtree() | 281 | .expect_subtree() |
222 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; | 282 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
223 | src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; | 283 | if expect_arrow { |
224 | src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; | 284 | src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; |
285 | src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; | ||
286 | } | ||
225 | let rhs = src | 287 | let rhs = src |
226 | .expect_subtree() | 288 | .expect_subtree() |
227 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; | 289 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
diff --git a/crates/mbe/src/mbe_expander.rs b/crates/mbe/src/mbe_expander.rs index a80b73db4..802c8fb0f 100644 --- a/crates/mbe/src/mbe_expander.rs +++ b/crates/mbe/src/mbe_expander.rs | |||
@@ -10,11 +10,10 @@ use syntax::SmolStr; | |||
10 | 10 | ||
11 | use crate::{ExpandError, ExpandResult}; | 11 | use crate::{ExpandError, ExpandResult}; |
12 | 12 | ||
13 | pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> { | 13 | pub(crate) fn expand_rules( |
14 | expand_rules(&rules.rules, input) | 14 | rules: &[crate::Rule], |
15 | } | 15 | input: &tt::Subtree, |
16 | 16 | ) -> ExpandResult<tt::Subtree> { | |
17 | fn expand_rules(rules: &[crate::Rule], input: &tt::Subtree) -> ExpandResult<tt::Subtree> { | ||
18 | let mut match_: Option<(matcher::Match, &crate::Rule)> = None; | 17 | let mut match_: Option<(matcher::Match, &crate::Rule)> = None; |
19 | for rule in rules { | 18 | for rule in rules { |
20 | let new_match = match matcher::match_(&rule.lhs, input) { | 19 | let new_match = match matcher::match_(&rule.lhs, input) { |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index bd2977ebd..8d978163d 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -6,7 +6,7 @@ use syntax::{ | |||
6 | SyntaxKind::{ERROR, IDENT}, | 6 | SyntaxKind::{ERROR, IDENT}, |
7 | SyntaxNode, WalkEvent, T, | 7 | SyntaxNode, WalkEvent, T, |
8 | }; | 8 | }; |
9 | use test_utils::assert_eq_text; | 9 | use test_utils::{assert_eq_text, mark}; |
10 | 10 | ||
11 | use super::*; | 11 | use super::*; |
12 | 12 | ||
@@ -675,6 +675,36 @@ fn test_match_literal() { | |||
675 | .assert_expand_items("foo! ['('];", "fn foo () {}"); | 675 | .assert_expand_items("foo! ['('];", "fn foo () {}"); |
676 | } | 676 | } |
677 | 677 | ||
678 | #[test] | ||
679 | fn test_parse_macro_def_simple() { | ||
680 | mark::check!(parse_macro_def_simple); | ||
681 | |||
682 | parse_macro2( | ||
683 | r#" | ||
684 | macro foo($id:ident) { | ||
685 | fn $id() {} | ||
686 | } | ||
687 | "#, | ||
688 | ) | ||
689 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
690 | } | ||
691 | |||
692 | #[test] | ||
693 | fn test_parse_macro_def_rules() { | ||
694 | mark::check!(parse_macro_def_rules); | ||
695 | |||
696 | parse_macro2( | ||
697 | r#" | ||
698 | macro foo { | ||
699 | ($id:ident) => { | ||
700 | fn $id() {} | ||
701 | } | ||
702 | } | ||
703 | "#, | ||
704 | ) | ||
705 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
706 | } | ||
707 | |||
678 | // The following tests are port from intellij-rust directly | 708 | // The following tests are port from intellij-rust directly |
679 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | 709 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt |
680 | 710 | ||
@@ -1699,95 +1729,122 @@ pub(crate) struct MacroFixture { | |||
1699 | rules: MacroRules, | 1729 | rules: MacroRules, |
1700 | } | 1730 | } |
1701 | 1731 | ||
1702 | impl MacroFixture { | 1732 | pub(crate) struct MacroFixture2 { |
1703 | pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { | 1733 | rules: MacroDef, |
1704 | self.try_expand_tt(invocation).unwrap() | 1734 | } |
1705 | } | ||
1706 | |||
1707 | fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> { | ||
1708 | let source_file = ast::SourceFile::parse(invocation).tree(); | ||
1709 | let macro_invocation = | ||
1710 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1711 | |||
1712 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()) | ||
1713 | .ok_or_else(|| ExpandError::ConversionError)?; | ||
1714 | 1735 | ||
1715 | self.rules.expand(&invocation_tt).result() | 1736 | macro_rules! impl_fixture { |
1716 | } | 1737 | ($name:ident) => { |
1738 | impl $name { | ||
1739 | pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { | ||
1740 | self.try_expand_tt(invocation).unwrap() | ||
1741 | } | ||
1717 | 1742 | ||
1718 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { | 1743 | fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> { |
1719 | assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); | 1744 | let source_file = ast::SourceFile::parse(invocation).tree(); |
1720 | } | 1745 | let macro_invocation = |
1746 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1721 | 1747 | ||
1722 | fn expand_items(&self, invocation: &str) -> SyntaxNode { | 1748 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()) |
1723 | let expanded = self.expand_tt(invocation); | 1749 | .ok_or_else(|| ExpandError::ConversionError)?; |
1724 | token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node() | ||
1725 | } | ||
1726 | 1750 | ||
1727 | fn expand_statements(&self, invocation: &str) -> SyntaxNode { | 1751 | self.rules.expand(&invocation_tt).result() |
1728 | let expanded = self.expand_tt(invocation); | 1752 | } |
1729 | token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node() | ||
1730 | } | ||
1731 | 1753 | ||
1732 | fn expand_expr(&self, invocation: &str) -> SyntaxNode { | 1754 | #[allow(unused)] |
1733 | let expanded = self.expand_tt(invocation); | 1755 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { |
1734 | token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() | 1756 | assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); |
1735 | } | 1757 | } |
1736 | 1758 | ||
1737 | fn assert_expand_tt(&self, invocation: &str, expected: &str) { | 1759 | #[allow(unused)] |
1738 | let expansion = self.expand_tt(invocation); | 1760 | fn expand_items(&self, invocation: &str) -> SyntaxNode { |
1739 | assert_eq!(expansion.to_string(), expected); | 1761 | let expanded = self.expand_tt(invocation); |
1740 | } | 1762 | token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node() |
1763 | } | ||
1741 | 1764 | ||
1742 | fn assert_expand(&self, invocation: &str, expected: &str) { | 1765 | #[allow(unused)] |
1743 | let expansion = self.expand_tt(invocation); | 1766 | fn expand_statements(&self, invocation: &str) -> SyntaxNode { |
1744 | let actual = format!("{:?}", expansion); | 1767 | let expanded = self.expand_tt(invocation); |
1745 | test_utils::assert_eq_text!(&expected.trim(), &actual.trim()); | 1768 | token_tree_to_syntax_node(&expanded, FragmentKind::Statements) |
1746 | } | 1769 | .unwrap() |
1770 | .0 | ||
1771 | .syntax_node() | ||
1772 | } | ||
1747 | 1773 | ||
1748 | fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { | 1774 | #[allow(unused)] |
1749 | self.assert_expansion(FragmentKind::Items, invocation, expected); | 1775 | fn expand_expr(&self, invocation: &str) -> SyntaxNode { |
1750 | self | 1776 | let expanded = self.expand_tt(invocation); |
1751 | } | 1777 | token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() |
1778 | } | ||
1752 | 1779 | ||
1753 | fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &MacroFixture { | 1780 | #[allow(unused)] |
1754 | self.assert_expansion(FragmentKind::Statements, invocation, expected); | 1781 | fn assert_expand_tt(&self, invocation: &str, expected: &str) { |
1755 | self | 1782 | let expansion = self.expand_tt(invocation); |
1756 | } | 1783 | assert_eq!(expansion.to_string(), expected); |
1784 | } | ||
1757 | 1785 | ||
1758 | fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { | 1786 | #[allow(unused)] |
1759 | let expanded = self.expand_tt(invocation); | 1787 | fn assert_expand(&self, invocation: &str, expected: &str) { |
1760 | assert_eq!(expanded.to_string(), expected); | 1788 | let expansion = self.expand_tt(invocation); |
1761 | 1789 | let actual = format!("{:?}", expansion); | |
1762 | let expected = expected.replace("$crate", "C_C__C"); | 1790 | test_utils::assert_eq_text!(&expected.trim(), &actual.trim()); |
1763 | 1791 | } | |
1764 | // wrap the given text to a macro call | ||
1765 | let expected = { | ||
1766 | let wrapped = format!("wrap_macro!( {} )", expected); | ||
1767 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
1768 | let wrapped = | ||
1769 | wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
1770 | let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; | ||
1771 | wrapped.delimiter = None; | ||
1772 | wrapped | ||
1773 | }; | ||
1774 | 1792 | ||
1775 | let expanded_tree = token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); | 1793 | fn assert_expand_items(&self, invocation: &str, expected: &str) -> &$name { |
1776 | let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); | 1794 | self.assert_expansion(FragmentKind::Items, invocation, expected); |
1795 | self | ||
1796 | } | ||
1777 | 1797 | ||
1778 | let expected_tree = token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); | 1798 | #[allow(unused)] |
1779 | let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); | 1799 | fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &$name { |
1800 | self.assert_expansion(FragmentKind::Statements, invocation, expected); | ||
1801 | self | ||
1802 | } | ||
1780 | 1803 | ||
1781 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | 1804 | fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { |
1782 | assert_eq!( | 1805 | let expanded = self.expand_tt(invocation); |
1783 | expanded_tree, expected_tree, | 1806 | assert_eq!(expanded.to_string(), expected); |
1784 | "\nleft:\n{}\nright:\n{}", | 1807 | |
1785 | expanded_tree, expected_tree, | 1808 | let expected = expected.replace("$crate", "C_C__C"); |
1786 | ); | 1809 | |
1787 | } | 1810 | // wrap the given text to a macro call |
1811 | let expected = { | ||
1812 | let wrapped = format!("wrap_macro!( {} )", expected); | ||
1813 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
1814 | let wrapped = wrapped | ||
1815 | .tree() | ||
1816 | .syntax() | ||
1817 | .descendants() | ||
1818 | .find_map(ast::TokenTree::cast) | ||
1819 | .unwrap(); | ||
1820 | let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; | ||
1821 | wrapped.delimiter = None; | ||
1822 | wrapped | ||
1823 | }; | ||
1824 | |||
1825 | let expanded_tree = | ||
1826 | token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); | ||
1827 | let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); | ||
1828 | |||
1829 | let expected_tree = | ||
1830 | token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); | ||
1831 | let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); | ||
1832 | |||
1833 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | ||
1834 | assert_eq!( | ||
1835 | expanded_tree, expected_tree, | ||
1836 | "\nleft:\n{}\nright:\n{}", | ||
1837 | expanded_tree, expected_tree, | ||
1838 | ); | ||
1839 | } | ||
1840 | } | ||
1841 | }; | ||
1788 | } | 1842 | } |
1789 | 1843 | ||
1790 | fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { | 1844 | impl_fixture!(MacroFixture); |
1845 | impl_fixture!(MacroFixture2); | ||
1846 | |||
1847 | fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree { | ||
1791 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | 1848 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1792 | let macro_definition = | 1849 | let macro_definition = |
1793 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); | 1850 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); |
@@ -1804,14 +1861,36 @@ fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { | |||
1804 | definition_tt | 1861 | definition_tt |
1805 | } | 1862 | } |
1806 | 1863 | ||
1864 | fn parse_macro_def_to_tt(ra_fixture: &str) -> tt::Subtree { | ||
1865 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | ||
1866 | let macro_definition = | ||
1867 | source_file.syntax().descendants().find_map(ast::MacroDef::cast).unwrap(); | ||
1868 | |||
1869 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.body().unwrap()).unwrap(); | ||
1870 | |||
1871 | let parsed = | ||
1872 | parse_to_token_tree(&ra_fixture[macro_definition.body().unwrap().syntax().text_range()]) | ||
1873 | .unwrap() | ||
1874 | .0; | ||
1875 | assert_eq!(definition_tt, parsed); | ||
1876 | |||
1877 | definition_tt | ||
1878 | } | ||
1879 | |||
1807 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | 1880 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { |
1808 | let definition_tt = parse_macro_to_tt(ra_fixture); | 1881 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); |
1809 | let rules = MacroRules::parse(&definition_tt).unwrap(); | 1882 | let rules = MacroRules::parse(&definition_tt).unwrap(); |
1810 | MacroFixture { rules } | 1883 | MacroFixture { rules } |
1811 | } | 1884 | } |
1812 | 1885 | ||
1886 | pub(crate) fn parse_macro2(ra_fixture: &str) -> MacroFixture2 { | ||
1887 | let definition_tt = parse_macro_def_to_tt(ra_fixture); | ||
1888 | let rules = MacroDef::parse(&definition_tt).unwrap(); | ||
1889 | MacroFixture2 { rules } | ||
1890 | } | ||
1891 | |||
1813 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { | 1892 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { |
1814 | let definition_tt = parse_macro_to_tt(ra_fixture); | 1893 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); |
1815 | 1894 | ||
1816 | match MacroRules::parse(&definition_tt) { | 1895 | match MacroRules::parse(&definition_tt) { |
1817 | Ok(_) => panic!("Expect error"), | 1896 | Ok(_) => panic!("Expect error"), |
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 1b259682d..22011cb33 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs | |||
@@ -6,6 +6,7 @@ use std::{ | |||
6 | path::{Component, Path, PathBuf}, | 6 | path::{Component, Path, PathBuf}, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | /// Wrapper around an absolute [`PathBuf`]. | ||
9 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 10 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
10 | pub struct AbsPathBuf(PathBuf); | 11 | pub struct AbsPathBuf(PathBuf); |
11 | 12 | ||
@@ -58,18 +59,33 @@ impl PartialEq<AbsPath> for AbsPathBuf { | |||
58 | } | 59 | } |
59 | 60 | ||
60 | impl AbsPathBuf { | 61 | impl AbsPathBuf { |
62 | /// Wrap the given absolute path in `AbsPathBuf` | ||
63 | /// | ||
64 | /// # Panics | ||
65 | /// | ||
66 | /// Panics if `path` is not absolute. | ||
61 | pub fn assert(path: PathBuf) -> AbsPathBuf { | 67 | pub fn assert(path: PathBuf) -> AbsPathBuf { |
62 | AbsPathBuf::try_from(path) | 68 | AbsPathBuf::try_from(path) |
63 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) | 69 | .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) |
64 | } | 70 | } |
71 | |||
72 | /// Coerces to a `AbsPath` slice. | ||
73 | /// | ||
74 | /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`. | ||
65 | pub fn as_path(&self) -> &AbsPath { | 75 | pub fn as_path(&self) -> &AbsPath { |
66 | AbsPath::assert(self.0.as_path()) | 76 | AbsPath::assert(self.0.as_path()) |
67 | } | 77 | } |
78 | |||
79 | /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`. | ||
80 | /// | ||
81 | /// Note that this won't remove the root component, so `self` will still be | ||
82 | /// absolute. | ||
68 | pub fn pop(&mut self) -> bool { | 83 | pub fn pop(&mut self) -> bool { |
69 | self.0.pop() | 84 | self.0.pop() |
70 | } | 85 | } |
71 | } | 86 | } |
72 | 87 | ||
88 | /// Wrapper around an absolute [`Path`]. | ||
73 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 89 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
74 | #[repr(transparent)] | 90 | #[repr(transparent)] |
75 | pub struct AbsPath(Path); | 91 | pub struct AbsPath(Path); |
@@ -98,28 +114,56 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath { | |||
98 | } | 114 | } |
99 | 115 | ||
100 | impl AbsPath { | 116 | impl AbsPath { |
117 | /// Wrap the given absolute path in `AbsPath` | ||
118 | /// | ||
119 | /// # Panics | ||
120 | /// | ||
121 | /// Panics if `path` is not absolute. | ||
101 | pub fn assert(path: &Path) -> &AbsPath { | 122 | pub fn assert(path: &Path) -> &AbsPath { |
102 | assert!(path.is_absolute()); | 123 | assert!(path.is_absolute()); |
103 | unsafe { &*(path as *const Path as *const AbsPath) } | 124 | unsafe { &*(path as *const Path as *const AbsPath) } |
104 | } | 125 | } |
105 | 126 | ||
127 | /// Equivalent of [`Path::parent`] for `AbsPath`. | ||
106 | pub fn parent(&self) -> Option<&AbsPath> { | 128 | pub fn parent(&self) -> Option<&AbsPath> { |
107 | self.0.parent().map(AbsPath::assert) | 129 | self.0.parent().map(AbsPath::assert) |
108 | } | 130 | } |
131 | |||
132 | /// Equivalent of [`Path::join`] for `AbsPath`. | ||
109 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { | 133 | pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { |
110 | self.as_ref().join(path).try_into().unwrap() | 134 | self.as_ref().join(path).try_into().unwrap() |
111 | } | 135 | } |
136 | |||
137 | /// Normalize the given path: | ||
138 | /// - Removes repeated separators: `/a//b` becomes `/a/b` | ||
139 | /// - Removes occurrences of `.` and resolves `..`. | ||
140 | /// - Removes trailing slashes: `/a/b/` becomes `/a/b`. | ||
141 | /// | ||
142 | /// # Example | ||
143 | /// ``` | ||
144 | /// # use paths::AbsPathBuf; | ||
145 | /// let abs_path_buf = AbsPathBuf::assert("/a/../../b/.//c//".into()); | ||
146 | /// let normalized = abs_path_buf.normalize(); | ||
147 | /// assert_eq!(normalized, AbsPathBuf::assert("/b/c".into())); | ||
148 | /// ``` | ||
112 | pub fn normalize(&self) -> AbsPathBuf { | 149 | pub fn normalize(&self) -> AbsPathBuf { |
113 | AbsPathBuf(normalize_path(&self.0)) | 150 | AbsPathBuf(normalize_path(&self.0)) |
114 | } | 151 | } |
152 | |||
153 | /// Equivalent of [`Path::to_path_buf`] for `AbsPath`. | ||
115 | pub fn to_path_buf(&self) -> AbsPathBuf { | 154 | pub fn to_path_buf(&self) -> AbsPathBuf { |
116 | AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() | 155 | AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() |
117 | } | 156 | } |
157 | |||
158 | /// Equivalent of [`Path::strip_prefix`] for `AbsPath`. | ||
159 | /// | ||
160 | /// Returns a relative path. | ||
118 | pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { | 161 | pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { |
119 | self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) | 162 | self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) |
120 | } | 163 | } |
121 | } | 164 | } |
122 | 165 | ||
166 | /// Wrapper around a relative [`PathBuf`]. | ||
123 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] | 167 | #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] |
124 | pub struct RelPathBuf(PathBuf); | 168 | pub struct RelPathBuf(PathBuf); |
125 | 169 | ||
@@ -160,11 +204,15 @@ impl TryFrom<&str> for RelPathBuf { | |||
160 | } | 204 | } |
161 | 205 | ||
162 | impl RelPathBuf { | 206 | impl RelPathBuf { |
207 | /// Coerces to a `RelPath` slice. | ||
208 | /// | ||
209 | /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`. | ||
163 | pub fn as_path(&self) -> &RelPath { | 210 | pub fn as_path(&self) -> &RelPath { |
164 | RelPath::new_unchecked(self.0.as_path()) | 211 | RelPath::new_unchecked(self.0.as_path()) |
165 | } | 212 | } |
166 | } | 213 | } |
167 | 214 | ||
215 | /// Wrapper around a relative [`Path`]. | ||
168 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] | 216 | #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] |
169 | #[repr(transparent)] | 217 | #[repr(transparent)] |
170 | pub struct RelPath(Path); | 218 | pub struct RelPath(Path); |
@@ -183,12 +231,13 @@ impl AsRef<Path> for RelPath { | |||
183 | } | 231 | } |
184 | 232 | ||
185 | impl RelPath { | 233 | impl RelPath { |
234 | /// Creates a new `RelPath` from `path`, without checking if it is relative. | ||
186 | pub fn new_unchecked(path: &Path) -> &RelPath { | 235 | pub fn new_unchecked(path: &Path) -> &RelPath { |
187 | unsafe { &*(path as *const Path as *const RelPath) } | 236 | unsafe { &*(path as *const Path as *const RelPath) } |
188 | } | 237 | } |
189 | } | 238 | } |
190 | 239 | ||
191 | // https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 | 240 | /// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 |
192 | fn normalize_path(path: &Path) -> PathBuf { | 241 | fn normalize_path(path: &Path) -> PathBuf { |
193 | let mut components = path.components().peekable(); | 242 | let mut components = path.components().peekable(); |
194 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { | 243 | let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 268c00942..82ea76666 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -37,6 +37,7 @@ lsp-server = "0.5.0" | |||
37 | tracing = "0.1" | 37 | tracing = "0.1" |
38 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 38 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } |
39 | tracing-tree = { version = "0.1.4" } | 39 | tracing-tree = { version = "0.1.4" } |
40 | always-assert = "0.1" | ||
40 | 41 | ||
41 | stdx = { path = "../stdx", version = "0.0.0" } | 42 | stdx = { path = "../stdx", version = "0.0.0" } |
42 | flycheck = { path = "../flycheck", version = "0.0.0" } | 43 | flycheck = { path = "../flycheck", version = "0.0.0" } |
@@ -72,3 +73,4 @@ tt = { path = "../tt" } | |||
72 | 73 | ||
73 | [features] | 74 | [features] |
74 | jemalloc = ["jemallocator", "profile/jemalloc"] | 75 | jemalloc = ["jemallocator", "profile/jemalloc"] |
76 | force-always-assert = ["always-assert/force"] | ||
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 1789d6a73..088b17b03 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -86,15 +86,6 @@ fn setup_logging(log_file: Option<PathBuf>, no_buffering: bool) -> Result<()> { | |||
86 | 86 | ||
87 | profile::init(); | 87 | profile::init(); |
88 | 88 | ||
89 | if !cfg!(debug_assertions) { | ||
90 | stdx::set_assert_hook(|loc, args| { | ||
91 | if env::var("RA_PROFILE").is_ok() { | ||
92 | panic!("assertion failed at {}: {}", loc, args) | ||
93 | } | ||
94 | log::error!("assertion failed at {}: {}", loc, args) | ||
95 | }); | ||
96 | } | ||
97 | |||
98 | Ok(()) | 89 | Ok(()) |
99 | } | 90 | } |
100 | 91 | ||
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 247bfe71e..071fde64d 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -39,7 +39,7 @@ config_data! { | |||
39 | /// Automatically refresh project info via `cargo metadata` on | 39 | /// Automatically refresh project info via `cargo metadata` on |
40 | /// `Cargo.toml` changes. | 40 | /// `Cargo.toml` changes. |
41 | cargo_autoreload: bool = "true", | 41 | cargo_autoreload: bool = "true", |
42 | /// Activate all available features. | 42 | /// Activate all available features (`--all-features`). |
43 | cargo_allFeatures: bool = "false", | 43 | cargo_allFeatures: bool = "false", |
44 | /// List of features to activate. | 44 | /// List of features to activate. |
45 | cargo_features: Vec<String> = "[]", | 45 | cargo_features: Vec<String> = "[]", |
@@ -55,10 +55,10 @@ config_data! { | |||
55 | 55 | ||
56 | /// Run specified `cargo check` command for diagnostics on save. | 56 | /// Run specified `cargo check` command for diagnostics on save. |
57 | checkOnSave_enable: bool = "true", | 57 | checkOnSave_enable: bool = "true", |
58 | /// Check with all features (will be passed as `--all-features`). | 58 | /// Check with all features (`--all-features`). |
59 | /// Defaults to `#rust-analyzer.cargo.allFeatures#`. | 59 | /// Defaults to `#rust-analyzer.cargo.allFeatures#`. |
60 | checkOnSave_allFeatures: Option<bool> = "null", | 60 | checkOnSave_allFeatures: Option<bool> = "null", |
61 | /// Check all targets and tests (will be passed as `--all-targets`). | 61 | /// Check all targets and tests (`--all-targets`). |
62 | checkOnSave_allTargets: bool = "true", | 62 | checkOnSave_allTargets: bool = "true", |
63 | /// Cargo command to use for `cargo check`. | 63 | /// Cargo command to use for `cargo check`. |
64 | checkOnSave_command: String = "\"check\"", | 64 | checkOnSave_command: String = "\"check\"", |
@@ -156,7 +156,7 @@ config_data! { | |||
156 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. | 156 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. |
157 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", | 157 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", |
158 | 158 | ||
159 | /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. | 159 | /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. |
160 | lruCapacity: Option<usize> = "null", | 160 | lruCapacity: Option<usize> = "null", |
161 | 161 | ||
162 | /// Whether to show `can't find Cargo.toml` error message. | 162 | /// Whether to show `can't find Cargo.toml` error message. |
@@ -844,15 +844,32 @@ mod tests { | |||
844 | fn schema_in_sync_with_package_json() { | 844 | fn schema_in_sync_with_package_json() { |
845 | let s = Config::json_schema(); | 845 | let s = Config::json_schema(); |
846 | let schema = format!("{:#}", s); | 846 | let schema = format!("{:#}", s); |
847 | let schema = schema.trim_start_matches('{').trim_end_matches('}'); | 847 | let mut schema = schema |
848 | 848 | .trim_start_matches('{') | |
849 | let package_json = project_dir().join("editors/code/package.json"); | 849 | .trim_end_matches('}') |
850 | let package_json = fs::read_to_string(&package_json).unwrap(); | 850 | .replace(" ", " ") |
851 | 851 | .replace("\n", "\n ") | |
852 | let p = remove_ws(&package_json); | 852 | .trim_start_matches('\n') |
853 | .trim_end() | ||
854 | .to_string(); | ||
855 | schema.push_str(",\n"); | ||
856 | |||
857 | let package_json_path = project_dir().join("editors/code/package.json"); | ||
858 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); | ||
859 | |||
860 | let start_marker = " \"$generated-start\": false,\n"; | ||
861 | let end_marker = " \"$generated-end\": false\n"; | ||
862 | |||
863 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); | ||
864 | let end = package_json.find(end_marker).unwrap(); | ||
865 | let p = remove_ws(&package_json[start..end]); | ||
853 | let s = remove_ws(&schema); | 866 | let s = remove_ws(&schema); |
854 | 867 | ||
855 | assert!(p.contains(&s), "update config in package.json. New config:\n{:#}", schema); | 868 | if !p.contains(&s) { |
869 | package_json.replace_range(start..end, &schema); | ||
870 | fs::write(&package_json_path, &mut package_json).unwrap(); | ||
871 | panic!("new config, updating package.json") | ||
872 | } | ||
856 | } | 873 | } |
857 | 874 | ||
858 | #[test] | 875 | #[test] |
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index c47e8d0a8..5866c0a28 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml | |||
@@ -11,6 +11,7 @@ doctest = false | |||
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | backtrace = { version = "0.3.44", optional = true } | 13 | backtrace = { version = "0.3.44", optional = true } |
14 | always-assert = { version = "0.1.1", features = ["log"] } | ||
14 | # Think twice before adding anything here | 15 | # Think twice before adding anything here |
15 | 16 | ||
16 | [features] | 17 | [features] |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index d42817078..d26be4853 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -4,7 +4,7 @@ use std::{cmp::Ordering, ops, process, time::Instant}; | |||
4 | mod macros; | 4 | mod macros; |
5 | pub mod panic_context; | 5 | pub mod panic_context; |
6 | 6 | ||
7 | pub use crate::macros::{on_assert_failure, set_assert_hook}; | 7 | pub use always_assert::{always, never}; |
8 | 8 | ||
9 | #[inline(always)] | 9 | #[inline(always)] |
10 | pub fn is_ci() -> bool { | 10 | pub fn is_ci() -> bool { |
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs index 4f5c6100d..d91fc690c 100644 --- a/crates/stdx/src/macros.rs +++ b/crates/stdx/src/macros.rs | |||
@@ -1,9 +1,5 @@ | |||
1 | //! Convenience macros. | 1 | //! Convenience macros. |
2 | 2 | ||
3 | use std::{ | ||
4 | fmt, mem, panic, | ||
5 | sync::atomic::{AtomicUsize, Ordering::SeqCst}, | ||
6 | }; | ||
7 | #[macro_export] | 3 | #[macro_export] |
8 | macro_rules! eprintln { | 4 | macro_rules! eprintln { |
9 | ($($tt:tt)*) => {{ | 5 | ($($tt:tt)*) => {{ |
@@ -49,50 +45,3 @@ macro_rules! impl_from { | |||
49 | )* | 45 | )* |
50 | } | 46 | } |
51 | } | 47 | } |
52 | |||
53 | /// A version of `assert!` macro which allows to handle an assertion failure. | ||
54 | /// | ||
55 | /// In release mode, it returns the condition and logs an error. | ||
56 | /// | ||
57 | /// ``` | ||
58 | /// if assert_never!(impossible) { | ||
59 | /// // Heh, this shouldn't have happened, but lets try to soldier on... | ||
60 | /// return None; | ||
61 | /// } | ||
62 | /// ``` | ||
63 | /// | ||
64 | /// Rust analyzer is a long-running process, and crashing really isn't an option. | ||
65 | /// | ||
66 | /// Shamelessly stolen from: https://www.sqlite.org/assert.html | ||
67 | #[macro_export] | ||
68 | macro_rules! assert_never { | ||
69 | ($cond:expr) => { $crate::assert_never!($cond, "") }; | ||
70 | ($cond:expr, $($fmt:tt)*) => {{ | ||
71 | let value = $cond; | ||
72 | if value { | ||
73 | $crate::on_assert_failure( | ||
74 | format_args!($($fmt)*) | ||
75 | ); | ||
76 | } | ||
77 | value | ||
78 | }}; | ||
79 | } | ||
80 | |||
81 | type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>); | ||
82 | static HOOK: AtomicUsize = AtomicUsize::new(0); | ||
83 | |||
84 | pub fn set_assert_hook(hook: AssertHook) { | ||
85 | HOOK.store(hook as usize, SeqCst); | ||
86 | } | ||
87 | |||
88 | #[cold] | ||
89 | #[track_caller] | ||
90 | pub fn on_assert_failure(args: fmt::Arguments) { | ||
91 | let hook: usize = HOOK.load(SeqCst); | ||
92 | if hook == 0 { | ||
93 | panic!("\n assertion failed: {}\n", args); | ||
94 | } | ||
95 | |||
96 | let hook: AssertHook = unsafe { mem::transmute::<usize, AssertHook>(hook) }; | ||
97 | hook(panic::Location::caller(), args) | ||
98 | } | ||