diff options
Diffstat (limited to 'crates')
27 files changed, 551 insertions, 222 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/body.rs b/crates/hir_def/src/body.rs index d0c84ab0b..b9ecf22fa 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -17,6 +17,7 @@ use hir_expand::{ | |||
17 | HirFileId, InFile, MacroDefId, | 17 | HirFileId, InFile, MacroDefId, |
18 | }; | 18 | }; |
19 | use la_arena::{Arena, ArenaMap}; | 19 | use la_arena::{Arena, ArenaMap}; |
20 | use profile::Count; | ||
20 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
21 | use syntax::{ast, AstNode, AstPtr}; | 22 | use syntax::{ast, AstNode, AstPtr}; |
22 | use test_utils::mark; | 23 | use test_utils::mark; |
@@ -237,6 +238,7 @@ pub struct Body { | |||
237 | /// The `ExprId` of the actual body expression. | 238 | /// The `ExprId` of the actual body expression. |
238 | pub body_expr: ExprId, | 239 | pub body_expr: ExprId, |
239 | pub item_scope: ItemScope, | 240 | pub item_scope: ItemScope, |
241 | _c: Count<Self>, | ||
240 | } | 242 | } |
241 | 243 | ||
242 | pub type ExprPtr = AstPtr<ast::Expr>; | 244 | pub type ExprPtr = AstPtr<ast::Expr>; |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 4ce5e5b72..209965fca 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -10,6 +10,7 @@ use hir_expand::{ | |||
10 | ExpandError, HirFileId, MacroDefId, MacroDefKind, | 10 | ExpandError, HirFileId, MacroDefId, MacroDefKind, |
11 | }; | 11 | }; |
12 | use la_arena::Arena; | 12 | use la_arena::Arena; |
13 | use profile::Count; | ||
13 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
14 | use syntax::{ | 15 | use syntax::{ |
15 | ast::{ | 16 | ast::{ |
@@ -77,6 +78,7 @@ pub(super) fn lower( | |||
77 | params: Vec::new(), | 78 | params: Vec::new(), |
78 | body_expr: dummy_expr_id(), | 79 | body_expr: dummy_expr_id(), |
79 | item_scope: Default::default(), | 80 | item_scope: Default::default(), |
81 | _c: Count::new(), | ||
80 | }, | 82 | }, |
81 | item_trees: { | 83 | item_trees: { |
82 | let mut map = FxHashMap::default(); | 84 | let mut map = FxHashMap::default(); |
diff --git a/crates/hir_expand/src/ast_id_map.rs b/crates/hir_expand/src/ast_id_map.rs index 0991fffd8..16cf29907 100644 --- a/crates/hir_expand/src/ast_id_map.rs +++ b/crates/hir_expand/src/ast_id_map.rs | |||
@@ -13,6 +13,7 @@ use std::{ | |||
13 | }; | 13 | }; |
14 | 14 | ||
15 | use la_arena::{Arena, Idx}; | 15 | use la_arena::{Arena, Idx}; |
16 | use profile::Count; | ||
16 | use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; | 17 | use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; |
17 | 18 | ||
18 | /// `AstId` points to an AST node in a specific file. | 19 | /// `AstId` points to an AST node in a specific file. |
@@ -62,12 +63,13 @@ type ErasedFileAstId = Idx<SyntaxNodePtr>; | |||
62 | #[derive(Debug, PartialEq, Eq, Default)] | 63 | #[derive(Debug, PartialEq, Eq, Default)] |
63 | pub struct AstIdMap { | 64 | pub struct AstIdMap { |
64 | arena: Arena<SyntaxNodePtr>, | 65 | arena: Arena<SyntaxNodePtr>, |
66 | _c: Count<Self>, | ||
65 | } | 67 | } |
66 | 68 | ||
67 | impl AstIdMap { | 69 | impl AstIdMap { |
68 | pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap { | 70 | pub(crate) fn from_source(node: &SyntaxNode) -> AstIdMap { |
69 | assert!(node.parent().is_none()); | 71 | assert!(node.parent().is_none()); |
70 | let mut res = AstIdMap { arena: Arena::default() }; | 72 | let mut res = AstIdMap::default(); |
71 | // By walking the tree in breadth-first order we make sure that parents | 73 | // By walking the tree in breadth-first order we make sure that parents |
72 | // get lower ids then children. That is, adding a new child does not | 74 | // get lower ids then children. That is, adding a new child does not |
73 | // change parent's id. This means that, say, adding a new function to a | 75 | // change parent's id. This means that, say, adding a new function to a |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index db42a00dc..c7502bf57 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,9 +17,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.50", default-features = false } | 20 | chalk-solve = { version = "0.51", default-features = false } |
21 | chalk-ir = "0.50" | 21 | chalk-ir = "0.51" |
22 | chalk-recursive = "0.50" | 22 | chalk-recursive = "0.51" |
23 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 23 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
24 | 24 | ||
25 | stdx = { path = "../stdx", version = "0.0.0" } | 25 | stdx = { path = "../stdx", version = "0.0.0" } |
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/profile/Cargo.toml b/crates/profile/Cargo.toml index 4dd9acc98..486f9c164 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml | |||
@@ -14,7 +14,7 @@ once_cell = "1.3.1" | |||
14 | cfg-if = "1" | 14 | cfg-if = "1" |
15 | libc = "0.2.73" | 15 | libc = "0.2.73" |
16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
17 | countme = { version = "2.0.0", features = ["enable"] } | 17 | countme = { version = "2.0.1", features = ["enable"] } |
18 | jemalloc-ctl = { version = "0.3.3", optional = true } | 18 | jemalloc-ctl = { version = "0.3.3", optional = true } |
19 | 19 | ||
20 | [target.'cfg(target_os = "linux")'.dependencies] | 20 | [target.'cfg(target_os = "linux")'.dependencies] |
diff --git a/crates/project_model/src/build_data.rs b/crates/project_model/src/build_data.rs index cf32995e0..3ff347e2c 100644 --- a/crates/project_model/src/build_data.rs +++ b/crates/project_model/src/build_data.rs | |||
@@ -175,7 +175,7 @@ fn is_dylib(path: &Path) -> bool { | |||
175 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> | 175 | /// Should be synced with <https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates> |
176 | fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { | 176 | fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { |
177 | // FIXME: Missing variables: | 177 | // FIXME: Missing variables: |
178 | // CARGO, CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name> | 178 | // CARGO_PKG_HOMEPAGE, CARGO_CRATE_NAME, CARGO_BIN_NAME, CARGO_BIN_EXE_<name> |
179 | 179 | ||
180 | let mut manifest_dir = package.manifest_path.clone(); | 180 | let mut manifest_dir = package.manifest_path.clone(); |
181 | manifest_dir.pop(); | 181 | manifest_dir.pop(); |
@@ -183,6 +183,9 @@ fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, St | |||
183 | env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into())); | 183 | env.push(("CARGO_MANIFEST_DIR".into(), cargo_manifest_dir.into())); |
184 | } | 184 | } |
185 | 185 | ||
186 | // Not always right, but works for common cases. | ||
187 | env.push(("CARGO".into(), "cargo".into())); | ||
188 | |||
186 | env.push(("CARGO_PKG_VERSION".into(), package.version.to_string())); | 189 | env.push(("CARGO_PKG_VERSION".into(), package.version.to_string())); |
187 | env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string())); | 190 | env.push(("CARGO_PKG_VERSION_MAJOR".into(), package.version.major.to_string())); |
188 | env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string())); | 191 | env.push(("CARGO_PKG_VERSION_MINOR".into(), package.version.minor.to_string())); |
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/args.rs b/crates/rust-analyzer/src/bin/args.rs index 7d917946e..2a532361d 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs | |||
@@ -14,7 +14,10 @@ use vfs::AbsPathBuf; | |||
14 | pub(crate) struct Args { | 14 | pub(crate) struct Args { |
15 | pub(crate) verbosity: Verbosity, | 15 | pub(crate) verbosity: Verbosity, |
16 | pub(crate) log_file: Option<PathBuf>, | 16 | pub(crate) log_file: Option<PathBuf>, |
17 | pub(crate) no_buffering: bool, | ||
17 | pub(crate) command: Command, | 18 | pub(crate) command: Command, |
19 | #[allow(unused)] | ||
20 | pub(crate) wait_dbg: bool, | ||
18 | } | 21 | } |
19 | 22 | ||
20 | pub(crate) enum Command { | 23 | pub(crate) enum Command { |
@@ -47,11 +50,17 @@ FLAGS: | |||
47 | -vv, --spammy | 50 | -vv, --spammy |
48 | -q, --quiet Set verbosity | 51 | -q, --quiet Set verbosity |
49 | 52 | ||
50 | --log-file <PATH> Log to the specified filed instead of stderr | 53 | --log-file <PATH> Log to the specified file instead of stderr |
54 | --no-log-buffering | ||
55 | Flush log records to the file immediately | ||
56 | |||
57 | --wait-dbg Wait until a debugger is attached to. | ||
58 | The flag is valid for debug builds only | ||
51 | 59 | ||
52 | ENVIRONMENTAL VARIABLES: | 60 | ENVIRONMENTAL VARIABLES: |
53 | RA_LOG Set log filter in env_logger format | 61 | RA_LOG Set log filter in env_logger format |
54 | RA_PROFILE Enable hierarchical profiler | 62 | RA_PROFILE Enable hierarchical profiler |
63 | RA_WAIT_DBG If set acts like a --wait-dbg flag | ||
55 | 64 | ||
56 | COMMANDS: | 65 | COMMANDS: |
57 | 66 | ||
@@ -114,6 +123,8 @@ impl Args { | |||
114 | verbosity: Verbosity::Normal, | 123 | verbosity: Verbosity::Normal, |
115 | log_file: None, | 124 | log_file: None, |
116 | command: Command::Version, | 125 | command: Command::Version, |
126 | no_buffering: false, | ||
127 | wait_dbg: false, | ||
117 | }); | 128 | }); |
118 | } | 129 | } |
119 | 130 | ||
@@ -130,21 +141,41 @@ impl Args { | |||
130 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), | 141 | (false, true, true) => bail!("Invalid flags: -q conflicts with -v"), |
131 | }; | 142 | }; |
132 | let log_file = matches.opt_value_from_str("--log-file")?; | 143 | let log_file = matches.opt_value_from_str("--log-file")?; |
144 | let no_buffering = matches.contains("--no-log-buffering"); | ||
145 | let wait_dbg = matches.contains("--wait-dbg"); | ||
133 | 146 | ||
134 | if matches.contains(["-h", "--help"]) { | 147 | if matches.contains(["-h", "--help"]) { |
135 | eprintln!("{}", HELP); | 148 | eprintln!("{}", HELP); |
136 | return Ok(Args { verbosity, log_file: None, command: Command::Help }); | 149 | return Ok(Args { |
150 | verbosity, | ||
151 | log_file: None, | ||
152 | command: Command::Help, | ||
153 | no_buffering, | ||
154 | wait_dbg, | ||
155 | }); | ||
137 | } | 156 | } |
138 | 157 | ||
139 | if matches.contains("--print-config-schema") { | 158 | if matches.contains("--print-config-schema") { |
140 | return Ok(Args { verbosity, log_file, command: Command::PrintConfigSchema }); | 159 | return Ok(Args { |
160 | verbosity, | ||
161 | log_file, | ||
162 | command: Command::PrintConfigSchema, | ||
163 | no_buffering, | ||
164 | wait_dbg, | ||
165 | }); | ||
141 | } | 166 | } |
142 | 167 | ||
143 | let subcommand = match matches.subcommand()? { | 168 | let subcommand = match matches.subcommand()? { |
144 | Some(it) => it, | 169 | Some(it) => it, |
145 | None => { | 170 | None => { |
146 | finish_args(matches)?; | 171 | finish_args(matches)?; |
147 | return Ok(Args { verbosity, log_file, command: Command::RunServer }); | 172 | return Ok(Args { |
173 | verbosity, | ||
174 | log_file, | ||
175 | command: Command::RunServer, | ||
176 | no_buffering, | ||
177 | wait_dbg, | ||
178 | }); | ||
148 | } | 179 | } |
149 | }; | 180 | }; |
150 | let command = match subcommand.as_str() { | 181 | let command = match subcommand.as_str() { |
@@ -219,11 +250,17 @@ impl Args { | |||
219 | }, | 250 | }, |
220 | _ => { | 251 | _ => { |
221 | eprintln!("{}", HELP); | 252 | eprintln!("{}", HELP); |
222 | return Ok(Args { verbosity, log_file: None, command: Command::Help }); | 253 | return Ok(Args { |
254 | verbosity, | ||
255 | log_file: None, | ||
256 | command: Command::Help, | ||
257 | no_buffering, | ||
258 | wait_dbg, | ||
259 | }); | ||
223 | } | 260 | } |
224 | }; | 261 | }; |
225 | finish_args(matches)?; | 262 | finish_args(matches)?; |
226 | Ok(Args { verbosity, log_file, command }) | 263 | Ok(Args { verbosity, log_file, command, no_buffering, wait_dbg }) |
227 | } | 264 | } |
228 | } | 265 | } |
229 | 266 | ||
diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs index 3bcb1ae37..14887c5cc 100644 --- a/crates/rust-analyzer/src/bin/logger.rs +++ b/crates/rust-analyzer/src/bin/logger.rs | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | use std::{ | 5 | use std::{ |
6 | fs::File, | 6 | fs::File, |
7 | io::{BufWriter, Write}, | 7 | io::{self, BufWriter, Write}, |
8 | }; | 8 | }; |
9 | 9 | ||
10 | use env_logger::filter::{Builder, Filter}; | 10 | use env_logger::filter::{Builder, Filter}; |
@@ -14,10 +14,11 @@ use parking_lot::Mutex; | |||
14 | pub(crate) struct Logger { | 14 | pub(crate) struct Logger { |
15 | filter: Filter, | 15 | filter: Filter, |
16 | file: Option<Mutex<BufWriter<File>>>, | 16 | file: Option<Mutex<BufWriter<File>>>, |
17 | no_buffering: bool, | ||
17 | } | 18 | } |
18 | 19 | ||
19 | impl Logger { | 20 | impl Logger { |
20 | pub(crate) fn new(log_file: Option<File>, filter: Option<&str>) -> Logger { | 21 | pub(crate) fn new(log_file: Option<File>, no_buffering: bool, filter: Option<&str>) -> Logger { |
21 | let filter = { | 22 | let filter = { |
22 | let mut builder = Builder::new(); | 23 | let mut builder = Builder::new(); |
23 | if let Some(filter) = filter { | 24 | if let Some(filter) = filter { |
@@ -28,7 +29,7 @@ impl Logger { | |||
28 | 29 | ||
29 | let file = log_file.map(|it| Mutex::new(BufWriter::new(it))); | 30 | let file = log_file.map(|it| Mutex::new(BufWriter::new(it))); |
30 | 31 | ||
31 | Logger { filter, file } | 32 | Logger { filter, file, no_buffering } |
32 | } | 33 | } |
33 | 34 | ||
34 | pub(crate) fn install(self) { | 35 | pub(crate) fn install(self) { |
@@ -46,7 +47,8 @@ impl Log for Logger { | |||
46 | if !self.filter.matches(record) { | 47 | if !self.filter.matches(record) { |
47 | return; | 48 | return; |
48 | } | 49 | } |
49 | match &self.file { | 50 | |
51 | let should_flush = match &self.file { | ||
50 | Some(w) => { | 52 | Some(w) => { |
51 | let _ = writeln!( | 53 | let _ = writeln!( |
52 | w.lock(), | 54 | w.lock(), |
@@ -55,19 +57,32 @@ impl Log for Logger { | |||
55 | record.module_path().unwrap_or_default(), | 57 | record.module_path().unwrap_or_default(), |
56 | record.args(), | 58 | record.args(), |
57 | ); | 59 | ); |
60 | self.no_buffering | ||
61 | } | ||
62 | None => { | ||
63 | eprintln!( | ||
64 | "[{} {}] {}", | ||
65 | record.level(), | ||
66 | record.module_path().unwrap_or_default(), | ||
67 | record.args(), | ||
68 | ); | ||
69 | true // flush stderr unconditionally | ||
58 | } | 70 | } |
59 | None => eprintln!( | 71 | }; |
60 | "[{} {}] {}", | 72 | |
61 | record.level(), | 73 | if should_flush { |
62 | record.module_path().unwrap_or_default(), | 74 | self.flush(); |
63 | record.args(), | ||
64 | ), | ||
65 | } | 75 | } |
66 | } | 76 | } |
67 | 77 | ||
68 | fn flush(&self) { | 78 | fn flush(&self) { |
69 | if let Some(w) = &self.file { | 79 | match &self.file { |
70 | let _ = w.lock().flush(); | 80 | Some(w) => { |
81 | let _ = w.lock().flush(); | ||
82 | } | ||
83 | None => { | ||
84 | let _ = io::stderr().flush(); | ||
85 | } | ||
71 | } | 86 | } |
72 | } | 87 | } |
73 | } | 88 | } |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 1d6e5478b..088b17b03 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -21,6 +21,7 @@ static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; | |||
21 | 21 | ||
22 | fn main() { | 22 | fn main() { |
23 | if let Err(err) = try_main() { | 23 | if let Err(err) = try_main() { |
24 | log::error!("Unexpected error: {}", err); | ||
24 | eprintln!("{}", err); | 25 | eprintln!("{}", err); |
25 | process::exit(101); | 26 | process::exit(101); |
26 | } | 27 | } |
@@ -28,7 +29,17 @@ fn main() { | |||
28 | 29 | ||
29 | fn try_main() -> Result<()> { | 30 | fn try_main() -> Result<()> { |
30 | let args = args::Args::parse()?; | 31 | let args = args::Args::parse()?; |
31 | setup_logging(args.log_file)?; | 32 | |
33 | #[cfg(debug_assertions)] | ||
34 | if args.wait_dbg || env::var("RA_WAIT_DBG").is_ok() { | ||
35 | #[allow(unused_mut)] | ||
36 | let mut d = 4; | ||
37 | while d == 4 { | ||
38 | d = 4; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | setup_logging(args.log_file, args.no_buffering)?; | ||
32 | match args.command { | 43 | match args.command { |
33 | args::Command::RunServer => run_server()?, | 44 | args::Command::RunServer => run_server()?, |
34 | args::Command::PrintConfigSchema => { | 45 | args::Command::PrintConfigSchema => { |
@@ -56,7 +67,7 @@ fn try_main() -> Result<()> { | |||
56 | Ok(()) | 67 | Ok(()) |
57 | } | 68 | } |
58 | 69 | ||
59 | fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { | 70 | fn setup_logging(log_file: Option<PathBuf>, no_buffering: bool) -> Result<()> { |
60 | env::set_var("RUST_BACKTRACE", "short"); | 71 | env::set_var("RUST_BACKTRACE", "short"); |
61 | 72 | ||
62 | let log_file = match log_file { | 73 | let log_file = match log_file { |
@@ -69,21 +80,12 @@ fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { | |||
69 | None => None, | 80 | None => None, |
70 | }; | 81 | }; |
71 | let filter = env::var("RA_LOG").ok(); | 82 | let filter = env::var("RA_LOG").ok(); |
72 | logger::Logger::new(log_file, filter.as_deref()).install(); | 83 | logger::Logger::new(log_file, no_buffering, filter.as_deref()).install(); |
73 | 84 | ||
74 | tracing_setup::setup_tracing()?; | 85 | tracing_setup::setup_tracing()?; |
75 | 86 | ||
76 | profile::init(); | 87 | profile::init(); |
77 | 88 | ||
78 | if !cfg!(debug_assertions) { | ||
79 | stdx::set_assert_hook(|loc, args| { | ||
80 | if env::var("RA_PROFILE").is_ok() { | ||
81 | panic!("assertion failed at {}: {}", loc, args) | ||
82 | } | ||
83 | log::error!("assertion failed at {}: {}", loc, args) | ||
84 | }); | ||
85 | } | ||
86 | |||
87 | Ok(()) | 89 | Ok(()) |
88 | } | 90 | } |
89 | 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 | } | ||
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index e70fbba9c..de65585cb 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | rowan = "0.12.2" | 15 | rowan = "0.12.2" |
16 | rustc_lexer = { version = "700.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "701.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |