aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml4
-rw-r--r--Cargo.lock56
-rw-r--r--crates/assists/Cargo.toml2
-rw-r--r--crates/assists/src/ast_transform.rs3
-rw-r--r--crates/assists/src/handlers/extract_assignment.rs325
-rw-r--r--crates/assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/assists/src/handlers/fill_match_arms.rs2
-rw-r--r--crates/assists/src/handlers/fix_visibility.rs27
-rw-r--r--crates/assists/src/handlers/replace_derive_with_manual_impl.rs31
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs29
-rw-r--r--crates/assists/src/utils.rs14
-rw-r--r--crates/assists/src/utils/import_assets.rs37
-rw-r--r--crates/base_db/src/input.rs7
-rw-r--r--crates/completion/Cargo.toml2
-rw-r--r--crates/completion/src/completions.rs5
-rw-r--r--crates/completion/src/completions/attribute.rs2
-rw-r--r--crates/completion/src/completions/postfix.rs2
-rw-r--r--crates/completion/src/completions/postfix/format_like.rs13
-rw-r--r--crates/completion/src/completions/record.rs68
-rw-r--r--crates/completion/src/completions/trait_impl.rs47
-rw-r--r--crates/completion/src/completions/unqualified_path.rs15
-rw-r--r--crates/completion/src/lib.rs2
-rw-r--r--crates/completion/src/render.rs3
-rw-r--r--crates/completion/src/render/const_.rs8
-rw-r--r--crates/completion/src/render/function.rs10
-rw-r--r--crates/completion/src/render/macro_.rs15
-rw-r--r--crates/completion/src/render/type_alias.rs8
-rw-r--r--crates/hir/Cargo.toml2
-rw-r--r--crates/hir/src/attrs.rs28
-rw-r--r--crates/hir/src/code_model.rs48
-rw-r--r--crates/hir/src/from_id.rs29
-rw-r--r--crates/hir/src/has_source.rs77
-rw-r--r--crates/hir/src/lib.rs7
-rw-r--r--crates/hir/src/semantics.rs12
-rw-r--r--crates/hir/src/semantics/source_to_def.rs18
-rw-r--r--crates/hir/src/source_analyzer.rs1
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/attr.rs36
-rw-r--r--crates/hir_def/src/generics.rs44
-rw-r--r--crates/hir_def/src/import_map.rs224
-rw-r--r--crates/hir_def/src/item_tree.rs9
-rw-r--r--crates/hir_def/src/keys.rs5
-rw-r--r--crates/hir_def/src/lib.rs29
-rw-r--r--crates/hir_def/src/nameres.rs4
-rw-r--r--crates/hir_def/src/nameres/tests/macros.rs4
-rw-r--r--crates/hir_def/src/path/lower.rs2
-rw-r--r--crates/hir_def/src/resolver.rs17
-rw-r--r--crates/hir_expand/src/builtin_macro.rs3
-rw-r--r--crates/hir_expand/src/db.rs2
-rw-r--r--crates/hir_expand/src/hygiene.rs151
-rw-r--r--crates/hir_expand/src/lib.rs11
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/db.rs7
-rw-r--r--crates/hir_ty/src/diagnostics/expr.rs26
-rw-r--r--crates/hir_ty/src/infer/expr.rs2
-rw-r--r--crates/hir_ty/src/infer/path.rs1
-rw-r--r--crates/hir_ty/src/lib.rs4
-rw-r--r--crates/hir_ty/src/lower.rs15
-rw-r--r--crates/hir_ty/src/tests/macros.rs31
-rw-r--r--crates/hir_ty/src/tests/regression.rs18
-rw-r--r--crates/hir_ty/src/tests/simple.rs16
-rw-r--r--crates/ide/Cargo.toml2
-rw-r--r--crates/ide/src/call_hierarchy.rs8
-rw-r--r--crates/ide/src/diagnostics.rs22
-rw-r--r--crates/ide/src/diagnostics/fixes.rs9
-rw-r--r--crates/ide/src/display/navigation_target.rs137
-rw-r--r--crates/ide/src/doc_links.rs1
-rw-r--r--crates/ide/src/goto_implementation.rs6
-rw-r--r--crates/ide/src/goto_type_definition.rs4
-rw-r--r--crates/ide/src/hover.rs74
-rw-r--r--crates/ide/src/lib.rs2
-rw-r--r--crates/ide/src/references.rs16
-rw-r--r--crates/ide/src/runnables.rs2
-rw-r--r--crates/ide/src/syntax_highlighting.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/injection.rs13
-rw-r--r--crates/ide/src/syntax_highlighting/tags.rs1
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlighting.html4
-rw-r--r--crates/ide/src/syntax_highlighting/tests.rs4
-rw-r--r--crates/ide_db/Cargo.toml2
-rw-r--r--crates/ide_db/src/defs.rs13
-rw-r--r--crates/ide_db/src/imports_locator.rs23
-rw-r--r--crates/ide_db/src/search.rs53
-rw-r--r--crates/mbe/src/lib.rs58
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs34
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs47
-rw-r--r--crates/mbe/src/parser.rs84
-rw-r--r--crates/mbe/src/syntax_bridge.rs3
-rw-r--r--crates/mbe/src/tests.rs48
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/proc_macro_api/src/msg.rs8
-rw-r--r--crates/proc_macro_api/src/process.rs7
-rw-r--r--crates/proc_macro_srv/src/rustc_server.rs43
-rw-r--r--crates/project_model/Cargo.toml2
-rw-r--r--crates/project_model/src/cargo_workspace.rs23
-rw-r--r--crates/project_model/src/project_json.rs3
-rw-r--r--crates/rust-analyzer/Cargo.toml3
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs11
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/diff.rs53
-rw-r--r--crates/rust-analyzer/src/handlers.rs25
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/markdown.rs13
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs2
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/main.rs31
-rw-r--r--crates/ssr/Cargo.toml2
-rw-r--r--crates/ssr/src/matching.rs4
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/edit.rs4
-rw-r--r--crates/syntax/src/ast/node_ext.rs8
-rw-r--r--docs/dev/style.md12
-rw-r--r--docs/user/manual.adoc2
-rw-r--r--editors/code/package.json8
-rw-r--r--editors/code/src/client.ts11
-rw-r--r--editors/code/src/commands.ts10
-rw-r--r--editors/code/src/config.ts3
-rw-r--r--editors/code/src/ctx.ts2
-rw-r--r--xtask/src/dist.rs10
-rw-r--r--xtask/tests/tidy.rs2
120 files changed, 1976 insertions, 633 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 7b9fbe6b8..889ada401 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -124,10 +124,10 @@ jobs:
124 - name: Checkout repository 124 - name: Checkout repository
125 uses: actions/checkout@v2 125 uses: actions/checkout@v2
126 126
127 - name: Install Rust toolchain (beta) 127 - name: Install Rust toolchain
128 uses: actions-rs/toolchain@v1 128 uses: actions-rs/toolchain@v1
129 with: 129 with:
130 toolchain: beta 130 toolchain: stable
131 target: aarch64-apple-darwin 131 target: aarch64-apple-darwin
132 profile: minimal 132 profile: minimal
133 override: true 133 override: true
diff --git a/Cargo.lock b/Cargo.lock
index fd04ec3c5..1aa0c072d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -26,9 +26,9 @@ dependencies = [
26 26
27[[package]] 27[[package]]
28name = "anyhow" 28name = "anyhow"
29version = "1.0.36" 29version = "1.0.37"
30source = "registry+https://github.com/rust-lang/crates.io-index" 30source = "registry+https://github.com/rust-lang/crates.io-index"
31checksum = "68803225a7b13e47191bab76f2687382b60d259e8cf37f6e1893658b84bb9479" 31checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86"
32 32
33[[package]] 33[[package]]
34name = "anymap" 34name = "anymap"
@@ -53,7 +53,7 @@ dependencies = [
53 "either", 53 "either",
54 "hir", 54 "hir",
55 "ide_db", 55 "ide_db",
56 "itertools", 56 "itertools 0.10.0",
57 "profile", 57 "profile",
58 "rustc-hash", 58 "rustc-hash",
59 "stdx", 59 "stdx",
@@ -211,7 +211,7 @@ dependencies = [
211 "chalk-derive", 211 "chalk-derive",
212 "chalk-ir", 212 "chalk-ir",
213 "ena", 213 "ena",
214 "itertools", 214 "itertools 0.9.0",
215 "petgraph", 215 "petgraph",
216 "rustc-hash", 216 "rustc-hash",
217 "tracing", 217 "tracing",
@@ -250,7 +250,7 @@ dependencies = [
250 "expect-test", 250 "expect-test",
251 "hir", 251 "hir",
252 "ide_db", 252 "ide_db",
253 "itertools", 253 "itertools 0.10.0",
254 "log", 254 "log",
255 "profile", 255 "profile",
256 "rustc-hash", 256 "rustc-hash",
@@ -349,6 +349,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
349checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" 349checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
350 350
351[[package]] 351[[package]]
352name = "dissimilar"
353version = "1.0.2"
354source = "registry+https://github.com/rust-lang/crates.io-index"
355checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb"
356
357[[package]]
352name = "drop_bomb" 358name = "drop_bomb"
353version = "0.1.5" 359version = "0.1.5"
354source = "registry+https://github.com/rust-lang/crates.io-index" 360source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -534,7 +540,7 @@ dependencies = [
534 "hir_def", 540 "hir_def",
535 "hir_expand", 541 "hir_expand",
536 "hir_ty", 542 "hir_ty",
537 "itertools", 543 "itertools 0.10.0",
538 "log", 544 "log",
539 "profile", 545 "profile",
540 "rustc-hash", 546 "rustc-hash",
@@ -557,7 +563,7 @@ dependencies = [
557 "fst", 563 "fst",
558 "hir_expand", 564 "hir_expand",
559 "indexmap", 565 "indexmap",
560 "itertools", 566 "itertools 0.10.0",
561 "log", 567 "log",
562 "mbe", 568 "mbe",
563 "once_cell", 569 "once_cell",
@@ -601,7 +607,7 @@ dependencies = [
601 "expect-test", 607 "expect-test",
602 "hir_def", 608 "hir_def",
603 "hir_expand", 609 "hir_expand",
604 "itertools", 610 "itertools 0.10.0",
605 "log", 611 "log",
606 "once_cell", 612 "once_cell",
607 "profile", 613 "profile",
@@ -637,7 +643,7 @@ dependencies = [
637 "hir", 643 "hir",
638 "ide_db", 644 "ide_db",
639 "indexmap", 645 "indexmap",
640 "itertools", 646 "itertools 0.10.0",
641 "log", 647 "log",
642 "oorandom", 648 "oorandom",
643 "profile", 649 "profile",
@@ -661,7 +667,7 @@ dependencies = [
661 "expect-test", 667 "expect-test",
662 "fst", 668 "fst",
663 "hir", 669 "hir",
664 "itertools", 670 "itertools 0.10.0",
665 "log", 671 "log",
666 "once_cell", 672 "once_cell",
667 "profile", 673 "profile",
@@ -742,10 +748,19 @@ dependencies = [
742] 748]
743 749
744[[package]] 750[[package]]
751name = "itertools"
752version = "0.10.0"
753source = "registry+https://github.com/rust-lang/crates.io-index"
754checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
755dependencies = [
756 "either",
757]
758
759[[package]]
745name = "itoa" 760name = "itoa"
746version = "0.4.6" 761version = "0.4.7"
747source = "registry+https://github.com/rust-lang/crates.io-index" 762source = "registry+https://github.com/rust-lang/crates.io-index"
748checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" 763checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
749 764
750[[package]] 765[[package]]
751name = "jod-thread" 766name = "jod-thread"
@@ -1219,7 +1234,7 @@ dependencies = [
1219 "base_db", 1234 "base_db",
1220 "cargo_metadata", 1235 "cargo_metadata",
1221 "cfg", 1236 "cfg",
1222 "itertools", 1237 "itertools 0.10.0",
1223 "log", 1238 "log",
1224 "paths", 1239 "paths",
1225 "proc_macro_api", 1240 "proc_macro_api",
@@ -1334,6 +1349,7 @@ dependencies = [
1334 "anyhow", 1349 "anyhow",
1335 "cfg", 1350 "cfg",
1336 "crossbeam-channel 0.5.0", 1351 "crossbeam-channel 0.5.0",
1352 "dissimilar",
1337 "env_logger", 1353 "env_logger",
1338 "expect-test", 1354 "expect-test",
1339 "flycheck", 1355 "flycheck",
@@ -1342,7 +1358,7 @@ dependencies = [
1342 "hir_ty", 1358 "hir_ty",
1343 "ide", 1359 "ide",
1344 "ide_db", 1360 "ide_db",
1345 "itertools", 1361 "itertools 0.10.0",
1346 "jod-thread", 1362 "jod-thread",
1347 "log", 1363 "log",
1348 "lsp-server", 1364 "lsp-server",
@@ -1518,9 +1534,9 @@ dependencies = [
1518 1534
1519[[package]] 1535[[package]]
1520name = "serde_json" 1536name = "serde_json"
1521version = "1.0.60" 1537version = "1.0.61"
1522source = "registry+https://github.com/rust-lang/crates.io-index" 1538source = "registry+https://github.com/rust-lang/crates.io-index"
1523checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" 1539checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
1524dependencies = [ 1540dependencies = [
1525 "indexmap", 1541 "indexmap",
1526 "itoa", 1542 "itoa",
@@ -1586,7 +1602,7 @@ dependencies = [
1586 "expect-test", 1602 "expect-test",
1587 "hir", 1603 "hir",
1588 "ide_db", 1604 "ide_db",
1589 "itertools", 1605 "itertools 0.10.0",
1590 "rustc-hash", 1606 "rustc-hash",
1591 "syntax", 1607 "syntax",
1592 "test_utils", 1608 "test_utils",
@@ -1599,9 +1615,9 @@ version = "0.0.0"
1599 1615
1600[[package]] 1616[[package]]
1601name = "syn" 1617name = "syn"
1602version = "1.0.55" 1618version = "1.0.56"
1603source = "registry+https://github.com/rust-lang/crates.io-index" 1619source = "registry+https://github.com/rust-lang/crates.io-index"
1604checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" 1620checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72"
1605dependencies = [ 1621dependencies = [
1606 "proc-macro2", 1622 "proc-macro2",
1607 "quote", 1623 "quote",
@@ -1627,7 +1643,7 @@ dependencies = [
1627 "arrayvec", 1643 "arrayvec",
1628 "expect-test", 1644 "expect-test",
1629 "indexmap", 1645 "indexmap",
1630 "itertools", 1646 "itertools 0.10.0",
1631 "once_cell", 1647 "once_cell",
1632 "parser", 1648 "parser",
1633 "profile", 1649 "profile",
diff --git a/crates/assists/Cargo.toml b/crates/assists/Cargo.toml
index 3fd8327d6..ed8ad666f 100644
--- a/crates/assists/Cargo.toml
+++ b/crates/assists/Cargo.toml
@@ -11,7 +11,7 @@ doctest = false
11 11
12[dependencies] 12[dependencies]
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.9.0" 14itertools = "0.10.0"
15either = "1.6.1" 15either = "1.6.1"
16 16
17stdx = { path = "../stdx", version = "0.0.0" } 17stdx = { path = "../stdx", version = "0.0.0" }
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs
index da94e9987..4a3ed7783 100644
--- a/crates/assists/src/ast_transform.rs
+++ b/crates/assists/src/ast_transform.rs
@@ -204,7 +204,8 @@ impl<'a> AstTransform<'a> for QualifyPaths<'a> {
204 } 204 }
205 PathResolution::Local(_) 205 PathResolution::Local(_)
206 | PathResolution::TypeParam(_) 206 | PathResolution::TypeParam(_)
207 | PathResolution::SelfType(_) => None, 207 | PathResolution::SelfType(_)
208 | PathResolution::ConstParam(_) => None,
208 PathResolution::Macro(_) => None, 209 PathResolution::Macro(_) => None,
209 PathResolution::AssocItem(_) => None, 210 PathResolution::AssocItem(_) => None,
210 } 211 }
diff --git a/crates/assists/src/handlers/extract_assignment.rs b/crates/assists/src/handlers/extract_assignment.rs
new file mode 100644
index 000000000..281cf5d24
--- /dev/null
+++ b/crates/assists/src/handlers/extract_assignment.rs
@@ -0,0 +1,325 @@
1use hir::AsName;
2use syntax::{
3 ast::{self, edit::AstNodeEdit, make},
4 AstNode,
5};
6use test_utils::mark;
7
8use crate::{
9 assist_context::{AssistContext, Assists},
10 AssistId, AssistKind,
11};
12
13// Assist: extract_assignment
14//
15// Extracts variable assigment to outside an if or match statement.
16//
17// ```
18// fn main() {
19// let mut foo = 6;
20//
21// if true {
22// <|>foo = 5;
23// } else {
24// foo = 4;
25// }
26// }
27// ```
28// ->
29// ```
30// fn main() {
31// let mut foo = 6;
32//
33// foo = if true {
34// 5
35// } else {
36// 4
37// };
38// }
39// ```
40pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41 let name = ctx.find_node_at_offset::<ast::NameRef>()?.as_name();
42
43 let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
44 (
45 ast::Expr::cast(if_expr.syntax().to_owned())?,
46 exprify_if(&if_expr, &name)?.indent(if_expr.indent_level()),
47 )
48 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
49 (ast::Expr::cast(match_expr.syntax().to_owned())?, exprify_match(&match_expr, &name)?)
50 } else {
51 return None;
52 };
53
54 let expr_stmt = make::expr_stmt(new_stmt);
55
56 acc.add(
57 AssistId("extract_assignment", AssistKind::RefactorExtract),
58 "Extract assignment",
59 old_stmt.syntax().text_range(),
60 move |edit| {
61 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name, expr_stmt));
62 },
63 )
64}
65
66fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::Expr> {
67 let new_arm_list = match_expr
68 .match_arm_list()?
69 .arms()
70 .map(|arm| {
71 if let ast::Expr::BlockExpr(block) = arm.expr()? {
72 let new_block = exprify_block(&block, name)?.indent(block.indent_level());
73 Some(arm.replace_descendant(block, new_block))
74 } else {
75 None
76 }
77 })
78 .collect::<Option<Vec<_>>>()?;
79 let new_arm_list = match_expr
80 .match_arm_list()?
81 .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list));
82 Some(make::expr_match(match_expr.expr()?, new_arm_list))
83}
84
85fn exprify_if(statement: &ast::IfExpr, name: &hir::Name) -> Option<ast::Expr> {
86 let then_branch = exprify_block(&statement.then_branch()?, name)?;
87 let else_branch = match statement.else_branch()? {
88 ast::ElseBranch::Block(ref block) => ast::ElseBranch::Block(exprify_block(block, name)?),
89 ast::ElseBranch::IfExpr(expr) => {
90 mark::hit!(test_extract_assigment_chained_if);
91 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
92 exprify_if(&expr, name)?.syntax().to_owned(),
93 )?)
94 }
95 };
96 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
97}
98
99fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockExpr> {
100 if block.expr().is_some() {
101 return None;
102 }
103
104 let mut stmts: Vec<_> = block.statements().collect();
105 let stmt = stmts.pop()?;
106
107 if let ast::Stmt::ExprStmt(stmt) = stmt {
108 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
109 if expr.op_kind()? == ast::BinOp::Assignment
110 && &expr.lhs()?.name_ref()?.as_name() == name
111 {
112 // The last statement in the block is an assignment to the name we want
113 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
114 }
115 }
116 }
117 None
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123
124 use crate::tests::{check_assist, check_assist_not_applicable};
125
126 #[test]
127 fn test_extract_assignment_if() {
128 check_assist(
129 extract_assigment,
130 r#"
131fn foo() {
132 let mut a = 1;
133
134 if true {
135 <|>a = 2;
136 } else {
137 a = 3;
138 }
139}"#,
140 r#"
141fn foo() {
142 let mut a = 1;
143
144 a = if true {
145 2
146 } else {
147 3
148 };
149}"#,
150 );
151 }
152
153 #[test]
154 fn test_extract_assignment_match() {
155 check_assist(
156 extract_assigment,
157 r#"
158fn foo() {
159 let mut a = 1;
160
161 match 1 {
162 1 => {
163 <|>a = 2;
164 },
165 2 => {
166 a = 3;
167 },
168 3 => {
169 a = 4;
170 }
171 }
172}"#,
173 r#"
174fn foo() {
175 let mut a = 1;
176
177 a = match 1 {
178 1 => {
179 2
180 },
181 2 => {
182 3
183 },
184 3 => {
185 4
186 }
187 };
188}"#,
189 );
190 }
191
192 #[test]
193 fn test_extract_assignment_not_last_not_applicable() {
194 check_assist_not_applicable(
195 extract_assigment,
196 r#"
197fn foo() {
198 let mut a = 1;
199
200 if true {
201 <|>a = 2;
202 b = a;
203 } else {
204 a = 3;
205 }
206}"#,
207 )
208 }
209
210 #[test]
211 fn test_extract_assignment_chained_if() {
212 mark::check!(test_extract_assigment_chained_if);
213 check_assist(
214 extract_assigment,
215 r#"
216fn foo() {
217 let mut a = 1;
218
219 if true {
220 <|>a = 2;
221 } else if false {
222 a = 3;
223 } else {
224 a = 4;
225 }
226}"#,
227 r#"
228fn foo() {
229 let mut a = 1;
230
231 a = if true {
232 2
233 } else if false {
234 3
235 } else {
236 4
237 };
238}"#,
239 );
240 }
241
242 #[test]
243 fn test_extract_assigment_retains_stmts() {
244 check_assist(
245 extract_assigment,
246 r#"
247fn foo() {
248 let mut a = 1;
249
250 if true {
251 let b = 2;
252 <|>a = 2;
253 } else {
254 let b = 3;
255 a = 3;
256 }
257}"#,
258 r#"
259fn foo() {
260 let mut a = 1;
261
262 a = if true {
263 let b = 2;
264 2
265 } else {
266 let b = 3;
267 3
268 };
269}"#,
270 )
271 }
272
273 #[test]
274 fn extract_assignment_let_stmt_not_applicable() {
275 check_assist_not_applicable(
276 extract_assigment,
277 r#"
278fn foo() {
279 let mut a = 1;
280
281 let b = if true {
282 <|>a = 2
283 } else {
284 a = 3
285 };
286}"#,
287 )
288 }
289
290 #[test]
291 fn extract_assignment_if_missing_assigment_not_applicable() {
292 check_assist_not_applicable(
293 extract_assigment,
294 r#"
295fn foo() {
296 let mut a = 1;
297
298 if true {
299 <|>a = 2;
300 } else {}
301}"#,
302 )
303 }
304
305 #[test]
306 fn extract_assignment_match_missing_assigment_not_applicable() {
307 check_assist_not_applicable(
308 extract_assigment,
309 r#"
310fn foo() {
311 let mut a = 1;
312
313 match 1 {
314 1 => {
315 <|>a = 2;
316 },
317 2 => {
318 a = 3;
319 },
320 3 => {},
321 }
322}"#,
323 )
324 }
325}
diff --git a/crates/assists/src/handlers/extract_variable.rs b/crates/assists/src/handlers/extract_variable.rs
index d2ae137cd..9957012fe 100644
--- a/crates/assists/src/handlers/extract_variable.rs
+++ b/crates/assists/src/handlers/extract_variable.rs
@@ -91,7 +91,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
91 // extra newlines in the indent block 91 // extra newlines in the indent block
92 let text = indent.text(); 92 let text = indent.text();
93 if text.starts_with('\n') { 93 if text.starts_with('\n') {
94 buf.push_str("\n"); 94 buf.push('\n');
95 buf.push_str(text.trim_start_matches('\n')); 95 buf.push_str(text.trim_start_matches('\n'));
96 } else { 96 } else {
97 buf.push_str(text); 97 buf.push_str(text);
diff --git a/crates/assists/src/handlers/fill_match_arms.rs b/crates/assists/src/handlers/fill_match_arms.rs
index cb60a3128..f9a62b9fa 100644
--- a/crates/assists/src/handlers/fill_match_arms.rs
+++ b/crates/assists/src/handlers/fill_match_arms.rs
@@ -196,7 +196,7 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio
196 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); 196 let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
197 197
198 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 198 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
199 let pat: ast::Pat = match var.source(db).value.kind() { 199 let pat: ast::Pat = match var.source(db)?.value.kind() {
200 ast::StructKind::Tuple(field_list) => { 200 ast::StructKind::Tuple(field_list) => {
201 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); 201 let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
202 make::tuple_struct_pat(path, pats).into() 202 make::tuple_struct_pat(path, pats).into()
diff --git a/crates/assists/src/handlers/fix_visibility.rs b/crates/assists/src/handlers/fix_visibility.rs
index 8558a8ff0..de1e8f0bf 100644
--- a/crates/assists/src/handlers/fix_visibility.rs
+++ b/crates/assists/src/handlers/fix_visibility.rs
@@ -97,7 +97,8 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
97 let parent_name = parent.name(ctx.db()); 97 let parent_name = parent.name(ctx.db());
98 let target_module = parent.module(ctx.db()); 98 let target_module = parent.module(ctx.db());
99 99
100 let in_file_source = record_field_def.source(ctx.db()); 100 #[allow(deprecated)]
101 let in_file_source = record_field_def.source(ctx.db())?;
101 let (offset, current_visibility, target) = match in_file_source.value { 102 let (offset, current_visibility, target) = match in_file_source.value {
102 hir::FieldSource::Named(it) => { 103 hir::FieldSource::Named(it) => {
103 let s = it.syntax(); 104 let s = it.syntax();
@@ -145,53 +146,53 @@ fn target_data_for_def(
145 fn offset_target_and_file_id<S, Ast>( 146 fn offset_target_and_file_id<S, Ast>(
146 db: &dyn HirDatabase, 147 db: &dyn HirDatabase,
147 x: S, 148 x: S,
148 ) -> (TextSize, Option<ast::Visibility>, TextRange, FileId) 149 ) -> Option<(TextSize, Option<ast::Visibility>, TextRange, FileId)>
149 where 150 where
150 S: HasSource<Ast = Ast>, 151 S: HasSource<Ast = Ast>,
151 Ast: AstNode + ast::VisibilityOwner, 152 Ast: AstNode + ast::VisibilityOwner,
152 { 153 {
153 let source = x.source(db); 154 let source = x.source(db)?;
154 let in_file_syntax = source.syntax(); 155 let in_file_syntax = source.syntax();
155 let file_id = in_file_syntax.file_id; 156 let file_id = in_file_syntax.file_id;
156 let syntax = in_file_syntax.value; 157 let syntax = in_file_syntax.value;
157 let current_visibility = source.value.visibility(); 158 let current_visibility = source.value.visibility();
158 ( 159 Some((
159 vis_offset(syntax), 160 vis_offset(syntax),
160 current_visibility, 161 current_visibility,
161 syntax.text_range(), 162 syntax.text_range(),
162 file_id.original_file(db.upcast()), 163 file_id.original_file(db.upcast()),
163 ) 164 ))
164 } 165 }
165 166
166 let target_name; 167 let target_name;
167 let (offset, current_visibility, target, target_file) = match def { 168 let (offset, current_visibility, target, target_file) = match def {
168 hir::ModuleDef::Function(f) => { 169 hir::ModuleDef::Function(f) => {
169 target_name = Some(f.name(db)); 170 target_name = Some(f.name(db));
170 offset_target_and_file_id(db, f) 171 offset_target_and_file_id(db, f)?
171 } 172 }
172 hir::ModuleDef::Adt(adt) => { 173 hir::ModuleDef::Adt(adt) => {
173 target_name = Some(adt.name(db)); 174 target_name = Some(adt.name(db));
174 match adt { 175 match adt {
175 hir::Adt::Struct(s) => offset_target_and_file_id(db, s), 176 hir::Adt::Struct(s) => offset_target_and_file_id(db, s)?,
176 hir::Adt::Union(u) => offset_target_and_file_id(db, u), 177 hir::Adt::Union(u) => offset_target_and_file_id(db, u)?,
177 hir::Adt::Enum(e) => offset_target_and_file_id(db, e), 178 hir::Adt::Enum(e) => offset_target_and_file_id(db, e)?,
178 } 179 }
179 } 180 }
180 hir::ModuleDef::Const(c) => { 181 hir::ModuleDef::Const(c) => {
181 target_name = c.name(db); 182 target_name = c.name(db);
182 offset_target_and_file_id(db, c) 183 offset_target_and_file_id(db, c)?
183 } 184 }
184 hir::ModuleDef::Static(s) => { 185 hir::ModuleDef::Static(s) => {
185 target_name = s.name(db); 186 target_name = s.name(db);
186 offset_target_and_file_id(db, s) 187 offset_target_and_file_id(db, s)?
187 } 188 }
188 hir::ModuleDef::Trait(t) => { 189 hir::ModuleDef::Trait(t) => {
189 target_name = Some(t.name(db)); 190 target_name = Some(t.name(db));
190 offset_target_and_file_id(db, t) 191 offset_target_and_file_id(db, t)?
191 } 192 }
192 hir::ModuleDef::TypeAlias(t) => { 193 hir::ModuleDef::TypeAlias(t) => {
193 target_name = Some(t.name(db)); 194 target_name = Some(t.name(db));
194 offset_target_and_file_id(db, t) 195 offset_target_and_file_id(db, t)?
195 } 196 }
196 hir::ModuleDef::Module(m) => { 197 hir::ModuleDef::Module(m) => {
197 target_name = m.name(db); 198 target_name = m.name(db);
diff --git a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
index 4d6a1956b..cb7a5c104 100644
--- a/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -62,21 +62,22 @@ pub(crate) fn replace_derive_with_manual_impl(
62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; 62 let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
63 let current_crate = current_module.krate(); 63 let current_crate = current_module.krate();
64 64
65 let found_traits = 65 let found_traits = imports_locator::find_exact_imports(
66 imports_locator::find_exact_imports(&ctx.sema, current_crate, trait_token.text()) 66 &ctx.sema,
67 .filter_map( 67 current_crate,
68 |candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { 68 trait_token.text().to_string(),
69 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), 69 )
70 _ => None, 70 .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate {
71 }, 71 either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_),
72 ) 72 _ => None,
73 .flat_map(|trait_| { 73 })
74 current_module 74 .flat_map(|trait_| {
75 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) 75 current_module
76 .as_ref() 76 .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_))
77 .map(mod_path_to_ast) 77 .as_ref()
78 .zip(Some(trait_)) 78 .map(mod_path_to_ast)
79 }); 79 .zip(Some(trait_))
80 });
80 81
81 let mut no_traits_found = true; 82 let mut no_traits_found = true;
82 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 83 for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index fdec886e9..212464f85 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -116,6 +116,7 @@ mod handlers {
116 mod convert_integer_literal; 116 mod convert_integer_literal;
117 mod early_return; 117 mod early_return;
118 mod expand_glob_import; 118 mod expand_glob_import;
119 mod extract_assignment;
119 mod extract_module_to_file; 120 mod extract_module_to_file;
120 mod extract_struct_from_enum_variant; 121 mod extract_struct_from_enum_variant;
121 mod extract_variable; 122 mod extract_variable;
@@ -167,6 +168,7 @@ mod handlers {
167 convert_integer_literal::convert_integer_literal, 168 convert_integer_literal::convert_integer_literal,
168 early_return::convert_to_guarded_return, 169 early_return::convert_to_guarded_return,
169 expand_glob_import::expand_glob_import, 170 expand_glob_import::expand_glob_import,
171 extract_assignment::extract_assigment,
170 extract_module_to_file::extract_module_to_file, 172 extract_module_to_file::extract_module_to_file,
171 extract_struct_from_enum_variant::extract_struct_from_enum_variant, 173 extract_struct_from_enum_variant::extract_struct_from_enum_variant,
172 extract_variable::extract_variable, 174 extract_variable::extract_variable,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index d3dfe24e7..b91a816e8 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -238,6 +238,35 @@ fn qux(bar: Bar, baz: Baz) {}
238} 238}
239 239
240#[test] 240#[test]
241fn doctest_extract_assignment() {
242 check_doc_test(
243 "extract_assignment",
244 r#####"
245fn main() {
246 let mut foo = 6;
247
248 if true {
249 <|>foo = 5;
250 } else {
251 foo = 4;
252 }
253}
254"#####,
255 r#####"
256fn main() {
257 let mut foo = 6;
258
259 foo = if true {
260 5
261 } else {
262 4
263 };
264}
265"#####,
266 )
267}
268
269#[test]
241fn doctest_extract_module_to_file() { 270fn doctest_extract_module_to_file() {
242 check_doc_test( 271 check_doc_test(
243 "extract_module_to_file", 272 "extract_module_to_file",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index d41084b59..b05596446 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -94,14 +94,18 @@ pub fn filter_assoc_items(
94 ast::AssocItem::MacroCall(_) => None, 94 ast::AssocItem::MacroCall(_) => None,
95 } 95 }
96 .is_some() 96 .is_some()
97 }; 97 }
98 98
99 items 99 items
100 .iter() 100 .iter()
101 .map(|i| match i { 101 // Note: This throws away items with no source.
102 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db).value), 102 .filter_map(|i| {
103 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db).value), 103 let item = match i {
104 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db).value), 104 hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db)?.value),
105 hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db)?.value),
106 hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db)?.value),
107 };
108 Some(item)
105 }) 109 })
106 .filter(has_def_name) 110 .filter(has_def_name)
107 .filter(|it| match it { 111 .filter(|it| match it {
diff --git a/crates/assists/src/utils/import_assets.rs b/crates/assists/src/utils/import_assets.rs
index ff5c0e78e..4ce82c1ba 100644
--- a/crates/assists/src/utils/import_assets.rs
+++ b/crates/assists/src/utils/import_assets.rs
@@ -179,25 +179,24 @@ impl ImportAssets {
179 } 179 }
180 }; 180 };
181 181
182 let mut res = 182 let mut res = imports_locator::find_exact_imports(
183 imports_locator::find_exact_imports(sema, current_crate, &self.get_search_query()) 183 sema,
184 .filter_map(filter) 184 current_crate,
185 .filter_map(|candidate| { 185 self.get_search_query().to_string(),
186 let item: hir::ItemInNs = candidate.either(Into::into, Into::into); 186 )
187 if let Some(prefix_kind) = prefixed { 187 .filter_map(filter)
188 self.module_with_name_to_import.find_use_path_prefixed( 188 .filter_map(|candidate| {
189 db, 189 let item: hir::ItemInNs = candidate.either(Into::into, Into::into);
190 item, 190 if let Some(prefix_kind) = prefixed {
191 prefix_kind, 191 self.module_with_name_to_import.find_use_path_prefixed(db, item, prefix_kind)
192 ) 192 } else {
193 } else { 193 self.module_with_name_to_import.find_use_path(db, item)
194 self.module_with_name_to_import.find_use_path(db, item) 194 }
195 } 195 .map(|path| (path, item))
196 .map(|path| (path, item)) 196 })
197 }) 197 .filter(|(use_path, _)| use_path.len() > 1)
198 .filter(|(use_path, _)| use_path.len() > 1) 198 .take(20)
199 .take(20) 199 .collect::<Vec<_>>();
200 .collect::<Vec<_>>();
201 res.sort_by_key(|(path, _)| path.clone()); 200 res.sort_by_key(|(path, _)| path.clone());
202 res 201 res
203 } 202 }
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 9567bcc42..2dd8fbe67 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -190,10 +190,11 @@ pub struct CrateData {
190 pub proc_macro: Vec<ProcMacro>, 190 pub proc_macro: Vec<ProcMacro>,
191} 191}
192 192
193#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 193#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
194pub enum Edition { 194pub enum Edition {
195 Edition2018,
196 Edition2015, 195 Edition2015,
196 Edition2018,
197 Edition2021,
197} 198}
198 199
199#[derive(Default, Debug, Clone, PartialEq, Eq)] 200#[derive(Default, Debug, Clone, PartialEq, Eq)]
@@ -393,6 +394,7 @@ impl FromStr for Edition {
393 let res = match s { 394 let res = match s {
394 "2015" => Edition::Edition2015, 395 "2015" => Edition::Edition2015,
395 "2018" => Edition::Edition2018, 396 "2018" => Edition::Edition2018,
397 "2021" => Edition::Edition2021,
396 _ => return Err(ParseEditionError { invalid_input: s.to_string() }), 398 _ => return Err(ParseEditionError { invalid_input: s.to_string() }),
397 }; 399 };
398 Ok(res) 400 Ok(res)
@@ -404,6 +406,7 @@ impl fmt::Display for Edition {
404 f.write_str(match self { 406 f.write_str(match self {
405 Edition::Edition2015 => "2015", 407 Edition::Edition2015 => "2015",
406 Edition::Edition2018 => "2018", 408 Edition::Edition2018 => "2018",
409 Edition::Edition2021 => "2021",
407 }) 410 })
408 } 411 }
409} 412}
diff --git a/crates/completion/Cargo.toml b/crates/completion/Cargo.toml
index 35e169a28..78e93e78e 100644
--- a/crates/completion/Cargo.toml
+++ b/crates/completion/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13itertools = "0.9.0" 13itertools = "0.10.0"
14log = "0.4.8" 14log = "0.4.8"
15rustc-hash = "1.1.0" 15rustc-hash = "1.1.0"
16either = "1.6.1" 16either = "1.6.1"
diff --git a/crates/completion/src/completions.rs b/crates/completion/src/completions.rs
index d9fe13485..00c9e76f0 100644
--- a/crates/completion/src/completions.rs
+++ b/crates/completion/src/completions.rs
@@ -106,8 +106,9 @@ impl Completions {
106 func: hir::Function, 106 func: hir::Function,
107 local_name: Option<String>, 107 local_name: Option<String>,
108 ) { 108 ) {
109 let item = render_fn(RenderContext::new(ctx), None, local_name, func); 109 if let Some(item) = render_fn(RenderContext::new(ctx), None, local_name, func) {
110 self.add(item) 110 self.add(item)
111 }
111 } 112 }
112 113
113 pub(crate) fn add_variant_pat( 114 pub(crate) fn add_variant_pat(
diff --git a/crates/completion/src/completions/attribute.rs b/crates/completion/src/completions/attribute.rs
index 19ce2482f..8695eed39 100644
--- a/crates/completion/src/completions/attribute.rs
+++ b/crates/completion/src/completions/attribute.rs
@@ -234,7 +234,7 @@ fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<Strin
234 current_derive = String::new(); 234 current_derive = String::new();
235 } 235 }
236 } else { 236 } else {
237 current_derive.push_str(token.to_string().trim()); 237 current_derive.push_str(token.text().trim());
238 } 238 }
239 } 239 }
240 240
diff --git a/crates/completion/src/completions/postfix.rs b/crates/completion/src/completions/postfix.rs
index d6db82a93..3883d6d21 100644
--- a/crates/completion/src/completions/postfix.rs
+++ b/crates/completion/src/completions/postfix.rs
@@ -502,7 +502,7 @@ fn main() {
502 #[test] 502 #[test]
503 fn postfix_completion_for_format_like_strings() { 503 fn postfix_completion_for_format_like_strings() {
504 check_edit( 504 check_edit(
505 "fmt", 505 "format",
506 r#"fn main() { "{some_var:?}".<|> }"#, 506 r#"fn main() { "{some_var:?}".<|> }"#,
507 r#"fn main() { format!("{:?}", some_var) }"#, 507 r#"fn main() { format!("{:?}", some_var) }"#,
508 ); 508 );
diff --git a/crates/completion/src/completions/postfix/format_like.rs b/crates/completion/src/completions/postfix/format_like.rs
index 88ba86acb..def4b13fb 100644
--- a/crates/completion/src/completions/postfix/format_like.rs
+++ b/crates/completion/src/completions/postfix/format_like.rs
@@ -22,7 +22,7 @@ use syntax::ast::{self, AstToken};
22 22
23/// Mapping ("postfix completion item" => "macro to use") 23/// Mapping ("postfix completion item" => "macro to use")
24static KINDS: &[(&str, &str)] = &[ 24static KINDS: &[(&str, &str)] = &[
25 ("fmt", "format!"), 25 ("format", "format!"),
26 ("panic", "panic!"), 26 ("panic", "panic!"),
27 ("println", "println!"), 27 ("println", "println!"),
28 ("eprintln", "eprintln!"), 28 ("eprintln", "eprintln!"),
@@ -108,7 +108,8 @@ impl FormatStrParser {
108 // "{MyStruct { val_a: 0, val_b: 1 }}". 108 // "{MyStruct { val_a: 0, val_b: 1 }}".
109 let mut inexpr_open_count = 0; 109 let mut inexpr_open_count = 0;
110 110
111 for chr in self.input.chars() { 111 let mut chars = self.input.chars().peekable();
112 while let Some(chr) = chars.next() {
112 match (self.state, chr) { 113 match (self.state, chr) {
113 (State::NotExpr, '{') => { 114 (State::NotExpr, '{') => {
114 self.output.push(chr); 115 self.output.push(chr);
@@ -157,6 +158,11 @@ impl FormatStrParser {
157 inexpr_open_count -= 1; 158 inexpr_open_count -= 1;
158 } 159 }
159 } 160 }
161 (State::Expr, ':') if chars.peek().copied() == Some(':') => {
162 // path seperator
163 current_expr.push_str("::");
164 chars.next();
165 }
160 (State::Expr, ':') => { 166 (State::Expr, ':') => {
161 if inexpr_open_count == 0 { 167 if inexpr_open_count == 0 {
162 // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" 168 // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}"
@@ -249,6 +255,9 @@ mod tests {
249 expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], 255 expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]],
250 ), 256 ),
251 ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), 257 ("{ 2 + 2 }", expect![["{}; 2 + 2"]]),
258 ("{strsim::jaro_winkle(a)}", expect![["{}; strsim::jaro_winkle(a)"]]),
259 ("{foo::bar::baz()}", expect![["{}; foo::bar::baz()"]]),
260 ("{foo::bar():?}", expect![["{:?}; foo::bar()"]]),
252 ]; 261 ];
253 262
254 for (input, output) in test_vector { 263 for (input, output) in test_vector {
diff --git a/crates/completion/src/completions/record.rs b/crates/completion/src/completions/record.rs
index 91bf4a8ad..e58b9a274 100644
--- a/crates/completion/src/completions/record.rs
+++ b/crates/completion/src/completions/record.rs
@@ -20,13 +20,17 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
20 20
21 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit); 21 let missing_fields = ctx.sema.record_literal_missing_fields(record_lit);
22 if impl_default_trait && !missing_fields.is_empty() { 22 if impl_default_trait && !missing_fields.is_empty() {
23 let completion_text = "..Default::default()";
24 let completion_text = completion_text
25 .strip_prefix(ctx.token.to_string().as_str())
26 .unwrap_or(completion_text);
23 acc.add( 27 acc.add(
24 CompletionItem::new( 28 CompletionItem::new(
25 CompletionKind::Snippet, 29 CompletionKind::Snippet,
26 ctx.source_range(), 30 ctx.source_range(),
27 "..Default::default()", 31 "..Default::default()",
28 ) 32 )
29 .insert_text("..Default::default()") 33 .insert_text(completion_text)
30 .kind(CompletionItemKind::Field) 34 .kind(CompletionItemKind::Field)
31 .build(), 35 .build(),
32 ); 36 );
@@ -48,7 +52,10 @@ mod tests {
48 use expect_test::{expect, Expect}; 52 use expect_test::{expect, Expect};
49 use ide_db::helpers::FamousDefs; 53 use ide_db::helpers::FamousDefs;
50 54
51 use crate::{test_utils::completion_list, CompletionKind}; 55 use crate::{
56 test_utils::{self, completion_list},
57 CompletionKind,
58 };
52 59
53 fn check(ra_fixture: &str, expect: Expect) { 60 fn check(ra_fixture: &str, expect: Expect) {
54 let actual = completion_list(ra_fixture, CompletionKind::Reference); 61 let actual = completion_list(ra_fixture, CompletionKind::Reference);
@@ -63,6 +70,18 @@ mod tests {
63 expect.assert_eq(&actual); 70 expect.assert_eq(&actual);
64 } 71 }
65 72
73 fn check_edit(what: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
74 test_utils::check_edit(
75 what,
76 &format!(
77 "//- /main.rs crate:main deps:core{}\n{}",
78 ra_fixture_before,
79 FamousDefs::FIXTURE,
80 ),
81 &(ra_fixture_after.to_owned() + "\n"),
82 );
83 }
84
66 #[test] 85 #[test]
67 fn test_record_literal_field_default() { 86 fn test_record_literal_field_default() {
68 let test_code = r#" 87 let test_code = r#"
@@ -102,6 +121,51 @@ fn process(f: S) {
102 } 121 }
103 122
104 #[test] 123 #[test]
124 fn test_record_literal_field_default_completion() {
125 check_edit(
126 "..Default::default()",
127 r#"
128struct S { foo: u32, bar: usize }
129
130impl core::default::Default for S {
131 fn default() -> Self {
132 S {
133 foo: 0,
134 bar: 0,
135 }
136 }
137}
138
139fn process(f: S) {
140 let other = S {
141 foo: 5,
142 .<|>
143 };
144}
145"#,
146 r#"
147struct S { foo: u32, bar: usize }
148
149impl core::default::Default for S {
150 fn default() -> Self {
151 S {
152 foo: 0,
153 bar: 0,
154 }
155 }
156}
157
158fn process(f: S) {
159 let other = S {
160 foo: 5,
161 ..Default::default()
162 };
163}
164"#,
165 );
166 }
167
168 #[test]
105 fn test_record_literal_field_without_default() { 169 fn test_record_literal_field_without_default() {
106 let test_code = r#" 170 let test_code = r#"
107struct S { foo: u32, bar: usize } 171struct S { foo: u32, bar: usize }
diff --git a/crates/completion/src/completions/trait_impl.rs b/crates/completion/src/completions/trait_impl.rs
index c4e0d0669..54bb897e9 100644
--- a/crates/completion/src/completions/trait_impl.rs
+++ b/crates/completion/src/completions/trait_impl.rs
@@ -156,19 +156,21 @@ fn add_function_impl(
156 }; 156 };
157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end()); 157 let range = TextRange::new(fn_def_node.text_range().start(), ctx.source_range().end());
158 158
159 let function_decl = function_declaration(&func.source(ctx.db).value); 159 if let Some(src) = func.source(ctx.db) {
160 match ctx.config.snippet_cap { 160 let function_decl = function_declaration(&src.value);
161 Some(cap) => { 161 match ctx.config.snippet_cap {
162 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 Some(cap) => {
163 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 } 164 builder.snippet_edit(cap, TextEdit::replace(range, snippet))
165 None => { 165 }
166 let header = format!("{} {{", function_decl); 166 None => {
167 builder.text_edit(TextEdit::replace(range, header)) 167 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header))
169 }
168 } 170 }
171 .kind(completion_kind)
172 .add_to(acc);
169 } 173 }
170 .kind(completion_kind)
171 .add_to(acc);
172} 174}
173 175
174fn add_type_alias_impl( 176fn add_type_alias_impl(
@@ -200,16 +202,19 @@ fn add_const_impl(
200 let const_name = const_.name(ctx.db).map(|n| n.to_string()); 202 let const_name = const_.name(ctx.db).map(|n| n.to_string());
201 203
202 if let Some(const_name) = const_name { 204 if let Some(const_name) = const_name {
203 let snippet = make_const_compl_syntax(&const_.source(ctx.db).value); 205 if let Some(source) = const_.source(ctx.db) {
204 206 let snippet = make_const_compl_syntax(&source.value);
205 let range = TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 207
206 208 let range =
207 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
208 .text_edit(TextEdit::replace(range, snippet)) 210
209 .lookup_by(const_name) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone())
210 .kind(CompletionItemKind::Const) 212 .text_edit(TextEdit::replace(range, snippet))
211 .set_documentation(const_.docs(ctx.db)) 213 .lookup_by(const_name)
212 .add_to(acc); 214 .kind(CompletionItemKind::Const)
215 .set_documentation(const_.docs(ctx.db))
216 .add_to(acc);
217 }
213 } 218 }
214} 219}
215 220
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index d09849752..81a6d00e2 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -101,8 +101,9 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T
101// 101//
102// .Fuzzy search details 102// .Fuzzy search details
103// 103//
104// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the identifiers only 104// To avoid an excessive amount of the results returned, completion input is checked for inclusion in the names only
105// (i.e. in `HashMap` in the `std::collections::HashMap` path), also not in the module indentifiers. 105// (i.e. in `HashMap` in the `std::collections::HashMap` path).
106// For the same reasons, avoids searching for any imports for inputs with their length less that 2 symbols.
106// 107//
107// .Merge Behavior 108// .Merge Behavior
108// 109//
@@ -126,15 +127,20 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
126 let _p = profile::span("fuzzy_completion"); 127 let _p = profile::span("fuzzy_completion");
127 let potential_import_name = ctx.token.to_string(); 128 let potential_import_name = ctx.token.to_string();
128 129
130 if potential_import_name.len() < 2 {
131 return None;
132 }
133
129 let current_module = ctx.scope.module()?; 134 let current_module = ctx.scope.module()?;
130 let anchor = ctx.name_ref_syntax.as_ref()?; 135 let anchor = ctx.name_ref_syntax.as_ref()?;
131 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?; 136 let import_scope = ImportScope::find_insert_use_container(anchor.syntax(), &ctx.sema)?;
132 137
138 let user_input_lowercased = potential_import_name.to_lowercase();
133 let mut all_mod_paths = imports_locator::find_similar_imports( 139 let mut all_mod_paths = imports_locator::find_similar_imports(
134 &ctx.sema, 140 &ctx.sema,
135 ctx.krate?, 141 ctx.krate?,
136 Some(100), 142 Some(40),
137 &potential_import_name, 143 potential_import_name,
138 true, 144 true,
139 ) 145 )
140 .filter_map(|import_candidate| { 146 .filter_map(|import_candidate| {
@@ -150,7 +156,6 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()
150 .filter(|(mod_path, _)| mod_path.len() > 1) 156 .filter(|(mod_path, _)| mod_path.len() > 1)
151 .collect::<Vec<_>>(); 157 .collect::<Vec<_>>();
152 158
153 let user_input_lowercased = potential_import_name.to_lowercase();
154 all_mod_paths.sort_by_cached_key(|(mod_path, _)| { 159 all_mod_paths.sort_by_cached_key(|(mod_path, _)| {
155 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) 160 compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased)
156 }); 161 });
diff --git a/crates/completion/src/lib.rs b/crates/completion/src/lib.rs
index 8e27bb153..c57d05bbe 100644
--- a/crates/completion/src/lib.rs
+++ b/crates/completion/src/lib.rs
@@ -137,7 +137,7 @@ pub fn resolve_completion_edits(
137 config: &CompletionConfig, 137 config: &CompletionConfig,
138 position: FilePosition, 138 position: FilePosition,
139 full_import_path: &str, 139 full_import_path: &str,
140 imported_name: &str, 140 imported_name: String,
141) -> Option<Vec<TextEdit>> { 141) -> Option<Vec<TextEdit>> {
142 let ctx = CompletionContext::new(db, position, config)?; 142 let ctx = CompletionContext::new(db, position, config)?;
143 let anchor = ctx.name_ref_syntax.as_ref()?; 143 let anchor = ctx.name_ref_syntax.as_ref()?;
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 1ba7201a1..ac0b2a513 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -157,8 +157,7 @@ impl<'a> Render<'a> {
157 157
158 let kind = match resolution { 158 let kind = match resolution {
159 ScopeDef::ModuleDef(Function(func)) => { 159 ScopeDef::ModuleDef(Function(func)) => {
160 let item = render_fn(self.ctx, import_to_add, Some(local_name), *func); 160 return render_fn(self.ctx, import_to_add, Some(local_name), *func);
161 return Some(item);
162 } 161 }
163 ScopeDef::ModuleDef(Variant(_)) 162 ScopeDef::ModuleDef(Variant(_))
164 if self.ctx.completion.is_pat_binding_or_const 163 if self.ctx.completion.is_pat_binding_or_const
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
index 039bdabc0..ce924f309 100644
--- a/crates/completion/src/render/const_.rs
+++ b/crates/completion/src/render/const_.rs
@@ -15,7 +15,7 @@ pub(crate) fn render_const<'a>(
15 ctx: RenderContext<'a>, 15 ctx: RenderContext<'a>,
16 const_: hir::Const, 16 const_: hir::Const,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 ConstRender::new(ctx, const_).render() 18 ConstRender::new(ctx, const_)?.render()
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -26,9 +26,9 @@ struct ConstRender<'a> {
26} 26}
27 27
28impl<'a> ConstRender<'a> { 28impl<'a> ConstRender<'a> {
29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> ConstRender<'a> { 29 fn new(ctx: RenderContext<'a>, const_: hir::Const) -> Option<ConstRender<'a>> {
30 let ast_node = const_.source(ctx.db()).value; 30 let ast_node = const_.source(ctx.db())?.value;
31 ConstRender { ctx, const_, ast_node } 31 Some(ConstRender { ctx, const_, ast_node })
32 } 32 }
33 33
34 fn render(self) -> Option<CompletionItem> { 34 fn render(self) -> Option<CompletionItem> {
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index 316e05b52..081be14f4 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -14,9 +14,9 @@ pub(crate) fn render_fn<'a>(
14 import_to_add: Option<ImportEdit>, 14 import_to_add: Option<ImportEdit>,
15 local_name: Option<String>, 15 local_name: Option<String>,
16 fn_: hir::Function, 16 fn_: hir::Function,
17) -> CompletionItem { 17) -> Option<CompletionItem> {
18 let _p = profile::span("render_fn"); 18 let _p = profile::span("render_fn");
19 FunctionRender::new(ctx, local_name, fn_).render(import_to_add) 19 Some(FunctionRender::new(ctx, local_name, fn_)?.render(import_to_add))
20} 20}
21 21
22#[derive(Debug)] 22#[derive(Debug)]
@@ -32,11 +32,11 @@ impl<'a> FunctionRender<'a> {
32 ctx: RenderContext<'a>, 32 ctx: RenderContext<'a>,
33 local_name: Option<String>, 33 local_name: Option<String>,
34 fn_: hir::Function, 34 fn_: hir::Function,
35 ) -> FunctionRender<'a> { 35 ) -> Option<FunctionRender<'a>> {
36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string()); 36 let name = local_name.unwrap_or_else(|| fn_.name(ctx.db()).to_string());
37 let ast_node = fn_.source(ctx.db()).value; 37 let ast_node = fn_.source(ctx.db())?.value;
38 38
39 FunctionRender { ctx, name, func: fn_, ast_node } 39 Some(FunctionRender { ctx, name, func: fn_, ast_node })
40 } 40 }
41 41
42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
diff --git a/crates/completion/src/render/macro_.rs b/crates/completion/src/render/macro_.rs
index dac79592f..6f4f9945c 100644
--- a/crates/completion/src/render/macro_.rs
+++ b/crates/completion/src/render/macro_.rs
@@ -39,20 +39,13 @@ impl<'a> MacroRender<'a> {
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 // FIXME: Currently proc-macro do not have ast-node,
43 // such that it does not have source
44 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
45 if self.macro_.is_proc_macro() {
46 return None;
47 }
48
49 let mut builder = 42 let mut builder =
50 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label())
51 .kind(CompletionItemKind::Macro) 44 .kind(CompletionItemKind::Macro)
52 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
53 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
54 .add_import(import_to_add) 47 .add_import(import_to_add)
55 .detail(self.detail()); 48 .set_detail(self.detail());
56 49
57 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
58 builder = match self.ctx.snippet_cap() { 51 builder = match self.ctx.snippet_cap() {
@@ -95,9 +88,9 @@ impl<'a> MacroRender<'a> {
95 format!("{}!", self.name) 88 format!("{}!", self.name)
96 } 89 }
97 90
98 fn detail(&self) -> String { 91 fn detail(&self) -> Option<String> {
99 let ast_node = self.macro_.source(self.ctx.db()).value; 92 let ast_node = self.macro_.source(self.ctx.db())?.value;
100 macro_label(&ast_node) 93 Some(macro_label(&ast_node))
101 } 94 }
102} 95}
103 96
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
index 9605c7fa9..69b445b9c 100644
--- a/crates/completion/src/render/type_alias.rs
+++ b/crates/completion/src/render/type_alias.rs
@@ -15,7 +15,7 @@ pub(crate) fn render_type_alias<'a>(
15 ctx: RenderContext<'a>, 15 ctx: RenderContext<'a>,
16 type_alias: hir::TypeAlias, 16 type_alias: hir::TypeAlias,
17) -> Option<CompletionItem> { 17) -> Option<CompletionItem> {
18 TypeAliasRender::new(ctx, type_alias).render() 18 TypeAliasRender::new(ctx, type_alias)?.render()
19} 19}
20 20
21#[derive(Debug)] 21#[derive(Debug)]
@@ -26,9 +26,9 @@ struct TypeAliasRender<'a> {
26} 26}
27 27
28impl<'a> TypeAliasRender<'a> { 28impl<'a> TypeAliasRender<'a> {
29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> TypeAliasRender<'a> { 29 fn new(ctx: RenderContext<'a>, type_alias: hir::TypeAlias) -> Option<TypeAliasRender<'a>> {
30 let ast_node = type_alias.source(ctx.db()).value; 30 let ast_node = type_alias.source(ctx.db())?.value;
31 TypeAliasRender { ctx, type_alias, ast_node } 31 Some(TypeAliasRender { ctx, type_alias, ast_node })
32 } 32 }
33 33
34 fn render(self) -> Option<CompletionItem> { 34 fn render(self) -> Option<CompletionItem> {
diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml
index 6dc5ad63b..d4ea7327e 100644
--- a/crates/hir/Cargo.toml
+++ b/crates/hir/Cargo.toml
@@ -14,7 +14,7 @@ log = "0.4.8"
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15either = "1.5.3" 15either = "1.5.3"
16arrayvec = "0.5.1" 16arrayvec = "0.5.1"
17itertools = "0.9.0" 17itertools = "0.10.0"
18 18
19stdx = { path = "../stdx", version = "0.0.0" } 19stdx = { path = "../stdx", version = "0.0.0" }
20syntax = { path = "../syntax", version = "0.0.0" } 20syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs
index d32ce37ed..99fb65bac 100644
--- a/crates/hir/src/attrs.rs
+++ b/crates/hir/src/attrs.rs
@@ -3,15 +3,15 @@ use hir_def::{
3 attr::{Attrs, Documentation}, 3 attr::{Attrs, Documentation},
4 path::ModPath, 4 path::ModPath,
5 resolver::HasResolver, 5 resolver::HasResolver,
6 AttrDefId, ModuleDefId, 6 AttrDefId, GenericParamId, ModuleDefId,
7}; 7};
8use hir_expand::hygiene::Hygiene; 8use hir_expand::hygiene::Hygiene;
9use hir_ty::db::HirDatabase; 9use hir_ty::db::HirDatabase;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 Adt, Const, Enum, Field, Function, MacroDef, Module, ModuleDef, Static, Struct, Trait, 13 Adt, Const, ConstParam, Enum, Field, Function, GenericParam, LifetimeParam, MacroDef, Module,
14 TypeAlias, Union, Variant, 14 ModuleDef, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasAttrs { 17pub trait HasAttrs {
@@ -62,25 +62,27 @@ impl_has_attrs![
62 (Function, FunctionId), 62 (Function, FunctionId),
63 (Adt, AdtId), 63 (Adt, AdtId),
64 (Module, ModuleId), 64 (Module, ModuleId),
65 (GenericParam, GenericParamId),
65]; 66];
66 67
67macro_rules! impl_has_attrs_adt { 68macro_rules! impl_has_attrs_enum {
68 ($($adt:ident),*) => {$( 69 ($($variant:ident),* for $enum:ident) => {$(
69 impl HasAttrs for $adt { 70 impl HasAttrs for $variant {
70 fn attrs(self, db: &dyn HirDatabase) -> Attrs { 71 fn attrs(self, db: &dyn HirDatabase) -> Attrs {
71 Adt::$adt(self).attrs(db) 72 $enum::$variant(self).attrs(db)
72 } 73 }
73 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> { 74 fn docs(self, db: &dyn HirDatabase) -> Option<Documentation> {
74 Adt::$adt(self).docs(db) 75 $enum::$variant(self).docs(db)
75 } 76 }
76 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> { 77 fn resolve_doc_path(self, db: &dyn HirDatabase, link: &str, ns: Option<Namespace>) -> Option<ModuleDef> {
77 Adt::$adt(self).resolve_doc_path(db, link, ns) 78 $enum::$variant(self).resolve_doc_path(db, link, ns)
78 } 79 }
79 } 80 }
80 )*}; 81 )*};
81} 82}
82 83
83impl_has_attrs_adt![Struct, Union, Enum]; 84impl_has_attrs_enum![Struct, Union, Enum for Adt];
85impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam];
84 86
85fn resolve_doc_path( 87fn resolve_doc_path(
86 db: &dyn HirDatabase, 88 db: &dyn HirDatabase,
@@ -99,6 +101,12 @@ fn resolve_doc_path(
99 AttrDefId::TraitId(it) => it.resolver(db.upcast()), 101 AttrDefId::TraitId(it) => it.resolver(db.upcast()),
100 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()), 102 AttrDefId::TypeAliasId(it) => it.resolver(db.upcast()),
101 AttrDefId::ImplId(it) => it.resolver(db.upcast()), 103 AttrDefId::ImplId(it) => it.resolver(db.upcast()),
104 AttrDefId::GenericParamId(it) => match it {
105 GenericParamId::TypeParamId(it) => it.parent,
106 GenericParamId::LifetimeParamId(it) => it.parent,
107 GenericParamId::ConstParamId(it) => it.parent,
108 }
109 .resolver(db.upcast()),
102 AttrDefId::MacroDefId(_) => return None, 110 AttrDefId::MacroDefId(_) => return None,
103 }; 111 };
104 let path = ast::Path::parse(link).ok()?; 112 let path = ast::Path::parse(link).ok()?;
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 804cdb143..62eccf475 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -18,10 +18,10 @@ use hir_def::{
18 resolver::{HasResolver, Resolver}, 18 resolver::{HasResolver, Resolver},
19 src::HasSource as _, 19 src::HasSource as _,
20 type_ref::{Mutability, TypeRef}, 20 type_ref::{Mutability, TypeRef},
21 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, DefWithBodyId, EnumId, 21 AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId,
22 FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, LocalEnumVariantId, LocalFieldId, 22 DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId,
23 LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 23 LocalEnumVariantId, LocalFieldId, LocalModuleId, Lookup, ModuleId, StaticId, StructId, TraitId,
24 UnionId, 24 TypeAliasId, TypeParamId, UnionId,
25}; 25};
26use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; 26use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility};
27use hir_expand::{ 27use hir_expand::{
@@ -996,13 +996,7 @@ impl MacroDef {
996 996
997 /// XXX: this parses the file 997 /// XXX: this parses the file
998 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { 998 pub fn name(self, db: &dyn HirDatabase) -> Option<Name> {
999 // FIXME: Currently proc-macro do not have ast-node, 999 self.source(db)?.value.name().map(|it| it.as_name())
1000 // such that it does not have source
1001 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
1002 if self.is_proc_macro() {
1003 return None;
1004 }
1005 self.source(db).value.name().map(|it| it.as_name())
1006 } 1000 }
1007 1001
1008 /// Indicate it is a proc-macro 1002 /// Indicate it is a proc-macro
@@ -1138,7 +1132,12 @@ impl GenericDef {
1138 id: LifetimeParamId { parent: self.into(), local_id }, 1132 id: LifetimeParamId { parent: self.into(), local_id },
1139 }) 1133 })
1140 .map(GenericParam::LifetimeParam); 1134 .map(GenericParam::LifetimeParam);
1141 ty_params.chain(lt_params).collect() 1135 let const_params = generics
1136 .consts
1137 .iter()
1138 .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } })
1139 .map(GenericParam::ConstParam);
1140 ty_params.chain(lt_params).chain(const_params).collect()
1142 } 1141 }
1143 1142
1144 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { 1143 pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
@@ -1250,8 +1249,9 @@ impl Label {
1250pub enum GenericParam { 1249pub enum GenericParam {
1251 TypeParam(TypeParam), 1250 TypeParam(TypeParam),
1252 LifetimeParam(LifetimeParam), 1251 LifetimeParam(LifetimeParam),
1252 ConstParam(ConstParam),
1253} 1253}
1254impl_from!(TypeParam, LifetimeParam for GenericParam); 1254impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam);
1255 1255
1256#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 1256#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1257pub struct TypeParam { 1257pub struct TypeParam {
@@ -1313,6 +1313,26 @@ impl LifetimeParam {
1313 } 1313 }
1314} 1314}
1315 1315
1316#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
1317pub struct ConstParam {
1318 pub(crate) id: ConstParamId,
1319}
1320
1321impl ConstParam {
1322 pub fn name(self, db: &dyn HirDatabase) -> Name {
1323 let params = db.generic_params(self.id.parent);
1324 params.consts[self.id.local_id].name.clone()
1325 }
1326
1327 pub fn module(self, db: &dyn HirDatabase) -> Module {
1328 self.id.parent.module(db.upcast()).into()
1329 }
1330
1331 pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
1332 self.id.parent.into()
1333 }
1334}
1335
1316#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 1336#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1317pub struct Impl { 1337pub struct Impl {
1318 pub(crate) id: ImplId, 1338 pub(crate) id: ImplId,
@@ -1365,7 +1385,7 @@ impl Impl {
1365 } 1385 }
1366 1386
1367 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { 1387 pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
1368 let src = self.source(db); 1388 let src = self.source(db)?;
1369 let item = src.file_id.is_builtin_derive(db.upcast())?; 1389 let item = src.file_id.is_builtin_derive(db.upcast())?;
1370 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); 1390 let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id);
1371 1391
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs
index a0792b9a6..3e47a5e9d 100644
--- a/crates/hir/src/from_id.rs
+++ b/crates/hir/src/from_id.rs
@@ -6,13 +6,13 @@
6use hir_def::{ 6use hir_def::{
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 item_scope::ItemInNs, 8 item_scope::ItemInNs,
9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, ModuleDefId, 9 AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, GenericDefId, GenericParamId,
10 VariantId, 10 ModuleDefId, VariantId,
11}; 11};
12 12
13use crate::{ 13use crate::{
14 Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, 14 code_model::GenericParam, Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local,
15 VariantDef, 15 MacroDef, ModuleDef, Variant, VariantDef,
16}; 16};
17 17
18macro_rules! from_id { 18macro_rules! from_id {
@@ -44,6 +44,7 @@ from_id![
44 (hir_def::ImplId, crate::Impl), 44 (hir_def::ImplId, crate::Impl),
45 (hir_def::TypeParamId, crate::TypeParam), 45 (hir_def::TypeParamId, crate::TypeParam),
46 (hir_def::LifetimeParamId, crate::LifetimeParam), 46 (hir_def::LifetimeParamId, crate::LifetimeParam),
47 (hir_def::ConstParamId, crate::ConstParam),
47 (hir_expand::MacroDefId, crate::MacroDef) 48 (hir_expand::MacroDefId, crate::MacroDef)
48]; 49];
49 50
@@ -67,6 +68,26 @@ impl From<Adt> for AdtId {
67 } 68 }
68} 69}
69 70
71impl From<GenericParamId> for GenericParam {
72 fn from(id: GenericParamId) -> Self {
73 match id {
74 GenericParamId::TypeParamId(it) => GenericParam::TypeParam(it.into()),
75 GenericParamId::LifetimeParamId(it) => GenericParam::LifetimeParam(it.into()),
76 GenericParamId::ConstParamId(it) => GenericParam::ConstParam(it.into()),
77 }
78 }
79}
80
81impl From<GenericParam> for GenericParamId {
82 fn from(id: GenericParam) -> Self {
83 match id {
84 GenericParam::TypeParam(it) => GenericParamId::TypeParamId(it.id),
85 GenericParam::LifetimeParam(it) => GenericParamId::LifetimeParamId(it.id),
86 GenericParam::ConstParam(it) => GenericParamId::ConstParamId(it.id),
87 }
88 }
89}
90
70impl From<EnumVariantId> for Variant { 91impl From<EnumVariantId> for Variant {
71 fn from(id: EnumVariantId) -> Self { 92 fn from(id: EnumVariantId) -> Self {
72 Variant { parent: id.parent.into(), id: id.local_id } 93 Variant { parent: id.parent.into(), id: id.local_id }
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 0dc07c33e..7c57d8378 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -10,13 +10,13 @@ use hir_expand::InFile;
10use syntax::ast; 10use syntax::ast;
11 11
12use crate::{ 12use crate::{
13 db::HirDatabase, Const, Enum, Field, FieldSource, Function, Impl, LifetimeParam, MacroDef, 13 db::HirDatabase, Const, ConstParam, Enum, Field, FieldSource, Function, Impl, LifetimeParam,
14 Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant, 14 MacroDef, Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, Variant,
15}; 15};
16 16
17pub trait HasSource { 17pub trait HasSource {
18 type Ast; 18 type Ast;
19 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast>; 19 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
20} 20}
21 21
22/// NB: Module is !HasSource, because it has two source nodes at the same time: 22/// NB: Module is !HasSource, because it has two source nodes at the same time:
@@ -46,97 +46,104 @@ impl Module {
46 46
47impl HasSource for Field { 47impl HasSource for Field {
48 type Ast = FieldSource; 48 type Ast = FieldSource;
49 fn source(self, db: &dyn HirDatabase) -> InFile<FieldSource> { 49 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
50 let var = VariantId::from(self.parent); 50 let var = VariantId::from(self.parent);
51 let src = var.child_source(db.upcast()); 51 let src = var.child_source(db.upcast());
52 src.map(|it| match it[self.id].clone() { 52 let field_source = src.map(|it| match it[self.id].clone() {
53 Either::Left(it) => FieldSource::Pos(it), 53 Either::Left(it) => FieldSource::Pos(it),
54 Either::Right(it) => FieldSource::Named(it), 54 Either::Right(it) => FieldSource::Named(it),
55 }) 55 });
56 Some(field_source)
56 } 57 }
57} 58}
58impl HasSource for Struct { 59impl HasSource for Struct {
59 type Ast = ast::Struct; 60 type Ast = ast::Struct;
60 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Struct> { 61 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
61 self.id.lookup(db.upcast()).source(db.upcast()) 62 Some(self.id.lookup(db.upcast()).source(db.upcast()))
62 } 63 }
63} 64}
64impl HasSource for Union { 65impl HasSource for Union {
65 type Ast = ast::Union; 66 type Ast = ast::Union;
66 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Union> { 67 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
67 self.id.lookup(db.upcast()).source(db.upcast()) 68 Some(self.id.lookup(db.upcast()).source(db.upcast()))
68 } 69 }
69} 70}
70impl HasSource for Enum { 71impl HasSource for Enum {
71 type Ast = ast::Enum; 72 type Ast = ast::Enum;
72 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Enum> { 73 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
73 self.id.lookup(db.upcast()).source(db.upcast()) 74 Some(self.id.lookup(db.upcast()).source(db.upcast()))
74 } 75 }
75} 76}
76impl HasSource for Variant { 77impl HasSource for Variant {
77 type Ast = ast::Variant; 78 type Ast = ast::Variant;
78 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Variant> { 79 fn source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Variant>> {
79 self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()) 80 Some(self.parent.id.child_source(db.upcast()).map(|map| map[self.id].clone()))
80 } 81 }
81} 82}
82impl HasSource for Function { 83impl HasSource for Function {
83 type Ast = ast::Fn; 84 type Ast = ast::Fn;
84 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Fn> { 85 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
85 self.id.lookup(db.upcast()).source(db.upcast()) 86 Some(self.id.lookup(db.upcast()).source(db.upcast()))
86 } 87 }
87} 88}
88impl HasSource for Const { 89impl HasSource for Const {
89 type Ast = ast::Const; 90 type Ast = ast::Const;
90 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Const> { 91 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
91 self.id.lookup(db.upcast()).source(db.upcast()) 92 Some(self.id.lookup(db.upcast()).source(db.upcast()))
92 } 93 }
93} 94}
94impl HasSource for Static { 95impl HasSource for Static {
95 type Ast = ast::Static; 96 type Ast = ast::Static;
96 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Static> { 97 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
97 self.id.lookup(db.upcast()).source(db.upcast()) 98 Some(self.id.lookup(db.upcast()).source(db.upcast()))
98 } 99 }
99} 100}
100impl HasSource for Trait { 101impl HasSource for Trait {
101 type Ast = ast::Trait; 102 type Ast = ast::Trait;
102 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Trait> { 103 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
103 self.id.lookup(db.upcast()).source(db.upcast()) 104 Some(self.id.lookup(db.upcast()).source(db.upcast()))
104 } 105 }
105} 106}
106impl HasSource for TypeAlias { 107impl HasSource for TypeAlias {
107 type Ast = ast::TypeAlias; 108 type Ast = ast::TypeAlias;
108 fn source(self, db: &dyn HirDatabase) -> InFile<ast::TypeAlias> { 109 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
109 self.id.lookup(db.upcast()).source(db.upcast()) 110 Some(self.id.lookup(db.upcast()).source(db.upcast()))
110 } 111 }
111} 112}
112impl HasSource for MacroDef { 113impl HasSource for MacroDef {
113 type Ast = ast::Macro; 114 type Ast = ast::Macro;
114 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Macro> { 115 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
115 InFile { 116 let ast_id = self.id.ast_id?;
116 file_id: self.id.ast_id.expect("MacroDef without ast_id").file_id, 117 Some(InFile { file_id: ast_id.file_id, value: ast_id.to_node(db.upcast()) })
117 value: self.id.ast_id.expect("MacroDef without ast_id").to_node(db.upcast()),
118 }
119 } 118 }
120} 119}
121impl HasSource for Impl { 120impl HasSource for Impl {
122 type Ast = ast::Impl; 121 type Ast = ast::Impl;
123 fn source(self, db: &dyn HirDatabase) -> InFile<ast::Impl> { 122 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
124 self.id.lookup(db.upcast()).source(db.upcast()) 123 Some(self.id.lookup(db.upcast()).source(db.upcast()))
125 } 124 }
126} 125}
127 126
128impl HasSource for TypeParam { 127impl HasSource for TypeParam {
129 type Ast = Either<ast::Trait, ast::TypeParam>; 128 type Ast = Either<ast::Trait, ast::TypeParam>;
130 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 129 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
131 let child_source = self.id.parent.child_source(db.upcast()); 130 let child_source = self.id.parent.child_source(db.upcast());
132 child_source.map(|it| it[self.id.local_id].clone()) 131 Some(child_source.map(|it| it[self.id.local_id].clone()))
133 } 132 }
134} 133}
135 134
136impl HasSource for LifetimeParam { 135impl HasSource for LifetimeParam {
137 type Ast = ast::LifetimeParam; 136 type Ast = ast::LifetimeParam;
138 fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { 137 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
138 let child_source = self.id.parent.child_source(db.upcast());
139 Some(child_source.map(|it| it[self.id.local_id].clone()))
140 }
141}
142
143impl HasSource for ConstParam {
144 type Ast = ast::ConstParam;
145 fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
139 let child_source = self.id.parent.child_source(db.upcast()); 146 let child_source = self.id.parent.child_source(db.upcast());
140 child_source.map(|it| it[self.id.local_id].clone()) 147 Some(child_source.map(|it| it[self.id.local_id].clone()))
141 } 148 }
142} 149}
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 7ac9fd507..769945c47 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -34,9 +34,10 @@ pub use crate::{
34 attrs::{HasAttrs, Namespace}, 34 attrs::{HasAttrs, Namespace},
35 code_model::{ 35 code_model::{
36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const, 36 Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, Callable, CallableKind, Const,
37 Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function, GenericDef, 37 ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, FieldSource, Function,
38 HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, ScopeDef, 38 GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
39 Static, Struct, Trait, Type, TypeAlias, TypeParam, Union, Variant, VariantDef, 39 Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Union,
40 Variant, VariantDef,
40 }, 41 },
41 has_source::HasSource, 42 has_source::HasSource,
42 semantics::{PathResolution, Semantics, SemanticsScope}, 43 semantics::{PathResolution, Semantics, SemanticsScope},
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 67cd16e31..cd689c869 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -25,9 +25,9 @@ use crate::{
25 diagnostics::Diagnostic, 25 diagnostics::Diagnostic,
26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, 26 semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
27 source_analyzer::{resolve_hir_path, SourceAnalyzer}, 27 source_analyzer::{resolve_hir_path, SourceAnalyzer},
28 AssocItem, Callable, Crate, Field, Function, HirFileId, Impl, InFile, Label, LifetimeParam, 28 AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label,
29 Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, 29 LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type,
30 VariantDef, 30 TypeAlias, TypeParam, VariantDef,
31}; 31};
32 32
33#[derive(Debug, Clone, PartialEq, Eq)] 33#[derive(Debug, Clone, PartialEq, Eq)]
@@ -38,6 +38,7 @@ pub enum PathResolution {
38 Local(Local), 38 Local(Local),
39 /// A generic parameter 39 /// A generic parameter
40 TypeParam(TypeParam), 40 TypeParam(TypeParam),
41 ConstParam(ConstParam),
41 SelfType(Impl), 42 SelfType(Impl),
42 Macro(MacroDef), 43 Macro(MacroDef),
43 AssocItem(AssocItem), 44 AssocItem(AssocItem),
@@ -59,7 +60,9 @@ impl PathResolution {
59 PathResolution::Def(ModuleDef::TypeAlias(alias)) => { 60 PathResolution::Def(ModuleDef::TypeAlias(alias)) => {
60 Some(TypeNs::TypeAliasId((*alias).into())) 61 Some(TypeNs::TypeAliasId((*alias).into()))
61 } 62 }
62 PathResolution::Local(_) | PathResolution::Macro(_) => None, 63 PathResolution::Local(_) | PathResolution::Macro(_) | PathResolution::ConstParam(_) => {
64 None
65 }
63 PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), 66 PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())),
64 PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), 67 PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())),
65 PathResolution::AssocItem(AssocItem::Const(_)) 68 PathResolution::AssocItem(AssocItem::Const(_))
@@ -744,6 +747,7 @@ to_def_impls![
744 (crate::Variant, ast::Variant, enum_variant_to_def), 747 (crate::Variant, ast::Variant, enum_variant_to_def),
745 (crate::TypeParam, ast::TypeParam, type_param_to_def), 748 (crate::TypeParam, ast::TypeParam, type_param_to_def),
746 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def), 749 (crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
750 (crate::ConstParam, ast::ConstParam, const_param_to_def),
747 (crate::MacroDef, ast::MacroRules, macro_rules_to_def), 751 (crate::MacroDef, ast::MacroRules, macro_rules_to_def),
748 (crate::Local, ast::IdentPat, bind_pat_to_def), 752 (crate::Local, ast::IdentPat, bind_pat_to_def),
749 (crate::Label, ast::Label, label_to_def), 753 (crate::Label, ast::Label, label_to_def),
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 424e6e8a9..4b9ebff72 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -6,9 +6,9 @@ use hir_def::{
6 dyn_map::DynMap, 6 dyn_map::DynMap,
7 expr::{LabelId, PatId}, 7 expr::{LabelId, PatId},
8 keys::{self, Key}, 8 keys::{self, Key},
9 ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, 9 ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId,
10 LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 10 ImplId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
11 VariantId, 11 UnionId, VariantId,
12}; 12};
13use hir_expand::{name::AsName, AstId, MacroDefKind}; 13use hir_expand::{name::AsName, AstId, MacroDefKind};
14use rustc_hash::FxHashMap; 14use rustc_hash::FxHashMap;
@@ -157,6 +157,18 @@ impl SourceToDefCtx<'_, '_> {
157 dyn_map[keys::LIFETIME_PARAM].get(&src).copied() 157 dyn_map[keys::LIFETIME_PARAM].get(&src).copied()
158 } 158 }
159 159
160 pub(super) fn const_param_to_def(
161 &mut self,
162 src: InFile<ast::ConstParam>,
163 ) -> Option<ConstParamId> {
164 let container: ChildContainer =
165 self.find_generic_param_container(src.as_ref().map(|it| it.syntax()))?.into();
166 let db = self.db;
167 let dyn_map =
168 &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
169 dyn_map[keys::CONST_PARAM].get(&src).copied()
170 }
171
160 // FIXME: use DynMap as well? 172 // FIXME: use DynMap as well?
161 pub(super) fn macro_rules_to_def( 173 pub(super) fn macro_rules_to_def(
162 &mut self, 174 &mut self,
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index bddc49c05..30a8e513d 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -479,6 +479,7 @@ pub(crate) fn resolve_hir_path(
479 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()), 479 ValueNs::StructId(it) => PathResolution::Def(Struct::from(it).into()),
480 ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()), 480 ValueNs::EnumVariantId(it) => PathResolution::Def(Variant::from(it).into()),
481 ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()), 481 ValueNs::ImplSelf(impl_id) => PathResolution::SelfType(impl_id.into()),
482 ValueNs::GenericParam(it) => PathResolution::ConstParam(it.into()),
482 }; 483 };
483 Some(res) 484 Some(res)
484 }); 485 });
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index a88b5f57e..e8b581e2f 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -17,7 +17,7 @@ either = "1.5.3"
17anymap = "0.12.1" 17anymap = "0.12.1"
18drop_bomb = "0.1.4" 18drop_bomb = "0.1.4"
19fst = { version = "0.4", default-features = false } 19fst = { version = "0.4", default-features = false }
20itertools = "0.9.0" 20itertools = "0.10.0"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smallvec = "1.4.0" 22smallvec = "1.4.0"
23 23
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 042e119b1..6b79e7bad 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -21,7 +21,7 @@ use crate::{
21 nameres::ModuleSource, 21 nameres::ModuleSource,
22 path::{ModPath, PathKind}, 22 path::{ModPath, PathKind},
23 src::HasChildSource, 23 src::HasChildSource,
24 AdtId, AttrDefId, Lookup, 24 AdtId, AttrDefId, GenericParamId, Lookup,
25}; 25};
26 26
27/// Holds documentation 27/// Holds documentation
@@ -235,6 +235,25 @@ impl Attrs {
235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db), 235 AttrDefId::StaticId(it) => attrs_from_item_tree(it.lookup(db).id, db),
236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db), 236 AttrDefId::FunctionId(it) => attrs_from_item_tree(it.lookup(db).id, db),
237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db), 237 AttrDefId::TypeAliasId(it) => attrs_from_item_tree(it.lookup(db).id, db),
238 AttrDefId::GenericParamId(it) => match it {
239 GenericParamId::TypeParamId(it) => {
240 let src = it.parent.child_source(db);
241 RawAttrs::from_attrs_owner(
242 db,
243 src.with_value(
244 src.value[it.local_id].as_ref().either(|it| it as _, |it| it as _),
245 ),
246 )
247 }
248 GenericParamId::LifetimeParamId(it) => {
249 let src = it.parent.child_source(db);
250 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
251 }
252 GenericParamId::ConstParamId(it) => {
253 let src = it.parent.child_source(db);
254 RawAttrs::from_attrs_owner(db, src.with_value(&src.value[it.local_id]))
255 }
256 },
238 }; 257 };
239 258
240 raw_attrs.filter(db, def.krate(db)) 259 raw_attrs.filter(db, def.krate(db))
@@ -260,14 +279,13 @@ impl Attrs {
260 } 279 }
261 280
262 pub fn docs(&self) -> Option<Documentation> { 281 pub fn docs(&self) -> Option<Documentation> {
263 let docs = self 282 let docs = self.by_key("doc").attrs().flat_map(|attr| match attr.input.as_ref()? {
264 .by_key("doc") 283 AttrInput::Literal(s) => Some(s),
265 .attrs() 284 AttrInput::TokenTree(_) => None,
266 .flat_map(|attr| match attr.input.as_ref()? { 285 });
267 AttrInput::Literal(s) => Some(s), 286 // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the
268 AttrInput::TokenTree(_) => None, 287 // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524).
269 }) 288 let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n"))
270 .intersperse(&SmolStr::new_inline("\n"))
271 .map(|it| it.as_str()) 289 .map(|it| it.as_str())
272 .collect::<String>(); 290 .collect::<String>();
273 if docs.is_empty() { 291 if docs.is_empty() {
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index bb8fca009..9b5b886c2 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -21,11 +21,11 @@ use crate::{
21 keys, 21 keys,
22 src::{HasChildSource, HasSource}, 22 src::{HasChildSource, HasSource},
23 type_ref::{LifetimeRef, TypeBound, TypeRef}, 23 type_ref::{LifetimeRef, TypeBound, TypeRef},
24 AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup, 24 AdtId, ConstParamId, GenericDefId, LifetimeParamId, LocalConstParamId, LocalLifetimeParamId,
25 TypeParamId, 25 LocalTypeParamId, Lookup, TypeParamId,
26}; 26};
27 27
28/// Data about a generic parameter (to a function, struct, impl, ...). 28/// Data about a generic type parameter (to a function, struct, impl, ...).
29#[derive(Clone, PartialEq, Eq, Debug)] 29#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct TypeParamData { 30pub struct TypeParamData {
31 pub name: Option<Name>, 31 pub name: Option<Name>,
@@ -33,12 +33,19 @@ pub struct TypeParamData {
33 pub provenance: TypeParamProvenance, 33 pub provenance: TypeParamProvenance,
34} 34}
35 35
36/// Data about a generic parameter (to a function, struct, impl, ...). 36/// Data about a generic lifetime parameter (to a function, struct, impl, ...).
37#[derive(Clone, PartialEq, Eq, Debug)] 37#[derive(Clone, PartialEq, Eq, Debug)]
38pub struct LifetimeParamData { 38pub struct LifetimeParamData {
39 pub name: Name, 39 pub name: Name,
40} 40}
41 41
42/// Data about a generic const parameter (to a function, struct, impl, ...).
43#[derive(Clone, PartialEq, Eq, Debug)]
44pub struct ConstParamData {
45 pub name: Name,
46 pub ty: TypeRef,
47}
48
42#[derive(Copy, Clone, PartialEq, Eq, Debug)] 49#[derive(Copy, Clone, PartialEq, Eq, Debug)]
43pub enum TypeParamProvenance { 50pub enum TypeParamProvenance {
44 TypeParamList, 51 TypeParamList,
@@ -51,6 +58,7 @@ pub enum TypeParamProvenance {
51pub struct GenericParams { 58pub struct GenericParams {
52 pub types: Arena<TypeParamData>, 59 pub types: Arena<TypeParamData>,
53 pub lifetimes: Arena<LifetimeParamData>, 60 pub lifetimes: Arena<LifetimeParamData>,
61 pub consts: Arena<ConstParamData>,
54 pub where_predicates: Vec<WherePredicate>, 62 pub where_predicates: Vec<WherePredicate>,
55} 63}
56 64
@@ -76,6 +84,7 @@ pub enum WherePredicateTypeTarget {
76pub(crate) struct SourceMap { 84pub(crate) struct SourceMap {
77 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>, 85 pub(crate) type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
78 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>, 86 lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
87 const_params: ArenaMap<LocalConstParamId, ast::ConstParam>,
79} 88}
80 89
81impl GenericParams { 90impl GenericParams {
@@ -268,6 +277,13 @@ impl GenericParams {
268 let lifetime_ref = LifetimeRef::new_name(name); 277 let lifetime_ref = LifetimeRef::new_name(name);
269 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); 278 self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
270 } 279 }
280 for const_param in params.const_params() {
281 let name = const_param.name().map_or_else(Name::missing, |it| it.as_name());
282 let ty = const_param.ty().map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
283 let param = ConstParamData { name, ty };
284 let param_id = self.consts.alloc(param);
285 sm.const_params.insert(param_id, const_param.clone());
286 }
271 } 287 }
272 288
273 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) { 289 fn fill_where_predicates(&mut self, lower_ctx: &LowerCtx, where_clause: ast::WhereClause) {
@@ -353,12 +369,16 @@ impl GenericParams {
353 }); 369 });
354 } 370 }
355 371
356 pub fn find_by_name(&self, name: &Name) -> Option<LocalTypeParamId> { 372 pub fn find_type_by_name(&self, name: &Name) -> Option<LocalTypeParamId> {
357 self.types 373 self.types
358 .iter() 374 .iter()
359 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None }) 375 .find_map(|(id, p)| if p.name.as_ref() == Some(name) { Some(id) } else { None })
360 } 376 }
361 377
378 pub fn find_const_by_name(&self, name: &Name) -> Option<LocalConstParamId> {
379 self.consts.iter().find_map(|(id, p)| if p.name == *name { Some(id) } else { None })
380 }
381
362 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> { 382 pub fn find_trait_self_param(&self) -> Option<LocalTypeParamId> {
363 self.types.iter().find_map(|(id, p)| { 383 self.types.iter().find_map(|(id, p)| {
364 if p.provenance == TypeParamProvenance::TraitSelf { 384 if p.provenance == TypeParamProvenance::TraitSelf {
@@ -390,6 +410,16 @@ impl HasChildSource<LocalLifetimeParamId> for GenericDefId {
390 } 410 }
391} 411}
392 412
413impl HasChildSource<LocalConstParamId> for GenericDefId {
414 type Value = ast::ConstParam;
415 fn child_source(
416 &self,
417 db: &dyn DefDatabase,
418 ) -> InFile<ArenaMap<LocalConstParamId, Self::Value>> {
419 GenericParams::new(db, *self).1.map(|source_maps| source_maps.const_params)
420 }
421}
422
393impl ChildBySource for GenericDefId { 423impl ChildBySource for GenericDefId {
394 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 424 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
395 let mut res = DynMap::default(); 425 let mut res = DynMap::default();
@@ -406,6 +436,10 @@ impl ChildBySource for GenericDefId {
406 let id = LifetimeParamId { parent: *self, local_id }; 436 let id = LifetimeParamId { parent: *self, local_id };
407 res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id); 437 res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id);
408 } 438 }
439 for (local_id, src) in sm.value.const_params.iter() {
440 let id = ConstParamId { parent: *self, local_id };
441 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id);
442 }
409 res 443 res
410 } 444 }
411} 445}
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index c0f108848..30b22f51d 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -238,32 +238,53 @@ pub enum ImportKind {
238 BuiltinType, 238 BuiltinType,
239} 239}
240 240
241/// A way to match import map contents against the search query.
242#[derive(Debug)]
243pub enum SearchMode {
244 /// Import map entry should strictly match the query string.
245 Equals,
246 /// Import map entry should contain the query string.
247 Contains,
248 /// Import map entry should contain all letters from the query string,
249 /// in the same order, but not necessary adjacent.
250 Fuzzy,
251}
252
241#[derive(Debug)] 253#[derive(Debug)]
242pub struct Query { 254pub struct Query {
243 query: String, 255 query: String,
244 lowercased: String, 256 lowercased: String,
245 anchor_end: bool, 257 name_only: bool,
258 search_mode: SearchMode,
246 case_sensitive: bool, 259 case_sensitive: bool,
247 limit: usize, 260 limit: usize,
248 exclude_import_kinds: FxHashSet<ImportKind>, 261 exclude_import_kinds: FxHashSet<ImportKind>,
249} 262}
250 263
251impl Query { 264impl Query {
252 pub fn new(query: &str) -> Self { 265 pub fn new(query: String) -> Self {
266 let lowercased = query.to_lowercase();
253 Self { 267 Self {
254 lowercased: query.to_lowercase(), 268 query,
255 query: query.to_string(), 269 lowercased,
256 anchor_end: false, 270 name_only: false,
271 search_mode: SearchMode::Contains,
257 case_sensitive: false, 272 case_sensitive: false,
258 limit: usize::max_value(), 273 limit: usize::max_value(),
259 exclude_import_kinds: FxHashSet::default(), 274 exclude_import_kinds: FxHashSet::default(),
260 } 275 }
261 } 276 }
262 277
263 /// Only returns items whose paths end with the (case-insensitive) query string as their last 278 /// Matches entries' names only, ignoring the rest of
264 /// segment. 279 /// the qualifier.
265 pub fn anchor_end(self) -> Self { 280 /// Example: for `std::marker::PhantomData`, the name is `PhantomData`.
266 Self { anchor_end: true, ..self } 281 pub fn name_only(self) -> Self {
282 Self { name_only: true, ..self }
283 }
284
285 /// Specifies the way to search for the entries using the query.
286 pub fn search_mode(self, search_mode: SearchMode) -> Self {
287 Self { search_mode, ..self }
267 } 288 }
268 289
269 /// Limits the returned number of items to `limit`. 290 /// Limits the returned number of items to `limit`.
@@ -283,6 +304,40 @@ impl Query {
283 } 304 }
284} 305}
285 306
307fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool {
308 let mut input = if query.name_only {
309 input_path.segments.last().unwrap().to_string()
310 } else {
311 input_path.to_string()
312 };
313 if enforce_lowercase || !query.case_sensitive {
314 input.make_ascii_lowercase();
315 }
316
317 let query_string =
318 if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased };
319
320 match query.search_mode {
321 SearchMode::Equals => &input == query_string,
322 SearchMode::Contains => input.contains(query_string),
323 SearchMode::Fuzzy => {
324 let mut unchecked_query_chars = query_string.chars();
325 let mut mismatching_query_char = unchecked_query_chars.next();
326
327 for input_char in input.chars() {
328 match mismatching_query_char {
329 None => return true,
330 Some(matching_query_char) if matching_query_char == input_char => {
331 mismatching_query_char = unchecked_query_chars.next();
332 }
333 _ => (),
334 }
335 }
336 mismatching_query_char.is_none()
337 }
338 }
339}
340
286/// Searches dependencies of `krate` for an importable path matching `query`. 341/// Searches dependencies of `krate` for an importable path matching `query`.
287/// 342///
288/// This returns a list of items that could be imported from dependencies of `krate`. 343/// This returns a list of items that could be imported from dependencies of `krate`.
@@ -312,39 +367,29 @@ pub fn search_dependencies<'a>(
312 let importables = &import_map.importables[indexed_value.value as usize..]; 367 let importables = &import_map.importables[indexed_value.value as usize..];
313 368
314 // Path shared by the importable items in this group. 369 // Path shared by the importable items in this group.
315 let path = &import_map.map[&importables[0]].path; 370 let common_importables_path = &import_map.map[&importables[0]].path;
316 371 if !contains_query(&query, common_importables_path, true) {
317 if query.anchor_end { 372 continue;
318 // Last segment must match query.
319 let last = path.segments.last().unwrap().to_string();
320 if last.to_lowercase() != query.lowercased {
321 continue;
322 }
323 } 373 }
324 374
375 let common_importables_path_fst = fst_path(common_importables_path);
325 // Add the items from this `ModPath` group. Those are all subsequent items in 376 // Add the items from this `ModPath` group. Those are all subsequent items in
326 // `importables` whose paths match `path`. 377 // `importables` whose paths match `path`.
327 let iter = importables 378 let iter = importables
328 .iter() 379 .iter()
329 .copied() 380 .copied()
330 .take_while(|item| { 381 .take_while(|item| {
331 let item_path = &import_map.map[item].path; 382 common_importables_path_fst == fst_path(&import_map.map[item].path)
332 fst_path(item_path) == fst_path(path)
333 }) 383 })
334 .filter(|&item| match item_import_kind(item) { 384 .filter(|&item| match item_import_kind(item) {
335 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), 385 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
336 None => true, 386 None => true,
387 })
388 .filter(|item| {
389 !query.case_sensitive // we've already checked the common importables path case-insensitively
390 || contains_query(&query, &import_map.map[item].path, false)
337 }); 391 });
338 392 res.extend(iter);
339 if query.case_sensitive {
340 // FIXME: This does not do a subsequence match.
341 res.extend(iter.filter(|item| {
342 let item_path = &import_map.map[item].path;
343 item_path.to_string().contains(&query.query)
344 }));
345 } else {
346 res.extend(iter);
347 }
348 393
349 if res.len() >= query.limit { 394 if res.len() >= query.limit {
350 res.truncate(query.limit); 395 res.truncate(query.limit);
@@ -387,8 +432,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
387mod tests { 432mod tests {
388 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 433 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
389 use expect_test::{expect, Expect}; 434 use expect_test::{expect, Expect};
435 use stdx::format_to;
390 436
391 use crate::{test_db::TestDB, AssocContainerId, Lookup}; 437 use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup};
392 438
393 use super::*; 439 use super::*;
394 440
@@ -407,14 +453,32 @@ mod tests {
407 .into_iter() 453 .into_iter()
408 .filter_map(|item| { 454 .filter_map(|item| {
409 let mark = match item { 455 let mark = match item {
456 ItemInNs::Types(ModuleDefId::FunctionId(_))
457 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
410 ItemInNs::Types(_) => "t", 458 ItemInNs::Types(_) => "t",
411 ItemInNs::Values(_) => "v", 459 ItemInNs::Values(_) => "v",
412 ItemInNs::Macros(_) => "m", 460 ItemInNs::Macros(_) => "m",
413 }; 461 };
414 let item = assoc_to_trait(&db, item);
415 item.krate(db.upcast()).map(|krate| { 462 item.krate(db.upcast()).map(|krate| {
416 let map = db.import_map(krate); 463 let map = db.import_map(krate);
417 let path = map.path_of(item).unwrap(); 464
465 let path = match assoc_to_trait(&db, item) {
466 Some(trait_) => {
467 let mut full_path = map.path_of(trait_).unwrap().to_string();
468 if let ItemInNs::Types(ModuleDefId::FunctionId(function_id))
469 | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
470 {
471 format_to!(
472 full_path,
473 "::{}",
474 FunctionData::fn_data_query(&db, function_id).name,
475 );
476 }
477 full_path
478 }
479 None => map.path_of(item).unwrap().to_string(),
480 };
481
418 format!( 482 format!(
419 "{}::{} ({})\n", 483 "{}::{} ({})\n",
420 crate_graph[krate].display_name.as_ref().unwrap(), 484 crate_graph[krate].display_name.as_ref().unwrap(),
@@ -427,15 +491,15 @@ mod tests {
427 expect.assert_eq(&actual) 491 expect.assert_eq(&actual)
428 } 492 }
429 493
430 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs { 494 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
431 let assoc: AssocItemId = match item { 495 let assoc: AssocItemId = match item {
432 ItemInNs::Types(it) | ItemInNs::Values(it) => match it { 496 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
433 ModuleDefId::TypeAliasId(it) => it.into(), 497 ModuleDefId::TypeAliasId(it) => it.into(),
434 ModuleDefId::FunctionId(it) => it.into(), 498 ModuleDefId::FunctionId(it) => it.into(),
435 ModuleDefId::ConstId(it) => it.into(), 499 ModuleDefId::ConstId(it) => it.into(),
436 _ => return item, 500 _ => return None,
437 }, 501 },
438 _ => return item, 502 _ => return None,
439 }; 503 };
440 504
441 let container = match assoc { 505 let container = match assoc {
@@ -445,8 +509,8 @@ mod tests {
445 }; 509 };
446 510
447 match container { 511 match container {
448 AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()), 512 AssocContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())),
449 _ => item, 513 _ => None,
450 } 514 }
451 } 515 }
452 516
@@ -685,7 +749,7 @@ mod tests {
685 } 749 }
686 750
687 #[test] 751 #[test]
688 fn search() { 752 fn search_mode() {
689 let ra_fixture = r#" 753 let ra_fixture = r#"
690 //- /main.rs crate:main deps:dep 754 //- /main.rs crate:main deps:dep
691 //- /dep.rs crate:dep deps:tdep 755 //- /dep.rs crate:dep deps:tdep
@@ -713,28 +777,96 @@ mod tests {
713 check_search( 777 check_search(
714 ra_fixture, 778 ra_fixture,
715 "main", 779 "main",
716 Query::new("fmt"), 780 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
717 expect![[r#" 781 expect![[r#"
718 dep::fmt (t) 782 dep::fmt (t)
719 dep::Fmt (t) 783 dep::Fmt (t)
720 dep::Fmt (v) 784 dep::Fmt (v)
721 dep::Fmt (m) 785 dep::Fmt (m)
722 dep::fmt::Display (t) 786 dep::fmt::Display (t)
723 dep::format (v) 787 dep::format (f)
788 dep::fmt::Display::fmt (f)
789 "#]],
790 );
791
792 check_search(
793 ra_fixture,
794 "main",
795 Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
796 expect![[r#"
797 dep::fmt (t)
798 dep::Fmt (t)
799 dep::Fmt (v)
800 dep::Fmt (m)
801 dep::fmt::Display::fmt (f)
802 "#]],
803 );
804
805 check_search(
806 ra_fixture,
807 "main",
808 Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
809 expect![[r#"
810 dep::fmt (t)
811 dep::Fmt (t)
812 dep::Fmt (v)
813 dep::Fmt (m)
724 dep::fmt::Display (t) 814 dep::fmt::Display (t)
815 dep::fmt::Display::fmt (f)
725 "#]], 816 "#]],
726 ); 817 );
818 }
819
820 #[test]
821 fn name_only() {
822 let ra_fixture = r#"
823 //- /main.rs crate:main deps:dep
824 //- /dep.rs crate:dep deps:tdep
825 use tdep::fmt as fmt_dep;
826 pub mod fmt {
827 pub trait Display {
828 fn fmt();
829 }
830 }
831 #[macro_export]
832 macro_rules! Fmt {
833 () => {};
834 }
835 pub struct Fmt;
836
837 pub fn format() {}
838 pub fn no() {}
839
840 //- /tdep.rs crate:tdep
841 pub mod fmt {
842 pub struct NotImportableFromMain;
843 }
844 "#;
727 845
728 check_search( 846 check_search(
729 ra_fixture, 847 ra_fixture,
730 "main", 848 "main",
731 Query::new("fmt").anchor_end(), 849 Query::new("fmt".to_string()),
732 expect![[r#" 850 expect![[r#"
733 dep::fmt (t) 851 dep::fmt (t)
734 dep::Fmt (t) 852 dep::Fmt (t)
735 dep::Fmt (v) 853 dep::Fmt (v)
736 dep::Fmt (m) 854 dep::Fmt (m)
737 dep::fmt::Display (t) 855 dep::fmt::Display (t)
856 dep::fmt::Display::fmt (f)
857 "#]],
858 );
859
860 check_search(
861 ra_fixture,
862 "main",
863 Query::new("fmt".to_string()).name_only(),
864 expect![[r#"
865 dep::fmt (t)
866 dep::Fmt (t)
867 dep::Fmt (v)
868 dep::Fmt (m)
869 dep::fmt::Display::fmt (f)
738 "#]], 870 "#]],
739 ); 871 );
740 } 872 }
@@ -752,7 +884,7 @@ mod tests {
752 check_search( 884 check_search(
753 ra_fixture, 885 ra_fixture,
754 "main", 886 "main",
755 Query::new("FMT"), 887 Query::new("FMT".to_string()),
756 expect![[r#" 888 expect![[r#"
757 dep::fmt (t) 889 dep::fmt (t)
758 dep::fmt (v) 890 dep::fmt (v)
@@ -764,7 +896,7 @@ mod tests {
764 check_search( 896 check_search(
765 ra_fixture, 897 ra_fixture,
766 "main", 898 "main",
767 Query::new("FMT").case_sensitive(), 899 Query::new("FMT".to_string()).case_sensitive(),
768 expect![[r#" 900 expect![[r#"
769 dep::FMT (t) 901 dep::FMT (t)
770 dep::FMT (v) 902 dep::FMT (v)
@@ -793,7 +925,7 @@ mod tests {
793 pub fn no() {} 925 pub fn no() {}
794 "#, 926 "#,
795 "main", 927 "main",
796 Query::new("").limit(2), 928 Query::new("".to_string()).limit(2),
797 expect![[r#" 929 expect![[r#"
798 dep::fmt (t) 930 dep::fmt (t)
799 dep::Fmt (t) 931 dep::Fmt (t)
@@ -814,7 +946,7 @@ mod tests {
814 check_search( 946 check_search(
815 ra_fixture, 947 ra_fixture,
816 "main", 948 "main",
817 Query::new("FMT"), 949 Query::new("FMT".to_string()),
818 expect![[r#" 950 expect![[r#"
819 dep::fmt (t) 951 dep::fmt (t)
820 dep::fmt (v) 952 dep::fmt (v)
@@ -826,7 +958,7 @@ mod tests {
826 check_search( 958 check_search(
827 ra_fixture, 959 ra_fixture,
828 "main", 960 "main",
829 Query::new("FMT").exclude_import_kind(ImportKind::Adt), 961 Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt),
830 expect![[r#""#]], 962 expect![[r#""#]],
831 ); 963 );
832 } 964 }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 100dbf5d6..b6f510731 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -260,6 +260,7 @@ impl GenericParamsStorage {
260 fn alloc(&mut self, params: GenericParams) -> GenericParamsId { 260 fn alloc(&mut self, params: GenericParams) -> GenericParamsId {
261 if params.types.is_empty() 261 if params.types.is_empty()
262 && params.lifetimes.is_empty() 262 && params.lifetimes.is_empty()
263 && params.consts.is_empty()
263 && params.where_predicates.is_empty() 264 && params.where_predicates.is_empty()
264 { 265 {
265 return GenericParamsId::EMPTY; 266 return GenericParamsId::EMPTY;
@@ -269,8 +270,12 @@ impl GenericParamsStorage {
269 } 270 }
270} 271}
271 272
272static EMPTY_GENERICS: GenericParams = 273static EMPTY_GENERICS: GenericParams = GenericParams {
273 GenericParams { types: Arena::new(), lifetimes: Arena::new(), where_predicates: Vec::new() }; 274 types: Arena::new(),
275 lifetimes: Arena::new(),
276 consts: Arena::new(),
277 where_predicates: Vec::new(),
278};
274 279
275#[derive(Default, Debug, Eq, PartialEq)] 280#[derive(Default, Debug, Eq, PartialEq)]
276struct ItemTreeData { 281struct ItemTreeData {
diff --git a/crates/hir_def/src/keys.rs b/crates/hir_def/src/keys.rs
index 9c585de2c..89b3ed868 100644
--- a/crates/hir_def/src/keys.rs
+++ b/crates/hir_def/src/keys.rs
@@ -8,8 +8,8 @@ use syntax::{ast, AstNode, AstPtr};
8 8
9use crate::{ 9use crate::{
10 dyn_map::{DynMap, Policy}, 10 dyn_map::{DynMap, Policy},
11 ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, StaticId, 11 ConstId, ConstParamId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId,
12 StructId, TraitId, TypeAliasId, TypeParamId, UnionId, 12 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
13}; 13};
14 14
15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; 15pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
@@ -29,6 +29,7 @@ pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new();
29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); 29pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new();
30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); 30pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
31pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); 31pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
32pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new();
32 33
33pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); 34pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();
34 35
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index ba09a9126..211cb2faf 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -232,6 +232,13 @@ pub struct LifetimeParamId {
232pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>; 232pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>;
233 233
234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 234#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub struct ConstParamId {
236 pub parent: GenericDefId,
237 pub local_id: LocalConstParamId,
238}
239pub type LocalConstParamId = Idx<generics::ConstParamData>;
240
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub enum ContainerId { 242pub enum ContainerId {
236 ModuleId(ModuleId), 243 ModuleId(ModuleId),
237 DefWithBodyId(DefWithBodyId), 244 DefWithBodyId(DefWithBodyId),
@@ -254,6 +261,15 @@ pub enum AdtId {
254} 261}
255impl_from!(StructId, UnionId, EnumId for AdtId); 262impl_from!(StructId, UnionId, EnumId for AdtId);
256 263
264/// A generic param
265#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
266pub enum GenericParamId {
267 TypeParamId(TypeParamId),
268 LifetimeParamId(LifetimeParamId),
269 ConstParamId(ConstParamId),
270}
271impl_from!(TypeParamId, LifetimeParamId, ConstParamId for GenericParamId);
272
257/// The defs which can be visible in the module. 273/// The defs which can be visible in the module.
258#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 274#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
259pub enum ModuleDefId { 275pub enum ModuleDefId {
@@ -350,6 +366,7 @@ pub enum AttrDefId {
350 TypeAliasId(TypeAliasId), 366 TypeAliasId(TypeAliasId),
351 MacroDefId(MacroDefId), 367 MacroDefId(MacroDefId),
352 ImplId(ImplId), 368 ImplId(ImplId),
369 GenericParamId(GenericParamId),
353} 370}
354 371
355impl_from!( 372impl_from!(
@@ -363,7 +380,8 @@ impl_from!(
363 TraitId, 380 TraitId,
364 TypeAliasId, 381 TypeAliasId,
365 MacroDefId, 382 MacroDefId,
366 ImplId 383 ImplId,
384 GenericParamId
367 for AttrDefId 385 for AttrDefId
368); 386);
369 387
@@ -488,6 +506,15 @@ impl AttrDefId {
488 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 506 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate,
489 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 507 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
490 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 508 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate,
509 AttrDefId::GenericParamId(it) => {
510 match it {
511 GenericParamId::TypeParamId(it) => it.parent,
512 GenericParamId::LifetimeParamId(it) => it.parent,
513 GenericParamId::ConstParamId(it) => it.parent,
514 }
515 .module(db)
516 .krate
517 }
491 // FIXME: `MacroDefId` should store the defining module, then this can implement 518 // FIXME: `MacroDefId` should store the defining module, then this can implement
492 // `HasModule` 519 // `HasModule`
493 AttrDefId::MacroDefId(it) => it.krate, 520 AttrDefId::MacroDefId(it) => it.krate,
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index ffd0381d4..5682e122d 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -249,7 +249,7 @@ impl CrateDefMap {
249 buf.push_str(" _"); 249 buf.push_str(" _");
250 } 250 }
251 251
252 buf.push_str("\n"); 252 buf.push('\n');
253 } 253 }
254 254
255 for (name, child) in map.modules[module].children.iter() { 255 for (name, child) in map.modules[module].children.iter() {
@@ -454,7 +454,7 @@ mod diagnostics {
454 }); 454 });
455 for token in tokens { 455 for token in tokens {
456 if token.kind() == SyntaxKind::IDENT 456 if token.kind() == SyntaxKind::IDENT
457 && token.to_string() == *name 457 && token.text() == name.as_str()
458 { 458 {
459 precise_location = Some(token.text_range()); 459 precise_location = Some(token.text_range());
460 break 'outer; 460 break 'outer;
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs
index f9bf5bc72..e5e9e8ca1 100644
--- a/crates/hir_def/src/nameres/tests/macros.rs
+++ b/crates/hir_def/src/nameres/tests/macros.rs
@@ -677,7 +677,7 @@ fn macro_expansion_overflow() {
677 r#" 677 r#"
678macro_rules! a { 678macro_rules! a {
679 ($e:expr; $($t:tt)*) => { 679 ($e:expr; $($t:tt)*) => {
680 b!($($t)*); 680 b!(static = (); $($t)*);
681 }; 681 };
682 () => {}; 682 () => {};
683} 683}
@@ -689,7 +689,7 @@ macro_rules! b {
689 () => {}; 689 () => {};
690} 690}
691 691
692b! { static = #[] (); } 692b! { static = #[] ();}
693"#, 693"#,
694 expect![[r#" 694 expect![[r#"
695 crate 695 crate
diff --git a/crates/hir_def/src/path/lower.rs b/crates/hir_def/src/path/lower.rs
index 8a01e6eea..9518ac109 100644
--- a/crates/hir_def/src/path/lower.rs
+++ b/crates/hir_def/src/path/lower.rs
@@ -123,7 +123,7 @@ pub(super) fn lower_path(mut path: ast::Path, hygiene: &Hygiene) -> Option<Path>
123 // We follow what it did anyway :) 123 // We follow what it did anyway :)
124 if segments.len() == 1 && kind == PathKind::Plain { 124 if segments.len() == 1 && kind == PathKind::Plain {
125 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) { 125 if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
126 if let Some(crate_id) = hygiene.local_inner_macros() { 126 if let Some(crate_id) = hygiene.local_inner_macros(path) {
127 kind = PathKind::DollarCrate(crate_id); 127 kind = PathKind::DollarCrate(crate_id);
128 } 128 }
129 } 129 }
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index f8cc5e075..129f1dbac 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -20,9 +20,9 @@ use crate::{
20 path::{ModPath, PathKind}, 20 path::{ModPath, PathKind},
21 per_ns::PerNs, 21 per_ns::PerNs,
22 visibility::{RawVisibility, Visibility}, 22 visibility::{RawVisibility, Visibility},
23 AdtId, AssocContainerId, ConstId, ContainerId, DefWithBodyId, EnumId, EnumVariantId, 23 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId,
24 FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId, ModuleId, 24 EnumVariantId, FunctionId, GenericDefId, HasModule, ImplId, LocalModuleId, Lookup, ModuleDefId,
25 StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId, 25 ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, VariantId,
26}; 26};
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
@@ -93,6 +93,7 @@ pub enum ValueNs {
93 StaticId(StaticId), 93 StaticId(StaticId),
94 StructId(StructId), 94 StructId(StructId),
95 EnumVariantId(EnumVariantId), 95 EnumVariantId(EnumVariantId),
96 GenericParam(ConstParamId),
96} 97}
97 98
98impl Resolver { 99impl Resolver {
@@ -163,7 +164,7 @@ impl Resolver {
163 } 164 }
164 165
165 Scope::GenericParams { params, def } => { 166 Scope::GenericParams { params, def } => {
166 if let Some(local_id) = params.find_by_name(first_name) { 167 if let Some(local_id) = params.find_type_by_name(first_name) {
167 let idx = if path.segments.len() == 1 { None } else { Some(1) }; 168 let idx = if path.segments.len() == 1 { None } else { Some(1) };
168 return Some(( 169 return Some((
169 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }), 170 TypeNs::GenericParam(TypeParamId { local_id, parent: *def }),
@@ -285,11 +286,17 @@ impl Resolver {
285 Scope::ExprScope(_) => continue, 286 Scope::ExprScope(_) => continue,
286 287
287 Scope::GenericParams { params, def } if n_segments > 1 => { 288 Scope::GenericParams { params, def } if n_segments > 1 => {
288 if let Some(local_id) = params.find_by_name(first_name) { 289 if let Some(local_id) = params.find_type_by_name(first_name) {
289 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def }); 290 let ty = TypeNs::GenericParam(TypeParamId { local_id, parent: *def });
290 return Some(ResolveValueResult::Partial(ty, 1)); 291 return Some(ResolveValueResult::Partial(ty, 1));
291 } 292 }
292 } 293 }
294 Scope::GenericParams { params, def } if n_segments == 1 => {
295 if let Some(local_id) = params.find_const_by_name(first_name) {
296 let val = ValueNs::GenericParam(ConstParamId { local_id, parent: *def });
297 return Some(ResolveValueResult::ValueNs(val));
298 }
299 }
293 Scope::GenericParams { .. } => continue, 300 Scope::GenericParams { .. } => continue,
294 301
295 Scope::ImplDefScope(impl_) => { 302 Scope::ImplDefScope(impl_) => {
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 6382521fb..80b60d59f 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -259,7 +259,8 @@ fn format_args_expand(
259 } 259 }
260 for arg in &mut args { 260 for arg in &mut args {
261 // Remove `key =`. 261 // Remove `key =`.
262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { 262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
263 {
263 arg.drain(..2); 264 arg.drain(..2);
264 } 265 }
265 } 266 }
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 06f0a3ed9..0a0d021e0 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -404,7 +404,7 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
404 TRY_EXPR => FragmentKind::Expr, 404 TRY_EXPR => FragmentKind::Expr,
405 TUPLE_EXPR => FragmentKind::Expr, 405 TUPLE_EXPR => FragmentKind::Expr,
406 PAREN_EXPR => FragmentKind::Expr, 406 PAREN_EXPR => FragmentKind::Expr,
407 407 ARRAY_EXPR => FragmentKind::Expr,
408 FOR_EXPR => FragmentKind::Expr, 408 FOR_EXPR => FragmentKind::Expr,
409 PATH_EXPR => FragmentKind::Expr, 409 PATH_EXPR => FragmentKind::Expr,
410 CLOSURE_EXPR => FragmentKind::Expr, 410 CLOSURE_EXPR => FragmentKind::Expr,
diff --git a/crates/hir_expand/src/hygiene.rs b/crates/hir_expand/src/hygiene.rs
index 7ab0a5e52..6042e15b2 100644
--- a/crates/hir_expand/src/hygiene.rs
+++ b/crates/hir_expand/src/hygiene.rs
@@ -2,30 +2,94 @@
2//! 2//!
3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at 3//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
4//! this moment, this is horribly incomplete and handles only `$crate`. 4//! this moment, this is horribly incomplete and handles only `$crate`.
5use std::sync::Arc;
6
7use arena::{Arena, Idx};
5use base_db::CrateId; 8use base_db::CrateId;
6use either::Either; 9use either::Either;
7use syntax::ast; 10use mbe::Origin;
11use syntax::{ast, AstNode};
8 12
9use crate::{ 13use crate::{
10 db::AstDatabase, 14 db::AstDatabase,
11 name::{AsName, Name}, 15 name::{AsName, Name},
12 HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind, 16 ExpansionInfo, HirFileId, HirFileIdRepr, MacroCallId, MacroDefKind,
13}; 17};
14 18
15#[derive(Clone, Debug)] 19#[derive(Clone, Debug)]
16pub struct Hygiene { 20pub struct Hygiene {
17 // This is what `$crate` expands to 21 frames: Option<Arc<HygieneFrames>>,
18 def_crate: Option<CrateId>, 22}
23
24impl Hygiene {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene {
26 Hygiene { frames: Some(Arc::new(HygieneFrames::new(db, file_id.clone()))) }
27 }
28
29 pub fn new_unhygienic() -> Hygiene {
30 Hygiene { frames: None }
31 }
32
33 // FIXME: this should just return name
34 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> {
35 if let Some(frames) = &self.frames {
36 if name_ref.text() == "$crate" {
37 if let Some(krate) = frames.root_crate(&name_ref) {
38 return Either::Right(krate);
39 }
40 }
41 }
42
43 Either::Left(name_ref.as_name())
44 }
45
46 pub fn local_inner_macros(&self, path: ast::Path) -> Option<CrateId> {
47 let frames = self.frames.as_ref()?;
48
49 let mut token = path.syntax().first_token()?;
50 let mut current = frames.first();
51
52 while let Some((frame, data)) =
53 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
54 {
55 let (mapped, origin) = data;
56 if origin == Origin::Def {
57 return if frame.local_inner { frame.krate } else { None };
58 }
59 current = Some(&frames.0[frame.call_site?]);
60 token = mapped.value;
61 }
62 None
63 }
64}
65
66#[derive(Default, Debug)]
67struct HygieneFrames(Arena<HygieneFrame>);
68
69#[derive(Clone, Debug)]
70struct HygieneFrame {
71 expansion: Option<ExpansionInfo>,
19 72
20 // Indicate this is a local inner macro 73 // Indicate this is a local inner macro
21 local_inner: bool, 74 local_inner: bool,
75 krate: Option<CrateId>,
76
77 call_site: Option<Idx<HygieneFrame>>,
78 def_site: Option<Idx<HygieneFrame>>,
22} 79}
23 80
24impl Hygiene { 81impl HygieneFrames {
25 pub fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Hygiene { 82 fn new(db: &dyn AstDatabase, file_id: HirFileId) -> Self {
26 let (def_crate, local_inner) = match file_id.0 { 83 let mut frames = HygieneFrames::default();
84 frames.add(db, file_id);
85 frames
86 }
87
88 fn add(&mut self, db: &dyn AstDatabase, file_id: HirFileId) -> Option<Idx<HygieneFrame>> {
89 let (krate, local_inner) = match file_id.0 {
27 HirFileIdRepr::FileId(_) => (None, false), 90 HirFileIdRepr::FileId(_) => (None, false),
28 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id { 91 HirFileIdRepr::MacroFile(macro_file) => match macro_file.macro_call_id {
92 MacroCallId::EagerMacro(_id) => (None, false),
29 MacroCallId::LazyMacro(id) => { 93 MacroCallId::LazyMacro(id) => {
30 let loc = db.lookup_intern_macro(id); 94 let loc = db.lookup_intern_macro(id);
31 match loc.def.kind { 95 match loc.def.kind {
@@ -36,31 +100,68 @@ impl Hygiene {
36 MacroDefKind::ProcMacro(_) => (None, false), 100 MacroDefKind::ProcMacro(_) => (None, false),
37 } 101 }
38 } 102 }
39 MacroCallId::EagerMacro(_id) => (None, false),
40 }, 103 },
41 }; 104 };
42 Hygiene { def_crate, local_inner }
43 }
44 105
45 pub fn new_unhygienic() -> Hygiene { 106 let expansion = file_id.expansion_info(db);
46 Hygiene { def_crate: None, local_inner: false } 107 let expansion = match expansion {
108 None => {
109 return Some(self.0.alloc(HygieneFrame {
110 expansion: None,
111 local_inner,
112 krate,
113 call_site: None,
114 def_site: None,
115 }));
116 }
117 Some(it) => it,
118 };
119
120 let def_site = expansion.def.clone();
121 let call_site = expansion.arg.file_id;
122 let idx = self.0.alloc(HygieneFrame {
123 expansion: Some(expansion),
124 local_inner,
125 krate,
126 call_site: None,
127 def_site: None,
128 });
129
130 self.0[idx].call_site = self.add(db, call_site);
131 self.0[idx].def_site = def_site.and_then(|it| self.add(db, it.file_id));
132
133 Some(idx)
47 } 134 }
48 135
49 // FIXME: this should just return name 136 fn first(&self) -> Option<&HygieneFrame> {
50 pub fn name_ref_to_name(&self, name_ref: ast::NameRef) -> Either<Name, CrateId> { 137 self.0.iter().next().map(|it| it.1)
51 if let Some(def_crate) = self.def_crate {
52 if name_ref.text() == "$crate" {
53 return Either::Right(def_crate);
54 }
55 }
56 Either::Left(name_ref.as_name())
57 } 138 }
58 139
59 pub fn local_inner_macros(&self) -> Option<CrateId> { 140 fn root_crate(&self, name_ref: &ast::NameRef) -> Option<CrateId> {
60 if self.local_inner { 141 let mut token = name_ref.syntax().first_token()?;
61 self.def_crate 142 let first = self.first()?;
62 } else { 143 let mut result = first.krate;
63 None 144 let mut current = Some(first);
145
146 while let Some((frame, (mapped, origin))) =
147 current.and_then(|it| Some((it, it.expansion.as_ref()?.map_token_up(&token)?)))
148 {
149 result = frame.krate;
150
151 let site = match origin {
152 Origin::Def => frame.def_site,
153 Origin::Call => frame.call_site,
154 };
155
156 let site = match site {
157 None => break,
158 Some(it) => it,
159 };
160
161 current = Some(&self.0[site]);
162 token = mapped.value;
64 } 163 }
164
165 result
65 } 166 }
66} 167}
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 3fa1b1d77..5b6734a5f 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -340,11 +340,8 @@ impl ExpansionInfo {
340 Some(self.expanded.with_value(token)) 340 Some(self.expanded.with_value(token))
341 } 341 }
342 342
343 pub fn map_token_up( 343 pub fn map_token_up(&self, token: &SyntaxToken) -> Option<(InFile<SyntaxToken>, Origin)> {
344 &self, 344 let token_id = self.exp_map.token_by_range(token.text_range())?;
345 token: InFile<&SyntaxToken>,
346 ) -> Option<(InFile<SyntaxToken>, Origin)> {
347 let token_id = self.exp_map.token_by_range(token.value.text_range())?;
348 345
349 let (token_id, origin) = self.macro_def.0.map_id_up(token_id); 346 let (token_id, origin) = self.macro_def.0.map_id_up(token_id);
350 let (token_map, tt) = match origin { 347 let (token_map, tt) = match origin {
@@ -359,7 +356,7 @@ impl ExpansionInfo {
359 ), 356 ),
360 }; 357 };
361 358
362 let range = token_map.range_by_token(token_id)?.by_kind(token.value.kind())?; 359 let range = token_map.range_by_token(token_id)?.by_kind(token.kind())?;
363 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) 360 let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start())
364 .into_token()?; 361 .into_token()?;
365 Some((tt.with_value(token), origin)) 362 Some((tt.with_value(token), origin))
@@ -495,7 +492,7 @@ fn ascend_call_token(
495 expansion: &ExpansionInfo, 492 expansion: &ExpansionInfo,
496 token: InFile<SyntaxToken>, 493 token: InFile<SyntaxToken>,
497) -> Option<InFile<SyntaxToken>> { 494) -> Option<InFile<SyntaxToken>> {
498 let (mapped, origin) = expansion.map_token_up(token.as_ref())?; 495 let (mapped, origin) = expansion.map_token_up(&token.value)?;
499 if origin != Origin::Call { 496 if origin != Origin::Call {
500 return None; 497 return None;
501 } 498 }
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 965c1780a..2dfccd191 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -10,7 +10,7 @@ edition = "2018"
10doctest = false 10doctest = false
11 11
12[dependencies] 12[dependencies]
13itertools = "0.9.0" 13itertools = "0.10.0"
14arrayvec = "0.5.1" 14arrayvec = "0.5.1"
15smallvec = "1.2.0" 15smallvec = "1.2.0"
16ena = "0.14.0" 16ena = "0.14.0"
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs
index 66bdb8e88..f3567c49e 100644
--- a/crates/hir_ty/src/db.rs
+++ b/crates/hir_ty/src/db.rs
@@ -5,8 +5,8 @@ use std::sync::Arc;
5use arena::map::ArenaMap; 5use arena::map::ArenaMap;
6use base_db::{impl_intern_key, salsa, CrateId, Upcast}; 6use base_db::{impl_intern_key, salsa, CrateId, Upcast};
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, expr::ExprId, DefWithBodyId, FunctionId, GenericDefId, ImplId, LocalFieldId, 8 db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
9 TypeParamId, VariantId, 9 LocalFieldId, TypeParamId, VariantId,
10}; 10};
11 11
12use crate::{ 12use crate::{
@@ -37,6 +37,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
37 #[salsa::cycle(crate::lower::impl_self_ty_recover)] 37 #[salsa::cycle(crate::lower::impl_self_ty_recover)]
38 fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>; 38 fn impl_self_ty(&self, def: ImplId) -> Binders<Ty>;
39 39
40 #[salsa::invoke(crate::lower::const_param_ty_query)]
41 fn const_param_ty(&self, def: ConstParamId) -> Ty;
42
40 #[salsa::invoke(crate::lower::impl_trait_query)] 43 #[salsa::invoke(crate::lower::impl_trait_query)]
41 fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>; 44 fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
42 45
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs
index 849415706..b4e453411 100644
--- a/crates/hir_ty/src/diagnostics/expr.rs
+++ b/crates/hir_ty/src/diagnostics/expr.rs
@@ -156,7 +156,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
156 // FIXME: Due to shortcomings in the current type system implementation, only emit this 156 // FIXME: Due to shortcomings in the current type system implementation, only emit this
157 // diagnostic if there are no type mismatches in the containing function. 157 // diagnostic if there are no type mismatches in the containing function.
158 if self.infer.type_mismatches.iter().next().is_some() { 158 if self.infer.type_mismatches.iter().next().is_some() {
159 return Some(()); 159 return None;
160 } 160 }
161 161
162 let is_method_call = matches!(expr, Expr::MethodCall { .. }); 162 let is_method_call = matches!(expr, Expr::MethodCall { .. });
@@ -170,6 +170,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
170 let mut args = args.clone(); 170 let mut args = args.clone();
171 args.insert(0, *receiver); 171 args.insert(0, *receiver);
172 172
173 let receiver = &self.infer.type_of_expr[*receiver];
174 if receiver.strip_references().is_unknown() {
175 // if the receiver is of unknown type, it's very likely we
176 // don't know enough to correctly resolve the method call.
177 // This is kind of a band-aid for #6975.
178 return None;
179 }
180
173 // FIXME: note that we erase information about substs here. This 181 // FIXME: note that we erase information about substs here. This
174 // is not right, but, luckily, doesn't matter as we care only 182 // is not right, but, luckily, doesn't matter as we care only
175 // about the number of params 183 // about the number of params
@@ -505,6 +513,22 @@ fn f() {
505 } 513 }
506 514
507 #[test] 515 #[test]
516 fn method_unknown_receiver() {
517 // note: this is incorrect code, so there might be errors on this in the
518 // future, but we shouldn't emit an argument count diagnostic here
519 check_diagnostics(
520 r#"
521trait Foo { fn method(&self, arg: usize) {} }
522
523fn f() {
524 let x;
525 x.method();
526}
527"#,
528 );
529 }
530
531 #[test]
508 fn tuple_struct() { 532 fn tuple_struct() {
509 check_diagnostics( 533 check_diagnostics(
510 r#" 534 r#"
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs
index 70a3f3075..f2fc69b2f 100644
--- a/crates/hir_ty/src/infer/expr.rs
+++ b/crates/hir_ty/src/infer/expr.rs
@@ -648,6 +648,8 @@ impl<'a> InferenceContext<'a> {
648 } 648 }
649 Expr::Array(array) => { 649 Expr::Array(array) => {
650 let elem_ty = match &expected.ty { 650 let elem_ty = match &expected.ty {
651 // FIXME: remove when https://github.com/rust-lang/rust/issues/80501 is fixed
652 #[allow(unreachable_patterns)]
651 ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => { 653 ty_app!(TypeCtor::Array, st) | ty_app!(TypeCtor::Slice, st) => {
652 st.as_single().clone() 654 st.as_single().clone()
653 } 655 }
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 80d7ed10e..5d541104e 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -89,6 +89,7 @@ impl<'a> InferenceContext<'a> {
89 return None; 89 return None;
90 } 90 }
91 } 91 }
92 ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
92 }; 93 };
93 94
94 let ty = self.db.value_ty(typable); 95 let ty = self.db.value_ty(typable);
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index 357bd92f9..e00c7e176 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -791,6 +791,10 @@ impl Ty {
791 matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. })) 791 matches!(self, Ty::Apply(ApplicationTy { ctor: TypeCtor::Never, .. }))
792 } 792 }
793 793
794 pub fn is_unknown(&self) -> bool {
795 matches!(self, Ty::Unknown)
796 }
797
794 /// If this is a `dyn Trait` type, this returns the `Trait` part. 798 /// If this is a `dyn Trait` type, this returns the `Trait` part.
795 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> { 799 pub fn dyn_trait_ref(&self) -> Option<&TraitRef> {
796 match self { 800 match self {
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 8da56cd11..222f61a11 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -16,9 +16,9 @@ use hir_def::{
16 path::{GenericArg, Path, PathSegment, PathSegments}, 16 path::{GenericArg, Path, PathSegment, PathSegments},
17 resolver::{HasResolver, Resolver, TypeNs}, 17 resolver::{HasResolver, Resolver, TypeNs},
18 type_ref::{TypeBound, TypeRef}, 18 type_ref::{TypeBound, TypeRef},
19 AdtId, AssocContainerId, AssocItemId, ConstId, EnumId, EnumVariantId, FunctionId, GenericDefId, 19 AdtId, AssocContainerId, AssocItemId, ConstId, ConstParamId, EnumId, EnumVariantId, FunctionId,
20 HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, 20 GenericDefId, HasModule, ImplId, LocalFieldId, Lookup, StaticId, StructId, TraitId,
21 UnionId, VariantId, 21 TypeAliasId, TypeParamId, UnionId, VariantId,
22}; 22};
23use hir_expand::name::Name; 23use hir_expand::name::Name;
24use smallvec::SmallVec; 24use smallvec::SmallVec;
@@ -1221,6 +1221,15 @@ pub(crate) fn impl_self_ty_query(db: &dyn HirDatabase, impl_id: ImplId) -> Binde
1221 Binders::new(generics.len(), Ty::from_hir(&ctx, &impl_data.target_type)) 1221 Binders::new(generics.len(), Ty::from_hir(&ctx, &impl_data.target_type))
1222} 1222}
1223 1223
1224pub(crate) fn const_param_ty_query(db: &dyn HirDatabase, def: ConstParamId) -> Ty {
1225 let parent_data = db.generic_params(def.parent);
1226 let data = &parent_data.consts[def.local_id];
1227 let resolver = def.parent.resolver(db.upcast());
1228 let ctx = TyLoweringContext::new(db, &resolver);
1229
1230 Ty::from_hir(&ctx, &data.ty)
1231}
1232
1224pub(crate) fn impl_self_ty_recover( 1233pub(crate) fn impl_self_ty_recover(
1225 db: &dyn HirDatabase, 1234 db: &dyn HirDatabase,
1226 _cycle: &[String], 1235 _cycle: &[String],
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index a7656b864..23b79abc4 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -371,6 +371,37 @@ expand!();
371} 371}
372 372
373#[test] 373#[test]
374fn infer_macro_with_dollar_crate_in_def_site() {
375 check_types(
376 r#"
377//- /main.rs crate:main deps:foo
378use foo::expand;
379
380macro_rules! list {
381 ($($tt:tt)*) => { $($tt)* }
382}
383
384fn test() {
385 let r = expand!();
386 r;
387 //^ u128
388}
389
390//- /lib.rs crate:foo
391#[macro_export]
392macro_rules! expand {
393 () => { list!($crate::m!()) };
394}
395
396#[macro_export]
397macro_rules! m {
398 () => { 0u128 };
399}
400"#,
401 );
402}
403
404#[test]
374fn infer_type_value_non_legacy_macro_use_as() { 405fn infer_type_value_non_legacy_macro_use_as() {
375 check_infer( 406 check_infer(
376 r#" 407 r#"
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs
index 307a257b1..cffe8630b 100644
--- a/crates/hir_ty/src/tests/regression.rs
+++ b/crates/hir_ty/src/tests/regression.rs
@@ -326,6 +326,24 @@ fn infer_paren_macro_call() {
326} 326}
327 327
328#[test] 328#[test]
329fn infer_array_macro_call() {
330 check_infer(
331 r#"
332 macro_rules! bar { () => {0u32} }
333 fn test() {
334 let a = [bar!()];
335 }
336 "#,
337 expect![[r#"
338 !0..4 '0u32': u32
339 44..69 '{ ...()]; }': ()
340 54..55 'a': [u32; _]
341 58..66 '[bar!()]': [u32; _]
342 "#]],
343 );
344}
345
346#[test]
329fn bug_1030() { 347fn bug_1030() {
330 check_infer( 348 check_infer(
331 r#" 349 r#"
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs
index a61282d5a..8d431b920 100644
--- a/crates/hir_ty/src/tests/simple.rs
+++ b/crates/hir_ty/src/tests/simple.rs
@@ -2375,3 +2375,19 @@ fn infer_operator_overload() {
2375 "#]], 2375 "#]],
2376 ); 2376 );
2377} 2377}
2378
2379#[test]
2380fn infer_const_params() {
2381 check_infer(
2382 r#"
2383 fn foo<const FOO: usize>() {
2384 let bar = FOO;
2385 }
2386 "#,
2387 expect![[r#"
2388 27..49 '{ ...FOO; }': ()
2389 37..40 'bar': usize
2390 43..46 'FOO': usize
2391 "#]],
2392 );
2393}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index 4d483580d..f1544dbe0 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12[dependencies] 12[dependencies]
13either = "1.5.3" 13either = "1.5.3"
14indexmap = "1.4.0" 14indexmap = "1.4.0"
15itertools = "0.9.0" 15itertools = "0.10.0"
16log = "0.4.8" 16log = "0.4.8"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18oorandom = "11.1.2" 18oorandom = "11.1.2"
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index 60e0cd4ad..3c2d39f5d 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -8,7 +8,7 @@ use ide_db::RootDatabase;
8use syntax::{ast, match_ast, AstNode, TextRange}; 8use syntax::{ast, match_ast, AstNode, TextRange};
9 9
10use crate::{ 10use crate::{
11 display::ToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo, 11 display::TryToNav, goto_definition, references, FilePosition, NavigationTarget, RangeInfo,
12}; 12};
13 13
14#[derive(Debug, Clone)] 14#[derive(Debug, Clone)]
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
61 match node { 61 match node {
62 ast::Fn(it) => { 62 ast::Fn(it) => {
63 let def = sema.to_def(&it)?; 63 let def = sema.to_def(&it)?;
64 Some(def.to_nav(sema.db)) 64 def.try_to_nav(sema.db)
65 }, 65 },
66 _ => None, 66 _ => None,
67 } 67 }
@@ -99,7 +99,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
99 match callable.kind() { 99 match callable.kind() {
100 hir::CallableKind::Function(it) => { 100 hir::CallableKind::Function(it) => {
101 let fn_def: hir::Function = it.into(); 101 let fn_def: hir::Function = it.into();
102 let nav = fn_def.to_nav(db); 102 let nav = fn_def.try_to_nav(db)?;
103 Some(nav) 103 Some(nav)
104 } 104 }
105 _ => None, 105 _ => None,
@@ -107,7 +107,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
107 } 107 }
108 FnCallNode::MethodCallExpr(expr) => { 108 FnCallNode::MethodCallExpr(expr) => {
109 let function = sema.resolve_method_call(&expr)?; 109 let function = sema.resolve_method_call(&expr)?;
110 Some(function.to_nav(db)) 110 function.try_to_nav(db)
111 } 111 }
112 } { 112 } {
113 Some((func_target, name_ref.syntax().text_range())) 113 Some((func_target, name_ref.syntax().text_range()))
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs
index 038273750..79d126ff2 100644
--- a/crates/ide/src/diagnostics.rs
+++ b/crates/ide/src/diagnostics.rs
@@ -199,6 +199,12 @@ fn check_unnecessary_braces_in_use_statement(
199) -> Option<()> { 199) -> Option<()> {
200 let use_tree_list = ast::UseTreeList::cast(node.clone())?; 200 let use_tree_list = ast::UseTreeList::cast(node.clone())?;
201 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { 201 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
202 // If there is a comment inside the bracketed `use`,
203 // assume it is a commented out module path and don't show diagnostic.
204 if use_tree_list.has_inner_comment() {
205 return Some(());
206 }
207
202 let use_range = use_tree_list.syntax().text_range(); 208 let use_range = use_tree_list.syntax().text_range();
203 let edit = 209 let edit =
204 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) 210 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree)
@@ -638,6 +644,22 @@ mod a {
638} 644}
639"#, 645"#,
640 ); 646 );
647 check_no_diagnostics(
648 r#"
649use a;
650use a::{
651 c,
652 // d::e
653};
654
655mod a {
656 mod c {}
657 mod d {
658 mod e {}
659 }
660}
661"#,
662 );
641 check_fix( 663 check_fix(
642 r" 664 r"
643 mod b {} 665 mod b {}
diff --git a/crates/ide/src/diagnostics/fixes.rs b/crates/ide/src/diagnostics/fixes.rs
index d79f5c170..ec0f840e9 100644
--- a/crates/ide/src/diagnostics/fixes.rs
+++ b/crates/ide/src/diagnostics/fixes.rs
@@ -156,20 +156,23 @@ fn missing_record_expr_field_fix(
156 let record_fields = match VariantDef::from(def_id) { 156 let record_fields = match VariantDef::from(def_id) {
157 VariantDef::Struct(s) => { 157 VariantDef::Struct(s) => {
158 module = s.module(sema.db); 158 module = s.module(sema.db);
159 let source = s.source(sema.db); 159 #[allow(deprecated)]
160 let source = s.source(sema.db)?;
160 def_file_id = source.file_id; 161 def_file_id = source.file_id;
161 let fields = source.value.field_list()?; 162 let fields = source.value.field_list()?;
162 record_field_list(fields)? 163 record_field_list(fields)?
163 } 164 }
164 VariantDef::Union(u) => { 165 VariantDef::Union(u) => {
165 module = u.module(sema.db); 166 module = u.module(sema.db);
166 let source = u.source(sema.db); 167 #[allow(deprecated)]
168 let source = u.source(sema.db)?;
167 def_file_id = source.file_id; 169 def_file_id = source.file_id;
168 source.value.record_field_list()? 170 source.value.record_field_list()?
169 } 171 }
170 VariantDef::Variant(e) => { 172 VariantDef::Variant(e) => {
171 module = e.module(sema.db); 173 module = e.module(sema.db);
172 let source = e.source(sema.db); 174 #[allow(deprecated)]
175 let source = e.source(sema.db)?;
173 def_file_id = source.file_id; 176 def_file_id = source.file_id;
174 let fields = source.value.field_list()?; 177 let fields = source.value.field_list()?;
175 record_field_list(fields)? 178 record_field_list(fields)?
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 6431e7d6d..e24c78301 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -24,6 +24,7 @@ pub enum SymbolKind {
24 Impl, 24 Impl,
25 Field, 25 Field,
26 TypeParam, 26 TypeParam,
27 ConstParam,
27 LifetimeParam, 28 LifetimeParam,
28 ValueParam, 29 ValueParam,
29 SelfParam, 30 SelfParam,
@@ -209,40 +210,32 @@ impl ToNav for FileSymbol {
209impl TryToNav for Definition { 210impl TryToNav for Definition {
210 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 211 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
211 match self { 212 match self {
212 Definition::Macro(it) => { 213 Definition::Macro(it) => it.try_to_nav(db),
213 // FIXME: Currently proc-macro do not have ast-node, 214 Definition::Field(it) => it.try_to_nav(db),
214 // such that it does not have source
215 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
216 if it.is_proc_macro() {
217 return None;
218 }
219 Some(it.to_nav(db))
220 }
221 Definition::Field(it) => Some(it.to_nav(db)),
222 Definition::ModuleDef(it) => it.try_to_nav(db), 215 Definition::ModuleDef(it) => it.try_to_nav(db),
223 Definition::SelfType(it) => Some(it.to_nav(db)), 216 Definition::SelfType(it) => it.try_to_nav(db),
224 Definition::Local(it) => Some(it.to_nav(db)), 217 Definition::Local(it) => Some(it.to_nav(db)),
225 Definition::TypeParam(it) => Some(it.to_nav(db)), 218 Definition::TypeParam(it) => it.try_to_nav(db),
226 Definition::LifetimeParam(it) => Some(it.to_nav(db)), 219 Definition::LifetimeParam(it) => it.try_to_nav(db),
227 Definition::Label(it) => Some(it.to_nav(db)), 220 Definition::Label(it) => Some(it.to_nav(db)),
221 Definition::ConstParam(it) => it.try_to_nav(db),
228 } 222 }
229 } 223 }
230} 224}
231 225
232impl TryToNav for hir::ModuleDef { 226impl TryToNav for hir::ModuleDef {
233 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 227 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
234 let res = match self { 228 match self {
235 hir::ModuleDef::Module(it) => it.to_nav(db), 229 hir::ModuleDef::Module(it) => Some(it.to_nav(db)),
236 hir::ModuleDef::Function(it) => it.to_nav(db), 230 hir::ModuleDef::Function(it) => it.try_to_nav(db),
237 hir::ModuleDef::Adt(it) => it.to_nav(db), 231 hir::ModuleDef::Adt(it) => it.try_to_nav(db),
238 hir::ModuleDef::Variant(it) => it.to_nav(db), 232 hir::ModuleDef::Variant(it) => it.try_to_nav(db),
239 hir::ModuleDef::Const(it) => it.to_nav(db), 233 hir::ModuleDef::Const(it) => it.try_to_nav(db),
240 hir::ModuleDef::Static(it) => it.to_nav(db), 234 hir::ModuleDef::Static(it) => it.try_to_nav(db),
241 hir::ModuleDef::Trait(it) => it.to_nav(db), 235 hir::ModuleDef::Trait(it) => it.try_to_nav(db),
242 hir::ModuleDef::TypeAlias(it) => it.to_nav(db), 236 hir::ModuleDef::TypeAlias(it) => it.try_to_nav(db),
243 hir::ModuleDef::BuiltinType(_) => return None, 237 hir::ModuleDef::BuiltinType(_) => None,
244 }; 238 }
245 Some(res)
246 } 239 }
247} 240}
248 241
@@ -277,13 +270,13 @@ impl ToNavFromAst for hir::Trait {
277 const KIND: SymbolKind = SymbolKind::Trait; 270 const KIND: SymbolKind = SymbolKind::Trait;
278} 271}
279 272
280impl<D> ToNav for D 273impl<D> TryToNav for D
281where 274where
282 D: HasSource + ToNavFromAst + Copy + HasAttrs, 275 D: HasSource + ToNavFromAst + Copy + HasAttrs,
283 D::Ast: ast::NameOwner + ShortLabel, 276 D::Ast: ast::NameOwner + ShortLabel,
284{ 277{
285 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 278 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
286 let src = self.source(db); 279 let src = self.source(db)?;
287 let mut res = NavigationTarget::from_named( 280 let mut res = NavigationTarget::from_named(
288 db, 281 db,
289 src.as_ref().map(|it| it as &dyn ast::NameOwner), 282 src.as_ref().map(|it| it as &dyn ast::NameOwner),
@@ -291,7 +284,7 @@ where
291 ); 284 );
292 res.docs = self.docs(db); 285 res.docs = self.docs(db);
293 res.description = src.value.short_label(); 286 res.description = src.value.short_label();
294 res 287 Some(res)
295 } 288 }
296} 289}
297 290
@@ -310,9 +303,9 @@ impl ToNav for hir::Module {
310 } 303 }
311} 304}
312 305
313impl ToNav for hir::Impl { 306impl TryToNav for hir::Impl {
314 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 307 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
315 let src = self.source(db); 308 let src = self.source(db)?;
316 let derive_attr = self.is_builtin_derive(db); 309 let derive_attr = self.is_builtin_derive(db);
317 let frange = if let Some(item) = &derive_attr { 310 let frange = if let Some(item) = &derive_attr {
318 item.syntax().original_file_range(db) 311 item.syntax().original_file_range(db)
@@ -325,21 +318,21 @@ impl ToNav for hir::Impl {
325 src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range) 318 src.value.self_ty().map(|ty| src.with_value(ty.syntax()).original_file_range(db).range)
326 }; 319 };
327 320
328 NavigationTarget::from_syntax( 321 Some(NavigationTarget::from_syntax(
329 frange.file_id, 322 frange.file_id,
330 "impl".into(), 323 "impl".into(),
331 focus_range, 324 focus_range,
332 frange.range, 325 frange.range,
333 SymbolKind::Impl, 326 SymbolKind::Impl,
334 ) 327 ))
335 } 328 }
336} 329}
337 330
338impl ToNav for hir::Field { 331impl TryToNav for hir::Field {
339 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 332 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
340 let src = self.source(db); 333 let src = self.source(db)?;
341 334
342 match &src.value { 335 let field_source = match &src.value {
343 FieldSource::Named(it) => { 336 FieldSource::Named(it) => {
344 let mut res = 337 let mut res =
345 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); 338 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field);
@@ -357,13 +350,14 @@ impl ToNav for hir::Field {
357 SymbolKind::Field, 350 SymbolKind::Field,
358 ) 351 )
359 } 352 }
360 } 353 };
354 Some(field_source)
361 } 355 }
362} 356}
363 357
364impl ToNav for hir::MacroDef { 358impl TryToNav for hir::MacroDef {
365 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 359 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
366 let src = self.source(db); 360 let src = self.source(db)?;
367 log::debug!("nav target {:#?}", src.value.syntax()); 361 log::debug!("nav target {:#?}", src.value.syntax());
368 let mut res = NavigationTarget::from_named( 362 let mut res = NavigationTarget::from_named(
369 db, 363 db,
@@ -371,26 +365,26 @@ impl ToNav for hir::MacroDef {
371 SymbolKind::Macro, 365 SymbolKind::Macro,
372 ); 366 );
373 res.docs = self.docs(db); 367 res.docs = self.docs(db);
374 res 368 Some(res)
375 } 369 }
376} 370}
377 371
378impl ToNav for hir::Adt { 372impl TryToNav for hir::Adt {
379 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 373 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
380 match self { 374 match self {
381 hir::Adt::Struct(it) => it.to_nav(db), 375 hir::Adt::Struct(it) => it.try_to_nav(db),
382 hir::Adt::Union(it) => it.to_nav(db), 376 hir::Adt::Union(it) => it.try_to_nav(db),
383 hir::Adt::Enum(it) => it.to_nav(db), 377 hir::Adt::Enum(it) => it.try_to_nav(db),
384 } 378 }
385 } 379 }
386} 380}
387 381
388impl ToNav for hir::AssocItem { 382impl TryToNav for hir::AssocItem {
389 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 383 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
390 match self { 384 match self {
391 AssocItem::Function(it) => it.to_nav(db), 385 AssocItem::Function(it) => it.try_to_nav(db),
392 AssocItem::Const(it) => it.to_nav(db), 386 AssocItem::Const(it) => it.try_to_nav(db),
393 AssocItem::TypeAlias(it) => it.to_nav(db), 387 AssocItem::TypeAlias(it) => it.try_to_nav(db),
394 } 388 }
395 } 389 }
396} 390}
@@ -444,9 +438,9 @@ impl ToNav for hir::Label {
444 } 438 }
445} 439}
446 440
447impl ToNav for hir::TypeParam { 441impl TryToNav for hir::TypeParam {
448 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 442 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
449 let src = self.source(db); 443 let src = self.source(db)?;
450 let full_range = match &src.value { 444 let full_range = match &src.value {
451 Either::Left(it) => it.syntax().text_range(), 445 Either::Left(it) => it.syntax().text_range(),
452 Either::Right(it) => it.syntax().text_range(), 446 Either::Right(it) => it.syntax().text_range(),
@@ -455,7 +449,7 @@ impl ToNav for hir::TypeParam {
455 Either::Left(_) => None, 449 Either::Left(_) => None,
456 Either::Right(it) => it.name().map(|it| it.syntax().text_range()), 450 Either::Right(it) => it.name().map(|it| it.syntax().text_range()),
457 }; 451 };
458 NavigationTarget { 452 Some(NavigationTarget {
459 file_id: src.file_id.original_file(db), 453 file_id: src.file_id.original_file(db),
460 name: self.name(db).to_string().into(), 454 name: self.name(db).to_string().into(),
461 kind: Some(SymbolKind::TypeParam), 455 kind: Some(SymbolKind::TypeParam),
@@ -464,15 +458,15 @@ impl ToNav for hir::TypeParam {
464 container_name: None, 458 container_name: None,
465 description: None, 459 description: None,
466 docs: None, 460 docs: None,
467 } 461 })
468 } 462 }
469} 463}
470 464
471impl ToNav for hir::LifetimeParam { 465impl TryToNav for hir::LifetimeParam {
472 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 466 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
473 let src = self.source(db); 467 let src = self.source(db)?;
474 let full_range = src.value.syntax().text_range(); 468 let full_range = src.value.syntax().text_range();
475 NavigationTarget { 469 Some(NavigationTarget {
476 file_id: src.file_id.original_file(db), 470 file_id: src.file_id.original_file(db),
477 name: self.name(db).to_string().into(), 471 name: self.name(db).to_string().into(),
478 kind: Some(SymbolKind::LifetimeParam), 472 kind: Some(SymbolKind::LifetimeParam),
@@ -481,7 +475,24 @@ impl ToNav for hir::LifetimeParam {
481 container_name: None, 475 container_name: None,
482 description: None, 476 description: None,
483 docs: None, 477 docs: None,
484 } 478 })
479 }
480}
481
482impl TryToNav for hir::ConstParam {
483 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
484 let src = self.source(db)?;
485 let full_range = src.value.syntax().text_range();
486 Some(NavigationTarget {
487 file_id: src.file_id.original_file(db),
488 name: self.name(db).to_string().into(),
489 kind: Some(SymbolKind::ConstParam),
490 full_range,
491 focus_range: src.value.name().map(|n| n.syntax().text_range()),
492 container_name: None,
493 description: None,
494 docs: None,
495 })
485 } 496 }
486} 497}
487 498
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index e10516f43..367fac05e 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -193,6 +193,7 @@ fn rewrite_intra_doc_link(
193 Definition::SelfType(_) 193 Definition::SelfType(_)
194 | Definition::Local(_) 194 | Definition::Local(_)
195 | Definition::TypeParam(_) 195 | Definition::TypeParam(_)
196 | Definition::ConstParam(_)
196 | Definition::LifetimeParam(_) 197 | Definition::LifetimeParam(_)
197 | Definition::Label(_) => return None, 198 | Definition::Label(_) => return None,
198 }?; 199 }?;
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs
index 6eac39639..da9378a97 100644
--- a/crates/ide/src/goto_implementation.rs
+++ b/crates/ide/src/goto_implementation.rs
@@ -2,7 +2,7 @@ use hir::{Crate, Impl, Semantics};
2use ide_db::RootDatabase; 2use ide_db::RootDatabase;
3use syntax::{algo::find_node_at_offset, ast, AstNode}; 3use syntax::{algo::find_node_at_offset, ast, AstNode};
4 4
5use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 5use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
6 6
7// Feature: Go to Implementation 7// Feature: Go to Implementation
8// 8//
@@ -55,7 +55,7 @@ fn impls_for_def(
55 impls 55 impls
56 .into_iter() 56 .into_iter()
57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) 57 .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db)))
58 .map(|imp| imp.to_nav(sema.db)) 58 .filter_map(|imp| imp.try_to_nav(sema.db))
59 .collect(), 59 .collect(),
60 ) 60 )
61} 61}
@@ -69,7 +69,7 @@ fn impls_for_trait(
69 69
70 let impls = Impl::for_trait(sema.db, krate, tr); 70 let impls = Impl::for_trait(sema.db, krate, tr);
71 71
72 Some(impls.into_iter().map(|imp| imp.to_nav(sema.db)).collect()) 72 Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect())
73} 73}
74 74
75#[cfg(test)] 75#[cfg(test)]
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index aba6bf5dc..7e84e06be 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -1,7 +1,7 @@
1use ide_db::RootDatabase; 1use ide_db::RootDatabase;
2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 2use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
3 3
4use crate::{display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 4use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo};
5 5
6// Feature: Go to Type Definition 6// Feature: Go to Type Definition
7// 7//
@@ -37,7 +37,7 @@ pub(crate) fn goto_type_definition(
37 37
38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?; 38 let adt_def = ty.autoderef(db).filter_map(|ty| ty.as_adt()).last()?;
39 39
40 let nav = adt_def.to_nav(db); 40 let nav = adt_def.try_to_nav(db)?;
41 Some(RangeInfo::new(node.text_range(), vec![nav])) 41 Some(RangeInfo::new(node.text_range(), vec![nav]))
42} 42}
43 43
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 73245fbe7..2737c900f 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -13,7 +13,7 @@ use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset,
13use test_utils::mark; 13use test_utils::mark;
14 14
15use crate::{ 15use crate::{
16 display::{macro_label, ShortLabel, ToNav, TryToNav}, 16 display::{macro_label, ShortLabel, TryToNav},
17 doc_links::{remove_links, rewrite_links}, 17 doc_links::{remove_links, rewrite_links},
18 markdown_remove::remove_markdown, 18 markdown_remove::remove_markdown,
19 markup::Markup, 19 markup::Markup,
@@ -109,6 +109,8 @@ pub(crate) fn hover(
109 match node { 109 match node {
110 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)), 110 ast::Name(name) => NameClass::classify(&sema, &name).and_then(|d| d.defined(sema.db)),
111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 111 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
112 ast::Lifetime(lifetime) => NameClass::classify_lifetime(&sema, &lifetime)
113 .map_or_else(|| NameRefClass::classify_lifetime(&sema, &lifetime).map(|d| d.referenced(sema.db)), |d| d.defined(sema.db)),
112 _ => None, 114 _ => None,
113 } 115 }
114 }; 116 };
@@ -181,10 +183,10 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
181 183
182 match def { 184 match def {
183 Definition::ModuleDef(it) => match it { 185 Definition::ModuleDef(it) => match it {
184 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), 186 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)),
185 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), 187 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)),
186 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), 188 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)),
187 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), 189 ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)),
188 _ => None, 190 _ => None,
189 }, 191 },
190 _ => None, 192 _ => None,
@@ -204,7 +206,8 @@ fn runnable_action(
204 _ => None, 206 _ => None,
205 }, 207 },
206 ModuleDef::Function(it) => { 208 ModuleDef::Function(it) => {
207 let src = it.source(sema.db); 209 #[allow(deprecated)]
210 let src = it.source(sema.db)?;
208 if src.file_id != file_id.into() { 211 if src.file_id != file_id.into() {
209 mark::hit!(hover_macro_generated_struct_fn_doc_comment); 212 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
210 mark::hit!(hover_macro_generated_struct_fn_doc_attr); 213 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
@@ -324,17 +327,12 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
324 let mod_path = definition_mod_path(db, &def); 327 let mod_path = definition_mod_path(db, &def);
325 return match def { 328 return match def {
326 Definition::Macro(it) => { 329 Definition::Macro(it) => {
327 // FIXME: Currently proc-macro do not have ast-node, 330 let label = macro_label(&it.source(db)?.value);
328 // such that it does not have source
329 // more discussion: https://github.com/rust-analyzer/rust-analyzer/issues/6913
330 if it.is_proc_macro() {
331 return None;
332 }
333 let label = macro_label(&it.source(db).value);
334 from_def_source_labeled(db, it, Some(label), mod_path) 331 from_def_source_labeled(db, it, Some(label), mod_path)
335 } 332 }
336 Definition::Field(def) => { 333 Definition::Field(def) => {
337 let src = def.source(db).value; 334 #[allow(deprecated)]
335 let src = def.source(db)?.value;
338 if let FieldSource::Named(it) = src { 336 if let FieldSource::Named(it) = src {
339 from_def_source_labeled(db, def, it.short_label(), mod_path) 337 from_def_source_labeled(db, def, it.short_label(), mod_path)
340 } else { 338 } else {
@@ -360,9 +358,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
360 ModuleDef::Static(it) => from_def_source(db, it, mod_path), 358 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
361 ModuleDef::Trait(it) => from_def_source(db, it, mod_path), 359 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
362 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path), 360 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
363 ModuleDef::BuiltinType(it) => return Some(it.to_string().into()), 361 ModuleDef::BuiltinType(it) => Some(Markup::fenced_block(&it)),
364 }, 362 },
365 Definition::Local(it) => return Some(Markup::fenced_block(&it.ty(db).display(db))), 363 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))),
366 Definition::SelfType(impl_def) => { 364 Definition::SelfType(impl_def) => {
367 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 365 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
368 Adt::Struct(it) => from_def_source(db, it, mod_path), 366 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -370,7 +368,9 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
370 Adt::Enum(it) => from_def_source(db, it, mod_path), 368 Adt::Enum(it) => from_def_source(db, it, mod_path),
371 }) 369 })
372 } 370 }
373 Definition::TypeParam(_) | Definition::LifetimeParam(_) | Definition::Label(_) => { 371 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
372 Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
373 Definition::TypeParam(_) | Definition::ConstParam(_) => {
374 // FIXME: Hover for generic param 374 // FIXME: Hover for generic param
375 None 375 None
376 } 376 }
@@ -381,7 +381,8 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
381 D: HasSource<Ast = A> + HasAttrs + Copy, 381 D: HasSource<Ast = A> + HasAttrs + Copy,
382 A: ShortLabel, 382 A: ShortLabel,
383 { 383 {
384 let short_label = def.source(db).value.short_label(); 384 #[allow(deprecated)]
385 let short_label = def.source(db)?.value.short_label();
385 from_def_source_labeled(db, def, short_label, mod_path) 386 from_def_source_labeled(db, def, short_label, mod_path)
386 } 387 }
387 388
@@ -403,7 +404,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
403 return tokens.max_by_key(priority); 404 return tokens.max_by_key(priority);
404 fn priority(n: &SyntaxToken) -> usize { 405 fn priority(n: &SyntaxToken) -> usize {
405 match n.kind() { 406 match n.kind() {
406 IDENT | INT_NUMBER => 3, 407 IDENT | INT_NUMBER | LIFETIME_IDENT => 3,
407 T!['('] | T![')'] => 2, 408 T!['('] | T![')'] => 2,
408 kind if kind.is_trivia() => 0, 409 kind if kind.is_trivia() => 0,
409 _ => 1, 410 _ => 1,
@@ -1169,7 +1170,10 @@ fn f() { fo<|>o!(); }
1169 r#"struct TS(String, i32<|>);"#, 1170 r#"struct TS(String, i32<|>);"#,
1170 expect![[r#" 1171 expect![[r#"
1171 *i32* 1172 *i32*
1173
1174 ```rust
1172 i32 1175 i32
1176 ```
1173 "#]], 1177 "#]],
1174 ) 1178 )
1175 } 1179 }
@@ -3221,4 +3225,36 @@ fn no_hover() {
3221"#, 3225"#,
3222 ); 3226 );
3223 } 3227 }
3228
3229 #[test]
3230 fn hover_label() {
3231 check(
3232 r#"
3233fn foo() {
3234 'label<|>: loop {}
3235}
3236"#,
3237 expect![[r#"
3238 *'label*
3239
3240 ```rust
3241 'label
3242 ```
3243 "#]],
3244 );
3245 }
3246
3247 #[test]
3248 fn hover_lifetime() {
3249 check(
3250 r#"fn foo<'lifetime>(_: &'lifetime<|> ()) {}"#,
3251 expect![[r#"
3252 *'lifetime*
3253
3254 ```rust
3255 'lifetime
3256 ```
3257 "#]],
3258 );
3259 }
3224} 3260}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 25c2047ca..a450794f3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -480,7 +480,7 @@ impl Analysis {
480 config: &CompletionConfig, 480 config: &CompletionConfig,
481 position: FilePosition, 481 position: FilePosition,
482 full_import_path: &str, 482 full_import_path: &str,
483 imported_name: &str, 483 imported_name: String,
484 ) -> Cancelable<Vec<TextEdit>> { 484 ) -> Cancelable<Vec<TextEdit>> {
485 Ok(self 485 Ok(self
486 .with_db(|db| { 486 .with_db(|db| {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 21b2d7ca1..fa58fc319 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -1144,4 +1144,20 @@ fn foo<'a>() -> &'a () {
1144 "#]], 1144 "#]],
1145 ); 1145 );
1146 } 1146 }
1147
1148 #[test]
1149 fn test_find_const_param() {
1150 check(
1151 r#"
1152fn foo<const FOO<|>: usize>() -> usize {
1153 FOO
1154}
1155"#,
1156 expect![[r#"
1157 FOO ConstParam FileId(0) 7..23 13..16 Other
1158
1159 FileId(0) 42..45 Other
1160 "#]],
1161 );
1162 }
1147} 1163}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 891183266..c893afc7c 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -230,7 +230,7 @@ impl TestAttr {
230 230
231const RUSTDOC_FENCE: &str = "```"; 231const RUSTDOC_FENCE: &str = "```";
232const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] = 232const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUNNABLE: &[&str] =
233 &["", "rust", "should_panic", "edition2015", "edition2018"]; 233 &["", "rust", "should_panic", "edition2015", "edition2018", "edition2021"];
234 234
235fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool { 235fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
236 attrs.docs().map_or(false, |doc| { 236 attrs.docs().map_or(false, |doc| {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 5ad96581b..ba0085244 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -819,6 +819,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
819 }, 819 },
820 Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl), 820 Definition::SelfType(_) => HighlightTag::Symbol(SymbolKind::Impl),
821 Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam), 821 Definition::TypeParam(_) => HighlightTag::Symbol(SymbolKind::TypeParam),
822 Definition::ConstParam(_) => HighlightTag::Symbol(SymbolKind::ConstParam),
822 Definition::Local(local) => { 823 Definition::Local(local) => {
823 let tag = if local.is_param(db) { 824 let tag = if local.is_param(db) {
824 HighlightTag::Symbol(SymbolKind::ValueParam) 825 HighlightTag::Symbol(SymbolKind::ValueParam)
diff --git a/crates/ide/src/syntax_highlighting/injection.rs b/crates/ide/src/syntax_highlighting/injection.rs
index 9eb184c74..6cbd683c6 100644
--- a/crates/ide/src/syntax_highlighting/injection.rs
+++ b/crates/ide/src/syntax_highlighting/injection.rs
@@ -54,8 +54,17 @@ pub(super) fn highlight_injection(
54type RangesMap = BTreeMap<TextSize, TextSize>; 54type RangesMap = BTreeMap<TextSize, TextSize>;
55 55
56const RUSTDOC_FENCE: &'static str = "```"; 56const RUSTDOC_FENCE: &'static str = "```";
57const RUSTDOC_FENCE_TOKENS: &[&'static str] = 57const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
58 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"]; 58 "",
59 "rust",
60 "should_panic",
61 "ignore",
62 "no_run",
63 "compile_fail",
64 "edition2015",
65 "edition2018",
66 "edition2021",
67];
59 68
60/// Extracts Rust code from documentation comments as well as a mapping from 69/// Extracts Rust code from documentation comments as well as a mapping from
61/// the extracted source code back to the original source ranges. 70/// the extracted source code back to the original source ranges.
diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs
index 2a6cc0cab..8b8867079 100644
--- a/crates/ide/src/syntax_highlighting/tags.rs
+++ b/crates/ide/src/syntax_highlighting/tags.rs
@@ -77,6 +77,7 @@ impl HighlightTag {
77 SymbolKind::Function => "function", 77 SymbolKind::Function => "function",
78 SymbolKind::TypeAlias => "type_alias", 78 SymbolKind::TypeAlias => "type_alias",
79 SymbolKind::TypeParam => "type_param", 79 SymbolKind::TypeParam => "type_param",
80 SymbolKind::ConstParam => "const_param",
80 SymbolKind::LifetimeParam => "lifetime", 81 SymbolKind::LifetimeParam => "lifetime",
81 SymbolKind::Macro => "macro", 82 SymbolKind::Macro => "macro",
82 SymbolKind::Local => "variable", 83 SymbolKind::Local => "variable",
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlighting.html b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
index 72ff9dd40..02270b077 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlighting.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlighting.html
@@ -118,6 +118,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
118 <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span> 118 <span class="keyword control">loop</span> <span class="punctuation">{</span><span class="punctuation">}</span>
119<span class="punctuation">}</span> 119<span class="punctuation">}</span>
120 120
121<span class="keyword">fn</span> <span class="function declaration">const_param</span><span class="punctuation">&lt;</span><span class="keyword">const</span> <span class="const_param declaration">FOO</span><span class="punctuation">:</span> <span class="builtin_type">usize</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="builtin_type">usize</span> <span class="punctuation">{</span>
122 <span class="const_param">FOO</span>
123<span class="punctuation">}</span>
124
121<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span> 125<span class="keyword">use</span> <span class="module">ops</span><span class="operator">::</span><span class="trait">Fn</span><span class="punctuation">;</span>
122<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span> 126<span class="keyword">fn</span> <span class="function declaration">baz</span><span class="punctuation">&lt;</span><span class="type_param declaration">F</span><span class="punctuation">:</span> <span class="trait">Fn</span><span class="punctuation">(</span><span class="punctuation">)</span> <span class="operator">-&gt;</span> <span class="punctuation">(</span><span class="punctuation">)</span><span class="punctuation">&gt;</span><span class="punctuation">(</span><span class="value_param declaration callable">f</span><span class="punctuation">:</span> <span class="type_param">F</span><span class="punctuation">)</span> <span class="punctuation">{</span>
123 <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span> 127 <span class="value_param callable">f</span><span class="punctuation">(</span><span class="punctuation">)</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs
index e0df0d2b5..30b5b648e 100644
--- a/crates/ide/src/syntax_highlighting/tests.rs
+++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -91,6 +91,10 @@ fn never() -> ! {
91 loop {} 91 loop {}
92} 92}
93 93
94fn const_param<const FOO: usize>() -> usize {
95 FOO
96}
97
94use ops::Fn; 98use ops::Fn;
95fn baz<F: Fn() -> ()>(f: F) { 99fn baz<F: Fn() -> ()>(f: F) {
96 f() 100 f()
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 0ad6e1000..ebe53c8ee 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -19,7 +19,7 @@ fst = { version = "0.4", default-features = false }
19rustc-hash = "1.1.0" 19rustc-hash = "1.1.0"
20once_cell = "1.3.1" 20once_cell = "1.3.1"
21either = "1.6.1" 21either = "1.6.1"
22itertools = "0.9.0" 22itertools = "0.10.0"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25syntax = { path = "../syntax", version = "0.0.0" } 25syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index d33a6cb86..cc5078bf0 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,8 +6,8 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef, 9 db::HirDatabase, ConstParam, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local,
10 Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility, 10 MacroDef, Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode},
@@ -26,6 +26,7 @@ pub enum Definition {
26 Local(Local), 26 Local(Local),
27 TypeParam(TypeParam), 27 TypeParam(TypeParam),
28 LifetimeParam(LifetimeParam), 28 LifetimeParam(LifetimeParam),
29 ConstParam(ConstParam),
29 Label(Label), 30 Label(Label),
30} 31}
31 32
@@ -39,6 +40,7 @@ impl Definition {
39 Definition::Local(it) => Some(it.module(db)), 40 Definition::Local(it) => Some(it.module(db)),
40 Definition::TypeParam(it) => Some(it.module(db)), 41 Definition::TypeParam(it) => Some(it.module(db)),
41 Definition::LifetimeParam(it) => Some(it.module(db)), 42 Definition::LifetimeParam(it) => Some(it.module(db)),
43 Definition::ConstParam(it) => Some(it.module(db)),
42 Definition::Label(it) => Some(it.module(db)), 44 Definition::Label(it) => Some(it.module(db)),
43 } 45 }
44 } 46 }
@@ -52,6 +54,7 @@ impl Definition {
52 Definition::Local(_) => None, 54 Definition::Local(_) => None,
53 Definition::TypeParam(_) => None, 55 Definition::TypeParam(_) => None,
54 Definition::LifetimeParam(_) => None, 56 Definition::LifetimeParam(_) => None,
57 Definition::ConstParam(_) => None,
55 Definition::Label(_) => None, 58 Definition::Label(_) => None,
56 } 59 }
57 } 60 }
@@ -79,6 +82,7 @@ impl Definition {
79 Definition::Local(it) => it.name(db)?, 82 Definition::Local(it) => it.name(db)?,
80 Definition::TypeParam(it) => it.name(db), 83 Definition::TypeParam(it) => it.name(db),
81 Definition::LifetimeParam(it) => it.name(db), 84 Definition::LifetimeParam(it) => it.name(db),
85 Definition::ConstParam(it) => it.name(db),
82 Definition::Label(it) => it.name(db), 86 Definition::Label(it) => it.name(db),
83 }; 87 };
84 Some(name) 88 Some(name)
@@ -233,6 +237,10 @@ impl NameClass {
233 let def = sema.to_def(&it)?; 237 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::TypeParam(def))) 238 Some(NameClass::Definition(Definition::TypeParam(def)))
235 }, 239 },
240 ast::ConstParam(it) => {
241 let def = sema.to_def(&it)?;
242 Some(NameClass::Definition(Definition::ConstParam(def)))
243 },
236 _ => None, 244 _ => None,
237 } 245 }
238 } 246 }
@@ -417,6 +425,7 @@ impl From<PathResolution> for Definition {
417 PathResolution::TypeParam(par) => Definition::TypeParam(par), 425 PathResolution::TypeParam(par) => Definition::TypeParam(par),
418 PathResolution::Macro(def) => Definition::Macro(def), 426 PathResolution::Macro(def) => Definition::Macro(def),
419 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), 427 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
428 PathResolution::ConstParam(par) => Definition::ConstParam(par),
420 } 429 }
421 } 430 }
422} 431}
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs
index b2980a5d6..0f4c2ca47 100644
--- a/crates/ide_db/src/imports_locator.rs
+++ b/crates/ide_db/src/imports_locator.rs
@@ -15,19 +15,23 @@ use rustc_hash::FxHashSet;
15pub fn find_exact_imports<'a>( 15pub fn find_exact_imports<'a>(
16 sema: &Semantics<'a, RootDatabase>, 16 sema: &Semantics<'a, RootDatabase>,
17 krate: Crate, 17 krate: Crate,
18 name_to_import: &str, 18 name_to_import: String,
19) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 19) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
20 let _p = profile::span("find_exact_imports"); 20 let _p = profile::span("find_exact_imports");
21 find_imports( 21 find_imports(
22 sema, 22 sema,
23 krate, 23 krate,
24 { 24 {
25 let mut local_query = symbol_index::Query::new(name_to_import.to_string()); 25 let mut local_query = symbol_index::Query::new(name_to_import.clone());
26 local_query.exact(); 26 local_query.exact();
27 local_query.limit(40); 27 local_query.limit(40);
28 local_query 28 local_query
29 }, 29 },
30 import_map::Query::new(name_to_import).anchor_end().case_sensitive().limit(40), 30 import_map::Query::new(name_to_import)
31 .limit(40)
32 .name_only()
33 .search_mode(import_map::SearchMode::Equals)
34 .case_sensitive(),
31 ) 35 )
32} 36}
33 37
@@ -35,17 +39,18 @@ pub fn find_similar_imports<'a>(
35 sema: &Semantics<'a, RootDatabase>, 39 sema: &Semantics<'a, RootDatabase>,
36 krate: Crate, 40 krate: Crate,
37 limit: Option<usize>, 41 limit: Option<usize>,
38 name_to_import: &str, 42 fuzzy_search_string: String,
39 ignore_modules: bool, 43 name_only: bool,
40) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { 44) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
41 let _p = profile::span("find_similar_imports"); 45 let _p = profile::span("find_similar_imports");
42 46
43 let mut external_query = import_map::Query::new(name_to_import); 47 let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
44 if ignore_modules { 48 .search_mode(import_map::SearchMode::Fuzzy);
45 external_query = external_query.exclude_import_kind(import_map::ImportKind::Module); 49 if name_only {
50 external_query = external_query.name_only();
46 } 51 }
47 52
48 let mut local_query = symbol_index::Query::new(name_to_import.to_string()); 53 let mut local_query = symbol_index::Query::new(fuzzy_search_string);
49 54
50 if let Some(limit) = limit { 55 if let Some(limit) = limit {
51 local_query.limit(limit); 56 local_query.limit(limit);
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index ff10f71c3..436c59d2c 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -121,31 +121,56 @@ impl Definition {
121 121
122 if let Definition::Local(var) = self { 122 if let Definition::Local(var) = self {
123 let range = match var.parent(db) { 123 let range = match var.parent(db) {
124 DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), 124 DefWithBody::Function(f) => {
125 DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), 125 f.source(db).and_then(|src| Some(src.value.syntax().text_range()))
126 DefWithBody::Static(s) => s.source(db).value.syntax().text_range(), 126 }
127 DefWithBody::Const(c) => {
128 c.source(db).and_then(|src| Some(src.value.syntax().text_range()))
129 }
130 DefWithBody::Static(s) => {
131 s.source(db).and_then(|src| Some(src.value.syntax().text_range()))
132 }
127 }; 133 };
128 let mut res = FxHashMap::default(); 134 let mut res = FxHashMap::default();
129 res.insert(file_id, Some(range)); 135 res.insert(file_id, range);
130 return SearchScope::new(res); 136 return SearchScope::new(res);
131 } 137 }
132 138
133 if let Definition::LifetimeParam(param) = self { 139 if let Definition::LifetimeParam(param) = self {
140 #[allow(deprecated)]
134 let range = match param.parent(db) { 141 let range = match param.parent(db) {
135 hir::GenericDef::Function(it) => it.source(db).value.syntax().text_range(), 142 hir::GenericDef::Function(it) => {
143 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
144 }
136 hir::GenericDef::Adt(it) => match it { 145 hir::GenericDef::Adt(it) => match it {
137 hir::Adt::Struct(it) => it.source(db).value.syntax().text_range(), 146 hir::Adt::Struct(it) => {
138 hir::Adt::Union(it) => it.source(db).value.syntax().text_range(), 147 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
139 hir::Adt::Enum(it) => it.source(db).value.syntax().text_range(), 148 }
149 hir::Adt::Union(it) => {
150 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
151 }
152 hir::Adt::Enum(it) => {
153 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
154 }
140 }, 155 },
141 hir::GenericDef::Trait(it) => it.source(db).value.syntax().text_range(), 156 hir::GenericDef::Trait(it) => {
142 hir::GenericDef::TypeAlias(it) => it.source(db).value.syntax().text_range(), 157 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
143 hir::GenericDef::Impl(it) => it.source(db).value.syntax().text_range(), 158 }
144 hir::GenericDef::Variant(it) => it.source(db).value.syntax().text_range(), 159 hir::GenericDef::TypeAlias(it) => {
145 hir::GenericDef::Const(it) => it.source(db).value.syntax().text_range(), 160 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
161 }
162 hir::GenericDef::Impl(it) => {
163 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
164 }
165 hir::GenericDef::Variant(it) => {
166 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
167 }
168 hir::GenericDef::Const(it) => {
169 it.source(db).and_then(|src| Some(src.value.syntax().text_range()))
170 }
146 }; 171 };
147 let mut res = FxHashMap::default(); 172 let mut res = FxHashMap::default();
148 res.insert(file_id, Some(range)); 173 res.insert(file_id, range);
149 return SearchScope::new(res); 174 return SearchScope::new(res);
150 } 175 }
151 176
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 3ad609a00..b3472879d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -14,10 +14,10 @@ mod tests;
14 14
15use std::fmt; 15use std::fmt;
16 16
17pub use tt::{Delimiter, Punct}; 17pub use tt::{Delimiter, DelimiterKind, Punct};
18 18
19use crate::{ 19use crate::{
20 parser::{parse_pattern, Op}, 20 parser::{parse_pattern, parse_template, Op},
21 tt_iter::TtIter, 21 tt_iter::TtIter,
22}; 22};
23 23
@@ -78,8 +78,24 @@ pub struct MacroRules {
78 78
79#[derive(Clone, Debug, PartialEq, Eq)] 79#[derive(Clone, Debug, PartialEq, Eq)]
80struct Rule { 80struct Rule {
81 lhs: tt::Subtree, 81 lhs: MetaTemplate,
82 rhs: tt::Subtree, 82 rhs: MetaTemplate,
83}
84
85#[derive(Clone, Debug, PartialEq, Eq)]
86struct MetaTemplate {
87 delimiter: Option<Delimiter>,
88 tokens: Vec<Result<Op, ExpandError>>,
89}
90
91impl<'a> MetaTemplate {
92 fn iter(&self) -> impl Iterator<Item = &Result<Op, ExpandError>> {
93 self.tokens.iter()
94 }
95
96 fn delimiter_kind(&self) -> Option<DelimiterKind> {
97 self.delimiter.map(|it| it.kind)
98 }
83} 99}
84 100
85#[derive(Clone, Copy, Debug, PartialEq, Eq)] 101#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -167,7 +183,7 @@ impl MacroRules {
167 rules.push(rule); 183 rules.push(rule);
168 if let Err(()) = src.expect_char(';') { 184 if let Err(()) = src.expect_char(';') {
169 if src.len() > 0 { 185 if src.len() > 0 {
170 return Err(ParseError::Expected("expected `:`".to_string())); 186 return Err(ParseError::Expected("expected `;`".to_string()));
171 } 187 }
172 break; 188 break;
173 } 189 }
@@ -201,23 +217,23 @@ impl MacroRules {
201 217
202impl Rule { 218impl Rule {
203 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { 219 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> {
204 let mut lhs = src 220 let lhs = src
205 .expect_subtree() 221 .expect_subtree()
206 .map_err(|()| ParseError::Expected("expected subtree".to_string()))? 222 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
207 .clone();
208 lhs.delimiter = None;
209 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; 223 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?;
210 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; 224 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?;
211 let mut rhs = src 225 let rhs = src
212 .expect_subtree() 226 .expect_subtree()
213 .map_err(|()| ParseError::Expected("expected subtree".to_string()))? 227 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
214 .clone(); 228
215 rhs.delimiter = None; 229 let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None };
230 let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None };
231
216 Ok(crate::Rule { lhs, rhs }) 232 Ok(crate::Rule { lhs, rhs })
217 } 233 }
218} 234}
219 235
220fn to_parse_error(e: ExpandError) -> ParseError { 236fn to_parse_error(e: &ExpandError) -> ParseError {
221 let msg = match e { 237 let msg = match e {
222 ExpandError::InvalidRepeat => "invalid repeat".to_string(), 238 ExpandError::InvalidRepeat => "invalid repeat".to_string(),
223 _ => "invalid macro definition".to_string(), 239 _ => "invalid macro definition".to_string(),
@@ -225,22 +241,22 @@ fn to_parse_error(e: ExpandError) -> ParseError {
225 ParseError::Expected(msg) 241 ParseError::Expected(msg)
226} 242}
227 243
228fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { 244fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
229 for op in parse_pattern(pattern) { 245 for op in pattern.iter() {
230 let op = op.map_err(to_parse_error)?; 246 let op = op.as_ref().map_err(|e| to_parse_error(&e))?;
231 247
232 match op { 248 match op {
233 Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?, 249 Op::Subtree(subtree) => validate(&subtree)?,
234 Op::Repeat { subtree, separator, .. } => { 250 Op::Repeat { subtree, separator, .. } => {
235 // Checks that no repetition which could match an empty token 251 // Checks that no repetition which could match an empty token
236 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 252 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
237 253
238 if separator.is_none() { 254 if separator.is_none() {
239 if parse_pattern(subtree).all(|child_op| { 255 if subtree.iter().all(|child_op| {
240 match child_op.map_err(to_parse_error) { 256 match child_op.as_ref().map_err(to_parse_error) {
241 Ok(Op::Var { kind, .. }) => { 257 Ok(Op::Var { kind, .. }) => {
242 // vis is optional 258 // vis is optional
243 if kind.map_or(false, |it| it == "vis") { 259 if kind.as_ref().map_or(false, |it| it == "vis") {
244 return true; 260 return true;
245 } 261 }
246 } 262 }
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 44722c0f1..385b46601 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -2,10 +2,10 @@
2 2
3use crate::{ 3use crate::{
4 mbe_expander::{Binding, Bindings, Fragment}, 4 mbe_expander::{Binding, Bindings, Fragment},
5 parser::{parse_pattern, Op, RepeatKind, Separator}, 5 parser::{Op, RepeatKind, Separator},
6 subtree_source::SubtreeTokenSource, 6 subtree_source::SubtreeTokenSource,
7 tt_iter::TtIter, 7 tt_iter::TtIter,
8 ExpandError, 8 ExpandError, MetaTemplate,
9}; 9};
10 10
11use super::ExpandResult; 11use super::ExpandResult;
@@ -83,7 +83,7 @@ impl Match {
83// sense to try using it. Matching errors are added to the `Match`. It might 83// sense to try using it. Matching errors are added to the `Match`. It might
84// make sense to make pattern parsing a separate step? 84// make sense to make pattern parsing a separate step?
85 85
86pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, ExpandError> { 86pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Result<Match, ExpandError> {
87 assert!(pattern.delimiter == None); 87 assert!(pattern.delimiter == None);
88 88
89 let mut res = Match::default(); 89 let mut res = Match::default();
@@ -101,12 +101,12 @@ pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match,
101 101
102fn match_subtree( 102fn match_subtree(
103 res: &mut Match, 103 res: &mut Match,
104 pattern: &tt::Subtree, 104 pattern: &MetaTemplate,
105 src: &mut TtIter, 105 src: &mut TtIter,
106) -> Result<(), ExpandError> { 106) -> Result<(), ExpandError> {
107 for op in parse_pattern(pattern) { 107 for op in pattern.iter() {
108 match op? { 108 match op.as_ref().map_err(|err| err.clone())? {
109 Op::TokenTree(tt::TokenTree::Leaf(lhs)) => { 109 Op::Leaf(lhs) => {
110 let rhs = match src.expect_leaf() { 110 let rhs = match src.expect_leaf() {
111 Ok(l) => l, 111 Ok(l) => l,
112 Err(()) => { 112 Err(()) => {
@@ -132,7 +132,7 @@ fn match_subtree(
132 } 132 }
133 } 133 }
134 } 134 }
135 Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { 135 Op::Subtree(lhs) => {
136 let rhs = match src.expect_subtree() { 136 let rhs = match src.expect_subtree() {
137 Ok(s) => s, 137 Ok(s) => s,
138 Err(()) => { 138 Err(()) => {
@@ -150,7 +150,7 @@ fn match_subtree(
150 res.add_err(err!("leftover tokens")); 150 res.add_err(err!("leftover tokens"));
151 } 151 }
152 } 152 }
153 Op::Var { name, kind } => { 153 Op::Var { name, kind, .. } => {
154 let kind = match kind { 154 let kind = match kind {
155 Some(k) => k, 155 Some(k) => k,
156 None => { 156 None => {
@@ -172,7 +172,7 @@ fn match_subtree(
172 } 172 }
173 } 173 }
174 Op::Repeat { subtree, kind, separator } => { 174 Op::Repeat { subtree, kind, separator } => {
175 match_repeat(res, subtree, kind, separator, src)?; 175 match_repeat(res, subtree, *kind, separator, src)?;
176 } 176 }
177 } 177 }
178 } 178 }
@@ -372,9 +372,9 @@ impl<'a> TtIter<'a> {
372 372
373pub(super) fn match_repeat( 373pub(super) fn match_repeat(
374 res: &mut Match, 374 res: &mut Match,
375 pattern: &tt::Subtree, 375 pattern: &MetaTemplate,
376 kind: RepeatKind, 376 kind: RepeatKind,
377 separator: Option<Separator>, 377 separator: &Option<Separator>,
378 src: &mut TtIter, 378 src: &mut TtIter,
379) -> Result<(), ExpandError> { 379) -> Result<(), ExpandError> {
380 // Dirty hack to make macro-expansion terminate. 380 // Dirty hack to make macro-expansion terminate.
@@ -489,12 +489,12 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
489 result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) 489 result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) })
490} 490}
491 491
492fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> { 492fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) -> Result<(), ExpandError> {
493 for op in parse_pattern(pattern) { 493 for op in pattern.iter() {
494 match op? { 494 match op.as_ref().map_err(|e| e.clone())? {
495 Op::Var { name, .. } => buf.push(name.clone()), 495 Op::Var { name, .. } => buf.push(name.clone()),
496 Op::TokenTree(tt::TokenTree::Leaf(_)) => (), 496 Op::Leaf(_) => (),
497 Op::TokenTree(tt::TokenTree::Subtree(subtree)) => collect_vars(buf, subtree)?, 497 Op::Subtree(subtree) => collect_vars(buf, subtree)?,
498 Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, 498 Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?,
499 } 499 }
500 } 500 }
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs
index 57592dc92..57f3f104d 100644
--- a/crates/mbe/src/mbe_expander/transcriber.rs
+++ b/crates/mbe/src/mbe_expander/transcriber.rs
@@ -6,8 +6,8 @@ use syntax::SmolStr;
6use super::ExpandResult; 6use super::ExpandResult;
7use crate::{ 7use crate::{
8 mbe_expander::{Binding, Bindings, Fragment}, 8 mbe_expander::{Binding, Bindings, Fragment},
9 parser::{parse_template, Op, RepeatKind, Separator}, 9 parser::{Op, RepeatKind, Separator},
10 ExpandError, 10 ExpandError, MetaTemplate,
11}; 11};
12 12
13impl Bindings { 13impl Bindings {
@@ -50,7 +50,10 @@ impl Bindings {
50 } 50 }
51} 51}
52 52
53pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { 53pub(super) fn transcribe(
54 template: &MetaTemplate,
55 bindings: &Bindings,
56) -> ExpandResult<tt::Subtree> {
54 assert!(template.delimiter == None); 57 assert!(template.delimiter == None);
55 let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; 58 let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() };
56 let mut arena: Vec<tt::TokenTree> = Vec::new(); 59 let mut arena: Vec<tt::TokenTree> = Vec::new();
@@ -76,35 +79,35 @@ struct ExpandCtx<'a> {
76 79
77fn expand_subtree( 80fn expand_subtree(
78 ctx: &mut ExpandCtx, 81 ctx: &mut ExpandCtx,
79 template: &tt::Subtree, 82 template: &MetaTemplate,
80 arena: &mut Vec<tt::TokenTree>, 83 arena: &mut Vec<tt::TokenTree>,
81) -> ExpandResult<tt::Subtree> { 84) -> ExpandResult<tt::Subtree> {
82 // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation 85 // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
83 let start_elements = arena.len(); 86 let start_elements = arena.len();
84 let mut err = None; 87 let mut err = None;
85 for op in parse_template(template) { 88 for op in template.iter() {
86 let op = match op { 89 let op = match op {
87 Ok(op) => op, 90 Ok(op) => op,
88 Err(e) => { 91 Err(e) => {
89 err = Some(e); 92 err = Some(e.clone());
90 break; 93 break;
91 } 94 }
92 }; 95 };
93 match op { 96 match op {
94 Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), 97 Op::Leaf(tt) => arena.push(tt.clone().into()),
95 Op::TokenTree(tt::TokenTree::Subtree(tt)) => { 98 Op::Subtree(tt) => {
96 let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena); 99 let ExpandResult { value: tt, err: e } = expand_subtree(ctx, &tt, arena);
97 err = err.or(e); 100 err = err.or(e);
98 arena.push(tt.into()); 101 arena.push(tt.into());
99 } 102 }
100 Op::Var { name, .. } => { 103 Op::Var { name, id, .. } => {
101 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name); 104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id);
102 err = err.or(e); 105 err = err.or(e);
103 push_fragment(arena, fragment); 106 push_fragment(arena, fragment);
104 } 107 }
105 Op::Repeat { subtree, kind, separator } => { 108 Op::Repeat { subtree, kind, separator } => {
106 let ExpandResult { value: fragment, err: e } = 109 let ExpandResult { value: fragment, err: e } =
107 expand_repeat(ctx, subtree, kind, separator, arena); 110 expand_repeat(ctx, subtree, *kind, separator, arena);
108 err = err.or(e); 111 err = err.or(e);
109 push_fragment(arena, fragment) 112 push_fragment(arena, fragment)
110 } 113 }
@@ -115,12 +118,10 @@ fn expand_subtree(
115 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } 118 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
116} 119}
117 120
118fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { 121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> {
119 if v == "crate" { 122 if v == "crate" {
120 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. 123 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
121 let tt = 124 let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into();
122 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
123 .into();
124 ExpandResult::ok(Fragment::Tokens(tt)) 125 ExpandResult::ok(Fragment::Tokens(tt))
125 } else if !ctx.bindings.contains(v) { 126 } else if !ctx.bindings.contains(v) {
126 // Note that it is possible to have a `$var` inside a macro which is not bound. 127 // Note that it is possible to have a `$var` inside a macro which is not bound.
@@ -139,14 +140,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
139 let tt = tt::Subtree { 140 let tt = tt::Subtree {
140 delimiter: None, 141 delimiter: None,
141 token_trees: vec![ 142 token_trees: vec![
142 tt::Leaf::from(tt::Punct { 143 tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(),
143 char: '$', 144 tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(),
144 spacing: tt::Spacing::Alone,
145 id: tt::TokenId::unspecified(),
146 })
147 .into(),
148 tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
149 .into(),
150 ], 145 ],
151 } 146 }
152 .into(); 147 .into();
@@ -161,9 +156,9 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
161 156
162fn expand_repeat( 157fn expand_repeat(
163 ctx: &mut ExpandCtx, 158 ctx: &mut ExpandCtx,
164 template: &tt::Subtree, 159 template: &MetaTemplate,
165 kind: RepeatKind, 160 kind: RepeatKind,
166 separator: Option<Separator>, 161 separator: &Option<Separator>,
167 arena: &mut Vec<tt::TokenTree>, 162 arena: &mut Vec<tt::TokenTree>,
168) -> ExpandResult<Fragment> { 163) -> ExpandResult<Fragment> {
169 let mut buf: Vec<tt::TokenTree> = Vec::new(); 164 let mut buf: Vec<tt::TokenTree> = Vec::new();
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index c3fdd4040..77cc739b6 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -4,16 +4,17 @@
4use smallvec::SmallVec; 4use smallvec::SmallVec;
5use syntax::SmolStr; 5use syntax::SmolStr;
6 6
7use crate::{tt_iter::TtIter, ExpandError}; 7use crate::{tt_iter::TtIter, ExpandError, MetaTemplate};
8 8
9#[derive(Debug)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub(crate) enum Op<'a> { 10pub(crate) enum Op {
11 Var { name: &'a SmolStr, kind: Option<&'a SmolStr> }, 11 Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
12 Repeat { subtree: &'a tt::Subtree, kind: RepeatKind, separator: Option<Separator> }, 12 Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
13 TokenTree(&'a tt::TokenTree), 13 Leaf(tt::Leaf),
14 Subtree(MetaTemplate),
14} 15}
15 16
16#[derive(Clone, Debug, PartialEq, Eq)] 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub(crate) enum RepeatKind { 18pub(crate) enum RepeatKind {
18 ZeroOrMore, 19 ZeroOrMore,
19 OneOrMore, 20 OneOrMore,
@@ -45,16 +46,12 @@ impl PartialEq for Separator {
45 } 46 }
46} 47}
47 48
48pub(crate) fn parse_template( 49pub(crate) fn parse_template(template: &tt::Subtree) -> Vec<Result<Op, ExpandError>> {
49 template: &tt::Subtree, 50 parse_inner(&template, Mode::Template)
50) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> {
51 parse_inner(template, Mode::Template)
52} 51}
53 52
54pub(crate) fn parse_pattern( 53pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Vec<Result<Op, ExpandError>> {
55 pattern: &tt::Subtree, 54 parse_inner(&pattern, Mode::Pattern)
56) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> {
57 parse_inner(pattern, Mode::Pattern)
58} 55}
59 56
60#[derive(Clone, Copy)] 57#[derive(Clone, Copy)]
@@ -63,12 +60,13 @@ enum Mode {
63 Template, 60 Template,
64} 61}
65 62
66fn parse_inner(src: &tt::Subtree, mode: Mode) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { 63fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ExpandError>> {
67 let mut src = TtIter::new(src); 64 let mut src = TtIter::new(&tt);
68 std::iter::from_fn(move || { 65 std::iter::from_fn(move || {
69 let first = src.next()?; 66 let first = src.next()?;
70 Some(next_op(first, &mut src, mode)) 67 Some(next_op(first, &mut src, mode))
71 }) 68 })
69 .collect()
72} 70}
73 71
74macro_rules! err { 72macro_rules! err {
@@ -83,37 +81,46 @@ macro_rules! bail {
83 }; 81 };
84} 82}
85 83
86fn next_op<'a>( 84fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ExpandError> {
87 first: &'a tt::TokenTree,
88 src: &mut TtIter<'a>,
89 mode: Mode,
90) -> Result<Op<'a>, ExpandError> {
91 let res = match first { 85 let res = match first {
92 tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { 86 tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => {
93 // Note that the '$' itself is a valid token inside macro_rules. 87 // Note that the '$' itself is a valid token inside macro_rules.
94 let second = match src.next() { 88 let second = match src.next() {
95 None => return Ok(Op::TokenTree(first)), 89 None => return Ok(Op::Leaf(leaf.clone())),
96 Some(it) => it, 90 Some(it) => it,
97 }; 91 };
98 match second { 92 match second {
99 tt::TokenTree::Subtree(subtree) => { 93 tt::TokenTree::Subtree(subtree) => {
100 let (separator, kind) = parse_repeat(src)?; 94 let (separator, kind) = parse_repeat(src)?;
95 let delimiter = subtree.delimiter;
96 let tokens = parse_inner(&subtree, mode);
97 let subtree = MetaTemplate { tokens, delimiter };
101 Op::Repeat { subtree, separator, kind } 98 Op::Repeat { subtree, separator, kind }
102 } 99 }
103 tt::TokenTree::Leaf(leaf) => match leaf { 100 tt::TokenTree::Leaf(leaf) => match leaf {
104 tt::Leaf::Punct(_) => { 101 tt::Leaf::Punct(punct) => {
105 return Err(ExpandError::UnexpectedToken); 102 static UNDERSCORE: SmolStr = SmolStr::new_inline("_");
103
104 if punct.char != '_' {
105 return Err(ExpandError::UnexpectedToken);
106 }
107 let name = UNDERSCORE.clone();
108 let kind = eat_fragment_kind(src, mode)?;
109 let id = punct.id;
110 Op::Var { name, kind, id }
106 } 111 }
107 tt::Leaf::Ident(ident) => { 112 tt::Leaf::Ident(ident) => {
108 let name = &ident.text; 113 let name = ident.text.clone();
109 let kind = eat_fragment_kind(src, mode)?; 114 let kind = eat_fragment_kind(src, mode)?;
110 Op::Var { name, kind } 115 let id = ident.id;
116 Op::Var { name, kind, id }
111 } 117 }
112 tt::Leaf::Literal(lit) => { 118 tt::Leaf::Literal(lit) => {
113 if is_boolean_literal(lit) { 119 if is_boolean_literal(&lit) {
114 let name = &lit.text; 120 let name = lit.text.clone();
115 let kind = eat_fragment_kind(src, mode)?; 121 let kind = eat_fragment_kind(src, mode)?;
116 Op::Var { name, kind } 122 let id = lit.id;
123 Op::Var { name, kind, id }
117 } else { 124 } else {
118 bail!("bad var 2"); 125 bail!("bad var 2");
119 } 126 }
@@ -121,19 +128,22 @@ fn next_op<'a>(
121 }, 128 },
122 } 129 }
123 } 130 }
124 tt => Op::TokenTree(tt), 131 tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()),
132 tt::TokenTree::Subtree(subtree) => {
133 let delimiter = subtree.delimiter;
134 let tokens = parse_inner(&subtree, mode);
135 let subtree = MetaTemplate { tokens, delimiter };
136 Op::Subtree(subtree)
137 }
125 }; 138 };
126 Ok(res) 139 Ok(res)
127} 140}
128 141
129fn eat_fragment_kind<'a>( 142fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ExpandError> {
130 src: &mut TtIter<'a>,
131 mode: Mode,
132) -> Result<Option<&'a SmolStr>, ExpandError> {
133 if let Mode::Pattern = mode { 143 if let Mode::Pattern = mode {
134 src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; 144 src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?;
135 let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; 145 let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?;
136 return Ok(Some(&ident.text)); 146 return Ok(Some(ident.text.clone()));
137 }; 147 };
138 Ok(None) 148 Ok(None)
139} 149}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 2bec7fd49..265c0d63d 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -313,7 +313,7 @@ trait TokenConvertor {
313 return; 313 return;
314 } 314 }
315 315
316 result.push(if k.is_punct() && k != UNDERSCORE { 316 result.push(if k.is_punct() {
317 assert_eq!(range.len(), TextSize::of('.')); 317 assert_eq!(range.len(), TextSize::of('.'));
318 let delim = match k { 318 let delim = match k {
319 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), 319 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])),
@@ -378,7 +378,6 @@ trait TokenConvertor {
378 let leaf: tt::Leaf = match k { 378 let leaf: tt::Leaf = match k {
379 T![true] | T![false] => make_leaf!(Ident), 379 T![true] | T![false] => make_leaf!(Ident),
380 IDENT => make_leaf!(Ident), 380 IDENT => make_leaf!(Ident),
381 UNDERSCORE => make_leaf!(Ident),
382 k if k.is_keyword() => make_leaf!(Ident), 381 k if k.is_keyword() => make_leaf!(Ident),
383 k if k.is_literal() => make_leaf!(Literal), 382 k if k.is_literal() => make_leaf!(Literal),
384 LIFETIME_IDENT => { 383 LIFETIME_IDENT => {
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 6cd0ed205..1d9afb4fb 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -761,6 +761,18 @@ fn test_last_expr() {
761} 761}
762 762
763#[test] 763#[test]
764fn test_expr_with_attr() {
765 parse_macro(
766 r#"
767macro_rules! m {
768 ($a:expr) => {0}
769}
770"#,
771 )
772 .assert_expand_items("m!(#[allow(a)]())", "0");
773}
774
775#[test]
764fn test_ty() { 776fn test_ty() {
765 parse_macro( 777 parse_macro(
766 r#" 778 r#"
@@ -1020,6 +1032,42 @@ fn test_underscore() {
1020} 1032}
1021 1033
1022#[test] 1034#[test]
1035fn test_underscore_not_greedily() {
1036 parse_macro(
1037 r#"
1038macro_rules! q {
1039 ($($a:ident)* _) => {0};
1040}
1041"#,
1042 )
1043 // `_` overlaps with `$a:ident` but rustc matches it under the `_` token
1044 .assert_expand_items(r#"q![a b c d _]"#, r#"0"#);
1045
1046 parse_macro(
1047 r#"
1048macro_rules! q {
1049 ($($a:expr => $b:ident)* _ => $c:expr) => {0};
1050}
1051"#,
1052 )
1053 // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr`
1054 .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#);
1055}
1056
1057#[test]
1058fn test_underscore_as_type() {
1059 parse_macro(
1060 r#"
1061macro_rules! q {
1062 ($a:ty) => {0};
1063}
1064"#,
1065 )
1066 // Underscore is a type
1067 .assert_expand_items(r#"q![_]"#, r#"0"#);
1068}
1069
1070#[test]
1023fn test_vertical_bar_with_pat() { 1071fn test_vertical_bar_with_pat() {
1024 parse_macro( 1072 parse_macro(
1025 r#" 1073 r#"
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index f08c8bab7..63cc90027 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -59,7 +59,7 @@ pub(crate) mod fragments {
59 }; 59 };
60 60
61 pub(crate) fn expr(p: &mut Parser) { 61 pub(crate) fn expr(p: &mut Parser) {
62 let _ = expressions::expr(p); 62 let _ = expressions::expr_with_attrs(p);
63 } 63 }
64 64
65 pub(crate) fn stmt(p: &mut Parser) { 65 pub(crate) fn stmt(p: &mut Parser) {
diff --git a/crates/proc_macro_api/src/msg.rs b/crates/proc_macro_api/src/msg.rs
index f84ebdbc5..4cd572101 100644
--- a/crates/proc_macro_api/src/msg.rs
+++ b/crates/proc_macro_api/src/msg.rs
@@ -58,7 +58,13 @@ pub trait Message: Serialize + DeserializeOwned {
58 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> { 58 fn read(inp: &mut impl BufRead) -> io::Result<Option<Self>> {
59 Ok(match read_json(inp)? { 59 Ok(match read_json(inp)? {
60 None => None, 60 None => None,
61 Some(text) => Some(serde_json::from_str(&text)?), 61 Some(text) => {
62 let mut deserializer = serde_json::Deserializer::from_str(&text);
63 // Note that some proc-macro generate very deep syntax tree
64 // We have to disable the current limit of serde here
65 deserializer.disable_recursion_limit();
66 Some(Self::deserialize(&mut deserializer)?)
67 }
62 }) 68 })
63 } 69 }
64 fn write(self, out: &mut impl Write) -> io::Result<()> { 70 fn write(self, out: &mut impl Write) -> io::Result<()> {
diff --git a/crates/proc_macro_api/src/process.rs b/crates/proc_macro_api/src/process.rs
index d68723ada..6d6ab8888 100644
--- a/crates/proc_macro_api/src/process.rs
+++ b/crates/proc_macro_api/src/process.rs
@@ -92,10 +92,11 @@ fn client_loop(task_rx: Receiver<Task>, mut process: Process) {
92 for Task { req, result_tx } in task_rx { 92 for Task { req, result_tx } in task_rx {
93 match send_request(&mut stdin, &mut stdout, req) { 93 match send_request(&mut stdin, &mut stdout, req) {
94 Ok(res) => result_tx.send(res).unwrap(), 94 Ok(res) => result_tx.send(res).unwrap(),
95 Err(_err) => { 95 Err(err) => {
96 log::error!( 96 log::error!(
97 "proc macro server crashed, server process state: {:?}", 97 "proc macro server crashed, server process state: {:?}, server request error: {:?}",
98 process.child.try_wait() 98 process.child.try_wait(),
99 err
99 ); 100 );
100 let res = Response::Error(ResponseError { 101 let res = Response::Error(ResponseError {
101 code: ErrorCode::ServerErrorEnd, 102 code: ErrorCode::ServerErrorEnd,
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs
index 503f4c101..b54aa1f3b 100644
--- a/crates/proc_macro_srv/src/rustc_server.rs
+++ b/crates/proc_macro_srv/src/rustc_server.rs
@@ -204,17 +204,18 @@ pub mod token_stream {
204 let content = subtree 204 let content = subtree
205 .token_trees 205 .token_trees
206 .iter() 206 .iter()
207 .map(|tkn| { 207 .fold((String::new(), true), |(last, last_to_joint), tkn| {
208 let s = to_text(tkn); 208 let s = [last, to_text(tkn)].join(if last_to_joint { "" } else { " " });
209 let mut is_joint = false;
209 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { 210 if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
210 if punct.spacing == tt::Spacing::Alone { 211 if punct.spacing == tt::Spacing::Joint {
211 return s + " "; 212 is_joint = true;
212 } 213 }
213 } 214 }
214 s 215 (s, is_joint)
215 }) 216 })
216 .collect::<Vec<_>>() 217 .0;
217 .concat(); 218
218 let (open, close) = match subtree.delimiter.map(|it| it.kind) { 219 let (open, close) = match subtree.delimiter.map(|it| it.kind) {
219 None => ("", ""), 220 None => ("", ""),
220 Some(tt::DelimiterKind::Brace) => ("{", "}"), 221 Some(tt::DelimiterKind::Brace) => ("{", "}"),
@@ -710,4 +711,32 @@ mod tests {
710 assert_eq!(srv.character('c').text, "'c'"); 711 assert_eq!(srv.character('c').text, "'c'");
711 assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); 712 assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
712 } 713 }
714
715 #[test]
716 fn test_rustc_server_to_string() {
717 let s = TokenStream {
718 subtree: tt::Subtree {
719 delimiter: None,
720 token_trees: vec![
721 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
722 text: "struct".into(),
723 id: tt::TokenId::unspecified(),
724 })),
725 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
726 text: "T".into(),
727 id: tt::TokenId::unspecified(),
728 })),
729 tt::TokenTree::Subtree(tt::Subtree {
730 delimiter: Some(tt::Delimiter {
731 id: tt::TokenId::unspecified(),
732 kind: tt::DelimiterKind::Brace,
733 }),
734 token_trees: vec![],
735 }),
736 ],
737 },
738 };
739
740 assert_eq!(s.to_string(), "struct T {}");
741 }
713} 742}
diff --git a/crates/project_model/Cargo.toml b/crates/project_model/Cargo.toml
index c55e85709..a65e42261 100644
--- a/crates/project_model/Cargo.toml
+++ b/crates/project_model/Cargo.toml
@@ -16,7 +16,7 @@ cargo_metadata = "=0.12.0"
16serde = { version = "1.0.106", features = ["derive"] } 16serde = { version = "1.0.106", features = ["derive"] }
17serde_json = "1.0.48" 17serde_json = "1.0.48"
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19itertools = "0.9.0" 19itertools = "0.10.0"
20 20
21arena = { path = "../arena", version = "0.0.0" } 21arena = { path = "../arena", version = "0.0.0" }
22cfg = { path = "../cfg", version = "0.0.0" } 22cfg = { path = "../cfg", version = "0.0.0" }
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index bb3b6f2ef..1700cb8a7 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::{ 3use std::{
4 convert::TryInto,
4 ffi::OsStr, 5 ffi::OsStr,
5 ops, 6 ops,
6 path::{Path, PathBuf}, 7 path::{Path, PathBuf},
@@ -196,8 +197,23 @@ impl CargoWorkspace {
196 if let Some(target) = target { 197 if let Some(target) = target {
197 meta.other_options(vec![String::from("--filter-platform"), target]); 198 meta.other_options(vec![String::from("--filter-platform"), target]);
198 } 199 }
200
199 let mut meta = meta.exec().with_context(|| { 201 let mut meta = meta.exec().with_context(|| {
200 format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display()) 202 let cwd: Option<AbsPathBuf> =
203 std::env::current_dir().ok().and_then(|p| p.try_into().ok());
204
205 let workdir = cargo_toml
206 .parent()
207 .map(|p| p.to_path_buf())
208 .or(cwd)
209 .map(|dir| dir.to_string_lossy().to_string())
210 .unwrap_or_else(|| "<failed to get path>".into());
211
212 format!(
213 "Failed to run `cargo metadata --manifest-path {}` in `{}`",
214 cargo_toml.display(),
215 workdir
216 )
201 })?; 217 })?;
202 218
203 let mut out_dir_by_id = FxHashMap::default(); 219 let mut out_dir_by_id = FxHashMap::default();
@@ -334,6 +350,11 @@ pub(crate) fn load_extern_resources(
334 let mut cmd = Command::new(toolchain::cargo()); 350 let mut cmd = Command::new(toolchain::cargo());
335 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); 351 cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
336 352
353 // --all-targets includes tests, benches and examples in addition to the
354 // default lib and bins. This is an independent concept from the --targets
355 // flag below.
356 cmd.arg("--all-targets");
357
337 if let Some(target) = &cargo_features.target { 358 if let Some(target) = &cargo_features.target {
338 cmd.args(&["--target", target]); 359 cmd.args(&["--target", target]);
339 } 360 }
diff --git a/crates/project_model/src/project_json.rs b/crates/project_model/src/project_json.rs
index aab279223..af884eb84 100644
--- a/crates/project_model/src/project_json.rs
+++ b/crates/project_model/src/project_json.rs
@@ -139,6 +139,8 @@ enum EditionData {
139 Edition2015, 139 Edition2015,
140 #[serde(rename = "2018")] 140 #[serde(rename = "2018")]
141 Edition2018, 141 Edition2018,
142 #[serde(rename = "2021")]
143 Edition2021,
142} 144}
143 145
144impl From<EditionData> for Edition { 146impl From<EditionData> for Edition {
@@ -146,6 +148,7 @@ impl From<EditionData> for Edition {
146 match data { 148 match data {
147 EditionData::Edition2015 => Edition::Edition2015, 149 EditionData::Edition2015 => Edition::Edition2015,
148 EditionData::Edition2018 => Edition::Edition2018, 150 EditionData::Edition2018 => Edition::Edition2018,
151 EditionData::Edition2021 => Edition::Edition2021,
149 } 152 }
150 } 153 }
151} 154}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 53e70eaf7..0a63593fb 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -17,8 +17,9 @@ path = "src/bin/main.rs"
17[dependencies] 17[dependencies]
18anyhow = "1.0.26" 18anyhow = "1.0.26"
19crossbeam-channel = "0.5.0" 19crossbeam-channel = "0.5.0"
20dissimilar = "1.0.2"
20env_logger = { version = "0.8.1", default-features = false } 21env_logger = { version = "0.8.1", default-features = false }
21itertools = "0.9.0" 22itertools = "0.10.0"
22jod-thread = "0.1.0" 23jod-thread = "0.1.0"
23log = "0.4.8" 24log = "0.4.8"
24lsp-types = { version = "0.86.0", features = ["proposed"] } 25lsp-types = { version = "0.86.0", features = ["proposed"] }
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index a23fb7a33..9445aec07 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -161,11 +161,12 @@ impl AnalysisStatsCmd {
161 } 161 }
162 let mut msg = format!("processing: {}", full_name); 162 let mut msg = format!("processing: {}", full_name);
163 if verbosity.is_verbose() { 163 if verbosity.is_verbose() {
164 let src = f.source(db); 164 if let Some(src) = f.source(db) {
165 let original_file = src.file_id.original_file(db); 165 let original_file = src.file_id.original_file(db);
166 let path = vfs.file_path(original_file); 166 let path = vfs.file_path(original_file);
167 let syntax_range = src.value.syntax().text_range(); 167 let syntax_range = src.value.syntax().text_range();
168 format_to!(msg, " ({} {:?})", path, syntax_range); 168 format_to!(msg, " ({} {:?})", path, syntax_range);
169 }
169 } 170 }
170 if verbosity.is_spammy() { 171 if verbosity.is_spammy() {
171 bar.println(msg.to_string()); 172 bar.println(msg.to_string());
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1db5b4e7d..685a9fdf0 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -679,7 +679,7 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json:
679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) { 679 for ((f1, ..), (f2, ..)) in fields.iter().zip(&fields[1..]) {
680 fn key(f: &str) -> &str { 680 fn key(f: &str) -> &str {
681 f.splitn(2, "_").next().unwrap() 681 f.splitn(2, "_").next().unwrap()
682 }; 682 }
683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2); 683 assert!(key(f1) <= key(f2), "wrong field order: {:?} {:?}", f1, f2);
684 } 684 }
685 685
diff --git a/crates/rust-analyzer/src/diff.rs b/crates/rust-analyzer/src/diff.rs
new file mode 100644
index 000000000..231be5807
--- /dev/null
+++ b/crates/rust-analyzer/src/diff.rs
@@ -0,0 +1,53 @@
1//! Generate minimal `TextEdit`s from different text versions
2use dissimilar::Chunk;
3use ide::{TextEdit, TextRange, TextSize};
4
5pub(crate) fn diff(left: &str, right: &str) -> TextEdit {
6 let chunks = dissimilar::diff(left, right);
7 textedit_from_chunks(chunks)
8}
9
10fn textedit_from_chunks(chunks: Vec<dissimilar::Chunk>) -> TextEdit {
11 let mut builder = TextEdit::builder();
12 let mut pos = TextSize::default();
13
14 let mut chunks = chunks.into_iter().peekable();
15 while let Some(chunk) = chunks.next() {
16 if let (Chunk::Delete(deleted), Some(&Chunk::Insert(inserted))) = (chunk, chunks.peek()) {
17 chunks.next().unwrap();
18 let deleted_len = TextSize::of(deleted);
19 builder.replace(TextRange::at(pos, deleted_len), inserted.into());
20 pos += deleted_len;
21 continue;
22 }
23
24 match chunk {
25 Chunk::Equal(text) => {
26 pos += TextSize::of(text);
27 }
28 Chunk::Delete(deleted) => {
29 let deleted_len = TextSize::of(deleted);
30 builder.delete(TextRange::at(pos, deleted_len));
31 pos += deleted_len;
32 }
33 Chunk::Insert(inserted) => {
34 builder.insert(pos, inserted.into());
35 }
36 }
37 }
38 builder.finish()
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 #[test]
46 fn diff_applies() {
47 let mut original = String::from("fn foo(a:u32){\n}");
48 let result = "fn foo(a: u32) {}";
49 let edit = diff(&original, result);
50 edit.apply(&mut original);
51 assert_eq!(original, result);
52 }
53}
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 85f1f81ad..dd486070b 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -31,6 +31,7 @@ use serde_json::to_value;
31use stdx::{format_to, split_once}; 31use stdx::{format_to, split_once};
32use syntax::{algo, ast, AstNode, TextRange, TextSize}; 32use syntax::{algo, ast, AstNode, TextRange, TextSize};
33 33
34use crate::diff::diff;
34use crate::{ 35use crate::{
35 cargo_target_spec::CargoTargetSpec, 36 cargo_target_spec::CargoTargetSpec,
36 config::RustfmtConfig, 37 config::RustfmtConfig,
@@ -691,7 +692,7 @@ pub(crate) fn handle_completion_resolve(
691 &snap.config.completion, 692 &snap.config.completion,
692 FilePosition { file_id, offset }, 693 FilePosition { file_id, offset },
693 &resolve_data.full_import_path, 694 &resolve_data.full_import_path,
694 &resolve_data.imported_name, 695 resolve_data.imported_name,
695 )? 696 )?
696 .into_iter() 697 .into_iter()
697 .flat_map(|edit| { 698 .flat_map(|edit| {
@@ -850,7 +851,7 @@ pub(crate) fn handle_formatting(
850 let crate_ids = snap.analysis.crate_for(file_id)?; 851 let crate_ids = snap.analysis.crate_for(file_id)?;
851 852
852 let file_line_index = snap.analysis.file_line_index(file_id)?; 853 let file_line_index = snap.analysis.file_line_index(file_id)?;
853 let end_position = to_proto::position(&file_line_index, TextSize::of(file.as_str())); 854 let file_line_endings = snap.file_line_endings(file_id);
854 855
855 let mut rustfmt = match &snap.config.rustfmt { 856 let mut rustfmt = match &snap.config.rustfmt {
856 RustfmtConfig::Rustfmt { extra_args } => { 857 RustfmtConfig::Rustfmt { extra_args } => {
@@ -871,16 +872,18 @@ pub(crate) fn handle_formatting(
871 } 872 }
872 }; 873 };
873 874
874 let mut rustfmt = rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).spawn()?; 875 let mut rustfmt =
876 rustfmt.stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn()?;
875 877
876 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; 878 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
877 879
878 let output = rustfmt.wait_with_output()?; 880 let output = rustfmt.wait_with_output()?;
879 let captured_stdout = String::from_utf8(output.stdout)?; 881 let captured_stdout = String::from_utf8(output.stdout)?;
882 let captured_stderr = String::from_utf8(output.stderr).unwrap_or_default();
880 883
881 if !output.status.success() { 884 if !output.status.success() {
882 match output.status.code() { 885 match output.status.code() {
883 Some(1) => { 886 Some(1) if !captured_stderr.contains("not installed") => {
884 // While `rustfmt` doesn't have a specific exit code for parse errors this is the 887 // While `rustfmt` doesn't have a specific exit code for parse errors this is the
885 // likely cause exiting with 1. Most Language Servers swallow parse errors on 888 // likely cause exiting with 1. Most Language Servers swallow parse errors on
886 // formatting because otherwise an error is surfaced to the user on top of the 889 // formatting because otherwise an error is surfaced to the user on top of the
@@ -896,8 +899,9 @@ pub(crate) fn handle_formatting(
896 format!( 899 format!(
897 r#"rustfmt exited with: 900 r#"rustfmt exited with:
898 Status: {} 901 Status: {}
899 stdout: {}"#, 902 stdout: {}
900 output.status, captured_stdout, 903 stderr: {}"#,
904 output.status, captured_stdout, captured_stderr,
901 ), 905 ),
902 ) 906 )
903 .into()); 907 .into());
@@ -909,10 +913,11 @@ pub(crate) fn handle_formatting(
909 // The document is already formatted correctly -- no edits needed. 913 // The document is already formatted correctly -- no edits needed.
910 Ok(None) 914 Ok(None)
911 } else { 915 } else {
912 Ok(Some(vec![lsp_types::TextEdit { 916 Ok(Some(to_proto::text_edit_vec(
913 range: Range::new(Position::new(0, 0), end_position), 917 &file_line_index,
914 new_text: captured_stdout, 918 file_line_endings,
915 }])) 919 diff(&file, &captured_stdout),
920 )))
916 } 921 }
917} 922}
918 923
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index d538ad69a..c9494e300 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -34,6 +34,7 @@ mod request_metrics;
34mod lsp_utils; 34mod lsp_utils;
35mod thread_pool; 35mod thread_pool;
36mod document; 36mod document;
37mod diff;
37pub mod lsp_ext; 38pub mod lsp_ext;
38pub mod config; 39pub mod config;
39 40
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
index 968ea55f0..a49a58c00 100644
--- a/crates/rust-analyzer/src/markdown.rs
+++ b/crates/rust-analyzer/src/markdown.rs
@@ -1,8 +1,17 @@
1//! Transforms markdown 1//! Transforms markdown
2 2
3const RUSTDOC_FENCE: &str = "```"; 3const RUSTDOC_FENCE: &str = "```";
4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = 4const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[
5 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"]; 5 "",
6 "rust",
7 "should_panic",
8 "ignore",
9 "no_run",
10 "compile_fail",
11 "edition2015",
12 "edition2018",
13 "edition2021",
14];
6 15
7pub(crate) fn format_docs(src: &str) -> String { 16pub(crate) fn format_docs(src: &str) -> String {
8 let mut processed_lines = Vec::new(); 17 let mut processed_lines = Vec::new();
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index c2f6a655d..5c4366f16 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -44,6 +44,7 @@ define_semantic_token_types![
44 (ESCAPE_SEQUENCE, "escapeSequence"), 44 (ESCAPE_SEQUENCE, "escapeSequence"),
45 (FORMAT_SPECIFIER, "formatSpecifier"), 45 (FORMAT_SPECIFIER, "formatSpecifier"),
46 (GENERIC, "generic"), 46 (GENERIC, "generic"),
47 (CONST_PARAMETER, "constParameter"),
47 (LIFETIME, "lifetime"), 48 (LIFETIME, "lifetime"),
48 (LABEL, "label"), 49 (LABEL, "label"),
49 (PUNCTUATION, "punctuation"), 50 (PUNCTUATION, "punctuation"),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 1a38e79f0..999b18351 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -42,6 +42,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
42 SymbolKind::Field => lsp_types::SymbolKind::Field, 42 SymbolKind::Field => lsp_types::SymbolKind::Field,
43 SymbolKind::Static => lsp_types::SymbolKind::Constant, 43 SymbolKind::Static => lsp_types::SymbolKind::Constant,
44 SymbolKind::Const => lsp_types::SymbolKind::Constant, 44 SymbolKind::Const => lsp_types::SymbolKind::Constant,
45 SymbolKind::ConstParam => lsp_types::SymbolKind::Constant,
45 SymbolKind::Impl => lsp_types::SymbolKind::Object, 46 SymbolKind::Impl => lsp_types::SymbolKind::Object,
46 SymbolKind::Local 47 SymbolKind::Local
47 | SymbolKind::SelfParam 48 | SymbolKind::SelfParam
@@ -378,6 +379,7 @@ fn semantic_token_type_and_modifiers(
378 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE, 379 SymbolKind::Impl => lsp_types::SemanticTokenType::TYPE,
379 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, 380 SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY,
380 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER, 381 SymbolKind::TypeParam => lsp_types::SemanticTokenType::TYPE_PARAMETER,
382 SymbolKind::ConstParam => semantic_tokens::CONST_PARAMETER,
381 SymbolKind::LifetimeParam => semantic_tokens::LIFETIME, 383 SymbolKind::LifetimeParam => semantic_tokens::LIFETIME,
382 SymbolKind::Label => semantic_tokens::LABEL, 384 SymbolKind::Label => semantic_tokens::LABEL,
383 SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER, 385 SymbolKind::ValueParam => lsp_types::SemanticTokenType::PARAMETER,
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs
index e51eb2626..84db0856d 100644
--- a/crates/rust-analyzer/tests/rust-analyzer/main.rs
+++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs
@@ -190,15 +190,10 @@ pub use std::collections::HashMap;
190 }, 190 },
191 json!([ 191 json!([
192 { 192 {
193 "newText": r#"mod bar; 193 "newText": "",
194
195fn main() {}
196
197pub use std::collections::HashMap;
198"#,
199 "range": { 194 "range": {
200 "end": { "character": 0, "line": 6 }, 195 "end": { "character": 0, "line": 3 },
201 "start": { "character": 0, "line": 0 } 196 "start": { "character": 11, "line": 2 }
202 } 197 }
203 } 198 }
204 ]), 199 ]),
@@ -248,17 +243,17 @@ pub use std::collections::HashMap;
248 }, 243 },
249 json!([ 244 json!([
250 { 245 {
251 "newText": r#"mod bar; 246 "newText": "",
252 247 "range": {
253async fn test() {} 248 "end": { "character": 0, "line": 3 },
254 249 "start": { "character": 17, "line": 2 }
255fn main() {} 250 }
256 251 },
257pub use std::collections::HashMap; 252 {
258"#, 253 "newText": "",
259 "range": { 254 "range": {
260 "end": { "character": 0, "line": 9 }, 255 "end": { "character": 0, "line": 6 },
261 "start": { "character": 0, "line": 0 } 256 "start": { "character": 11, "line": 5 }
262 } 257 }
263 } 258 }
264 ]), 259 ]),
diff --git a/crates/ssr/Cargo.toml b/crates/ssr/Cargo.toml
index 98ed25fb6..339eda86a 100644
--- a/crates/ssr/Cargo.toml
+++ b/crates/ssr/Cargo.toml
@@ -12,7 +12,7 @@ doctest = false
12 12
13[dependencies] 13[dependencies]
14rustc-hash = "1.1.0" 14rustc-hash = "1.1.0"
15itertools = "0.9.0" 15itertools = "0.10.0"
16 16
17text_edit = { path = "../text_edit", version = "0.0.0" } 17text_edit = { path = "../text_edit", version = "0.0.0" }
18syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
diff --git a/crates/ssr/src/matching.rs b/crates/ssr/src/matching.rs
index 99b187311..6cf831431 100644
--- a/crates/ssr/src/matching.rs
+++ b/crates/ssr/src/matching.rs
@@ -473,7 +473,9 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
473 } 473 }
474 SyntaxElement::Node(n) => { 474 SyntaxElement::Node(n) => {
475 if let Some(first_token) = n.first_token() { 475 if let Some(first_token) = n.first_token() {
476 if Some(first_token.to_string()) == next_pattern_token { 476 if Some(first_token.text().as_str())
477 == next_pattern_token.as_deref()
478 {
477 if let Some(SyntaxElement::Node(p)) = pattern.next() { 479 if let Some(SyntaxElement::Node(p)) = pattern.next() {
478 // We have a subtree that starts with the next token in our pattern. 480 // We have a subtree that starts with the next token in our pattern.
479 self.attempt_match_token_tree(phase, &p, &n)?; 481 self.attempt_match_token_tree(phase, &p, &n)?;
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 21015591c..5d8389ade 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -11,7 +11,7 @@ edition = "2018"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.9.0" 14itertools = "0.10.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs
index 77233ab31..824ebf41c 100644
--- a/crates/syntax/src/ast/edit.rs
+++ b/crates/syntax/src/ast/edit.rs
@@ -220,7 +220,7 @@ impl ast::RecordExprFieldList {
220 InsertPosition::After($anchor.syntax().clone().into()) 220 InsertPosition::After($anchor.syntax().clone().into())
221 } 221 }
222 }; 222 };
223 }; 223 }
224 224
225 let position = match position { 225 let position = match position {
226 InsertPosition::First => after_l_curly!(), 226 InsertPosition::First => after_l_curly!(),
@@ -533,7 +533,7 @@ impl ast::GenericParamList {
533 InsertPosition::After($anchor.syntax().clone().into()) 533 InsertPosition::After($anchor.syntax().clone().into())
534 } 534 }
535 }; 535 };
536 }; 536 }
537 537
538 let position = match self.generic_params().last() { 538 let position = match self.generic_params().last() {
539 Some(it) => after_field!(it), 539 Some(it) => after_field!(it),
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index c45cb514a..2aa472fb4 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -193,6 +193,14 @@ impl ast::UseTreeList {
193 .and_then(ast::UseTree::cast) 193 .and_then(ast::UseTree::cast)
194 .expect("UseTreeLists are always nested in UseTrees") 194 .expect("UseTreeLists are always nested in UseTrees")
195 } 195 }
196
197 pub fn has_inner_comment(&self) -> bool {
198 self.syntax()
199 .children_with_tokens()
200 .filter_map(|it| it.into_token())
201 .find_map(ast::Comment::cast)
202 .is_some()
203 }
196} 204}
197 205
198impl ast::Impl { 206impl ast::Impl {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 13c6a2a16..58b309379 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -63,6 +63,18 @@ After you are happy with the state of the code, please use [interactive rebase](
63Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors). 63Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors).
64Such messages create a lot of duplicate notification traffic during rebases. 64Such messages create a lot of duplicate notification traffic during rebases.
65 65
66If possible, write commit messages from user's perspective:
67
68```
69# Good
70Goto definition works inside macros
71
72# Not as good
73Use original span for FileId
74```
75
76This makes it easier to prepare a changelog.
77
66## Clippy 78## Clippy
67 79
68We don't enforce Clippy. 80We don't enforce Clippy.
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index d4121b401..75445a02e 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -339,7 +339,7 @@ interface Crate {
339 /// Path to the root module of the crate. 339 /// Path to the root module of the crate.
340 root_module: string; 340 root_module: string;
341 /// Edition of the crate. 341 /// Edition of the crate.
342 edition: "2015" | "2018"; 342 edition: "2015" | "2018" | "2021";
343 /// Dependencies 343 /// Dependencies
344 deps: Dep[]; 344 deps: Dep[];
345 /// Should this crate be treated as a member of current "workspace". 345 /// Should this crate be treated as a member of current "workspace".
diff --git a/editors/code/package.json b/editors/code/package.json
index 4bae5d647..3e55a3523 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -288,6 +288,14 @@
288 "default": null, 288 "default": null,
289 "markdownDescription": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then `#rust-analyzer.updates.channel#` setting is not used" 289 "markdownDescription": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then `#rust-analyzer.updates.channel#` setting is not used"
290 }, 290 },
291 "rust-analyzer.server.extraEnv": {
292 "type": [
293 "null",
294 "object"
295 ],
296 "default": null,
297 "markdownDescription": "Extra environment variables that will be passed to the rust-analyzer executable. Useful for passing e.g. `RA_LOG` for debugging."
298 },
291 "rust-analyzer.trace.server": { 299 "rust-analyzer.trace.server": {
292 "type": "string", 300 "type": "string",
293 "scope": "window", 301 "scope": "window",
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 63ab82dde..539e487ec 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -6,6 +6,10 @@ import { DocumentSemanticsTokensSignature, DocumentSemanticsTokensEditsSignature
6import { assert } from './util'; 6import { assert } from './util';
7import { WorkspaceEdit } from 'vscode'; 7import { WorkspaceEdit } from 'vscode';
8 8
9export interface Env {
10 [name: string]: string;
11}
12
9function renderCommand(cmd: ra.CommandLink) { 13function renderCommand(cmd: ra.CommandLink) {
10 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`; 14 return `[${cmd.title}](command:${cmd.command}?${encodeURIComponent(JSON.stringify(cmd.arguments))} '${cmd.tooltip!}')`;
11} 15}
@@ -27,14 +31,17 @@ async function semanticHighlightingWorkaround<R, F extends (...args: any[]) => v
27 return res; 31 return res;
28} 32}
29 33
30export function createClient(serverPath: string, cwd: string): lc.LanguageClient { 34export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient {
31 // '.' Is the fallback if no folder is open 35 // '.' Is the fallback if no folder is open
32 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 36 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
33 // It might be a good idea to test if the uri points to a file. 37 // It might be a good idea to test if the uri points to a file.
34 38
39 const newEnv = Object.assign({}, process.env);
40 Object.assign(newEnv, extraEnv);
41
35 const run: lc.Executable = { 42 const run: lc.Executable = {
36 command: serverPath, 43 command: serverPath,
37 options: { cwd }, 44 options: { cwd, env: newEnv },
38 }; 45 };
39 const serverOptions: lc.ServerOptions = { 46 const serverOptions: lc.ServerOptions = {
40 run, 47 run,
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts
index 2911bccd5..c1c9f9754 100644
--- a/editors/code/src/commands.ts
+++ b/editors/code/src/commands.ts
@@ -524,8 +524,14 @@ export function resolveCodeAction(ctx: Ctx): Cmd {
524 if (!item.edit) { 524 if (!item.edit) {
525 return; 525 return;
526 } 526 }
527 const edit = client.protocol2CodeConverter.asWorkspaceEdit(item.edit); 527 const itemEdit = item.edit;
528 await vscode.workspace.applyEdit(edit); 528 const edit = client.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
529 // filter out all text edits and recreate the WorkspaceEdit without them so we can apply
530 // snippet edits on our own
531 const itemEditWithoutTextEdits = { ...item, documentChanges: itemEdit.documentChanges?.filter(change => "kind" in change) };
532 const editWithoutTextEdits = client.protocol2CodeConverter.asWorkspaceEdit(itemEditWithoutTextEdits);
533 await applySnippetWorkspaceEdit(edit);
534 await vscode.workspace.applyEdit(editWithoutTextEdits);
529 }; 535 };
530} 536}
531 537
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 848e92af9..fe9f3b4a8 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -1,4 +1,5 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import { Env } from './client';
2import { log } from "./util"; 3import { log } from "./util";
3 4
4export type UpdatesChannel = "stable" | "nightly"; 5export type UpdatesChannel = "stable" | "nightly";
@@ -13,6 +14,7 @@ export class Config {
13 readonly rootSection = "rust-analyzer"; 14 readonly rootSection = "rust-analyzer";
14 private readonly requiresReloadOpts = [ 15 private readonly requiresReloadOpts = [
15 "serverPath", 16 "serverPath",
17 "server",
16 "cargo", 18 "cargo",
17 "procMacro", 19 "procMacro",
18 "files", 20 "files",
@@ -92,6 +94,7 @@ export class Config {
92 } 94 }
93 95
94 get serverPath() { return this.get<null | string>("serverPath"); } 96 get serverPath() { return this.get<null | string>("serverPath"); }
97 get serverExtraEnv() { return this.get<Env | null>("server.extraEnv") ?? {}; }
95 get channel() { return this.get<UpdatesChannel>("updates.channel"); } 98 get channel() { return this.get<UpdatesChannel>("updates.channel"); }
96 get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } 99 get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); }
97 get traceExtension() { return this.get<boolean>("trace.extension"); } 100 get traceExtension() { return this.get<boolean>("trace.extension"); }
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index d39864d33..e7585184b 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -24,7 +24,7 @@ export class Ctx {
24 serverPath: string, 24 serverPath: string,
25 cwd: string, 25 cwd: string,
26 ): Promise<Ctx> { 26 ): Promise<Ctx> {
27 const client = createClient(serverPath, cwd); 27 const client = createClient(serverPath, cwd, config.serverExtraEnv);
28 28
29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
30 extCtx.subscriptions.push(statusBar); 30 extCtx.subscriptions.push(statusBar);
diff --git a/xtask/src/dist.rs b/xtask/src/dist.rs
index d59b88131..6bc34106b 100644
--- a/xtask/src/dist.rs
+++ b/xtask/src/dist.rs
@@ -63,8 +63,7 @@ fn dist_server() -> Result<()> {
63 env::set_var("CC", "clang"); 63 env::set_var("CC", "clang");
64 } 64 }
65 65
66 let toolchain = toolchain(&target); 66 cmd!("cargo build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --release").run()?;
67 cmd!("cargo +{toolchain} build --manifest-path ./crates/rust-analyzer/Cargo.toml --bin rust-analyzer --target {target} --release").run()?;
68 67
69 let suffix = exe_suffix(&target); 68 let suffix = exe_suffix(&target);
70 let src = 69 let src =
@@ -118,13 +117,6 @@ fn exe_suffix(target: &str) -> String {
118 } 117 }
119} 118}
120 119
121fn toolchain(target: &str) -> String {
122 match target {
123 "aarch64-apple-darwin" => "beta".to_string(),
124 _ => "stable".to_string(),
125 }
126}
127
128fn gzip(src_path: &Path, dest_path: &Path) -> Result<()> { 120fn gzip(src_path: &Path, dest_path: &Path) -> Result<()> {
129 let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best()); 121 let mut encoder = GzEncoder::new(File::create(dest_path)?, Compression::best());
130 let mut input = io::BufReader::new(File::open(src_path)?); 122 let mut input = io::BufReader::new(File::open(src_path)?);
diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs
index 6bfa922e6..d1ffb70ad 100644
--- a/xtask/tests/tidy.rs
+++ b/xtask/tests/tidy.rs
@@ -139,7 +139,7 @@ fn deny_clippy(path: &PathBuf, text: &String) {
139 return; 139 return;
140 } 140 }
141 141
142 if text.contains("[\u{61}llow(clippy") { 142 if text.contains("\u{61}llow(clippy") {
143 panic!( 143 panic!(
144 "\n\nallowing lints is forbidden: {}. 144 "\n\nallowing lints is forbidden: {}.
145rust-analyzer intentionally doesn't check clippy on CI. 145rust-analyzer intentionally doesn't check clippy on CI.