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 = s