diff options
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]] |
28 | name = "anyhow" | 28 | name = "anyhow" |
29 | version = "1.0.36" | 29 | version = "1.0.37" |
30 | source = "registry+https://github.com/rust-lang/crates.io-index" | 30 | source = "registry+https://github.com/rust-lang/crates.io-index" |
31 | checksum = "68803225a7b13e47191bab76f2687382b60d259e8cf37f6e1893658b84bb9479" | 31 | checksum = "ee67c11feeac938fae061b232e38e0b6d94f97a9df10e6271319325ac4c56a86" |
32 | 32 | ||
33 | [[package]] | 33 | [[package]] |
34 | name = "anymap" | 34 | name = "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" | |||
349 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" | 349 | checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" |
350 | 350 | ||
351 | [[package]] | 351 | [[package]] |
352 | name = "dissimilar" | ||
353 | version = "1.0.2" | ||
354 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
355 | checksum = "fc4b29f4b9bb94bf267d57269fd0706d343a160937108e9619fe380645428abb" | ||
356 | |||
357 | [[package]] | ||
352 | name = "drop_bomb" | 358 | name = "drop_bomb" |
353 | version = "0.1.5" | 359 | version = "0.1.5" |
354 | source = "registry+https://github.com/rust-lang/crates.io-index" | 360 | source = "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]] |
751 | name = "itertools" | ||
752 | version = "0.10.0" | ||
753 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
754 | checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319" | ||
755 | dependencies = [ | ||
756 | "either", | ||
757 | ] | ||
758 | |||
759 | [[package]] | ||
745 | name = "itoa" | 760 | name = "itoa" |
746 | version = "0.4.6" | 761 | version = "0.4.7" |
747 | source = "registry+https://github.com/rust-lang/crates.io-index" | 762 | source = "registry+https://github.com/rust-lang/crates.io-index" |
748 | checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" | 763 | checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" |
749 | 764 | ||
750 | [[package]] | 765 | [[package]] |
751 | name = "jod-thread" | 766 | name = "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]] |
1520 | name = "serde_json" | 1536 | name = "serde_json" |
1521 | version = "1.0.60" | 1537 | version = "1.0.61" |
1522 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1538 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1523 | checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" | 1539 | checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a" |
1524 | dependencies = [ | 1540 | dependencies = [ |
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]] |
1601 | name = "syn" | 1617 | name = "syn" |
1602 | version = "1.0.55" | 1618 | version = "1.0.56" |
1603 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1619 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1604 | checksum = "a571a711dddd09019ccc628e1b17fe87c59b09d513c06c026877aa708334f37a" | 1620 | checksum = "a9802ddde94170d186eeee5005b798d9c159fa970403f1be19976d0cfb939b72" |
1605 | dependencies = [ | 1621 | dependencies = [ |
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] |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | itertools = "0.9.0" | 14 | itertools = "0.10.0" |
15 | either = "1.6.1" | 15 | either = "1.6.1" |
16 | 16 | ||
17 | stdx = { path = "../stdx", version = "0.0.0" } | 17 | stdx = { 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 @@ | |||
1 | use hir::AsName; | ||
2 | use syntax::{ | ||
3 | ast::{self, edit::AstNodeEdit, make}, | ||
4 | AstNode, | ||
5 | }; | ||
6 | use test_utils::mark; | ||
7 | |||
8 | use 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 | // ``` | ||
40 | pub(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 | |||
66 | fn 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 | |||
85 | fn 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 | |||
99 | fn 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)] | ||
121 | mod 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#" | ||
131 | fn foo() { | ||
132 | let mut a = 1; | ||
133 | |||
134 | if true { | ||
135 | <|>a = 2; | ||
136 | } else { | ||
137 | a = 3; | ||
138 | } | ||
139 | }"#, | ||
140 | r#" | ||
141 | fn 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#" | ||
158 | fn 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#" | ||
174 | fn 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#" | ||
197 | fn 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#" | ||
216 | fn 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#" | ||
228 | fn 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#" | ||
247 | fn 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#" | ||
259 | fn 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#" | ||
278 | fn 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#" | ||
295 | fn 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#" | ||
310 | fn 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] |
241 | fn doctest_extract_assignment() { | ||
242 | check_doc_test( | ||
243 | "extract_assignment", | ||
244 | r#####" | ||
245 | fn main() { | ||
246 | let mut foo = 6; | ||
247 | |||
248 | if true { | ||
249 | <|>foo = 5; | ||
250 | } else { | ||
251 | foo = 4; | ||
252 | } | ||
253 | } | ||
254 | "#####, | ||
255 | r#####" | ||
256 | fn 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] | ||
241 | fn doctest_extract_module_to_file() { | 270 | fn 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)] |
194 | pub enum Edition { | 194 | pub 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" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | itertools = "0.9.0" | 13 | itertools = "0.10.0" |
14 | log = "0.4.8" | 14 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
16 | either = "1.6.1" | 16 | either = "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") |
24 | static KINDS: &[(&str, &str)] = &[ | 24 | static 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#" | ||
128 | struct S { foo: u32, bar: usize } | ||
129 | |||
130 | impl core::default::Default for S { | ||
131 | fn default() -> Self { | ||
132 | S { | ||
133 | foo: 0, | ||
134 | bar: 0, | ||
135 | } | ||
136 | } | ||
137 | } | ||
138 | |||
139 | fn process(f: S) { | ||
140 | let other = S { | ||
141 | foo: 5, | ||
142 | .<|> | ||
143 | }; | ||
144 | } | ||
145 | "#, | ||
146 | r#" | ||
147 | struct S { foo: u32, bar: usize } | ||
148 | |||
149 | impl core::default::Default for S { | ||
150 | fn default() -> Self { | ||
151 | S { | ||
152 | foo: 0, | ||
153 | bar: 0, | ||
154 | } | ||
155 | } | ||
156 | } | ||
157 | |||
158 | fn 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#" |
107 | struct S { foo: u32, bar: usize } | 171 | struct 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 | ||
174 | fn add_type_alias_impl( | 176 | fn 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 | ||
28 | impl<'a> ConstRender<'a> { | 28 | impl<'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 | ||
28 | impl<'a> TypeAliasRender<'a> { | 28 | impl<'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" | |||
14 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
15 | either = "1.5.3" | 15 | either = "1.5.3" |
16 | arrayvec = "0.5.1" | 16 | arrayvec = "0.5.1" |
17 | itertools = "0.9.0" | 17 | itertools = "0.10.0" |
18 | 18 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 19 | stdx = { path = "../stdx", version = "0.0.0" } |
20 | syntax = { path = "../syntax", version = "0.0.0" } | 20 | syntax = { 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 | }; |
8 | use hir_expand::hygiene::Hygiene; | 8 | use hir_expand::hygiene::Hygiene; |
9 | use hir_ty::db::HirDatabase; | 9 | use hir_ty::db::HirDatabase; |
10 | use syntax::ast; | 10 | use syntax::ast; |
11 | 11 | ||
12 | use crate::{ | 12 | use 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 | ||
17 | pub trait HasAttrs { | 17 | pub 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 | ||
67 | macro_rules! impl_has_attrs_adt { | 68 | macro_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 | ||
83 | impl_has_attrs_adt![Struct, Union, Enum]; | 84 | impl_has_attrs_enum![Struct, Union, Enum for Adt]; |
85 | impl_has_attrs_enum![TypeParam, ConstParam, LifetimeParam for GenericParam]; | ||
84 | 86 | ||
85 | fn resolve_doc_path( | 87 | fn 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 | }; |
26 | use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; | 26 | use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; |
27 | use hir_expand::{ | 27 | use 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 { | |||
1250 | pub enum GenericParam { | 1249 | pub enum GenericParam { |
1251 | TypeParam(TypeParam), | 1250 | TypeParam(TypeParam), |
1252 | LifetimeParam(LifetimeParam), | 1251 | LifetimeParam(LifetimeParam), |
1252 | ConstParam(ConstParam), | ||
1253 | } | 1253 | } |
1254 | impl_from!(TypeParam, LifetimeParam for GenericParam); | 1254 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); |
1255 | 1255 | ||
1256 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 1256 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1257 | pub struct TypeParam { | 1257 | pub struct TypeParam { |
@@ -1313,6 +1313,26 @@ impl LifetimeParam { | |||
1313 | } | 1313 | } |
1314 | } | 1314 | } |
1315 | 1315 | ||
1316 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1317 | pub struct ConstParam { | ||
1318 | pub(crate) id: ConstParamId, | ||
1319 | } | ||
1320 | |||
1321 | impl 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)] |
1317 | pub struct Impl { | 1337 | pub 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 @@ | |||
6 | use hir_def::{ | 6 | use 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 | ||
13 | use crate::{ | 13 | use 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 | ||
18 | macro_rules! from_id { | 18 | macro_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 | ||
71 | impl 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 | |||
81 | impl 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 | |||
70 | impl From<EnumVariantId> for Variant { | 91 | impl 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; | |||
10 | use syntax::ast; | 10 | use syntax::ast; |
11 | 11 | ||
12 | use crate::{ | 12 | use 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 | ||
17 | pub trait HasSource { | 17 | pub 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 | ||
47 | impl HasSource for Field { | 47 | impl 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 | } |
58 | impl HasSource for Struct { | 59 | impl 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 | } |
64 | impl HasSource for Union { | 65 | impl 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 | } |
70 | impl HasSource for Enum { | 71 | impl 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 | } |
76 | impl HasSource for Variant { | 77 | impl 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 | } |
82 | impl HasSource for Function { | 83 | impl 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 | } |
88 | impl HasSource for Const { | 89 | impl 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 | } |
94 | impl HasSource for Static { | 95 | impl 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 | } |
100 | impl HasSource for Trait { | 101 | impl 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 | } |
106 | impl HasSource for TypeAlias { | 107 | impl 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 | } |
112 | impl HasSource for MacroDef { | 113 | impl 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 | } |
121 | impl HasSource for Impl { | 120 | impl 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 | ||
128 | impl HasSource for TypeParam { | 127 | impl 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 | ||
136 | impl HasSource for LifetimeParam { | 135 | impl 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 | |||
143 | impl 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 | }; |
13 | use hir_expand::{name::AsName, AstId, MacroDefKind}; | 13 | use hir_expand::{name::AsName, AstId, MacroDefKind}; |
14 | use rustc_hash::FxHashMap; | 14 | use 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" | |||
17 | anymap = "0.12.1" | 17 | anymap = "0.12.1" |
18 | drop_bomb = "0.1.4" | 18 | drop_bomb = "0.1.4" |
19 | fst = { version = "0.4", default-features = false } | 19 | fst = { version = "0.4", default-features = false } |
20 | itertools = "0.9.0" | 20 | itertools = "0.10.0" |
21 | indexmap = "1.4.0" | 21 | indexmap = "1.4.0" |
22 | smallvec = "1.4.0" | 22 | smallvec = "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)] |
30 | pub struct TypeParamData { | 30 | pub 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)] |
38 | pub struct LifetimeParamData { | 38 | pub 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)] | ||
44 | pub 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)] |
43 | pub enum TypeParamProvenance { | 50 | pub enum TypeParamProvenance { |
44 | TypeParamList, | 51 | TypeParamList, |
@@ -51,6 +58,7 @@ pub enum TypeParamProvenance { | |||
51 | pub struct GenericParams { | 58 | pub 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 { | |||
76 | pub(crate) struct SourceMap { | 84 | pub(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 | ||
81 | impl GenericParams { | 90 | impl 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 | ||
413 | impl 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 | |||
393 | impl ChildBySource for GenericDefId { | 423 | impl 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)] | ||
243 | pub 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)] |
242 | pub struct Query { | 254 | pub 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 | ||
251 | impl Query { | 264 | impl 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 | ||
307 | fn 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> { | |||
387 | mod tests { | 432 | mod 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 | ||
272 | static EMPTY_GENERICS: GenericParams = | 273 | static 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)] |
276 | struct ItemTreeData { | 281 | struct 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 | ||
9 | use crate::{ | 9 | use 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 | ||
15 | pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; | 15 | pub 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(); | |||
29 | pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); | 29 | pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); |
30 | pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); | 30 | pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); |
31 | pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); | 31 | pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new(); |
32 | pub const CONST_PARAM: Key<ast::ConstParam, ConstParamId> = Key::new(); | ||
32 | 33 | ||
33 | pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); | 34 | pub 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 { | |||
232 | pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>; | 232 | pub type LocalLifetimeParamId = Idx<generics::LifetimeParamData>; |
233 | 233 | ||
234 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 234 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
235 | pub struct ConstParamId { | ||
236 | pub parent: GenericDefId, | ||
237 | pub local_id: LocalConstParamId, | ||
238 | } | ||
239 | pub type LocalConstParamId = Idx<generics::ConstParamData>; | ||
240 | |||
241 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
235 | pub enum ContainerId { | 242 | pub enum ContainerId { |
236 | ModuleId(ModuleId), | 243 | ModuleId(ModuleId), |
237 | DefWithBodyId(DefWithBodyId), | 244 | DefWithBodyId(DefWithBodyId), |
@@ -254,6 +261,15 @@ pub enum AdtId { | |||
254 | } | 261 | } |
255 | impl_from!(StructId, UnionId, EnumId for AdtId); | 262 | impl_from!(StructId, UnionId, EnumId for AdtId); |
256 | 263 | ||
264 | /// A generic param | ||
265 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
266 | pub enum GenericParamId { | ||
267 | TypeParamId(TypeParamId), | ||
268 | LifetimeParamId(LifetimeParamId), | ||
269 | ConstParamId(ConstParamId), | ||
270 | } | ||
271 | impl_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)] |
259 | pub enum ModuleDefId { | 275 | pub 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 | ||
355 | impl_from!( | 372 | impl_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#" |
678 | macro_rules! a { | 678 | macro_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 | ||
692 | b! { static = #[] (); } | 692 | b! { 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 | ||
98 | impl Resolver { | 99 | impl 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`. |
5 | use std::sync::Arc; | ||
6 | |||
7 | use arena::{Arena, Idx}; | ||
5 | use base_db::CrateId; | 8 | use base_db::CrateId; |
6 | use either::Either; | 9 | use either::Either; |
7 | use syntax::ast; | 10 | use mbe::Origin; |
11 | use syntax::{ast, AstNode}; | ||
8 | 12 | ||
9 | use crate::{ | 13 | use 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)] |
16 | pub struct Hygiene { | 20 | pub struct Hygiene { |
17 | // This is what `$crate` expands to | 21 | frames: Option<Arc<HygieneFrames>>, |
18 | def_crate: Option<CrateId>, | 22 | } |
23 | |||
24 | impl 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)] | ||
67 | struct HygieneFrames(Arena<HygieneFrame>); | ||
68 | |||
69 | #[derive(Clone, Debug)] | ||
70 | struct 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 | ||
24 | impl Hygiene { | 81 | impl 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 |