aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/launch.json6
-rw-r--r--Cargo.lock61
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs49
-rw-r--r--crates/assists/src/handlers/replace_if_let_with_match.rs73
-rw-r--r--crates/assists/src/utils.rs19
-rw-r--r--crates/completion/src/item.rs4
-rw-r--r--crates/hir_def/src/body.rs2
-rw-r--r--crates/hir_def/src/body/lower.rs2
-rw-r--r--crates/hir_expand/src/ast_id_map.rs4
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/ide/src/references/rename.rs6
-rw-r--r--crates/ide_db/src/source_change.rs7
-rw-r--r--crates/ide_db/src/ty_filter.rs15
-rw-r--r--crates/mbe/Cargo.toml3
-rw-r--r--crates/mbe/src/lib.rs72
-rw-r--r--crates/mbe/src/mbe_expander.rs9
-rw-r--r--crates/mbe/src/tests.rs233
-rw-r--r--crates/paths/src/lib.rs51
-rw-r--r--crates/profile/Cargo.toml2
-rw-r--r--crates/project_model/src/build_data.rs5
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/bin/args.rs49
-rw-r--r--crates/rust-analyzer/src/bin/logger.rs39
-rw-r--r--crates/rust-analyzer/src/bin/main.rs26
-rw-r--r--crates/rust-analyzer/src/config.rs39
-rw-r--r--crates/stdx/Cargo.toml1
-rw-r--r--crates/stdx/src/lib.rs2
-rw-r--r--crates/stdx/src/macros.rs51
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--docs/dev/README.md4
-rw-r--r--docs/dev/debugging.md8
-rw-r--r--docs/dev/style.md2
-rw-r--r--docs/user/generated_config.adoc8
-rw-r--r--editors/code/package.json12
-rw-r--r--xtask/src/install.rs2
35 files changed, 618 insertions, 258 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 8ca27d878..021b8f048 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -120,6 +120,12 @@
120 "sourceMaps": true, 120 "sourceMaps": true,
121 "outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ], 121 "outFiles": [ "${workspaceFolder}/editors/code/out/tests/unit/**/*.js" ],
122 "preLaunchTask": "Pretest" 122 "preLaunchTask": "Pretest"
123 },
124 {
125 "name": "Win Attach to Server",
126 "type": "cppvsdbg",
127 "processId":"${command:pickProcess}",
128 "request": "attach"
123 } 129 }
124 ] 130 ]
125} 131}
diff --git a/Cargo.lock b/Cargo.lock
index efd1c6d33..fabe69285 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -16,6 +16,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
16checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" 16checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
17 17
18[[package]] 18[[package]]
19name = "always-assert"
20version = "0.1.1"
21source = "registry+https://github.com/rust-lang/crates.io-index"
22checksum = "727786f78c5bc0cda8011831616589f72084cb16b7df4213a997b78749b55a60"
23dependencies = [
24 "log",
25]
26
27[[package]]
19name = "ansi_term" 28name = "ansi_term"
20version = "0.12.1" 29version = "0.12.1"
21source = "registry+https://github.com/rust-lang/crates.io-index" 30source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -175,9 +184,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
175 184
176[[package]] 185[[package]]
177name = "chalk-derive" 186name = "chalk-derive"
178version = "0.50.0" 187version = "0.51.0"
179source = "registry+https://github.com/rust-lang/crates.io-index" 188source = "registry+https://github.com/rust-lang/crates.io-index"
180checksum = "ac605cf409013573e971d7292d4bec6f5495b19d5f98fc9d8b1a12270c3888e0" 189checksum = "f1a2ad10b964771ffc53ce7a8d6fce4bde3da0da82a1bc7a72668548db1c56ee"
181dependencies = [ 190dependencies = [
182 "proc-macro2", 191 "proc-macro2",
183 "quote", 192 "quote",
@@ -187,9 +196,9 @@ dependencies = [
187 196
188[[package]] 197[[package]]
189name = "chalk-ir" 198name = "chalk-ir"
190version = "0.50.0" 199version = "0.51.0"
191source = "registry+https://github.com/rust-lang/crates.io-index" 200source = "registry+https://github.com/rust-lang/crates.io-index"
192checksum = "fa1dbfb3c2c8b67edb5cd981f720550e43579090574f786145731f90c5d401ff" 201checksum = "17b14b40a09687aa5f0973721841d83a8a0e12a6b3ada1d2e01f0a694bb800ee"
193dependencies = [ 202dependencies = [
194 "bitflags", 203 "bitflags",
195 "chalk-derive", 204 "chalk-derive",
@@ -198,9 +207,9 @@ dependencies = [
198 207
199[[package]] 208[[package]]
200name = "chalk-recursive" 209name = "chalk-recursive"
201version = "0.50.0" 210version = "0.51.0"
202source = "registry+https://github.com/rust-lang/crates.io-index" 211source = "registry+https://github.com/rust-lang/crates.io-index"
203checksum = "0882e2a3ba66901717a64f8bb0655e809f800ac6abed05cb605e7a41d4bf8999" 212checksum = "1c7297bb1683f63a4b334b53cfdd81a721e8adde3441514e1126d24aaafe2552"
204dependencies = [ 213dependencies = [
205 "chalk-derive", 214 "chalk-derive",
206 "chalk-ir", 215 "chalk-ir",
@@ -211,9 +220,9 @@ dependencies = [
211 220
212[[package]] 221[[package]]
213name = "chalk-solve" 222name = "chalk-solve"
214version = "0.50.0" 223version = "0.51.0"
215source = "registry+https://github.com/rust-lang/crates.io-index" 224source = "registry+https://github.com/rust-lang/crates.io-index"
216checksum = "0d43cce07150eac39771ff4b198537cefef744734b2218a89c682295b54cd8d0" 225checksum = "82765c1a19e801d53fc9d145449701ddf79f394e6519f1e9c01fca1ee95d0ba7"
217dependencies = [ 226dependencies = [
218 "chalk-derive", 227 "chalk-derive",
219 "chalk-ir", 228 "chalk-ir",
@@ -275,9 +284,9 @@ checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
275 284
276[[package]] 285[[package]]
277name = "countme" 286name = "countme"
278version = "2.0.0" 287version = "2.0.3"
279source = "registry+https://github.com/rust-lang/crates.io-index" 288source = "registry+https://github.com/rust-lang/crates.io-index"
280checksum = "4f8038ded86523aa26c1321dfe8bb432707d0a3e2608f9d0108803727546e089" 289checksum = "be0bdf97802dfc074a4cf9a1f087f7f8d6478ee039a46eba1644690bcc854d61"
281dependencies = [ 290dependencies = [
282 "dashmap", 291 "dashmap",
283 "once_cell", 292 "once_cell",
@@ -878,11 +887,11 @@ dependencies = [
878 887
879[[package]] 888[[package]]
880name = "log" 889name = "log"
881version = "0.4.13" 890version = "0.4.14"
882source = "registry+https://github.com/rust-lang/crates.io-index" 891source = "registry+https://github.com/rust-lang/crates.io-index"
883checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2" 892checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
884dependencies = [ 893dependencies = [
885 "cfg-if 0.1.10", 894 "cfg-if 1.0.0",
886] 895]
887 896
888[[package]] 897[[package]]
@@ -1415,6 +1424,7 @@ dependencies = [
1415name = "rust-analyzer" 1424name = "rust-analyzer"
1416version = "0.0.0" 1425version = "0.0.0"
1417dependencies = [ 1426dependencies = [
1427 "always-assert",
1418 "anyhow", 1428 "anyhow",
1419 "cfg", 1429 "cfg",
1420 "crossbeam-channel 0.5.0", 1430 "crossbeam-channel 0.5.0",
@@ -1464,9 +1474,9 @@ dependencies = [
1464 1474
1465[[package]] 1475[[package]]
1466name = "rustc-ap-rustc_lexer" 1476name = "rustc-ap-rustc_lexer"
1467version = "700.0.0" 1477version = "701.0.0"
1468source = "registry+https://github.com/rust-lang/crates.io-index" 1478source = "registry+https://github.com/rust-lang/crates.io-index"
1469checksum = "5ed36784376b69c941d7aa36e960a52ac712e2663960357121a4d9f2cc58e225" 1479checksum = "3601c20fc378d11e965fb1f670cf8bad1ef5164fe4ec8cbb7c57fa9d656f2541"
1470dependencies = [ 1480dependencies = [
1471 "unicode-xid", 1481 "unicode-xid",
1472] 1482]
@@ -1560,18 +1570,18 @@ dependencies = [
1560 1570
1561[[package]] 1571[[package]]
1562name = "serde" 1572name = "serde"
1563version = "1.0.120" 1573version = "1.0.123"
1564source = "registry+https://github.com/rust-lang/crates.io-index" 1574source = "registry+https://github.com/rust-lang/crates.io-index"
1565checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab" 1575checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
1566dependencies = [ 1576dependencies = [
1567 "serde_derive", 1577 "serde_derive",
1568] 1578]
1569 1579
1570[[package]] 1580[[package]]
1571name = "serde_derive" 1581name = "serde_derive"
1572version = "1.0.120" 1582version = "1.0.123"
1573source = "registry+https://github.com/rust-lang/crates.io-index" 1583source = "registry+https://github.com/rust-lang/crates.io-index"
1574checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775" 1584checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
1575dependencies = [ 1585dependencies = [
1576 "proc-macro2", 1586 "proc-macro2",
1577 "quote", 1587 "quote",
@@ -1658,14 +1668,15 @@ dependencies = [
1658name = "stdx" 1668name = "stdx"
1659version = "0.0.0" 1669version = "0.0.0"
1660dependencies = [ 1670dependencies = [
1671 "always-assert",
1661 "backtrace", 1672 "backtrace",
1662] 1673]
1663 1674
1664[[package]] 1675[[package]]
1665name = "syn" 1676name = "syn"
1666version = "1.0.59" 1677version = "1.0.60"
1667source = "registry+https://github.com/rust-lang/crates.io-index" 1678source = "registry+https://github.com/rust-lang/crates.io-index"
1668checksum = "07cb8b1b4ebf86a89ee88cbd201b022b94138c623644d035185c84d3f41b7e66" 1679checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
1669dependencies = [ 1680dependencies = [
1670 "proc-macro2", 1681 "proc-macro2",
1671 "quote", 1682 "quote",
@@ -1742,9 +1753,9 @@ dependencies = [
1742 1753
1743[[package]] 1754[[package]]
1744name = "thread_local" 1755name = "thread_local"
1745version = "1.1.1" 1756version = "1.1.2"
1746source = "registry+https://github.com/rust-lang/crates.io-index" 1757source = "registry+https://github.com/rust-lang/crates.io-index"
1747checksum = "301bdd13d23c49672926be451130892d274d3ba0b410c18e00daa7990ff38d99" 1758checksum = "d8208a331e1cb318dd5bd76951d2b8fc48ca38a69f5f4e4af1b6a9f8c6236915"
1748dependencies = [ 1759dependencies = [
1749 "once_cell", 1760 "once_cell",
1750] 1761]
@@ -1771,9 +1782,9 @@ dependencies = [
1771 1782
1772[[package]] 1783[[package]]
1773name = "tinyvec" 1784name = "tinyvec"
1774version = "1.1.0" 1785version = "1.1.1"
1775source = "registry+https://github.com/rust-lang/crates.io-index" 1786source = "registry+https://github.com/rust-lang/crates.io-index"
1776checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" 1787checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023"
1777dependencies = [ 1788dependencies = [
1778 "tinyvec_macros", 1789 "tinyvec_macros",
1779] 1790]
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};
8use test_utils::mark; 8use test_utils::mark;
9 9
10use crate::{ 10use crate::{
11 utils::{render_snippet, Cursor}, 11 utils::{does_pat_match_variant, render_snippet, Cursor},
12 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
13}; 13};
14 14
@@ -147,25 +147,6 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
147 }) 147 })
148} 148}
149 149
150fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
151 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
152
153 let pat_head = match pat {
154 Pat::IdentPat(bind_pat) => {
155 if let Some(p) = bind_pat.pat() {
156 first_node_text(&p)
157 } else {
158 return false;
159 }
160 }
161 pat => first_node_text(pat),
162 };
163
164 let var_head = first_node_text(var);
165
166 pat_head == var_head
167}
168
169fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 150fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
170 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { 151 sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
171 Some(Adt::Enum(e)) => Some(e), 152 Some(Adt::Enum(e)) => Some(e),
@@ -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#"
279enum Option<T> { Some(T), None }
280use Option::*;
281
282fn main() {
283 match None$0 {
284 None => {}
285 }
286}
287 "#,
288 r#"
289enum Option<T> { Some(T), None }
290use Option::*;
291
292fn 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
13use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; 13use crate::{
14 utils::{does_pat_match_variant, unwrap_trivial_block},
15 AssistContext, AssistId, AssistKind, Assists,
16};
14 17
15// Assist: replace_if_let_with_match 18// Assist: replace_if_let_with_match
16// 19//
@@ -66,7 +69,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
66 .sema 69 .sema
67 .type_of_pat(&pat) 70 .type_of_pat(&pat)
68 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) 71 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
69 .map(|it| it.sad_pattern()) 72 .map(|it| {
73 if does_pat_match_variant(&pat, &it.sad_pattern()) {
74 it.happy_pattern()
75 } else {
76 it.sad_pattern()
77 }
78 })
70 .unwrap_or_else(|| make::wildcard_pat().into()); 79 .unwrap_or_else(|| make::wildcard_pat().into());
71 let else_expr = unwrap_trivial_block(else_block); 80 let else_expr = unwrap_trivial_block(else_block);
72 make::match_arm(vec![pattern], else_expr) 81 make::match_arm(vec![pattern], else_expr)
@@ -279,6 +288,36 @@ fn foo(x: Option<i32>) {
279 } 288 }
280 289
281 #[test] 290 #[test]
291 fn special_case_inverted_option() {
292 check_assist(
293 replace_if_let_with_match,
294 r#"
295enum Option<T> { Some(T), None }
296use Option::*;
297
298fn foo(x: Option<i32>) {
299 $0if let None = x {
300 println!("none")
301 } else {
302 println!("some")
303 }
304}
305 "#,
306 r#"
307enum Option<T> { Some(T), None }
308use Option::*;
309
310fn foo(x: Option<i32>) {
311 match x {
312 None => println!("none"),
313 Some(_) => println!("some"),
314 }
315}
316 "#,
317 );
318 }
319
320 #[test]
282 fn special_case_result() { 321 fn special_case_result() {
283 check_assist( 322 check_assist(
284 replace_if_let_with_match, 323 replace_if_let_with_match,
@@ -309,6 +348,36 @@ fn foo(x: Result<i32, ()>) {
309 } 348 }
310 349
311 #[test] 350 #[test]
351 fn special_case_inverted_result() {
352 check_assist(
353 replace_if_let_with_match,
354 r#"
355enum Result<T, E> { Ok(T), Err(E) }
356use Result::*;
357
358fn foo(x: Result<i32, ()>) {
359 $0if let Err(x) = x {
360 println!("{}", x)
361 } else {
362 println!("ok")
363 }
364}
365 "#,
366 r#"
367enum Result<T, E> { Ok(T), Err(E) }
368use Result::*;
369
370fn foo(x: Result<i32, ()>) {
371 match x {
372 Err(x) => println!("{}", x),
373 Ok(_) => println!("ok"),
374 }
375}
376 "#,
377 );
378 }
379
380 #[test]
312 fn nested_indent() { 381 fn nested_indent() {
313 check_assist( 382 check_assist(
314 replace_if_let_with_match, 383 replace_if_let_with_match,
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 44c35bafa..4e762e18b 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -248,3 +248,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
248pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { 248pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
249 [Direction::Next, Direction::Prev].iter().copied() 249 [Direction::Next, Direction::Prev].iter().copied()
250} 250}
251
252pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
253 let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text());
254
255 let pat_head = match pat {
256 ast::Pat::IdentPat(bind_pat) => {
257 if let Some(p) = bind_pat.pat() {
258 first_node_text(&p)
259 } else {
260 return pat.syntax().text() == var.syntax().text();
261 }
262 }
263 pat => first_node_text(pat),
264 };
265
266 let var_head = first_node_text(var);
267
268 pat_head == var_head
269}
diff --git a/crates/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};
13use stdx::{assert_never, impl_from}; 13use stdx::{impl_from, never};
14use syntax::{algo, TextRange}; 14use syntax::{algo, TextRange};
15use text_edit::TextEdit; 15use 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};
19use la_arena::{Arena, ArenaMap}; 19use la_arena::{Arena, ArenaMap};
20use profile::Count;
20use rustc_hash::FxHashMap; 21use rustc_hash::FxHashMap;
21use syntax::{ast, AstNode, AstPtr}; 22use syntax::{ast, AstNode, AstPtr};
22use test_utils::mark; 23use 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
242pub type ExprPtr = AstPtr<ast::Expr>; 244pub 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};
12use la_arena::Arena; 12use la_arena::Arena;
13use profile::Count;
13use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
14use syntax::{ 15use 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
15use la_arena::{Arena, Idx}; 15use la_arena::{Arena, Idx};
16use profile::Count;
16use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; 17use 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)]
63pub struct AstIdMap { 64pub struct AstIdMap {
64 arena: Arena<SyntaxNodePtr>, 65 arena: Arena<SyntaxNodePtr>,
66 _c: Count<Self>,
65} 67}
66 68
67impl AstIdMap { 69impl 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"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.50", default-features = false } 20chalk-solve = { version = "0.51", default-features = false }
21chalk-ir = "0.50" 21chalk-ir = "0.51"
22chalk-recursive = "0.50" 22chalk-recursive = "0.51"
23la-arena = { version = "0.2.0", path = "../../lib/arena" } 23la-arena = { version = "0.2.0", path = "../../lib/arena" }
24 24
25stdx = { path = "../stdx", version = "0.0.0" } 25stdx = { 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};
12use stdx::assert_never; 12use stdx::never;
13use syntax::{ 13use 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
287fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { 287fn 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
11use base_db::{AnchoredPathBuf, FileId}; 11use base_db::{AnchoredPathBuf, FileId};
12use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
13use stdx::assert_never; 13use stdx::never;
14use text_edit::TextEdit; 14use 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"
17syntax = { path = "../syntax", version = "0.0.0" } 17syntax = { path = "../syntax", version = "0.0.0" }
18parser = { path = "../parser", version = "0.0.0" } 18parser = { path = "../parser", version = "0.0.0" }
19tt = { path = "../tt", version = "0.0.0" } 19tt = { path = "../tt", version = "0.0.0" }
20
21[dev-dependencies]
22test_utils = { path = "../test_utils" } 20test_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
15use std::fmt; 15use std::fmt;
16 16
17use test_utils::mark;
17pub use tt::{Delimiter, DelimiterKind, Punct}; 18pub use tt::{Delimiter, DelimiterKind, Punct};
18 19
19use crate::{ 20use 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)]
82pub 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)]
80struct Rule { 89struct 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
227impl 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
218impl Rule { 278impl 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
11use crate::{ExpandError, ExpandResult}; 11use crate::{ExpandError, ExpandResult};
12 12
13pub(crate) fn expand(rules: &crate::MacroRules, input: &tt::Subtree) -> ExpandResult<tt::Subtree> { 13pub(crate) fn expand_rules(
14 expand_rules(&rules.rules, input) 14 rules: &[crate::Rule],
15} 15 input: &tt::Subtree,
16 16) -> ExpandResult<tt::Subtree> {
17fn 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};
9use test_utils::assert_eq_text; 9use test_utils::{assert_eq_text, mark};
10 10
11use super::*; 11use 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]
679fn test_parse_macro_def_simple() {
680 mark::check!(parse_macro_def_simple);
681
682 parse_macro2(
683 r#"
684macro foo($id:ident) {
685 fn $id() {}
686}
687"#,
688 )
689 .assert_expand_items("foo!(bar);", "fn bar () {}");
690}
691
692#[test]
693fn test_parse_macro_def_rules() {
694 mark::check!(parse_macro_def_rules);
695
696 parse_macro2(
697 r#"
698macro 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
1702impl MacroFixture { 1732pub(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(&macro_invocation.token_tree().unwrap())
1713 .ok_or_else(|| ExpandError::ConversionError)?;
1714 1735
1715 self.rules.expand(&invocation_tt).result() 1736macro_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(&macro_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
1790fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { 1844impl_fixture!(MacroFixture);
1845impl_fixture!(MacroFixture2);
1846
1847fn 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
1864fn 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(&macro_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
1807pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { 1880pub(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
1886pub(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
1813pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { 1892pub(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)]
10pub struct AbsPathBuf(PathBuf); 11pub struct AbsPathBuf(PathBuf);
11 12
@@ -58,18 +59,33 @@ impl PartialEq<AbsPath> for AbsPathBuf {
58} 59}
59 60
60impl AbsPathBuf { 61impl AbsPathBuf {
62 /// Wrap the given absolute path in `AbsPathBuf`
63 ///
64 /// # Panics
65 ///
66 /// Panics if `path` is not absolute.
61 pub fn assert(path: PathBuf) -> AbsPathBuf { 67 pub fn assert(path: PathBuf) -> AbsPathBuf {
62 AbsPathBuf::try_from(path) 68 AbsPathBuf::try_from(path)
63 .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display())) 69 .unwrap_or_else(|path| panic!("expected absolute path, got {}", path.display()))
64 } 70 }
71
72 /// Coerces to a `AbsPath` slice.
73 ///
74 /// Equivalent of [`PathBuf::as_path`] for `AbsPathBuf`.
65 pub fn as_path(&self) -> &AbsPath { 75 pub fn as_path(&self) -> &AbsPath {
66 AbsPath::assert(self.0.as_path()) 76 AbsPath::assert(self.0.as_path())
67 } 77 }
78
79 /// Equivalent of [`PathBuf::pop`] for `AbsPathBuf`.
80 ///
81 /// Note that this won't remove the root component, so `self` will still be
82 /// absolute.
68 pub fn pop(&mut self) -> bool { 83 pub fn pop(&mut self) -> bool {
69 self.0.pop() 84 self.0.pop()
70 } 85 }
71} 86}
72 87
88/// Wrapper around an absolute [`Path`].
73#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] 89#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
74#[repr(transparent)] 90#[repr(transparent)]
75pub struct AbsPath(Path); 91pub struct AbsPath(Path);
@@ -98,28 +114,56 @@ impl<'a> TryFrom<&'a Path> for &'a AbsPath {
98} 114}
99 115
100impl AbsPath { 116impl AbsPath {
117 /// Wrap the given absolute path in `AbsPath`
118 ///
119 /// # Panics
120 ///
121 /// Panics if `path` is not absolute.
101 pub fn assert(path: &Path) -> &AbsPath { 122 pub fn assert(path: &Path) -> &AbsPath {
102 assert!(path.is_absolute()); 123 assert!(path.is_absolute());
103 unsafe { &*(path as *const Path as *const AbsPath) } 124 unsafe { &*(path as *const Path as *const AbsPath) }
104 } 125 }
105 126
127 /// Equivalent of [`Path::parent`] for `AbsPath`.
106 pub fn parent(&self) -> Option<&AbsPath> { 128 pub fn parent(&self) -> Option<&AbsPath> {
107 self.0.parent().map(AbsPath::assert) 129 self.0.parent().map(AbsPath::assert)
108 } 130 }
131
132 /// Equivalent of [`Path::join`] for `AbsPath`.
109 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf { 133 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
110 self.as_ref().join(path).try_into().unwrap() 134 self.as_ref().join(path).try_into().unwrap()
111 } 135 }
136
137 /// Normalize the given path:
138 /// - Removes repeated separators: `/a//b` becomes `/a/b`
139 /// - Removes occurrences of `.` and resolves `..`.
140 /// - Removes trailing slashes: `/a/b/` becomes `/a/b`.
141 ///
142 /// # Example
143 /// ```
144 /// # use paths::AbsPathBuf;
145 /// let abs_path_buf = AbsPathBuf::assert("/a/../../b/.//c//".into());
146 /// let normalized = abs_path_buf.normalize();
147 /// assert_eq!(normalized, AbsPathBuf::assert("/b/c".into()));
148 /// ```
112 pub fn normalize(&self) -> AbsPathBuf { 149 pub fn normalize(&self) -> AbsPathBuf {
113 AbsPathBuf(normalize_path(&self.0)) 150 AbsPathBuf(normalize_path(&self.0))
114 } 151 }
152
153 /// Equivalent of [`Path::to_path_buf`] for `AbsPath`.
115 pub fn to_path_buf(&self) -> AbsPathBuf { 154 pub fn to_path_buf(&self) -> AbsPathBuf {
116 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap() 155 AbsPathBuf::try_from(self.0.to_path_buf()).unwrap()
117 } 156 }
157
158 /// Equivalent of [`Path::strip_prefix`] for `AbsPath`.
159 ///
160 /// Returns a relative path.
118 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> { 161 pub fn strip_prefix(&self, base: &AbsPath) -> Option<&RelPath> {
119 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked) 162 self.0.strip_prefix(base).ok().map(RelPath::new_unchecked)
120 } 163 }
121} 164}
122 165
166/// Wrapper around a relative [`PathBuf`].
123#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] 167#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
124pub struct RelPathBuf(PathBuf); 168pub struct RelPathBuf(PathBuf);
125 169
@@ -160,11 +204,15 @@ impl TryFrom<&str> for RelPathBuf {
160} 204}
161 205
162impl RelPathBuf { 206impl RelPathBuf {
207 /// Coerces to a `RelPath` slice.
208 ///
209 /// Equivalent of [`PathBuf::as_path`] for `RelPathBuf`.
163 pub fn as_path(&self) -> &RelPath { 210 pub fn as_path(&self) -> &RelPath {
164 RelPath::new_unchecked(self.0.as_path()) 211 RelPath::new_unchecked(self.0.as_path())
165 } 212 }
166} 213}
167 214
215/// Wrapper around a relative [`Path`].
168#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] 216#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
169#[repr(transparent)] 217#[repr(transparent)]
170pub struct RelPath(Path); 218pub struct RelPath(Path);
@@ -183,12 +231,13 @@ impl AsRef<Path> for RelPath {
183} 231}
184 232
185impl RelPath { 233impl RelPath {
234 /// Creates a new `RelPath` from `path`, without checking if it is relative.
186 pub fn new_unchecked(path: &Path) -> &RelPath { 235 pub fn new_unchecked(path: &Path) -> &RelPath {
187 unsafe { &*(path as *const Path as *const RelPath) } 236 unsafe { &*(path as *const Path as *const RelPath) }
188 } 237 }
189} 238}
190 239
191// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85 240/// Taken from https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
192fn normalize_path(path: &Path) -> PathBuf { 241fn normalize_path(path: &Path) -> PathBuf {
193 let mut components = path.components().peekable(); 242 let mut components = path.components().peekable();
194 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() { 243 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
diff --git a/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"
14cfg-if = "1" 14cfg-if = "1"
15libc = "0.2.73" 15libc = "0.2.73"
16la-arena = { version = "0.2.0", path = "../../lib/arena" } 16la-arena = { version = "0.2.0", path = "../../lib/arena" }
17countme = { version = "2.0.0", features = ["enable"] } 17countme = { version = "2.0.1", features = ["enable"] }
18jemalloc-ctl = { version = "0.3.3", optional = true } 18jemalloc-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>
176fn inject_cargo_env(package: &cargo_metadata::Package, env: &mut Vec<(String, String)>) { 176fn 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"
37tracing = "0.1" 37tracing = "0.1"
38tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 38tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
39tracing-tree = { version = "0.1.4" } 39tracing-tree = { version = "0.1.4" }
40always-assert = "0.1"
40 41
41stdx = { path = "../stdx", version = "0.0.0" } 42stdx = { path = "../stdx", version = "0.0.0" }
42flycheck = { path = "../flycheck", version = "0.0.0" } 43flycheck = { path = "../flycheck", version = "0.0.0" }
@@ -72,3 +73,4 @@ tt = { path = "../tt" }
72 73
73[features] 74[features]
74jemalloc = ["jemallocator", "profile/jemalloc"] 75jemalloc = ["jemallocator", "profile/jemalloc"]
76force-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;
14pub(crate) struct Args { 14pub(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
20pub(crate) enum Command { 23pub(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
52ENVIRONMENTAL VARIABLES: 60ENVIRONMENTAL 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
56COMMANDS: 65COMMANDS:
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
5use std::{ 5use std::{
6 fs::File, 6 fs::File,
7 io::{BufWriter, Write}, 7 io::{self, BufWriter, Write},
8}; 8};
9 9
10use env_logger::filter::{Builder, Filter}; 10use env_logger::filter::{Builder, Filter};
@@ -14,10 +14,11 @@ use parking_lot::Mutex;
14pub(crate) struct Logger { 14pub(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
19impl Logger { 20impl 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
22fn main() { 22fn 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
29fn try_main() -> Result<()> { 30fn 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
59fn setup_logging(log_file: Option<PathBuf>) -> Result<()> { 70fn 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]
13backtrace = { version = "0.3.44", optional = true } 13backtrace = { version = "0.3.44", optional = true }
14always-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};
4mod macros; 4mod macros;
5pub mod panic_context; 5pub mod panic_context;
6 6
7pub use crate::macros::{on_assert_failure, set_assert_hook}; 7pub use always_assert::{always, never};
8 8
9#[inline(always)] 9#[inline(always)]
10pub fn is_ci() -> bool { 10pub 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
3use std::{
4 fmt, mem, panic,
5 sync::atomic::{AtomicUsize, Ordering::SeqCst},
6};
7#[macro_export] 3#[macro_export]
8macro_rules! eprintln { 4macro_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]
68macro_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
81type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>);
82static HOOK: AtomicUsize = AtomicUsize::new(0);
83
84pub fn set_assert_hook(hook: AssertHook) {
85 HOOK.store(hook as usize, SeqCst);
86}
87
88#[cold]
89#[track_caller]
90pub 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]
14itertools = "0.10.0" 14itertools = "0.10.0"
15rowan = "0.12.2" 15rowan = "0.12.2"
16rustc_lexer = { version = "700.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "701.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 6a6ba1443..6bce38e56 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -43,6 +43,10 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0
43 while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels. 43 while unactionable ones are effectively wont-fix. Each triaged issue should have one of these labels.
44* [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun) 44* [fun](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%3Afun)
45 is for cool, but probably hard stuff. 45 is for cool, but probably hard stuff.
46* [Design](https://github.com/rust-analyzer/rust-analyzer/issues?q=is%3Aopen+is%3Aissue+label%Design)
47 is for moderate/large scale architecture discussion.
48 Also a kind of fun.
49 These issues should generally include a link to a Zulip discussion thread.
46 50
47# CI 51# CI
48 52
diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md
index 8c48fd5a1..cc7a790ff 100644
--- a/docs/dev/debugging.md
+++ b/docs/dev/debugging.md
@@ -57,6 +57,14 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k
57 57
58- Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit. 58- Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit.
59 59
60If you need to debug the server from the very beginning, including its initialization code, you can use the `--wait-dbg` command line argument or `RA_WAIT_DBG` environment variable. The server will spin at the beginning of the `try_main` function (see `crates\rust-analyzer\src\bin\main.rs`)
61```rust
62 let mut d = 4;
63 while d == 4 { // set a breakpoint here and change the value
64 d = 4;
65 }
66```
67
60## Demo 68## Demo
61 69
62- [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM). 70- [Debugging TypeScript VScode extension](https://www.youtube.com/watch?v=T-hvpK6s4wM).
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 6dc6868c2..0c5e2ad33 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -232,7 +232,7 @@ if idx >= len {
232## Assertions 232## Assertions
233 233
234Assert liberally. 234Assert liberally.
235Prefer `stdx::assert_never!` to standard `assert!`. 235Prefer `stdx::never!` to standard `assert!`.
236 236
237## Getters & Setters 237## Getters & Setters
238 238
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 2f681b01a..1974082da 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -7,7 +7,7 @@
7[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: 7[[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`)::
8 Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes. 8 Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes.
9[[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: 9[[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`)::
10 Activate all available features. 10 Activate all available features (`--all-features`).
11[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: 11[[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`)::
12 List of features to activate. 12 List of features to activate.
13[[rust-analyzer.cargo.loadOutDirsFromCheck]]rust-analyzer.cargo.loadOutDirsFromCheck (default: `false`):: 13[[rust-analyzer.cargo.loadOutDirsFromCheck]]rust-analyzer.cargo.loadOutDirsFromCheck (default: `false`)::
@@ -21,9 +21,9 @@
21[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: 21[[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`)::
22 Run specified `cargo check` command for diagnostics on save. 22 Run specified `cargo check` command for diagnostics on save.
23[[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: 23[[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`)::
24 Check with all features (will be passed as `--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`. 24 Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.
25[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: 25[[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`)::
26 Check all targets and tests (will be passed as `--all-targets`). 26 Check all targets and tests (`--all-targets`).
27[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: 27[[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`)::
28 Cargo command to use for `cargo check`. 28 Cargo command to use for `cargo check`.
29[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: 29[[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`)::
@@ -91,7 +91,7 @@
91[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: 91[[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`)::
92 Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. 92 Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.
93[[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: 93[[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`)::
94 Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. 94 Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
95[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: 95[[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`)::
96 Whether to show `can't find Cargo.toml` error message. 96 Whether to show `can't find Cargo.toml` error message.
97[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: 97[[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`)::
diff --git a/editors/code/package.json b/editors/code/package.json
index 2225cf1dd..ee54638f1 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -349,6 +349,7 @@
349 "default": {}, 349 "default": {},
350 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`" 350 "markdownDescription": "Optional settings passed to the debug engine. Example: `{ \"lldb\": { \"terminal\":\"external\"} }`"
351 }, 351 },
352 "$generated-start": false,
352 "rust-analyzer.assist.importMergeBehavior": { 353 "rust-analyzer.assist.importMergeBehavior": {
353 "markdownDescription": "The strategy to use when inserting new imports or merging imports.", 354 "markdownDescription": "The strategy to use when inserting new imports or merging imports.",
354 "default": "full", 355 "default": "full",
@@ -390,7 +391,7 @@
390 "type": "boolean" 391 "type": "boolean"
391 }, 392 },
392 "rust-analyzer.cargo.allFeatures": { 393 "rust-analyzer.cargo.allFeatures": {
393 "markdownDescription": "Activate all available features.", 394 "markdownDescription": "Activate all available features (`--all-features`).",
394 "default": false, 395 "default": false,
395 "type": "boolean" 396 "type": "boolean"
396 }, 397 },
@@ -431,7 +432,7 @@
431 "type": "boolean" 432 "type": "boolean"
432 }, 433 },
433 "rust-analyzer.checkOnSave.allFeatures": { 434 "rust-analyzer.checkOnSave.allFeatures": {
434 "markdownDescription": "Check with all features (will be passed as `--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.", 435 "markdownDescription": "Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.",
435 "default": null, 436 "default": null,
436 "type": [ 437 "type": [
437 "null", 438 "null",
@@ -439,7 +440,7 @@
439 ] 440 ]
440 }, 441 },
441 "rust-analyzer.checkOnSave.allTargets": { 442 "rust-analyzer.checkOnSave.allTargets": {
442 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`).", 443 "markdownDescription": "Check all targets and tests (`--all-targets`).",
443 "default": true, 444 "default": true,
444 "type": "boolean" 445 "type": "boolean"
445 }, 446 },
@@ -650,7 +651,7 @@
650 } 651 }
651 }, 652 },
652 "rust-analyzer.lruCapacity": { 653 "rust-analyzer.lruCapacity": {
653 "markdownDescription": "Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.", 654 "markdownDescription": "Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.",
654 "default": null, 655 "default": null,
655 "type": [ 656 "type": [
656 "null", 657 "null",
@@ -718,7 +719,8 @@
718 "items": { 719 "items": {
719 "type": "string" 720 "type": "string"
720 } 721 }
721 } 722 },
723 "$generated-end": false
722 } 724 }
723 }, 725 },
724 "problemPatterns": [ 726 "problemPatterns": [
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 202c74426..81b9956b8 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -180,7 +180,7 @@ fn install_server(opts: ServerOpt) -> Result<()> {
180 Malloc::Jemalloc => &["--features", "jemalloc"], 180 Malloc::Jemalloc => &["--features", "jemalloc"],
181 }; 181 };
182 182
183 let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}"); 183 let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force --features force-always-assert {features...}");
184 let res = cmd.run(); 184 let res = cmd.run();
185 185
186 if res.is_err() && old_rust { 186 if res.is_err() && old_rust {