diff options
175 files changed, 7387 insertions, 3882 deletions
diff --git a/.gitignore b/.gitignore index dab51647d..aef0fac33 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -8,3 +8,5 @@ crates/*/target | |||
8 | *.iml | 8 | *.iml |
9 | .vscode/settings.json | 9 | .vscode/settings.json |
10 | *.html | 10 | *.html |
11 | generated_assists.adoc | ||
12 | generated_features.adoc | ||
diff --git a/Cargo.lock b/Cargo.lock index 9981a2e33..308e36836 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -113,8 +113,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |||
113 | 113 | ||
114 | [[package]] | 114 | [[package]] |
115 | name = "chalk-derive" | 115 | name = "chalk-derive" |
116 | version = "0.10.1-dev" | 116 | version = "0.11.0" |
117 | source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" | 117 | source = "registry+https://github.com/rust-lang/crates.io-index" |
118 | checksum = "5b9bd01eab87277d973183a1d2e56bace1c11f8242c52c20636fb7dddf343ac9" | ||
118 | dependencies = [ | 119 | dependencies = [ |
119 | "proc-macro2", | 120 | "proc-macro2", |
120 | "quote", | 121 | "quote", |
@@ -124,8 +125,9 @@ dependencies = [ | |||
124 | 125 | ||
125 | [[package]] | 126 | [[package]] |
126 | name = "chalk-engine" | 127 | name = "chalk-engine" |
127 | version = "0.10.1-dev" | 128 | version = "0.11.0" |
128 | source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" | 129 | source = "registry+https://github.com/rust-lang/crates.io-index" |
130 | checksum = "6c7a637c3d17ed555aef16e16952a5d1e127bd55178cc30be22afeb92da90c7d" | ||
129 | dependencies = [ | 131 | dependencies = [ |
130 | "chalk-derive", | 132 | "chalk-derive", |
131 | "chalk-ir", | 133 | "chalk-ir", |
@@ -134,8 +136,9 @@ dependencies = [ | |||
134 | 136 | ||
135 | [[package]] | 137 | [[package]] |
136 | name = "chalk-ir" | 138 | name = "chalk-ir" |
137 | version = "0.10.1-dev" | 139 | version = "0.11.0" |
138 | source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" | 140 | source = "registry+https://github.com/rust-lang/crates.io-index" |
141 | checksum = "595e5735ded16c3f3dc348f7b15bbb2521a0080b1863cac38ad5271589944670" | ||
139 | dependencies = [ | 142 | dependencies = [ |
140 | "chalk-derive", | 143 | "chalk-derive", |
141 | "lazy_static", | 144 | "lazy_static", |
@@ -143,8 +146,9 @@ dependencies = [ | |||
143 | 146 | ||
144 | [[package]] | 147 | [[package]] |
145 | name = "chalk-solve" | 148 | name = "chalk-solve" |
146 | version = "0.10.1-dev" | 149 | version = "0.11.0" |
147 | source = "git+https://github.com/rust-lang/chalk.git?rev=329b7f3fdd2431ed6f6778cde53f22374c7d094c#329b7f3fdd2431ed6f6778cde53f22374c7d094c" | 150 | source = "registry+https://github.com/rust-lang/crates.io-index" |
151 | checksum = "5d9d938139db425867a30cc0cfec0269406d8238d0571d829041eaa7a8455d11" | ||
148 | dependencies = [ | 152 | dependencies = [ |
149 | "chalk-derive", | 153 | "chalk-derive", |
150 | "chalk-engine", | 154 | "chalk-engine", |
@@ -243,12 +247,13 @@ dependencies = [ | |||
243 | 247 | ||
244 | [[package]] | 248 | [[package]] |
245 | name = "crossbeam-queue" | 249 | name = "crossbeam-queue" |
246 | version = "0.2.2" | 250 | version = "0.2.3" |
247 | source = "registry+https://github.com/rust-lang/crates.io-index" | 251 | source = "registry+https://github.com/rust-lang/crates.io-index" |
248 | checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2" | 252 | checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" |
249 | dependencies = [ | 253 | dependencies = [ |
250 | "cfg-if", | 254 | "cfg-if", |
251 | "crossbeam-utils", | 255 | "crossbeam-utils", |
256 | "maybe-uninit", | ||
252 | ] | 257 | ] |
253 | 258 | ||
254 | [[package]] | 259 | [[package]] |
@@ -462,18 +467,18 @@ dependencies = [ | |||
462 | 467 | ||
463 | [[package]] | 468 | [[package]] |
464 | name = "indexmap" | 469 | name = "indexmap" |
465 | version = "1.3.2" | 470 | version = "1.4.0" |
466 | source = "registry+https://github.com/rust-lang/crates.io-index" | 471 | source = "registry+https://github.com/rust-lang/crates.io-index" |
467 | checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292" | 472 | checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe" |
468 | dependencies = [ | 473 | dependencies = [ |
469 | "autocfg", | 474 | "autocfg", |
470 | ] | 475 | ] |
471 | 476 | ||
472 | [[package]] | 477 | [[package]] |
473 | name = "inotify" | 478 | name = "inotify" |
474 | version = "0.7.0" | 479 | version = "0.7.1" |
475 | source = "registry+https://github.com/rust-lang/crates.io-index" | 480 | source = "registry+https://github.com/rust-lang/crates.io-index" |
476 | checksum = "24e40d6fd5d64e2082e0c796495c8ef5ad667a96d03e5aaa0becfd9d47bcbfb8" | 481 | checksum = "4816c66d2c8ae673df83366c18341538f234a26d65a9ecea5c348b453ac1d02f" |
477 | dependencies = [ | 482 | dependencies = [ |
478 | "bitflags", | 483 | "bitflags", |
479 | "inotify-sys", | 484 | "inotify-sys", |
@@ -561,9 +566,9 @@ dependencies = [ | |||
561 | 566 | ||
562 | [[package]] | 567 | [[package]] |
563 | name = "jod-thread" | 568 | name = "jod-thread" |
564 | version = "0.1.1" | 569 | version = "0.1.2" |
565 | source = "registry+https://github.com/rust-lang/crates.io-index" | 570 | source = "registry+https://github.com/rust-lang/crates.io-index" |
566 | checksum = "4022656272c3e564a7cdebcaaba6518d844b0d0c1836597196efb5bfeb98bb49" | 571 | checksum = "8b23360e99b8717f20aaa4598f5a6541efbe30630039fbc7706cf954a87947ae" |
567 | 572 | ||
568 | [[package]] | 573 | [[package]] |
569 | name = "kernel32-sys" | 574 | name = "kernel32-sys" |
@@ -809,9 +814,9 @@ dependencies = [ | |||
809 | 814 | ||
810 | [[package]] | 815 | [[package]] |
811 | name = "paste" | 816 | name = "paste" |
812 | version = "0.1.15" | 817 | version = "0.1.16" |
813 | source = "registry+https://github.com/rust-lang/crates.io-index" | 818 | source = "registry+https://github.com/rust-lang/crates.io-index" |
814 | checksum = "d53181dcd37421c08d3b69f887784956674d09c3f9a47a04fece2b130a5b346b" | 819 | checksum = "d508492eeb1e5c38ee696371bf7b9fc33c83d46a7d451606b96458fbbbdc2dec" |
815 | dependencies = [ | 820 | dependencies = [ |
816 | "paste-impl", | 821 | "paste-impl", |
817 | "proc-macro-hack", | 822 | "proc-macro-hack", |
@@ -819,9 +824,9 @@ dependencies = [ | |||
819 | 824 | ||
820 | [[package]] | 825 | [[package]] |
821 | name = "paste-impl" | 826 | name = "paste-impl" |
822 | version = "0.1.15" | 827 | version = "0.1.16" |
823 | source = "registry+https://github.com/rust-lang/crates.io-index" | 828 | source = "registry+https://github.com/rust-lang/crates.io-index" |
824 | checksum = "05ca490fa1c034a71412b4d1edcb904ec5a0981a4426c9eb2128c0fda7a68d17" | 829 | checksum = "84f328a6a63192b333fce5fbb4be79db6758a4d518dfac6d54412f1492f72d32" |
825 | dependencies = [ | 830 | dependencies = [ |
826 | "proc-macro-hack", | 831 | "proc-macro-hack", |
827 | "proc-macro2", | 832 | "proc-macro2", |
@@ -871,18 +876,18 @@ checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" | |||
871 | 876 | ||
872 | [[package]] | 877 | [[package]] |
873 | name = "proc-macro2" | 878 | name = "proc-macro2" |
874 | version = "1.0.17" | 879 | version = "1.0.18" |
875 | source = "registry+https://github.com/rust-lang/crates.io-index" | 880 | source = "registry+https://github.com/rust-lang/crates.io-index" |
876 | checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" | 881 | checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" |
877 | dependencies = [ | 882 | dependencies = [ |
878 | "unicode-xid", | 883 | "unicode-xid", |
879 | ] | 884 | ] |
880 | 885 | ||
881 | [[package]] | 886 | [[package]] |
882 | name = "quote" | 887 | name = "quote" |
883 | version = "1.0.6" | 888 | version = "1.0.7" |
884 | source = "registry+https://github.com/rust-lang/crates.io-index" | 889 | source = "registry+https://github.com/rust-lang/crates.io-index" |
885 | checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" | 890 | checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" |
886 | dependencies = [ | 891 | dependencies = [ |
887 | "proc-macro2", | 892 | "proc-macro2", |
888 | ] | 893 | ] |
@@ -977,7 +982,10 @@ dependencies = [ | |||
977 | "anymap", | 982 | "anymap", |
978 | "drop_bomb", | 983 | "drop_bomb", |
979 | "either", | 984 | "either", |
985 | "fst", | ||
986 | "indexmap", | ||
980 | "insta", | 987 | "insta", |
988 | "itertools", | ||
981 | "log", | 989 | "log", |
982 | "once_cell", | 990 | "once_cell", |
983 | "ra_arena", | 991 | "ra_arena", |
@@ -1006,6 +1014,7 @@ dependencies = [ | |||
1006 | "ra_prof", | 1014 | "ra_prof", |
1007 | "ra_syntax", | 1015 | "ra_syntax", |
1008 | "ra_tt", | 1016 | "ra_tt", |
1017 | "rustc-hash", | ||
1009 | "test_utils", | 1018 | "test_utils", |
1010 | ] | 1019 | ] |
1011 | 1020 | ||
@@ -1119,6 +1128,7 @@ dependencies = [ | |||
1119 | "memmap", | 1128 | "memmap", |
1120 | "ra_mbe", | 1129 | "ra_mbe", |
1121 | "ra_proc_macro", | 1130 | "ra_proc_macro", |
1131 | "ra_toolchain", | ||
1122 | "ra_tt", | 1132 | "ra_tt", |
1123 | "serde_derive", | 1133 | "serde_derive", |
1124 | "test_utils", | 1134 | "test_utils", |
@@ -1400,9 +1410,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" | |||
1400 | 1410 | ||
1401 | [[package]] | 1411 | [[package]] |
1402 | name = "ryu" | 1412 | name = "ryu" |
1403 | version = "1.0.4" | 1413 | version = "1.0.5" |
1404 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1414 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1405 | checksum = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1" | 1415 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" |
1406 | 1416 | ||
1407 | [[package]] | 1417 | [[package]] |
1408 | name = "salsa" | 1418 | name = "salsa" |
@@ -1511,9 +1521,9 @@ dependencies = [ | |||
1511 | 1521 | ||
1512 | [[package]] | 1522 | [[package]] |
1513 | name = "serde_json" | 1523 | name = "serde_json" |
1514 | version = "1.0.53" | 1524 | version = "1.0.55" |
1515 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1525 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1516 | checksum = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2" | 1526 | checksum = "ec2c5d7e739bc07a3e73381a39d61fdb5f671c60c1df26a130690665803d8226" |
1517 | dependencies = [ | 1527 | dependencies = [ |
1518 | "itoa", | 1528 | "itoa", |
1519 | "ryu", | 1529 | "ryu", |
@@ -1533,9 +1543,9 @@ dependencies = [ | |||
1533 | 1543 | ||
1534 | [[package]] | 1544 | [[package]] |
1535 | name = "serde_yaml" | 1545 | name = "serde_yaml" |
1536 | version = "0.8.12" | 1546 | version = "0.8.13" |
1537 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1547 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1538 | checksum = "16c7a592a1ec97c9c1c68d75b6e537dcbf60c7618e038e7841e00af1d9ccf0c4" | 1548 | checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5" |
1539 | dependencies = [ | 1549 | dependencies = [ |
1540 | "dtoa", | 1550 | "dtoa", |
1541 | "linked-hash-map", | 1551 | "linked-hash-map", |
@@ -1576,9 +1586,9 @@ checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" | |||
1576 | 1586 | ||
1577 | [[package]] | 1587 | [[package]] |
1578 | name = "syn" | 1588 | name = "syn" |
1579 | version = "1.0.29" | 1589 | version = "1.0.31" |
1580 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1581 | checksum = "bb37da98a55b1d08529362d9cbb863be17556873df2585904ab9d2bc951291d0" | 1591 | checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" |
1582 | dependencies = [ | 1592 | dependencies = [ |
1583 | "proc-macro2", | 1593 | "proc-macro2", |
1584 | "quote", | 1594 | "quote", |
@@ -1587,9 +1597,9 @@ dependencies = [ | |||
1587 | 1597 | ||
1588 | [[package]] | 1598 | [[package]] |
1589 | name = "synstructure" | 1599 | name = "synstructure" |
1590 | version = "0.12.3" | 1600 | version = "0.12.4" |
1591 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1592 | checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" | 1602 | checksum = "b834f2d66f734cb897113e34aaff2f1ab4719ca946f9a7358dba8f8064148701" |
1593 | dependencies = [ | 1603 | dependencies = [ |
1594 | "proc-macro2", | 1604 | "proc-macro2", |
1595 | "quote", | 1605 | "quote", |
@@ -1639,6 +1649,7 @@ dependencies = [ | |||
1639 | "relative-path", | 1649 | "relative-path", |
1640 | "rustc-hash", | 1650 | "rustc-hash", |
1641 | "serde_json", | 1651 | "serde_json", |
1652 | "stdx", | ||
1642 | "text-size", | 1653 | "text-size", |
1643 | ] | 1654 | ] |
1644 | 1655 | ||
@@ -1797,9 +1808,9 @@ dependencies = [ | |||
1797 | 1808 | ||
1798 | [[package]] | 1809 | [[package]] |
1799 | name = "yaml-rust" | 1810 | name = "yaml-rust" |
1800 | version = "0.4.3" | 1811 | version = "0.4.4" |
1801 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1802 | checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" | 1813 | checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d" |
1803 | dependencies = [ | 1814 | dependencies = [ |
1804 | "linked-hash-map", | 1815 | "linked-hash-map", |
1805 | ] | 1816 | ] |
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs index 5b1a4680b..edd8255f4 100644 --- a/crates/ra_assists/src/assist_context.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -1,5 +1,7 @@ | |||
1 | //! See `AssistContext` | 1 | //! See `AssistContext` |
2 | 2 | ||
3 | use std::mem; | ||
4 | |||
3 | use algo::find_covering_element; | 5 | use algo::find_covering_element; |
4 | use hir::Semantics; | 6 | use hir::Semantics; |
5 | use ra_db::{FileId, FileRange}; | 7 | use ra_db::{FileId, FileRange}; |
@@ -170,13 +172,32 @@ impl Assists { | |||
170 | 172 | ||
171 | pub(crate) struct AssistBuilder { | 173 | pub(crate) struct AssistBuilder { |
172 | edit: TextEditBuilder, | 174 | edit: TextEditBuilder, |
173 | file: FileId, | 175 | file_id: FileId, |
174 | is_snippet: bool, | 176 | is_snippet: bool, |
177 | edits: Vec<SourceFileEdit>, | ||
175 | } | 178 | } |
176 | 179 | ||
177 | impl AssistBuilder { | 180 | impl AssistBuilder { |
178 | pub(crate) fn new(file: FileId) -> AssistBuilder { | 181 | pub(crate) fn new(file_id: FileId) -> AssistBuilder { |
179 | AssistBuilder { edit: TextEditBuilder::default(), file, is_snippet: false } | 182 | AssistBuilder { |
183 | edit: TextEditBuilder::default(), | ||
184 | file_id, | ||
185 | is_snippet: false, | ||
186 | edits: Vec::new(), | ||
187 | } | ||
188 | } | ||
189 | |||
190 | pub(crate) fn edit_file(&mut self, file_id: FileId) { | ||
191 | self.file_id = file_id; | ||
192 | } | ||
193 | |||
194 | fn commit(&mut self) { | ||
195 | let edit = mem::take(&mut self.edit).finish(); | ||
196 | if !edit.is_empty() { | ||
197 | let new_edit = SourceFileEdit { file_id: self.file_id, edit }; | ||
198 | assert!(!self.edits.iter().any(|it| it.file_id == new_edit.file_id)); | ||
199 | self.edits.push(new_edit); | ||
200 | } | ||
180 | } | 201 | } |
181 | 202 | ||
182 | /// Remove specified `range` of text. | 203 | /// Remove specified `range` of text. |
@@ -234,21 +255,15 @@ impl AssistBuilder { | |||
234 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | 255 | algo::diff(&node, &new).into_text_edit(&mut self.edit) |
235 | } | 256 | } |
236 | 257 | ||
237 | // FIXME: better API | ||
238 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | ||
239 | self.file = assist_file; | ||
240 | } | ||
241 | |||
242 | // FIXME: kill this API | 258 | // FIXME: kill this API |
243 | /// Get access to the raw `TextEditBuilder`. | 259 | /// Get access to the raw `TextEditBuilder`. |
244 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | 260 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { |
245 | &mut self.edit | 261 | &mut self.edit |
246 | } | 262 | } |
247 | 263 | ||
248 | fn finish(self) -> SourceChange { | 264 | fn finish(mut self) -> SourceChange { |
249 | let edit = self.edit.finish(); | 265 | self.commit(); |
250 | let source_file_edit = SourceFileEdit { file_id: self.file, edit }; | 266 | let mut res: SourceChange = mem::take(&mut self.edits).into(); |
251 | let mut res: SourceChange = source_file_edit.into(); | ||
252 | if self.is_snippet { | 267 | if self.is_snippet { |
253 | res.is_snippet = true; | 268 | res.is_snippet = true; |
254 | } | 269 | } |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index ab20c6649..90b06a625 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -195,7 +195,7 @@ struct Test<K, T = u8> { | |||
195 | } | 195 | } |
196 | 196 | ||
197 | fn main() { | 197 | fn main() { |
198 | let test<|> = Test { t: 23, k: 33 }; | 198 | let test<|> = Test { t: 23u8, k: 33 }; |
199 | }"#, | 199 | }"#, |
200 | r#" | 200 | r#" |
201 | struct Test<K, T = u8> { | 201 | struct Test<K, T = u8> { |
@@ -204,7 +204,7 @@ struct Test<K, T = u8> { | |||
204 | } | 204 | } |
205 | 205 | ||
206 | fn main() { | 206 | fn main() { |
207 | let test: Test<i32> = Test { t: 23, k: 33 }; | 207 | let test: Test<i32> = Test { t: 23u8, k: 33 }; |
208 | }"#, | 208 | }"#, |
209 | ); | 209 | ); |
210 | } | 210 | } |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 24f931a85..1cfbd75aa 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -64,7 +64,7 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
64 | let target = call.syntax().text_range(); | 64 | let target = call.syntax().text_range(); |
65 | acc.add(AssistId("add_function"), "Add function", target, |builder| { | 65 | acc.add(AssistId("add_function"), "Add function", target, |builder| { |
66 | let function_template = function_builder.render(); | 66 | let function_template = function_builder.render(); |
67 | builder.set_file(function_template.file); | 67 | builder.edit_file(function_template.file); |
68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); | 68 | let new_fn = function_template.to_string(ctx.config.snippet_cap); |
69 | match ctx.config.snippet_cap { | 69 | match ctx.config.snippet_cap { |
70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), | 70 | Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index edf96d50e..5092bf336 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -130,7 +130,7 @@ impl AutoImportAssets { | |||
130 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | 130 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { |
131 | let _p = profile("auto_import::search_for_imports"); | 131 | let _p = profile("auto_import::search_for_imports"); |
132 | let current_crate = self.module_with_name_to_import.krate(); | 132 | let current_crate = self.module_with_name_to_import.krate(); |
133 | ImportsLocator::new(db) | 133 | ImportsLocator::new(db, current_crate) |
134 | .find_imports(&self.get_search_query()) | 134 | .find_imports(&self.get_search_query()) |
135 | .into_iter() | 135 | .into_iter() |
136 | .filter_map(|candidate| match &self.import_candidate { | 136 | .filter_map(|candidate| match &self.import_candidate { |
@@ -841,4 +841,105 @@ fn main() { | |||
841 | ", | 841 | ", |
842 | ) | 842 | ) |
843 | } | 843 | } |
844 | |||
845 | #[test] | ||
846 | fn dep_import() { | ||
847 | check_assist( | ||
848 | auto_import, | ||
849 | r" | ||
850 | //- /lib.rs crate:dep | ||
851 | pub struct Struct; | ||
852 | |||
853 | //- /main.rs crate:main deps:dep | ||
854 | fn main() { | ||
855 | Struct<|> | ||
856 | }", | ||
857 | r"use dep::Struct; | ||
858 | |||
859 | fn main() { | ||
860 | Struct | ||
861 | } | ||
862 | ", | ||
863 | ); | ||
864 | } | ||
865 | |||
866 | #[test] | ||
867 | fn whole_segment() { | ||
868 | // Tests that only imports whose last segment matches the identifier get suggested. | ||
869 | check_assist( | ||
870 | auto_import, | ||
871 | r" | ||
872 | //- /lib.rs crate:dep | ||
873 | pub mod fmt { | ||
874 | pub trait Display {} | ||
875 | } | ||
876 | |||
877 | pub fn panic_fmt() {} | ||
878 | |||
879 | //- /main.rs crate:main deps:dep | ||
880 | struct S; | ||
881 | |||
882 | impl f<|>mt::Display for S {}", | ||
883 | r"use dep::fmt; | ||
884 | |||
885 | struct S; | ||
886 | impl fmt::Display for S {} | ||
887 | ", | ||
888 | ); | ||
889 | } | ||
890 | |||
891 | #[test] | ||
892 | fn macro_generated() { | ||
893 | // Tests that macro-generated items are suggested from external crates. | ||
894 | check_assist( | ||
895 | auto_import, | ||
896 | r" | ||
897 | //- /lib.rs crate:dep | ||
898 | |||
899 | macro_rules! mac { | ||
900 | () => { | ||
901 | pub struct Cheese; | ||
902 | }; | ||
903 | } | ||
904 | |||
905 | mac!(); | ||
906 | |||
907 | //- /main.rs crate:main deps:dep | ||
908 | |||
909 | fn main() { | ||
910 | Cheese<|>; | ||
911 | }", | ||
912 | r"use dep::Cheese; | ||
913 | |||
914 | fn main() { | ||
915 | Cheese; | ||
916 | } | ||
917 | ", | ||
918 | ); | ||
919 | } | ||
920 | |||
921 | #[test] | ||
922 | fn casing() { | ||
923 | // Tests that differently cased names don't interfere and we only suggest the matching one. | ||
924 | check_assist( | ||
925 | auto_import, | ||
926 | r" | ||
927 | //- /lib.rs crate:dep | ||
928 | |||
929 | pub struct FMT; | ||
930 | pub struct fmt; | ||
931 | |||
932 | //- /main.rs crate:main deps:dep | ||
933 | |||
934 | fn main() { | ||
935 | FMT<|>; | ||
936 | }", | ||
937 | r"use dep::FMT; | ||
938 | |||
939 | fn main() { | ||
940 | FMT; | ||
941 | } | ||
942 | ", | ||
943 | ); | ||
944 | } | ||
844 | } | 945 | } |
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 4cc75a7ce..dfade7432 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -154,7 +154,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
154 | parent_block: &ast::BlockExpr, | 154 | parent_block: &ast::BlockExpr, |
155 | if_expr: &ast::IfExpr, | 155 | if_expr: &ast::IfExpr, |
156 | ) -> SyntaxNode { | 156 | ) -> SyntaxNode { |
157 | let then_block_items = then_block.dedent(IndentLevel::from(1)); | 157 | let then_block_items = then_block.dedent(IndentLevel(1)); |
158 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); | 158 | let end_of_then = then_block_items.syntax().last_child_or_token().unwrap(); |
159 | let end_of_then = | 159 | let end_of_then = |
160 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { | 160 | if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) { |
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs new file mode 100644 index 000000000..44db7917a --- /dev/null +++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -0,0 +1,320 @@ | |||
1 | use hir::{EnumVariant, Module, ModuleDef, Name}; | ||
2 | use ra_db::FileId; | ||
3 | use ra_fmt::leading_indent; | ||
4 | use ra_ide_db::{defs::Definition, search::Reference, RootDatabase}; | ||
5 | use ra_syntax::{ | ||
6 | algo::find_node_at_offset, | ||
7 | ast::{self, ArgListOwner, AstNode, NameOwner, VisibilityOwner}, | ||
8 | SourceFile, SyntaxNode, TextRange, TextSize, | ||
9 | }; | ||
10 | use rustc_hash::FxHashSet; | ||
11 | |||
12 | use crate::{ | ||
13 | assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists, | ||
14 | }; | ||
15 | |||
16 | // Assist: extract_struct_from_enum_variant | ||
17 | // | ||
18 | // Extracts a struct from enum variant. | ||
19 | // | ||
20 | // ``` | ||
21 | // enum A { <|>One(u32, u32) } | ||
22 | // ``` | ||
23 | // -> | ||
24 | // ``` | ||
25 | // struct One(pub u32, pub u32); | ||
26 | // | ||
27 | // enum A { One(One) } | ||
28 | // ``` | ||
29 | pub(crate) fn extract_struct_from_enum_variant( | ||
30 | acc: &mut Assists, | ||
31 | ctx: &AssistContext, | ||
32 | ) -> Option<()> { | ||
33 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | ||
34 | let field_list = match variant.kind() { | ||
35 | ast::StructKind::Tuple(field_list) => field_list, | ||
36 | _ => return None, | ||
37 | }; | ||
38 | let variant_name = variant.name()?.to_string(); | ||
39 | let variant_hir = ctx.sema.to_def(&variant)?; | ||
40 | if existing_struct_def(ctx.db, &variant_name, &variant_hir) { | ||
41 | return None; | ||
42 | } | ||
43 | let enum_ast = variant.parent_enum(); | ||
44 | let visibility = enum_ast.visibility(); | ||
45 | let enum_hir = ctx.sema.to_def(&enum_ast)?; | ||
46 | let variant_hir_name = variant_hir.name(ctx.db); | ||
47 | let enum_module_def = ModuleDef::from(enum_hir); | ||
48 | let current_module = enum_hir.module(ctx.db); | ||
49 | let target = variant.syntax().text_range(); | ||
50 | acc.add( | ||
51 | AssistId("extract_struct_from_enum_variant"), | ||
52 | "Extract struct from enum variant", | ||
53 | target, | ||
54 | |builder| { | ||
55 | let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); | ||
56 | let res = definition.find_usages(&ctx.db, None); | ||
57 | let start_offset = variant.parent_enum().syntax().text_range().start(); | ||
58 | let mut visited_modules_set = FxHashSet::default(); | ||
59 | visited_modules_set.insert(current_module); | ||
60 | for reference in res { | ||
61 | let source_file = ctx.sema.parse(reference.file_range.file_id); | ||
62 | update_reference( | ||
63 | ctx, | ||
64 | builder, | ||
65 | reference, | ||
66 | &source_file, | ||
67 | &enum_module_def, | ||
68 | &variant_hir_name, | ||
69 | &mut visited_modules_set, | ||
70 | ); | ||
71 | } | ||
72 | extract_struct_def( | ||
73 | builder, | ||
74 | enum_ast.syntax(), | ||
75 | &variant_name, | ||
76 | &field_list.to_string(), | ||
77 | start_offset, | ||
78 | ctx.frange.file_id, | ||
79 | &visibility, | ||
80 | ); | ||
81 | let list_range = field_list.syntax().text_range(); | ||
82 | update_variant(builder, &variant_name, ctx.frange.file_id, list_range); | ||
83 | }, | ||
84 | ) | ||
85 | } | ||
86 | |||
87 | fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool { | ||
88 | variant | ||
89 | .parent_enum(db) | ||
90 | .module(db) | ||
91 | .scope(db, None) | ||
92 | .into_iter() | ||
93 | .any(|(name, _)| name.to_string() == variant_name.to_string()) | ||
94 | } | ||
95 | |||
96 | fn insert_import( | ||
97 | ctx: &AssistContext, | ||
98 | builder: &mut AssistBuilder, | ||
99 | path: &ast::PathExpr, | ||
100 | module: &Module, | ||
101 | enum_module_def: &ModuleDef, | ||
102 | variant_hir_name: &Name, | ||
103 | ) -> Option<()> { | ||
104 | let db = ctx.db; | ||
105 | let mod_path = module.find_use_path(db, enum_module_def.clone()); | ||
106 | if let Some(mut mod_path) = mod_path { | ||
107 | mod_path.segments.pop(); | ||
108 | mod_path.segments.push(variant_hir_name.clone()); | ||
109 | insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder()); | ||
110 | } | ||
111 | Some(()) | ||
112 | } | ||
113 | |||
114 | fn extract_struct_def( | ||
115 | builder: &mut AssistBuilder, | ||
116 | enum_ast: &SyntaxNode, | ||
117 | variant_name: &str, | ||
118 | variant_list: &str, | ||
119 | start_offset: TextSize, | ||
120 | file_id: FileId, | ||
121 | visibility: &Option<ast::Visibility>, | ||
122 | ) -> Option<()> { | ||
123 | let visibility_string = if let Some(visibility) = visibility { | ||
124 | format!("{} ", visibility.to_string()) | ||
125 | } else { | ||
126 | "".to_string() | ||
127 | }; | ||
128 | let indent = if let Some(indent) = leading_indent(enum_ast) { | ||
129 | indent.to_string() | ||
130 | } else { | ||
131 | "".to_string() | ||
132 | }; | ||
133 | let struct_def = format!( | ||
134 | r#"{}struct {}{}; | ||
135 | |||
136 | {}"#, | ||
137 | visibility_string, | ||
138 | variant_name, | ||
139 | list_with_visibility(variant_list), | ||
140 | indent | ||
141 | ); | ||
142 | builder.edit_file(file_id); | ||
143 | builder.insert(start_offset, struct_def); | ||
144 | Some(()) | ||
145 | } | ||
146 | |||
147 | fn update_variant( | ||
148 | builder: &mut AssistBuilder, | ||
149 | variant_name: &str, | ||
150 | file_id: FileId, | ||
151 | list_range: TextRange, | ||
152 | ) -> Option<()> { | ||
153 | let inside_variant_range = TextRange::new( | ||
154 | list_range.start().checked_add(TextSize::from(1))?, | ||
155 | list_range.end().checked_sub(TextSize::from(1))?, | ||
156 | ); | ||
157 | builder.edit_file(file_id); | ||
158 | builder.replace(inside_variant_range, variant_name); | ||
159 | Some(()) | ||
160 | } | ||
161 | |||
162 | fn update_reference( | ||
163 | ctx: &AssistContext, | ||
164 | builder: &mut AssistBuilder, | ||
165 | reference: Reference, | ||
166 | source_file: &SourceFile, | ||
167 | enum_module_def: &ModuleDef, | ||
168 | variant_hir_name: &Name, | ||
169 | visited_modules_set: &mut FxHashSet<Module>, | ||
170 | ) -> Option<()> { | ||
171 | let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>( | ||
172 | source_file.syntax(), | ||
173 | reference.file_range.range.start(), | ||
174 | )?; | ||
175 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | ||
176 | let list = call.arg_list()?; | ||
177 | let segment = path_expr.path()?.segment()?; | ||
178 | let module = ctx.sema.scope(&path_expr.syntax()).module()?; | ||
179 | let list_range = list.syntax().text_range(); | ||
180 | let inside_list_range = TextRange::new( | ||
181 | list_range.start().checked_add(TextSize::from(1))?, | ||
182 | list_range.end().checked_sub(TextSize::from(1))?, | ||
183 | ); | ||
184 | builder.edit_file(reference.file_range.file_id); | ||
185 | if !visited_modules_set.contains(&module) { | ||
186 | if insert_import(ctx, builder, &path_expr, &module, enum_module_def, variant_hir_name) | ||
187 | .is_some() | ||
188 | { | ||
189 | visited_modules_set.insert(module); | ||
190 | } | ||
191 | } | ||
192 | builder.replace(inside_list_range, format!("{}{}", segment, list)); | ||
193 | Some(()) | ||
194 | } | ||
195 | |||
196 | fn list_with_visibility(list: &str) -> String { | ||
197 | list.split(',') | ||
198 | .map(|part| { | ||
199 | let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 }; | ||
200 | let mut mod_part = part.trim().to_string(); | ||
201 | mod_part.insert_str(index, "pub "); | ||
202 | mod_part | ||
203 | }) | ||
204 | .collect::<Vec<String>>() | ||
205 | .join(", ") | ||
206 | } | ||
207 | |||
208 | #[cfg(test)] | ||
209 | mod tests { | ||
210 | |||
211 | use crate::{ | ||
212 | tests::{check_assist, check_assist_not_applicable}, | ||
213 | utils::FamousDefs, | ||
214 | }; | ||
215 | |||
216 | use super::*; | ||
217 | |||
218 | #[test] | ||
219 | fn test_extract_struct_several_fields() { | ||
220 | check_assist( | ||
221 | extract_struct_from_enum_variant, | ||
222 | "enum A { <|>One(u32, u32) }", | ||
223 | r#"struct One(pub u32, pub u32); | ||
224 | |||
225 | enum A { One(One) }"#, | ||
226 | ); | ||
227 | } | ||
228 | |||
229 | #[test] | ||
230 | fn test_extract_struct_one_field() { | ||
231 | check_assist( | ||
232 | extract_struct_from_enum_variant, | ||
233 | "enum A { <|>One(u32) }", | ||
234 | r#"struct One(pub u32); | ||
235 | |||
236 | enum A { One(One) }"#, | ||
237 | ); | ||
238 | } | ||
239 | |||
240 | #[test] | ||
241 | fn test_extract_struct_pub_visibility() { | ||
242 | check_assist( | ||
243 | extract_struct_from_enum_variant, | ||
244 | "pub enum A { <|>One(u32, u32) }", | ||
245 | r#"pub struct One(pub u32, pub u32); | ||
246 | |||
247 | pub enum A { One(One) }"#, | ||
248 | ); | ||
249 | } | ||
250 | |||
251 | #[test] | ||
252 | fn test_extract_struct_with_complex_imports() { | ||
253 | check_assist( | ||
254 | extract_struct_from_enum_variant, | ||
255 | r#"mod my_mod { | ||
256 | fn another_fn() { | ||
257 | let m = my_other_mod::MyEnum::MyField(1, 1); | ||
258 | } | ||
259 | |||
260 | pub mod my_other_mod { | ||
261 | fn another_fn() { | ||
262 | let m = MyEnum::MyField(1, 1); | ||
263 | } | ||
264 | |||
265 | pub enum MyEnum { | ||
266 | <|>MyField(u8, u8), | ||
267 | } | ||
268 | } | ||
269 | } | ||
270 | |||
271 | fn another_fn() { | ||
272 | let m = my_mod::my_other_mod::MyEnum::MyField(1, 1); | ||
273 | }"#, | ||
274 | r#"use my_mod::my_other_mod::MyField; | ||
275 | |||
276 | mod my_mod { | ||
277 | use my_other_mod::MyField; | ||
278 | |||
279 | fn another_fn() { | ||
280 | let m = my_other_mod::MyEnum::MyField(MyField(1, 1)); | ||
281 | } | ||
282 | |||
283 | pub mod my_other_mod { | ||
284 | fn another_fn() { | ||
285 | let m = MyEnum::MyField(MyField(1, 1)); | ||
286 | } | ||
287 | |||
288 | pub struct MyField(pub u8, pub u8); | ||
289 | |||
290 | pub enum MyEnum { | ||
291 | MyField(MyField), | ||
292 | } | ||
293 | } | ||
294 | } | ||
295 | |||
296 | fn another_fn() { | ||
297 | let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1)); | ||
298 | }"#, | ||
299 | ); | ||
300 | } | ||
301 | |||
302 | fn check_not_applicable(ra_fixture: &str) { | ||
303 | let fixture = | ||
304 | format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); | ||
305 | check_assist_not_applicable(extract_struct_from_enum_variant, &fixture) | ||
306 | } | ||
307 | |||
308 | #[test] | ||
309 | fn test_extract_enum_not_applicable_for_element_with_no_fields() { | ||
310 | check_not_applicable("enum A { <|>One }"); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn test_extract_enum_not_applicable_if_struct_exists() { | ||
315 | check_not_applicable( | ||
316 | r#"struct One; | ||
317 | enum A { <|>One(u8) }"#, | ||
318 | ); | ||
319 | } | ||
320 | } | ||
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 9ec42f568..531b3560f 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs | |||
@@ -63,7 +63,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O | |||
63 | }; | 63 | }; |
64 | 64 | ||
65 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | 65 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { |
66 | builder.set_file(target_file); | 66 | builder.edit_file(target_file); |
67 | match ctx.config.snippet_cap { | 67 | match ctx.config.snippet_cap { |
68 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | 68 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), |
69 | None => builder.insert(offset, format!("{} ", missing_visibility)), | 69 | None => builder.insert(offset, format!("{} ", missing_visibility)), |
@@ -106,7 +106,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> | |||
106 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); | 106 | format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); |
107 | 107 | ||
108 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { | 108 | acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { |
109 | builder.set_file(target_file); | 109 | builder.edit_file(target_file); |
110 | match ctx.config.snippet_cap { | 110 | match ctx.config.snippet_cap { |
111 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), | 111 | Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), |
112 | None => builder.insert(offset, format!("{} ", missing_visibility)), | 112 | None => builder.insert(offset, format!("{} ", missing_visibility)), |
diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs index beb5b7366..28fcbc9ba 100644 --- a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs | |||
@@ -41,8 +41,6 @@ pub(crate) fn introduce_named_lifetime(acc: &mut Assists, ctx: &AssistContext) - | |||
41 | if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { | 41 | if let Some(fn_def) = lifetime_token.ancestors().find_map(ast::FnDef::cast) { |
42 | generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) | 42 | generate_fn_def_assist(acc, &fn_def, lifetime_token.text_range()) |
43 | } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { | 43 | } else if let Some(impl_def) = lifetime_token.ancestors().find_map(ast::ImplDef::cast) { |
44 | // only allow naming the last anonymous lifetime | ||
45 | lifetime_token.next_token().filter(|tok| tok.kind() == SyntaxKind::R_ANGLE)?; | ||
46 | generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) | 44 | generate_impl_def_assist(acc, &impl_def, lifetime_token.text_range()) |
47 | } else { | 45 | } else { |
48 | None | 46 | None |
@@ -191,6 +189,23 @@ mod tests { | |||
191 | } | 189 | } |
192 | 190 | ||
193 | #[test] | 191 | #[test] |
192 | fn test_impl_with_other_type_param() { | ||
193 | check_assist( | ||
194 | introduce_named_lifetime, | ||
195 | "impl<I> fmt::Display for SepByBuilder<'_<|>, I> | ||
196 | where | ||
197 | I: Iterator, | ||
198 | I::Item: fmt::Display, | ||
199 | {", | ||
200 | "impl<I, 'a> fmt::Display for SepByBuilder<'a, I> | ||
201 | where | ||
202 | I: Iterator, | ||
203 | I::Item: fmt::Display, | ||
204 | {", | ||
205 | ) | ||
206 | } | ||
207 | |||
208 | #[test] | ||
194 | fn test_example_case_cursor_before_tick() { | 209 | fn test_example_case_cursor_before_tick() { |
195 | check_assist( | 210 | check_assist( |
196 | introduce_named_lifetime, | 211 | introduce_named_lifetime, |
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs index e016f51c3..dfcd787de 100644 --- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -51,6 +51,7 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
51 | acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { | 51 | acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { |
52 | let match_expr = { | 52 | let match_expr = { |
53 | let then_arm = { | 53 | let then_arm = { |
54 | let then_block = then_block.reset_indent().indent(IndentLevel(1)); | ||
54 | let then_expr = unwrap_trivial_block(then_block); | 55 | let then_expr = unwrap_trivial_block(then_block); |
55 | make::match_arm(vec![pat.clone()], then_expr) | 56 | make::match_arm(vec![pat.clone()], then_expr) |
56 | }; | 57 | }; |
@@ -64,8 +65,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
64 | let else_expr = unwrap_trivial_block(else_block); | 65 | let else_expr = unwrap_trivial_block(else_block); |
65 | make::match_arm(vec![pattern], else_expr) | 66 | make::match_arm(vec![pattern], else_expr) |
66 | }; | 67 | }; |
67 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | 68 | let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])); |
68 | .indent(IndentLevel::from_node(if_expr.syntax())) | 69 | match_expr.indent(IndentLevel::from_node(if_expr.syntax())) |
69 | }; | 70 | }; |
70 | 71 | ||
71 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); | 72 | edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); |
@@ -213,4 +214,36 @@ fn foo(x: Result<i32, ()>) { | |||
213 | "#, | 214 | "#, |
214 | ); | 215 | ); |
215 | } | 216 | } |
217 | |||
218 | #[test] | ||
219 | fn nested_indent() { | ||
220 | check_assist( | ||
221 | replace_if_let_with_match, | ||
222 | r#" | ||
223 | fn main() { | ||
224 | if true { | ||
225 | <|>if let Ok(rel_path) = path.strip_prefix(root_path) { | ||
226 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
227 | Some((*id, rel_path)) | ||
228 | } else { | ||
229 | None | ||
230 | } | ||
231 | } | ||
232 | } | ||
233 | "#, | ||
234 | r#" | ||
235 | fn main() { | ||
236 | if true { | ||
237 | match path.strip_prefix(root_path) { | ||
238 | Ok(rel_path) => { | ||
239 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
240 | Some((*id, rel_path)) | ||
241 | } | ||
242 | _ => None, | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | "#, | ||
247 | ) | ||
248 | } | ||
216 | } | 249 | } |
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 8440c7d0f..1fb13f481 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | use ra_fmt::unwrap_trivial_block; | 1 | use ra_fmt::unwrap_trivial_block; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, ElseBranch, Expr, LoopBodyOwner}, | 3 | ast::{ |
4 | match_ast, AstNode, TextRange, T, | 4 | self, |
5 | edit::{AstNodeEdit, IndentLevel}, | ||
6 | }, | ||
7 | AstNode, TextRange, T, | ||
5 | }; | 8 | }; |
6 | 9 | ||
7 | use crate::{AssistContext, AssistId, Assists}; | 10 | use crate::{AssistContext, AssistId, Assists}; |
@@ -24,94 +27,73 @@ use crate::{AssistContext, AssistId, Assists}; | |||
24 | // } | 27 | // } |
25 | // ``` | 28 | // ``` |
26 | pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 29 | pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
27 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | ||
28 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | ||
29 | let parent = block.syntax().parent()?; | ||
30 | let assist_id = AssistId("unwrap_block"); | 30 | let assist_id = AssistId("unwrap_block"); |
31 | let assist_label = "Unwrap block"; | 31 | let assist_label = "Unwrap block"; |
32 | 32 | ||
33 | let (expr, expr_to_unwrap) = match_ast! { | 33 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
34 | match parent { | 34 | let mut block = ast::BlockExpr::cast(l_curly_token.parent())?; |
35 | ast::ForExpr(for_expr) => { | 35 | let mut parent = block.syntax().parent()?; |
36 | let block_expr = for_expr.loop_body()?; | 36 | if ast::MatchArm::can_cast(parent.kind()) { |
37 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | 37 | parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))? |
38 | (ast::Expr::ForExpr(for_expr), expr_to_unwrap) | 38 | } |
39 | }, | ||
40 | ast::WhileExpr(while_expr) => { | ||
41 | let block_expr = while_expr.loop_body()?; | ||
42 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | ||
43 | (ast::Expr::WhileExpr(while_expr), expr_to_unwrap) | ||
44 | }, | ||
45 | ast::LoopExpr(loop_expr) => { | ||
46 | let block_expr = loop_expr.loop_body()?; | ||
47 | let expr_to_unwrap = extract_expr(ctx.frange.range, block_expr)?; | ||
48 | (ast::Expr::LoopExpr(loop_expr), expr_to_unwrap) | ||
49 | }, | ||
50 | ast::IfExpr(if_expr) => { | ||
51 | let mut resp = None; | ||
52 | |||
53 | let then_branch = if_expr.then_branch()?; | ||
54 | if then_branch.l_curly_token()?.text_range().contains_range(ctx.frange.range) { | ||
55 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
56 | // For `else if` blocks | ||
57 | let ancestor_then_branch = ancestor.then_branch()?; | ||
58 | let l_curly_token = then_branch.l_curly_token()?; | ||
59 | |||
60 | let target = then_branch.syntax().text_range(); | ||
61 | return acc.add(assist_id, assist_label, target, |edit| { | ||
62 | let range_to_del_else_if = TextRange::new(ancestor_then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
63 | let range_to_del_rest = TextRange::new(then_branch.syntax().text_range().end(), if_expr.syntax().text_range().end()); | ||
64 | |||
65 | edit.delete(range_to_del_rest); | ||
66 | edit.delete(range_to_del_else_if); | ||
67 | edit.replace(target, update_expr_string(then_branch.to_string(), &[' ', '{'])); | ||
68 | }); | ||
69 | } else { | ||
70 | resp = Some((ast::Expr::IfExpr(if_expr.clone()), Expr::BlockExpr(then_branch))); | ||
71 | } | ||
72 | } else if let Some(else_branch) = if_expr.else_branch() { | ||
73 | match else_branch { | ||
74 | ElseBranch::Block(else_block) => { | ||
75 | let l_curly_token = else_block.l_curly_token()?; | ||
76 | if l_curly_token.text_range().contains_range(ctx.frange.range) { | ||
77 | let target = else_block.syntax().text_range(); | ||
78 | return acc.add(assist_id, assist_label, target, |edit| { | ||
79 | let range_to_del = TextRange::new(then_branch.syntax().text_range().end(), l_curly_token.text_range().start()); | ||
80 | |||
81 | edit.delete(range_to_del); | ||
82 | edit.replace(target, update_expr_string(else_block.to_string(), &[' ', '{'])); | ||
83 | }); | ||
84 | } | ||
85 | }, | ||
86 | ElseBranch::IfExpr(_) => {}, | ||
87 | } | ||
88 | } | ||
89 | 39 | ||
90 | resp? | 40 | let parent = ast::Expr::cast(parent)?; |
91 | }, | 41 | |
92 | _ => return None, | 42 | match parent.clone() { |
43 | ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (), | ||
44 | ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)), | ||
45 | ast::Expr::IfExpr(if_expr) => { | ||
46 | let then_branch = if_expr.then_branch()?; | ||
47 | if then_branch == block { | ||
48 | if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) { | ||
49 | // For `else if` blocks | ||
50 | let ancestor_then_branch = ancestor.then_branch()?; | ||
51 | |||
52 | let target = then_branch.syntax().text_range(); | ||
53 | return acc.add(assist_id, assist_label, target, |edit| { | ||
54 | let range_to_del_else_if = TextRange::new( | ||
55 | ancestor_then_branch.syntax().text_range().end(), | ||
56 | l_curly_token.text_range().start(), | ||
57 | ); | ||
58 | let range_to_del_rest = TextRange::new( | ||
59 | then_branch.syntax().text_range().end(), | ||
60 | if_expr.syntax().text_range().end(), | ||
61 | ); | ||
62 | |||
63 | edit.delete(range_to_del_rest); | ||
64 | edit.delete(range_to_del_else_if); | ||
65 | edit.replace( | ||
66 | target, | ||
67 | update_expr_string(then_branch.to_string(), &[' ', '{']), | ||
68 | ); | ||
69 | }); | ||
70 | } | ||
71 | } else { | ||
72 | let target = block.syntax().text_range(); | ||
73 | return acc.add(assist_id, assist_label, target, |edit| { | ||
74 | let range_to_del = TextRange::new( | ||
75 | then_branch.syntax().text_range().end(), | ||
76 | l_curly_token.text_range().start(), | ||
77 | ); | ||
78 | |||
79 | edit.delete(range_to_del); | ||
80 | edit.replace(target, update_expr_string(block.to_string(), &[' ', '{'])); | ||
81 | }); | ||
82 | } | ||
93 | } | 83 | } |
84 | _ => return None, | ||
94 | }; | 85 | }; |
95 | 86 | ||
96 | let target = expr_to_unwrap.syntax().text_range(); | 87 | let unwrapped = unwrap_trivial_block(block); |
97 | acc.add(assist_id, assist_label, target, |edit| { | 88 | let target = unwrapped.syntax().text_range(); |
98 | edit.replace( | 89 | acc.add(assist_id, assist_label, target, |builder| { |
99 | expr.syntax().text_range(), | 90 | builder.replace( |
100 | update_expr_string(expr_to_unwrap.to_string(), &[' ', '{', '\n']), | 91 | parent.syntax().text_range(), |
92 | update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']), | ||
101 | ); | 93 | ); |
102 | }) | 94 | }) |
103 | } | 95 | } |
104 | 96 | ||
105 | fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::Expr> { | ||
106 | let cursor_in_range = block.l_curly_token()?.text_range().contains_range(cursor_range); | ||
107 | |||
108 | if cursor_in_range { | ||
109 | Some(unwrap_trivial_block(block)) | ||
110 | } else { | ||
111 | None | ||
112 | } | ||
113 | } | ||
114 | |||
115 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { | 97 | fn update_expr_string(expr_str: String, trim_start_pat: &[char]) -> String { |
116 | let expr_string = expr_str.trim_start_matches(trim_start_pat); | 98 | let expr_string = expr_str.trim_start_matches(trim_start_pat); |
117 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); | 99 | let mut expr_string_lines: Vec<&str> = expr_string.lines().collect(); |
@@ -490,6 +472,30 @@ mod tests { | |||
490 | } | 472 | } |
491 | 473 | ||
492 | #[test] | 474 | #[test] |
475 | fn unwrap_match_arm() { | ||
476 | check_assist( | ||
477 | unwrap_block, | ||
478 | r#" | ||
479 | fn main() { | ||
480 | match rel_path { | ||
481 | Ok(rel_path) => {<|> | ||
482 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
483 | Some((*id, rel_path)) | ||
484 | } | ||
485 | Err(_) => None, | ||
486 | } | ||
487 | } | ||
488 | "#, | ||
489 | r#" | ||
490 | fn main() { | ||
491 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
492 | Some((*id, rel_path)) | ||
493 | } | ||
494 | "#, | ||
495 | ); | ||
496 | } | ||
497 | |||
498 | #[test] | ||
493 | fn simple_if_in_while_bad_cursor_position() { | 499 | fn simple_if_in_while_bad_cursor_position() { |
494 | check_assist_not_applicable( | 500 | check_assist_not_applicable( |
495 | unwrap_block, | 501 | unwrap_block, |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index fb5d59a87..185428bd5 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -115,6 +115,7 @@ mod handlers { | |||
115 | mod change_return_type_to_result; | 115 | mod change_return_type_to_result; |
116 | mod change_visibility; | 116 | mod change_visibility; |
117 | mod early_return; | 117 | mod early_return; |
118 | mod extract_struct_from_enum_variant; | ||
118 | mod fill_match_arms; | 119 | mod fill_match_arms; |
119 | mod fix_visibility; | 120 | mod fix_visibility; |
120 | mod flip_binexpr; | 121 | mod flip_binexpr; |
@@ -155,6 +156,7 @@ mod handlers { | |||
155 | change_return_type_to_result::change_return_type_to_result, | 156 | change_return_type_to_result::change_return_type_to_result, |
156 | change_visibility::change_visibility, | 157 | change_visibility::change_visibility, |
157 | early_return::convert_to_guarded_return, | 158 | early_return::convert_to_guarded_return, |
159 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | ||
158 | fill_match_arms::fill_match_arms, | 160 | fill_match_arms::fill_match_arms, |
159 | fix_visibility::fix_visibility, | 161 | fix_visibility::fix_visibility, |
160 | flip_binexpr::flip_binexpr, | 162 | flip_binexpr::flip_binexpr, |
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs index d17504529..40a223727 100644 --- a/crates/ra_assists/src/tests/generated.rs +++ b/crates/ra_assists/src/tests/generated.rs | |||
@@ -338,6 +338,21 @@ fn main() { | |||
338 | } | 338 | } |
339 | 339 | ||
340 | #[test] | 340 | #[test] |
341 | fn doctest_extract_struct_from_enum_variant() { | ||
342 | check_doc_test( | ||
343 | "extract_struct_from_enum_variant", | ||
344 | r#####" | ||
345 | enum A { <|>One(u32, u32) } | ||
346 | "#####, | ||
347 | r#####" | ||
348 | struct One(pub u32, pub u32); | ||
349 | |||
350 | enum A { One(One) } | ||
351 | "#####, | ||
352 | ) | ||
353 | } | ||
354 | |||
355 | #[test] | ||
341 | fn doctest_fill_match_arms() { | 356 | fn doctest_fill_match_arms() { |
342 | check_doc_test( | 357 | check_doc_test( |
343 | "fill_match_arms", | 358 | "fill_match_arms", |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 4d2d3b48a..bf26048f2 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -15,12 +15,10 @@ use std::{ | |||
15 | 15 | ||
16 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
17 | use ra_syntax::SmolStr; | 17 | use ra_syntax::SmolStr; |
18 | use rustc_hash::FxHashMap; | 18 | use ra_tt::TokenExpander; |
19 | use rustc_hash::FxHashSet; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | 20 | ||
21 | use crate::{RelativePath, RelativePathBuf}; | 21 | use crate::{RelativePath, RelativePathBuf}; |
22 | use fmt::Display; | ||
23 | use ra_tt::TokenExpander; | ||
24 | 22 | ||
25 | /// `FileId` is an integer which uniquely identifies a file. File paths are | 23 | /// `FileId` is an integer which uniquely identifies a file. File paths are |
26 | /// messy and system-dependent, so most of the code should work directly with | 24 | /// messy and system-dependent, so most of the code should work directly with |
@@ -111,7 +109,7 @@ impl CrateName { | |||
111 | } | 109 | } |
112 | } | 110 | } |
113 | 111 | ||
114 | impl Display for CrateName { | 112 | impl fmt::Display for CrateName { |
115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | 113 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
116 | write!(f, "{}", self.0) | 114 | write!(f, "{}", self.0) |
117 | } | 115 | } |
@@ -337,15 +335,11 @@ impl Env { | |||
337 | } | 335 | } |
338 | 336 | ||
339 | impl ExternSource { | 337 | impl ExternSource { |
340 | pub fn extern_path(&self, path: impl AsRef<Path>) -> Option<(ExternSourceId, RelativePathBuf)> { | 338 | pub fn extern_path(&self, path: &Path) -> Option<(ExternSourceId, RelativePathBuf)> { |
341 | let path = path.as_ref(); | ||
342 | self.extern_paths.iter().find_map(|(root_path, id)| { | 339 | self.extern_paths.iter().find_map(|(root_path, id)| { |
343 | if let Ok(rel_path) = path.strip_prefix(root_path) { | 340 | let rel_path = path.strip_prefix(root_path).ok()?; |
344 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | 341 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; |
345 | Some((*id, rel_path)) | 342 | Some((*id, rel_path)) |
346 | } else { | ||
347 | None | ||
348 | } | ||
349 | }) | 343 | }) |
350 | } | 344 | } |
351 | 345 | ||
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index fd4280de2..80ddb6058 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -7,12 +7,13 @@ use std::{panic, sync::Arc}; | |||
7 | 7 | ||
8 | use ra_prof::profile; | 8 | use ra_prof::profile; |
9 | use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | 9 | use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; |
10 | use rustc_hash::FxHashSet; | ||
10 | 11 | ||
11 | pub use crate::{ | 12 | pub use crate::{ |
12 | cancellation::Canceled, | 13 | cancellation::Canceled, |
13 | input::{ | 14 | input::{ |
14 | CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, | 15 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, |
15 | FileId, ProcMacroId, SourceRoot, SourceRootId, | 16 | ExternSourceId, FileId, ProcMacroId, SourceRoot, SourceRootId, |
16 | }, | 17 | }, |
17 | }; | 18 | }; |
18 | pub use relative_path::{RelativePath, RelativePathBuf}; | 19 | pub use relative_path::{RelativePath, RelativePathBuf}; |
@@ -89,15 +90,13 @@ pub const DEFAULT_LRU_CAP: usize = 128; | |||
89 | pub trait FileLoader { | 90 | pub trait FileLoader { |
90 | /// Text of the file. | 91 | /// Text of the file. |
91 | fn file_text(&self, file_id: FileId) -> Arc<String>; | 92 | fn file_text(&self, file_id: FileId) -> Arc<String>; |
92 | fn resolve_relative_path(&self, anchor: FileId, relative_path: &RelativePath) | 93 | /// Note that we intentionally accept a `&str` and not a `&Path` here. This |
93 | -> Option<FileId>; | 94 | /// method exists to handle `#[path = "/some/path.rs"] mod foo;` and such, |
94 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; | 95 | /// so the input is guaranteed to be utf-8 string. We might introduce |
95 | 96 | /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we | |
96 | fn resolve_extern_path( | 97 | /// get by with a `&str` for the time being. |
97 | &self, | 98 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; |
98 | extern_id: ExternSourceId, | 99 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>; |
99 | relative_path: &RelativePath, | ||
100 | ) -> Option<FileId>; | ||
101 | } | 100 | } |
102 | 101 | ||
103 | /// Database which stores all significant input facts: source code and project | 102 | /// Database which stores all significant input facts: source code and project |
@@ -135,16 +134,21 @@ pub trait SourceDatabaseExt: SourceDatabase { | |||
135 | #[salsa::input] | 134 | #[salsa::input] |
136 | fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; | 135 | fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; |
137 | 136 | ||
138 | fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; | 137 | fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>; |
139 | } | 138 | } |
140 | 139 | ||
141 | fn source_root_crates( | 140 | fn source_root_crates( |
142 | db: &(impl SourceDatabaseExt + SourceDatabase), | 141 | db: &(impl SourceDatabaseExt + SourceDatabase), |
143 | id: SourceRootId, | 142 | id: SourceRootId, |
144 | ) -> Arc<Vec<CrateId>> { | 143 | ) -> Arc<FxHashSet<CrateId>> { |
145 | let root = db.source_root(id); | ||
146 | let graph = db.crate_graph(); | 144 | let graph = db.crate_graph(); |
147 | let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); | 145 | let res = graph |
146 | .iter() | ||
147 | .filter(|&krate| { | ||
148 | let root_file = graph[krate].root_file_id; | ||
149 | db.file_source_root(root_file) == id | ||
150 | }) | ||
151 | .collect::<FxHashSet<_>>(); | ||
148 | Arc::new(res) | 152 | Arc::new(res) |
149 | } | 153 | } |
150 | 154 | ||
@@ -155,33 +159,30 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> { | |||
155 | fn file_text(&self, file_id: FileId) -> Arc<String> { | 159 | fn file_text(&self, file_id: FileId) -> Arc<String> { |
156 | SourceDatabaseExt::file_text(self.0, file_id) | 160 | SourceDatabaseExt::file_text(self.0, file_id) |
157 | } | 161 | } |
158 | fn resolve_relative_path( | 162 | fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { |
159 | &self, | 163 | // FIXME: this *somehow* should be platform agnostic... |
160 | anchor: FileId, | 164 | if std::path::Path::new(path).is_absolute() { |
161 | relative_path: &RelativePath, | 165 | let krate = *self.relevant_crates(anchor).iter().next()?; |
162 | ) -> Option<FileId> { | 166 | let (extern_source_id, relative_file) = |
163 | let path = { | 167 | self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?; |
164 | let mut path = self.0.file_relative_path(anchor); | 168 | |
165 | assert!(path.pop()); | 169 | let source_root = self.0.source_root(SourceRootId(extern_source_id.0)); |
166 | path.push(relative_path); | 170 | source_root.file_by_relative_path(&relative_file) |
167 | path.normalize() | 171 | } else { |
168 | }; | 172 | let rel_path = { |
169 | let source_root = self.0.file_source_root(anchor); | 173 | let mut rel_path = self.0.file_relative_path(anchor); |
170 | let source_root = self.0.source_root(source_root); | 174 | assert!(rel_path.pop()); |
171 | source_root.file_by_relative_path(&path) | 175 | rel_path.push(path); |
176 | rel_path.normalize() | ||
177 | }; | ||
178 | let source_root = self.0.file_source_root(anchor); | ||
179 | let source_root = self.0.source_root(source_root); | ||
180 | source_root.file_by_relative_path(&rel_path) | ||
181 | } | ||
172 | } | 182 | } |
173 | 183 | ||
174 | fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { | 184 | fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> { |
175 | let source_root = self.0.file_source_root(file_id); | 185 | let source_root = self.0.file_source_root(file_id); |
176 | self.0.source_root_crates(source_root) | 186 | self.0.source_root_crates(source_root) |
177 | } | 187 | } |
178 | |||
179 | fn resolve_extern_path( | ||
180 | &self, | ||
181 | extern_id: ExternSourceId, | ||
182 | relative_path: &RelativePath, | ||
183 | ) -> Option<FileId> { | ||
184 | let source_root = self.0.source_root(SourceRootId(extern_id.0)); | ||
185 | source_root.file_by_relative_path(&relative_path) | ||
186 | } | ||
187 | } | 188 | } |
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 041e38a9f..6c4170529 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -18,8 +18,17 @@ pub use cargo_metadata::diagnostic::{ | |||
18 | 18 | ||
19 | #[derive(Clone, Debug, PartialEq, Eq)] | 19 | #[derive(Clone, Debug, PartialEq, Eq)] |
20 | pub enum FlycheckConfig { | 20 | pub enum FlycheckConfig { |
21 | CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec<String> }, | 21 | CargoCommand { |
22 | CustomCommand { command: String, args: Vec<String> }, | 22 | command: String, |
23 | all_targets: bool, | ||
24 | all_features: bool, | ||
25 | features: Vec<String>, | ||
26 | extra_args: Vec<String>, | ||
27 | }, | ||
28 | CustomCommand { | ||
29 | command: String, | ||
30 | args: Vec<String>, | ||
31 | }, | ||
23 | } | 32 | } |
24 | 33 | ||
25 | /// Flycheck wraps the shared state and communication machinery used for | 34 | /// Flycheck wraps the shared state and communication machinery used for |
@@ -188,7 +197,13 @@ impl FlycheckThread { | |||
188 | self.check_process = None; | 197 | self.check_process = None; |
189 | 198 | ||
190 | let mut cmd = match &self.config { | 199 | let mut cmd = match &self.config { |
191 | FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { | 200 | FlycheckConfig::CargoCommand { |
201 | command, | ||
202 | all_targets, | ||
203 | all_features, | ||
204 | extra_args, | ||
205 | features, | ||
206 | } => { | ||
192 | let mut cmd = Command::new(ra_toolchain::cargo()); | 207 | let mut cmd = Command::new(ra_toolchain::cargo()); |
193 | cmd.arg(command); | 208 | cmd.arg(command); |
194 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) | 209 | cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) |
@@ -198,6 +213,9 @@ impl FlycheckThread { | |||
198 | } | 213 | } |
199 | if *all_features { | 214 | if *all_features { |
200 | cmd.arg("--all-features"); | 215 | cmd.arg("--all-features"); |
216 | } else if !features.is_empty() { | ||
217 | cmd.arg("--features"); | ||
218 | cmd.arg(features.join(" ")); | ||
201 | } | 219 | } |
202 | cmd.args(extra_args); | 220 | cmd.args(extra_args); |
203 | cmd | 221 | cmd |
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index e40aeffbc..1a9f6cc76 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -9,6 +9,7 @@ use hir_def::{ | |||
9 | builtin_type::BuiltinType, | 9 | builtin_type::BuiltinType, |
10 | docs::Documentation, | 10 | docs::Documentation, |
11 | expr::{BindingAnnotation, Pat, PatId}, | 11 | expr::{BindingAnnotation, Pat, PatId}, |
12 | import_map, | ||
12 | per_ns::PerNs, | 13 | per_ns::PerNs, |
13 | resolver::{HasResolver, Resolver}, | 14 | resolver::{HasResolver, Resolver}, |
14 | type_ref::{Mutability, TypeRef}, | 15 | type_ref::{Mutability, TypeRef}, |
@@ -98,6 +99,23 @@ impl Crate { | |||
98 | db.crate_graph()[self.id].display_name.as_ref().cloned() | 99 | db.crate_graph()[self.id].display_name.as_ref().cloned() |
99 | } | 100 | } |
100 | 101 | ||
102 | pub fn query_external_importables( | ||
103 | self, | ||
104 | db: &dyn DefDatabase, | ||
105 | query: &str, | ||
106 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
107 | import_map::search_dependencies( | ||
108 | db, | ||
109 | self.into(), | ||
110 | import_map::Query::new(query).anchor_end().case_sensitive().limit(40), | ||
111 | ) | ||
112 | .into_iter() | ||
113 | .map(|item| match item { | ||
114 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | ||
115 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | ||
116 | }) | ||
117 | } | ||
118 | |||
101 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | 119 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { |
102 | db.crate_graph().iter().map(|id| Crate { id }).collect() | 120 | db.crate_graph().iter().map(|id| Crate { id }).collect() |
103 | } | 121 | } |
@@ -637,6 +655,10 @@ impl Function { | |||
637 | db.function_data(self.id).params.clone() | 655 | db.function_data(self.id).params.clone() |
638 | } | 656 | } |
639 | 657 | ||
658 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
659 | db.function_data(self.id).is_unsafe | ||
660 | } | ||
661 | |||
640 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | 662 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { |
641 | let _p = profile("Function::diagnostics"); | 663 | let _p = profile("Function::diagnostics"); |
642 | let infer = db.infer(self.id.into()); | 664 | let infer = db.infer(self.id.into()); |
@@ -1190,6 +1212,10 @@ impl Type { | |||
1190 | ) | 1212 | ) |
1191 | } | 1213 | } |
1192 | 1214 | ||
1215 | pub fn is_raw_ptr(&self) -> bool { | ||
1216 | matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) | ||
1217 | } | ||
1218 | |||
1193 | pub fn contains_unknown(&self) -> bool { | 1219 | pub fn contains_unknown(&self) -> bool { |
1194 | return go(&self.ty.value); | 1220 | return go(&self.ty.value); |
1195 | 1221 | ||
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index ec931b34f..b6b665de1 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -3,11 +3,11 @@ | |||
3 | pub use hir_def::db::{ | 3 | pub use hir_def::db::{ |
4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, | 4 | AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ConstDataQuery, CrateDefMapQueryQuery, |
5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, | 5 | CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, |
6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery, | 6 | ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, ImportMapQuery, |
7 | InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery, | 7 | InternConstQuery, InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, |
8 | InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery, | 8 | InternImplQuery, InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, |
9 | LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery, | 9 | InternUnionQuery, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, |
10 | TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, | 10 | StructDataQuery, TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, |
11 | }; | 11 | }; |
12 | pub use hir_expand::db::{ | 12 | pub use hir_expand::db::{ |
13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, | 13 | AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery, |
@@ -18,8 +18,8 @@ pub use hir_ty::db::{ | |||
18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, | 18 | GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, |
19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, | 19 | HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, |
20 | ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, | 20 | ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, |
21 | InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery, | 21 | InternTypeCtorQuery, InternTypeParamIdQuery, ReturnTypeImplTraitsQuery, StructDatumQuery, |
22 | TraitSolveQuery, TyQuery, ValueTyQuery, | 22 | TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, |
23 | }; | 23 | }; |
24 | 24 | ||
25 | #[test] | 25 | #[test] |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 7c1f79f27..a232a5856 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -122,8 +122,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
122 | let macro_call = | 122 | let macro_call = |
123 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); | 123 | self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); |
124 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); | 124 | let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); |
125 | let krate = sa.resolver.krate()?; | ||
125 | let macro_call_id = macro_call | 126 | let macro_call_id = macro_call |
126 | .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; | 127 | .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; |
127 | hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) | 128 | hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) |
128 | } | 129 | } |
129 | 130 | ||
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index 4b509f07c..7c6bbea13 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs | |||
@@ -307,7 +307,8 @@ impl SourceAnalyzer { | |||
307 | db: &dyn HirDatabase, | 307 | db: &dyn HirDatabase, |
308 | macro_call: InFile<&ast::MacroCall>, | 308 | macro_call: InFile<&ast::MacroCall>, |
309 | ) -> Option<HirFileId> { | 309 | ) -> Option<HirFileId> { |
310 | let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { | 310 | let krate = self.resolver.krate()?; |
311 | let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| { | ||
311 | self.resolver.resolve_path_as_macro(db.upcast(), &path) | 312 | self.resolver.resolve_path_as_macro(db.upcast(), &path) |
312 | })?; | 313 | })?; |
313 | Some(macro_call_id.as_file()) | 314 | Some(macro_call_id.as_file()) |
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index b85358308..ef1f65ee0 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml | |||
@@ -14,6 +14,9 @@ rustc-hash = "1.1.0" | |||
14 | either = "1.5.3" | 14 | either = "1.5.3" |
15 | anymap = "0.12.1" | 15 | anymap = "0.12.1" |
16 | drop_bomb = "0.1.4" | 16 | drop_bomb = "0.1.4" |
17 | fst = { version = "0.4", default-features = false } | ||
18 | itertools = "0.9.0" | ||
19 | indexmap = "1.4.0" | ||
17 | 20 | ||
18 | stdx = { path = "../stdx" } | 21 | stdx = { path = "../stdx" } |
19 | 22 | ||
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs index 8b6c0bede..2eeba0572 100644 --- a/crates/ra_hir_def/src/attr.rs +++ b/crates/ra_hir_def/src/attr.rs | |||
@@ -87,12 +87,18 @@ impl Attrs { | |||
87 | } | 87 | } |
88 | 88 | ||
89 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { | 89 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { |
90 | let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map( | ||
91 | |docs_text| Attr { | ||
92 | input: Some(AttrInput::Literal(SmolStr::new(docs_text))), | ||
93 | path: ModPath::from(hir_expand::name!(doc)), | ||
94 | }, | ||
95 | ); | ||
90 | let mut attrs = owner.attrs().peekable(); | 96 | let mut attrs = owner.attrs().peekable(); |
91 | let entries = if attrs.peek().is_none() { | 97 | let entries = if attrs.peek().is_none() { |
92 | // Avoid heap allocation | 98 | // Avoid heap allocation |
93 | None | 99 | None |
94 | } else { | 100 | } else { |
95 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) | 101 | Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect()) |
96 | }; | 102 | }; |
97 | Attrs { entries } | 103 | Attrs { entries } |
98 | } | 104 | } |
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 273036cee..4f2350915 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs | |||
@@ -97,7 +97,7 @@ impl Expander { | |||
97 | 97 | ||
98 | let macro_call = InFile::new(self.current_file_id, ¯o_call); | 98 | let macro_call = InFile::new(self.current_file_id, ¯o_call); |
99 | 99 | ||
100 | if let Some(call_id) = macro_call.as_call_id(db, |path| { | 100 | if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| { |
101 | if let Some(local_scope) = local_scope { | 101 | if let Some(local_scope) = local_scope { |
102 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { | 102 | if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { |
103 | return Some(def); | 103 | return Some(def); |
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index e2130d931..53599e74a 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs | |||
@@ -34,6 +34,7 @@ pub struct FunctionData { | |||
34 | /// True if the first param is `self`. This is relevant to decide whether this | 34 | /// True if the first param is `self`. This is relevant to decide whether this |
35 | /// can be called as a method. | 35 | /// can be called as a method. |
36 | pub has_self_param: bool, | 36 | pub has_self_param: bool, |
37 | pub is_unsafe: bool, | ||
37 | pub visibility: RawVisibility, | 38 | pub visibility: RawVisibility, |
38 | } | 39 | } |
39 | 40 | ||
@@ -85,17 +86,20 @@ impl FunctionData { | |||
85 | ret_type | 86 | ret_type |
86 | }; | 87 | }; |
87 | 88 | ||
89 | let is_unsafe = src.value.unsafe_token().is_some(); | ||
90 | |||
88 | let vis_default = RawVisibility::default_for_container(loc.container); | 91 | let vis_default = RawVisibility::default_for_container(loc.container); |
89 | let visibility = | 92 | let visibility = |
90 | RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); | 93 | RawVisibility::from_ast_with_default(db, vis_default, src.map(|s| s.visibility())); |
91 | 94 | ||
92 | let sig = FunctionData { name, params, ret_type, has_self_param, visibility, attrs }; | 95 | let sig = |
96 | FunctionData { name, params, ret_type, has_self_param, is_unsafe, visibility, attrs }; | ||
93 | Arc::new(sig) | 97 | Arc::new(sig) |
94 | } | 98 | } |
95 | } | 99 | } |
96 | 100 | ||
97 | fn desugar_future_path(orig: TypeRef) -> Path { | 101 | fn desugar_future_path(orig: TypeRef) -> Path { |
98 | let path = path![std::future::Future]; | 102 | let path = path![core::future::Future]; |
99 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); | 103 | let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); |
100 | let mut last = GenericArgs::empty(); | 104 | let mut last = GenericArgs::empty(); |
101 | last.bindings.push(AssociatedTypeBinding { | 105 | last.bindings.push(AssociatedTypeBinding { |
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index 945a0025e..10cc26480 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Defines database & queries for name resolution. | 1 | //! Defines database & queries for name resolution. |
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use hir_expand::{db::AstDatabase, name::Name, HirFileId}; | 4 | use hir_expand::{db::AstDatabase, HirFileId}; |
5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; | 5 | use ra_db::{salsa, CrateId, SourceDatabase, Upcast}; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
7 | use ra_syntax::SmolStr; | 7 | use ra_syntax::SmolStr; |
@@ -12,13 +12,10 @@ use crate::{ | |||
12 | body::{scope::ExprScopes, Body, BodySourceMap}, | 12 | body::{scope::ExprScopes, Body, BodySourceMap}, |
13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, | 13 | data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData}, |
14 | docs::Documentation, | 14 | docs::Documentation, |
15 | find_path, | ||
16 | generics::GenericParams, | 15 | generics::GenericParams, |
17 | item_scope::ItemInNs, | 16 | import_map::ImportMap, |
18 | lang_item::{LangItemTarget, LangItems}, | 17 | lang_item::{LangItemTarget, LangItems}, |
19 | nameres::{raw::RawItems, CrateDefMap}, | 18 | nameres::{raw::RawItems, CrateDefMap}, |
20 | path::ModPath, | ||
21 | visibility::Visibility, | ||
22 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
23 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, | 20 | GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId, |
24 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, | 21 | TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, |
@@ -113,15 +110,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
113 | #[salsa::invoke(Documentation::documentation_query)] | 110 | #[salsa::invoke(Documentation::documentation_query)] |
114 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; | 111 | fn documentation(&self, def: AttrDefId) -> Option<Documentation>; |
115 | 112 | ||
116 | #[salsa::invoke(find_path::importable_locations_of_query)] | 113 | #[salsa::invoke(ImportMap::import_map_query)] |
117 | fn importable_locations_of( | 114 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
118 | &self, | ||
119 | item: ItemInNs, | ||
120 | krate: CrateId, | ||
121 | ) -> Arc<[(ModuleId, Name, Visibility)]>; | ||
122 | |||
123 | #[salsa::invoke(find_path::find_path_inner_query)] | ||
124 | fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>; | ||
125 | } | 115 | } |
126 | 116 | ||
127 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { | 117 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { |
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs index b221ae1ce..2630b3d89 100644 --- a/crates/ra_hir_def/src/docs.rs +++ b/crates/ra_hir_def/src/docs.rs | |||
@@ -29,6 +29,13 @@ impl Documentation { | |||
29 | Documentation(s.into()) | 29 | Documentation(s.into()) |
30 | } | 30 | } |
31 | 31 | ||
32 | pub fn from_ast<N>(node: &N) -> Option<Documentation> | ||
33 | where | ||
34 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
35 | { | ||
36 | docs_from_ast(node) | ||
37 | } | ||
38 | |||
32 | pub fn as_str(&self) -> &str { | 39 | pub fn as_str(&self) -> &str { |
33 | &*self.0 | 40 | &*self.0 |
34 | } | 41 | } |
@@ -70,6 +77,45 @@ impl Documentation { | |||
70 | } | 77 | } |
71 | } | 78 | } |
72 | 79 | ||
73 | pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { | 80 | pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation> |
74 | node.doc_comment_text().map(|it| Documentation::new(&it)) | 81 | where |
82 | N: ast::DocCommentsOwner + ast::AttrsOwner, | ||
83 | { | ||
84 | let doc_comment_text = node.doc_comment_text(); | ||
85 | let doc_attr_text = expand_doc_attrs(node); | ||
86 | let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text); | ||
87 | docs.map(|it| Documentation::new(&it)) | ||
88 | } | ||
89 | |||
90 | fn merge_doc_comments_and_attrs( | ||
91 | doc_comment_text: Option<String>, | ||
92 | doc_attr_text: Option<String>, | ||
93 | ) -> Option<String> { | ||
94 | match (doc_comment_text, doc_attr_text) { | ||
95 | (Some(mut comment_text), Some(attr_text)) => { | ||
96 | comment_text.push_str("\n\n"); | ||
97 | comment_text.push_str(&attr_text); | ||
98 | Some(comment_text) | ||
99 | } | ||
100 | (Some(comment_text), None) => Some(comment_text), | ||
101 | (None, Some(attr_text)) => Some(attr_text), | ||
102 | (None, None) => None, | ||
103 | } | ||
104 | } | ||
105 | |||
106 | fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> { | ||
107 | let mut docs = String::new(); | ||
108 | for attr in owner.attrs() { | ||
109 | if let Some(("doc", value)) = | ||
110 | attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str())) | ||
111 | { | ||
112 | docs.push_str(value); | ||
113 | docs.push_str("\n\n"); | ||
114 | } | ||
115 | } | ||
116 | if docs.is_empty() { | ||
117 | None | ||
118 | } else { | ||
119 | Some(docs.trim_end_matches("\n\n").to_owned()) | ||
120 | } | ||
75 | } | 121 | } |
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs index 4db798473..06701a830 100644 --- a/crates/ra_hir_def/src/find_path.rs +++ b/crates/ra_hir_def/src/find_path.rs | |||
@@ -1,9 +1,8 @@ | |||
1 | //! An algorithm to find a path to refer to a certain item. | 1 | //! An algorithm to find a path to refer to a certain item. |
2 | 2 | ||
3 | use std::sync::Arc; | ||
4 | |||
5 | use hir_expand::name::{known, AsName, Name}; | 3 | use hir_expand::name::{known, AsName, Name}; |
6 | use ra_prof::profile; | 4 | use ra_prof::profile; |
5 | use rustc_hash::FxHashSet; | ||
7 | use test_utils::mark; | 6 | use test_utils::mark; |
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
@@ -11,7 +10,7 @@ use crate::{ | |||
11 | item_scope::ItemInNs, | 10 | item_scope::ItemInNs, |
12 | path::{ModPath, PathKind}, | 11 | path::{ModPath, PathKind}, |
13 | visibility::Visibility, | 12 | visibility::Visibility, |
14 | CrateId, ModuleDefId, ModuleId, | 13 | ModuleDefId, ModuleId, |
15 | }; | 14 | }; |
16 | 15 | ||
17 | // FIXME: handle local items | 16 | // FIXME: handle local items |
@@ -20,7 +19,7 @@ use crate::{ | |||
20 | /// *from where* you're referring to the item, hence the `from` parameter. | 19 | /// *from where* you're referring to the item, hence the `from` parameter. |
21 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { | 20 | pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> { |
22 | let _p = profile("find_path"); | 21 | let _p = profile("find_path"); |
23 | db.find_path_inner(item, from, MAX_PATH_LEN) | 22 | find_path_inner(db, item, from, MAX_PATH_LEN) |
24 | } | 23 | } |
25 | 24 | ||
26 | const MAX_PATH_LEN: usize = 15; | 25 | const MAX_PATH_LEN: usize = 15; |
@@ -36,20 +35,9 @@ impl ModPath { | |||
36 | let first_segment = self.segments.first(); | 35 | let first_segment = self.segments.first(); |
37 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) | 36 | first_segment == Some(&known::alloc) || first_segment == Some(&known::core) |
38 | } | 37 | } |
39 | |||
40 | fn len(&self) -> usize { | ||
41 | self.segments.len() | ||
42 | + match self.kind { | ||
43 | PathKind::Plain => 0, | ||
44 | PathKind::Super(i) => i as usize, | ||
45 | PathKind::Crate => 1, | ||
46 | PathKind::Abs => 0, | ||
47 | PathKind::DollarCrate(_) => 1, | ||
48 | } | ||
49 | } | ||
50 | } | 38 | } |
51 | 39 | ||
52 | pub(crate) fn find_path_inner_query( | 40 | fn find_path_inner( |
53 | db: &dyn DefDatabase, | 41 | db: &dyn DefDatabase, |
54 | item: ItemInNs, | 42 | item: ItemInNs, |
55 | from: ModuleId, | 43 | from: ModuleId, |
@@ -133,31 +121,67 @@ pub(crate) fn find_path_inner_query( | |||
133 | } | 121 | } |
134 | 122 | ||
135 | // - otherwise, look for modules containing (reexporting) it and import it from one of those | 123 | // - otherwise, look for modules containing (reexporting) it and import it from one of those |
124 | |||
136 | let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; | 125 | let crate_root = ModuleId { local_id: def_map.root, krate: from.krate }; |
137 | let crate_attrs = db.attrs(crate_root.into()); | 126 | let crate_attrs = db.attrs(crate_root.into()); |
138 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); | 127 | let prefer_no_std = crate_attrs.by_key("no_std").exists(); |
139 | let importable_locations = find_importable_locations(db, item, from); | ||
140 | let mut best_path = None; | 128 | let mut best_path = None; |
141 | let mut best_path_len = max_len; | 129 | let mut best_path_len = max_len; |
142 | for (module_id, name) in importable_locations { | ||
143 | let mut path = match db.find_path_inner( | ||
144 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | ||
145 | from, | ||
146 | best_path_len - 1, | ||
147 | ) { | ||
148 | None => continue, | ||
149 | Some(path) => path, | ||
150 | }; | ||
151 | path.segments.push(name); | ||
152 | 130 | ||
153 | let new_path = if let Some(best_path) = best_path { | 131 | if item.krate(db) == Some(from.krate) { |
154 | select_best_path(best_path, path, prefer_no_std) | 132 | // Item was defined in the same crate that wants to import it. It cannot be found in any |
155 | } else { | 133 | // dependency in this case. |
156 | path | 134 | |
157 | }; | 135 | let local_imports = find_local_import_locations(db, item, from); |
158 | best_path_len = new_path.len(); | 136 | for (module_id, name) in local_imports { |
159 | best_path = Some(new_path); | 137 | if let Some(mut path) = find_path_inner( |
138 | db, | ||
139 | ItemInNs::Types(ModuleDefId::ModuleId(module_id)), | ||
140 | from, | ||
141 | best_path_len - 1, | ||
142 | ) { | ||
143 | path.segments.push(name); | ||
144 | |||
145 | let new_path = if let Some(best_path) = best_path { | ||
146 | select_best_path(best_path, path, prefer_no_std) | ||
147 | } else { | ||
148 | path | ||
149 | }; | ||
150 | best_path_len = new_path.len(); | ||
151 | best_path = Some(new_path); | ||
152 | } | ||
153 | } | ||
154 | } else { | ||
155 | // Item was defined in some upstream crate. This means that it must be exported from one, | ||
156 | // too (unless we can't name it at all). It could *also* be (re)exported by the same crate | ||
157 | // that wants to import it here, but we always prefer to use the external path here. | ||
158 | |||
159 | let crate_graph = db.crate_graph(); | ||
160 | let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { | ||
161 | let import_map = db.import_map(dep.crate_id); | ||
162 | import_map.import_info_for(item).and_then(|info| { | ||
163 | // Determine best path for containing module and append last segment from `info`. | ||
164 | let mut path = find_path_inner( | ||
165 | db, | ||
166 | ItemInNs::Types(ModuleDefId::ModuleId(info.container)), | ||
167 | from, | ||
168 | best_path_len - 1, | ||
169 | )?; | ||
170 | path.segments.push(info.path.segments.last().unwrap().clone()); | ||
171 | Some(path) | ||
172 | }) | ||
173 | }); | ||
174 | |||
175 | for path in extern_paths { | ||
176 | let new_path = if let Some(best_path) = best_path { | ||
177 | select_best_path(best_path, path, prefer_no_std) | ||
178 | } else { | ||
179 | path | ||
180 | }; | ||
181 | best_path = Some(new_path); | ||
182 | } | ||
160 | } | 183 | } |
184 | |||
161 | best_path | 185 | best_path |
162 | } | 186 | } |
163 | 187 | ||
@@ -185,69 +209,86 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) - | |||
185 | } | 209 | } |
186 | } | 210 | } |
187 | 211 | ||
188 | fn find_importable_locations( | 212 | /// Finds locations in `from.krate` from which `item` can be imported by `from`. |
213 | fn find_local_import_locations( | ||
189 | db: &dyn DefDatabase, | 214 | db: &dyn DefDatabase, |
190 | item: ItemInNs, | 215 | item: ItemInNs, |
191 | from: ModuleId, | 216 | from: ModuleId, |
192 | ) -> Vec<(ModuleId, Name)> { | 217 | ) -> Vec<(ModuleId, Name)> { |
193 | let crate_graph = db.crate_graph(); | 218 | let _p = profile("find_local_import_locations"); |
194 | let mut result = Vec::new(); | 219 | |
195 | // We only look in the crate from which we are importing, and the direct | 220 | // `from` can import anything below `from` with visibility of at least `from`, and anything |
196 | // dependencies. We cannot refer to names from transitive dependencies | 221 | // above `from` with any visibility. That means we do not need to descend into private siblings |
197 | // directly (only through reexports in direct dependencies). | 222 | // of `from` (and similar). |
198 | for krate in Some(from.krate) | 223 | |
199 | .into_iter() | 224 | let def_map = db.crate_def_map(from.krate); |
200 | .chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id)) | 225 | |
201 | { | 226 | // Compute the initial worklist. We start with all direct child modules of `from` as well as all |
202 | result.extend( | 227 | // of its (recursive) parent modules. |
203 | db.importable_locations_of(item, krate) | 228 | let data = &def_map.modules[from.local_id]; |
204 | .iter() | 229 | let mut worklist = data |
205 | .filter(|(_, _, vis)| vis.is_visible_from(db, from)) | 230 | .children |
206 | .map(|(m, n, _)| (*m, n.clone())), | 231 | .values() |
207 | ); | 232 | .map(|child| ModuleId { krate: from.krate, local_id: *child }) |
208 | } | 233 | .collect::<Vec<_>>(); |
209 | result | 234 | let mut parent = data.parent; |
210 | } | 235 | while let Some(p) = parent { |
236 | worklist.push(ModuleId { krate: from.krate, local_id: p }); | ||
237 | parent = def_map.modules[p].parent; | ||
238 | } | ||
239 | |||
240 | let mut seen: FxHashSet<_> = FxHashSet::default(); | ||
241 | |||
242 | let mut locations = Vec::new(); | ||
243 | while let Some(module) = worklist.pop() { | ||
244 | if !seen.insert(module) { | ||
245 | continue; // already processed this module | ||
246 | } | ||
247 | |||
248 | let ext_def_map; | ||
249 | let data = if module.krate == from.krate { | ||
250 | &def_map[module.local_id] | ||
251 | } else { | ||
252 | // The crate might reexport a module defined in another crate. | ||
253 | ext_def_map = db.crate_def_map(module.krate); | ||
254 | &ext_def_map[module.local_id] | ||
255 | }; | ||
211 | 256 | ||
212 | /// Collects all locations from which we might import the item in a particular | ||
213 | /// crate. These include the original definition of the item, and any | ||
214 | /// non-private `use`s. | ||
215 | /// | ||
216 | /// Note that the crate doesn't need to be the one in which the item is defined; | ||
217 | /// it might be re-exported in other crates. | ||
218 | pub(crate) fn importable_locations_of_query( | ||
219 | db: &dyn DefDatabase, | ||
220 | item: ItemInNs, | ||
221 | krate: CrateId, | ||
222 | ) -> Arc<[(ModuleId, Name, Visibility)]> { | ||
223 | let _p = profile("importable_locations_of_query"); | ||
224 | let def_map = db.crate_def_map(krate); | ||
225 | let mut result = Vec::new(); | ||
226 | for (local_id, data) in def_map.modules.iter() { | ||
227 | if let Some((name, vis)) = data.scope.name_of(item) { | 257 | if let Some((name, vis)) = data.scope.name_of(item) { |
228 | let is_private = if let Visibility::Module(private_to) = vis { | 258 | if vis.is_visible_from(db, from) { |
229 | private_to.local_id == local_id | 259 | let is_private = if let Visibility::Module(private_to) = vis { |
230 | } else { | 260 | private_to.local_id == module.local_id |
231 | false | 261 | } else { |
232 | }; | 262 | false |
233 | let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { | 263 | }; |
234 | data.scope.declarations().any(|it| it == module_def_id) | 264 | let is_original_def = if let Some(module_def_id) = item.as_module_def_id() { |
235 | } else { | 265 | data.scope.declarations().any(|it| it == module_def_id) |
236 | false | 266 | } else { |
237 | }; | 267 | false |
238 | if is_private && !is_original_def { | 268 | }; |
269 | |||
239 | // Ignore private imports. these could be used if we are | 270 | // Ignore private imports. these could be used if we are |
240 | // in a submodule of this module, but that's usually not | 271 | // in a submodule of this module, but that's usually not |
241 | // what the user wants; and if this module can import | 272 | // what the user wants; and if this module can import |
242 | // the item and we're a submodule of it, so can we. | 273 | // the item and we're a submodule of it, so can we. |
243 | // Also this keeps the cached data smaller. | 274 | // Also this keeps the cached data smaller. |
244 | continue; | 275 | if !is_private || is_original_def { |
276 | locations.push((module, name.clone())); | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | // Descend into all modules visible from `from`. | ||
282 | for (_, per_ns) in data.scope.entries() { | ||
283 | if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() { | ||
284 | if vis.is_visible_from(db, from) { | ||
285 | worklist.push(module); | ||
286 | } | ||
245 | } | 287 | } |
246 | result.push((ModuleId { krate, local_id }, name.clone(), vis)); | ||
247 | } | 288 | } |
248 | } | 289 | } |
249 | 290 | ||
250 | Arc::from(result) | 291 | locations |
251 | } | 292 | } |
252 | 293 | ||
253 | #[cfg(test)] | 294 | #[cfg(test)] |
@@ -264,8 +305,8 @@ mod tests { | |||
264 | /// `code` needs to contain a cursor marker; checks that `find_path` for the | 305 | /// `code` needs to contain a cursor marker; checks that `find_path` for the |
265 | /// item the `path` refers to returns that same path when called from the | 306 | /// item the `path` refers to returns that same path when called from the |
266 | /// module the cursor is in. | 307 | /// module the cursor is in. |
267 | fn check_found_path(code: &str, path: &str) { | 308 | fn check_found_path(ra_fixture: &str, path: &str) { |
268 | let (db, pos) = TestDB::with_position(code); | 309 | let (db, pos) = TestDB::with_position(ra_fixture); |
269 | let module = db.module_for_file(pos.file_id); | 310 | let module = db.module_for_file(pos.file_id); |
270 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); | 311 | let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); |
271 | let ast_path = parsed_path_file | 312 | let ast_path = parsed_path_file |
@@ -396,6 +437,44 @@ mod tests { | |||
396 | } | 437 | } |
397 | 438 | ||
398 | #[test] | 439 | #[test] |
440 | fn partially_imported() { | ||
441 | // Tests that short paths are used even for external items, when parts of the path are | ||
442 | // already in scope. | ||
443 | check_found_path( | ||
444 | r#" | ||
445 | //- /main.rs crate:main deps:ra_syntax | ||
446 | |||
447 | use ra_syntax::ast; | ||
448 | <|> | ||
449 | |||
450 | //- /lib.rs crate:ra_syntax | ||
451 | pub mod ast { | ||
452 | pub enum ModuleItem { | ||
453 | A, B, C, | ||
454 | } | ||
455 | } | ||
456 | "#, | ||
457 | "ast::ModuleItem", | ||
458 | ); | ||
459 | |||
460 | check_found_path( | ||
461 | r#" | ||
462 | //- /main.rs crate:main deps:ra_syntax | ||
463 | |||
464 | <|> | ||
465 | |||
466 | //- /lib.rs crate:ra_syntax | ||
467 | pub mod ast { | ||
468 | pub enum ModuleItem { | ||
469 | A, B, C, | ||
470 | } | ||
471 | } | ||
472 | "#, | ||
473 | "ra_syntax::ast::ModuleItem", | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | #[test] | ||
399 | fn same_crate_reexport() { | 478 | fn same_crate_reexport() { |
400 | let code = r#" | 479 | let code = r#" |
401 | //- /main.rs | 480 | //- /main.rs |
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs new file mode 100644 index 000000000..68e20d06b --- /dev/null +++ b/crates/ra_hir_def/src/import_map.rs | |||
@@ -0,0 +1,679 @@ | |||
1 | //! A map of all publicly exported items in a crate. | ||
2 | |||
3 | use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc}; | ||
4 | |||
5 | use fst::{self, Streamer}; | ||
6 | use indexmap::{map::Entry, IndexMap}; | ||
7 | use ra_db::CrateId; | ||
8 | use rustc_hash::FxHasher; | ||
9 | |||
10 | use crate::{ | ||
11 | db::DefDatabase, | ||
12 | item_scope::ItemInNs, | ||
13 | path::{ModPath, PathKind}, | ||
14 | visibility::Visibility, | ||
15 | ModuleDefId, ModuleId, | ||
16 | }; | ||
17 | |||
18 | type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>; | ||
19 | |||
20 | /// Item import details stored in the `ImportMap`. | ||
21 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
22 | pub struct ImportInfo { | ||
23 | /// A path that can be used to import the item, relative to the crate's root. | ||
24 | pub path: ModPath, | ||
25 | /// The module containing this item. | ||
26 | pub container: ModuleId, | ||
27 | } | ||
28 | |||
29 | /// A map from publicly exported items to the path needed to import/name them from a downstream | ||
30 | /// crate. | ||
31 | /// | ||
32 | /// Reexports of items are taken into account, ie. if something is exported under multiple | ||
33 | /// names, the one with the shortest import path will be used. | ||
34 | /// | ||
35 | /// Note that all paths are relative to the containing crate's root, so the crate name still needs | ||
36 | /// to be prepended to the `ModPath` before the path is valid. | ||
37 | pub struct ImportMap { | ||
38 | map: FxIndexMap<ItemInNs, ImportInfo>, | ||
39 | |||
40 | /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the | ||
41 | /// values returned by running `fst`. | ||
42 | /// | ||
43 | /// Since a path can refer to multiple items due to namespacing, we store all items with the | ||
44 | /// same path right after each other. This allows us to find all items after the FST gives us | ||
45 | /// the index of the first one. | ||
46 | importables: Vec<ItemInNs>, | ||
47 | fst: fst::Map<Vec<u8>>, | ||
48 | } | ||
49 | |||
50 | impl ImportMap { | ||
51 | pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { | ||
52 | let _p = ra_prof::profile("import_map_query"); | ||
53 | let def_map = db.crate_def_map(krate); | ||
54 | let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default()); | ||
55 | |||
56 | // We look only into modules that are public(ly reexported), starting with the crate root. | ||
57 | let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; | ||
58 | let root = ModuleId { krate, local_id: def_map.root }; | ||
59 | let mut worklist = vec![(root, empty)]; | ||
60 | while let Some((module, mod_path)) = worklist.pop() { | ||
61 | let ext_def_map; | ||
62 | let mod_data = if module.krate == krate { | ||
63 | &def_map[module.local_id] | ||
64 | } else { | ||
65 | // The crate might reexport a module defined in another crate. | ||
66 | ext_def_map = db.crate_def_map(module.krate); | ||
67 | &ext_def_map[module.local_id] | ||
68 | }; | ||
69 | |||
70 | let visible_items = mod_data.scope.entries().filter_map(|(name, per_ns)| { | ||
71 | let per_ns = per_ns.filter_visibility(|vis| vis == Visibility::Public); | ||
72 | if per_ns.is_none() { | ||
73 | None | ||
74 | } else { | ||
75 | Some((name, per_ns)) | ||
76 | } | ||
77 | }); | ||
78 | |||
79 | for (name, per_ns) in visible_items { | ||
80 | let mk_path = || { | ||
81 | let mut path = mod_path.clone(); | ||
82 | path.segments.push(name.clone()); | ||
83 | path | ||
84 | }; | ||
85 | |||
86 | for item in per_ns.iter_items() { | ||
87 | let path = mk_path(); | ||
88 | match import_map.entry(item) { | ||
89 | Entry::Vacant(entry) => { | ||
90 | entry.insert(ImportInfo { path, container: module }); | ||
91 | } | ||
92 | Entry::Occupied(mut entry) => { | ||
93 | // If the new path is shorter, prefer that one. | ||
94 | if path.len() < entry.get().path.len() { | ||
95 | *entry.get_mut() = ImportInfo { path, container: module }; | ||
96 | } else { | ||
97 | continue; | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | |||
102 | // If we've just added a path to a module, descend into it. We might traverse | ||
103 | // modules multiple times, but only if the new path to it is shorter than the | ||
104 | // first (else we `continue` above). | ||
105 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | ||
106 | worklist.push((mod_id, mk_path())); | ||
107 | } | ||
108 | } | ||
109 | } | ||
110 | } | ||
111 | |||
112 | let mut importables = import_map.iter().collect::<Vec<_>>(); | ||
113 | |||
114 | importables.sort_by(cmp); | ||
115 | |||
116 | // Build the FST, taking care not to insert duplicate values. | ||
117 | |||
118 | let mut builder = fst::MapBuilder::memory(); | ||
119 | let mut last_batch_start = 0; | ||
120 | |||
121 | for idx in 0..importables.len() { | ||
122 | if let Some(next_item) = importables.get(idx + 1) { | ||
123 | if cmp(&importables[last_batch_start], next_item) == Ordering::Equal { | ||
124 | continue; | ||
125 | } | ||
126 | } | ||
127 | |||
128 | let start = last_batch_start; | ||
129 | last_batch_start = idx + 1; | ||
130 | |||
131 | let key = fst_path(&importables[start].1.path); | ||
132 | |||
133 | builder.insert(key, start as u64).unwrap(); | ||
134 | } | ||
135 | |||
136 | let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | ||
137 | let importables = importables.iter().map(|(item, _)| **item).collect(); | ||
138 | |||
139 | Arc::new(Self { map: import_map, fst, importables }) | ||
140 | } | ||
141 | |||
142 | /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. | ||
143 | pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { | ||
144 | Some(&self.map.get(&item)?.path) | ||
145 | } | ||
146 | |||
147 | pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> { | ||
148 | self.map.get(&item) | ||
149 | } | ||
150 | } | ||
151 | |||
152 | impl PartialEq for ImportMap { | ||
153 | fn eq(&self, other: &Self) -> bool { | ||
154 | // `fst` and `importables` are built from `map`, so we don't need to compare them. | ||
155 | self.map == other.map | ||
156 | } | ||
157 | } | ||
158 | |||
159 | impl Eq for ImportMap {} | ||
160 | |||
161 | impl fmt::Debug for ImportMap { | ||
162 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
163 | let mut importable_paths: Vec<_> = self | ||
164 | .map | ||
165 | .iter() | ||
166 | .map(|(item, info)| { | ||
167 | let ns = match item { | ||
168 | ItemInNs::Types(_) => "t", | ||
169 | ItemInNs::Values(_) => "v", | ||
170 | ItemInNs::Macros(_) => "m", | ||
171 | }; | ||
172 | format!("- {} ({})", info.path, ns) | ||
173 | }) | ||
174 | .collect(); | ||
175 | |||
176 | importable_paths.sort(); | ||
177 | f.write_str(&importable_paths.join("\n")) | ||
178 | } | ||
179 | } | ||
180 | |||
181 | fn fst_path(path: &ModPath) -> String { | ||
182 | let mut s = path.to_string(); | ||
183 | s.make_ascii_lowercase(); | ||
184 | s | ||
185 | } | ||
186 | |||
187 | fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { | ||
188 | let lhs_str = fst_path(&lhs.path); | ||
189 | let rhs_str = fst_path(&rhs.path); | ||
190 | lhs_str.cmp(&rhs_str) | ||
191 | } | ||
192 | |||
193 | #[derive(Debug)] | ||
194 | pub struct Query { | ||
195 | query: String, | ||
196 | lowercased: String, | ||
197 | anchor_end: bool, | ||
198 | case_sensitive: bool, | ||
199 | limit: usize, | ||
200 | } | ||
201 | |||
202 | impl Query { | ||
203 | pub fn new(query: &str) -> Self { | ||
204 | Self { | ||
205 | lowercased: query.to_lowercase(), | ||
206 | query: query.to_string(), | ||
207 | anchor_end: false, | ||
208 | case_sensitive: false, | ||
209 | limit: usize::max_value(), | ||
210 | } | ||
211 | } | ||
212 | |||
213 | /// Only returns items whose paths end with the (case-insensitive) query string as their last | ||
214 | /// segment. | ||
215 | pub fn anchor_end(self) -> Self { | ||
216 | Self { anchor_end: true, ..self } | ||
217 | } | ||
218 | |||
219 | /// Limits the returned number of items to `limit`. | ||
220 | pub fn limit(self, limit: usize) -> Self { | ||
221 | Self { limit, ..self } | ||
222 | } | ||
223 | |||
224 | /// Respect casing of the query string when matching. | ||
225 | pub fn case_sensitive(self) -> Self { | ||
226 | Self { case_sensitive: true, ..self } | ||
227 | } | ||
228 | } | ||
229 | |||
230 | /// Searches dependencies of `krate` for an importable path matching `query`. | ||
231 | /// | ||
232 | /// This returns a list of items that could be imported from dependencies of `krate`. | ||
233 | pub fn search_dependencies<'a>( | ||
234 | db: &'a dyn DefDatabase, | ||
235 | krate: CrateId, | ||
236 | query: Query, | ||
237 | ) -> Vec<ItemInNs> { | ||
238 | let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query)); | ||
239 | |||
240 | let graph = db.crate_graph(); | ||
241 | let import_maps: Vec<_> = | ||
242 | graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect(); | ||
243 | |||
244 | let automaton = fst::automaton::Subsequence::new(&query.lowercased); | ||
245 | |||
246 | let mut op = fst::map::OpBuilder::new(); | ||
247 | for map in &import_maps { | ||
248 | op = op.add(map.fst.search(&automaton)); | ||
249 | } | ||
250 | |||
251 | let mut stream = op.union(); | ||
252 | let mut res = Vec::new(); | ||
253 | while let Some((_, indexed_values)) = stream.next() { | ||
254 | for indexed_value in indexed_values { | ||
255 | let import_map = &import_maps[indexed_value.index]; | ||
256 | let importables = &import_map.importables[indexed_value.value as usize..]; | ||
257 | |||
258 | // Path shared by the importable items in this group. | ||
259 | let path = &import_map.map[&importables[0]].path; | ||
260 | |||
261 | if query.anchor_end { | ||
262 | // Last segment must match query. | ||
263 | let last = path.segments.last().unwrap().to_string(); | ||
264 | if last.to_lowercase() != query.lowercased { | ||
265 | continue; | ||
266 | } | ||
267 | } | ||
268 | |||
269 | // Add the items from this `ModPath` group. Those are all subsequent items in | ||
270 | // `importables` whose paths match `path`. | ||
271 | let iter = importables.iter().copied().take_while(|item| { | ||
272 | let item_path = &import_map.map[item].path; | ||
273 | fst_path(item_path) == fst_path(path) | ||
274 | }); | ||
275 | |||
276 | if query.case_sensitive { | ||
277 | // FIXME: This does not do a subsequence match. | ||
278 | res.extend(iter.filter(|item| { | ||
279 | let item_path = &import_map.map[item].path; | ||
280 | item_path.to_string().contains(&query.query) | ||
281 | })); | ||
282 | } else { | ||
283 | res.extend(iter); | ||
284 | } | ||
285 | |||
286 | if res.len() >= query.limit { | ||
287 | res.truncate(query.limit); | ||
288 | return res; | ||
289 | } | ||
290 | } | ||
291 | } | ||
292 | |||
293 | res | ||
294 | } | ||
295 | |||
296 | #[cfg(test)] | ||
297 | mod tests { | ||
298 | use super::*; | ||
299 | use crate::test_db::TestDB; | ||
300 | use insta::assert_snapshot; | ||
301 | use itertools::Itertools; | ||
302 | use ra_db::fixture::WithFixture; | ||
303 | use ra_db::{SourceDatabase, Upcast}; | ||
304 | |||
305 | fn import_map(ra_fixture: &str) -> String { | ||
306 | let db = TestDB::with_files(ra_fixture); | ||
307 | let crate_graph = db.crate_graph(); | ||
308 | |||
309 | let s = crate_graph | ||
310 | .iter() | ||
311 | .filter_map(|krate| { | ||
312 | let cdata = &crate_graph[krate]; | ||
313 | let name = cdata.display_name.as_ref()?; | ||
314 | |||
315 | let map = db.import_map(krate); | ||
316 | |||
317 | Some(format!("{}:\n{:?}", name, map)) | ||
318 | }) | ||
319 | .join("\n"); | ||
320 | s | ||
321 | } | ||
322 | |||
323 | fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String { | ||
324 | let db = TestDB::with_files(ra_fixture); | ||
325 | let crate_graph = db.crate_graph(); | ||
326 | let krate = crate_graph | ||
327 | .iter() | ||
328 | .find(|krate| { | ||
329 | crate_graph[*krate].display_name.as_ref().map(|n| n.to_string()) | ||
330 | == Some(krate_name.to_string()) | ||
331 | }) | ||
332 | .unwrap(); | ||
333 | |||
334 | search_dependencies(db.upcast(), krate, query) | ||
335 | .into_iter() | ||
336 | .filter_map(|item| { | ||
337 | let mark = match item { | ||
338 | ItemInNs::Types(_) => "t", | ||
339 | ItemInNs::Values(_) => "v", | ||
340 | ItemInNs::Macros(_) => "m", | ||
341 | }; | ||
342 | item.krate(db.upcast()).map(|krate| { | ||
343 | let map = db.import_map(krate); | ||
344 | let path = map.path_of(item).unwrap(); | ||
345 | format!( | ||
346 | "{}::{} ({})", | ||
347 | crate_graph[krate].display_name.as_ref().unwrap(), | ||
348 | path, | ||
349 | mark | ||
350 | ) | ||
351 | }) | ||
352 | }) | ||
353 | .join("\n") | ||
354 | } | ||
355 | |||
356 | #[test] | ||
357 | fn smoke() { | ||
358 | let map = import_map( | ||
359 | r" | ||
360 | //- /main.rs crate:main deps:lib | ||
361 | |||
362 | mod private { | ||
363 | pub use lib::Pub; | ||
364 | pub struct InPrivateModule; | ||
365 | } | ||
366 | |||
367 | pub mod publ1 { | ||
368 | use lib::Pub; | ||
369 | } | ||
370 | |||
371 | pub mod real_pub { | ||
372 | pub use lib::Pub; | ||
373 | } | ||
374 | pub mod real_pu2 { // same path length as above | ||
375 | pub use lib::Pub; | ||
376 | } | ||
377 | |||
378 | //- /lib.rs crate:lib | ||
379 | pub struct Pub {} | ||
380 | pub struct Pub2; // t + v | ||
381 | struct Priv; | ||
382 | ", | ||
383 | ); | ||
384 | |||
385 | assert_snapshot!(map, @r###" | ||
386 | main: | ||
387 | - publ1 (t) | ||
388 | - real_pu2 (t) | ||
389 | - real_pub (t) | ||
390 | - real_pub::Pub (t) | ||
391 | lib: | ||
392 | - Pub (t) | ||
393 | - Pub2 (t) | ||
394 | - Pub2 (v) | ||
395 | "###); | ||
396 | } | ||
397 | |||
398 | #[test] | ||
399 | fn prefers_shortest_path() { | ||
400 | let map = import_map( | ||
401 | r" | ||
402 | //- /main.rs crate:main | ||
403 | |||
404 | pub mod sub { | ||
405 | pub mod subsub { | ||
406 | pub struct Def {} | ||
407 | } | ||
408 | |||
409 | pub use super::sub::subsub::Def; | ||
410 | } | ||
411 | ", | ||
412 | ); | ||
413 | |||
414 | assert_snapshot!(map, @r###" | ||
415 | main: | ||
416 | - sub (t) | ||
417 | - sub::Def (t) | ||
418 | - sub::subsub (t) | ||
419 | "###); | ||
420 | } | ||
421 | |||
422 | #[test] | ||
423 | fn type_reexport_cross_crate() { | ||
424 | // Reexports need to be visible from a crate, even if the original crate exports the item | ||
425 | // at a shorter path. | ||
426 | let map = import_map( | ||
427 | r" | ||
428 | //- /main.rs crate:main deps:lib | ||
429 | pub mod m { | ||
430 | pub use lib::S; | ||
431 | } | ||
432 | //- /lib.rs crate:lib | ||
433 | pub struct S; | ||
434 | ", | ||
435 | ); | ||
436 | |||
437 | assert_snapshot!(map, @r###" | ||
438 | main: | ||
439 | - m (t) | ||
440 | - m::S (t) | ||
441 | - m::S (v) | ||
442 | lib: | ||
443 | - S (t) | ||
444 | - S (v) | ||
445 | "###); | ||
446 | } | ||
447 | |||
448 | #[test] | ||
449 | fn macro_reexport() { | ||
450 | let map = import_map( | ||
451 | r" | ||
452 | //- /main.rs crate:main deps:lib | ||
453 | pub mod m { | ||
454 | pub use lib::pub_macro; | ||
455 | } | ||
456 | //- /lib.rs crate:lib | ||
457 | #[macro_export] | ||
458 | macro_rules! pub_macro { | ||
459 | () => {}; | ||
460 | } | ||
461 | ", | ||
462 | ); | ||
463 | |||
464 | assert_snapshot!(map, @r###" | ||
465 | main: | ||
466 | - m (t) | ||
467 | - m::pub_macro (m) | ||
468 | lib: | ||
469 | - pub_macro (m) | ||
470 | "###); | ||
471 | } | ||
472 | |||
473 | #[test] | ||
474 | fn module_reexport() { | ||
475 | // Reexporting modules from a dependency adds all contents to the import map. | ||
476 | let map = import_map( | ||
477 | r" | ||
478 | //- /main.rs crate:main deps:lib | ||
479 | pub use lib::module as reexported_module; | ||
480 | //- /lib.rs crate:lib | ||
481 | pub mod module { | ||
482 | pub struct S; | ||
483 | } | ||
484 | ", | ||
485 | ); | ||
486 | |||
487 | assert_snapshot!(map, @r###" | ||
488 | main: | ||
489 | - reexported_module (t) | ||
490 | - reexported_module::S (t) | ||
491 | - reexported_module::S (v) | ||
492 | lib: | ||
493 | - module (t) | ||
494 | - module::S (t) | ||
495 | - module::S (v) | ||
496 | "###); | ||
497 | } | ||
498 | |||
499 | #[test] | ||
500 | fn cyclic_module_reexport() { | ||
501 | // A cyclic reexport does not hang. | ||
502 | let map = import_map( | ||
503 | r" | ||
504 | //- /lib.rs crate:lib | ||
505 | pub mod module { | ||
506 | pub struct S; | ||
507 | pub use super::sub::*; | ||
508 | } | ||
509 | |||
510 | pub mod sub { | ||
511 | pub use super::module; | ||
512 | } | ||
513 | ", | ||
514 | ); | ||
515 | |||
516 | assert_snapshot!(map, @r###" | ||
517 | lib: | ||
518 | - module (t) | ||
519 | - module::S (t) | ||
520 | - module::S (v) | ||
521 | - sub (t) | ||
522 | "###); | ||
523 | } | ||
524 | |||
525 | #[test] | ||
526 | fn private_macro() { | ||
527 | let map = import_map( | ||
528 | r" | ||
529 | //- /lib.rs crate:lib | ||
530 | macro_rules! private_macro { | ||
531 | () => {}; | ||
532 | } | ||
533 | ", | ||
534 | ); | ||
535 | |||
536 | assert_snapshot!(map, @r###" | ||
537 | lib: | ||
538 | "###); | ||
539 | } | ||
540 | |||
541 | #[test] | ||
542 | fn namespacing() { | ||
543 | let map = import_map( | ||
544 | r" | ||
545 | //- /lib.rs crate:lib | ||
546 | pub struct Thing; // t + v | ||
547 | #[macro_export] | ||
548 | macro_rules! Thing { // m | ||
549 | () => {}; | ||
550 | } | ||
551 | ", | ||
552 | ); | ||
553 | |||
554 | assert_snapshot!(map, @r###" | ||
555 | lib: | ||
556 | - Thing (m) | ||
557 | - Thing (t) | ||
558 | - Thing (v) | ||
559 | "###); | ||
560 | |||
561 | let map = import_map( | ||
562 | r" | ||
563 | //- /lib.rs crate:lib | ||
564 | pub mod Thing {} // t | ||
565 | #[macro_export] | ||
566 | macro_rules! Thing { // m | ||
567 | () => {}; | ||
568 | } | ||
569 | ", | ||
570 | ); | ||
571 | |||
572 | assert_snapshot!(map, @r###" | ||
573 | lib: | ||
574 | - Thing (m) | ||
575 | - Thing (t) | ||
576 | "###); | ||
577 | } | ||
578 | |||
579 | #[test] | ||
580 | fn search() { | ||
581 | let ra_fixture = r#" | ||
582 | //- /main.rs crate:main deps:dep | ||
583 | //- /dep.rs crate:dep deps:tdep | ||
584 | use tdep::fmt as fmt_dep; | ||
585 | pub mod fmt { | ||
586 | pub trait Display { | ||
587 | fn fmt(); | ||
588 | } | ||
589 | } | ||
590 | #[macro_export] | ||
591 | macro_rules! Fmt { | ||
592 | () => {}; | ||
593 | } | ||
594 | pub struct Fmt; | ||
595 | |||
596 | pub fn format() {} | ||
597 | pub fn no() {} | ||
598 | |||
599 | //- /tdep.rs crate:tdep | ||
600 | pub mod fmt { | ||
601 | pub struct NotImportableFromMain; | ||
602 | } | ||
603 | "#; | ||
604 | |||
605 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt")); | ||
606 | assert_snapshot!(res, @r###" | ||
607 | dep::fmt (t) | ||
608 | dep::Fmt (t) | ||
609 | dep::Fmt (v) | ||
610 | dep::Fmt (m) | ||
611 | dep::fmt::Display (t) | ||
612 | dep::format (v) | ||
613 | "###); | ||
614 | |||
615 | let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end()); | ||
616 | assert_snapshot!(res, @r###" | ||
617 | dep::fmt (t) | ||
618 | dep::Fmt (t) | ||
619 | dep::Fmt (v) | ||
620 | dep::Fmt (m) | ||
621 | "###); | ||
622 | } | ||
623 | |||
624 | #[test] | ||
625 | fn search_casing() { | ||
626 | let ra_fixture = r#" | ||
627 | //- /main.rs crate:main deps:dep | ||
628 | //- /dep.rs crate:dep | ||
629 | |||
630 | pub struct fmt; | ||
631 | pub struct FMT; | ||
632 | "#; | ||
633 | |||
634 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT")); | ||
635 | |||
636 | assert_snapshot!(res, @r###" | ||
637 | dep::fmt (t) | ||
638 | dep::fmt (v) | ||
639 | dep::FMT (t) | ||
640 | dep::FMT (v) | ||
641 | "###); | ||
642 | |||
643 | let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive()); | ||
644 | |||
645 | assert_snapshot!(res, @r###" | ||
646 | dep::FMT (t) | ||
647 | dep::FMT (v) | ||
648 | "###); | ||
649 | } | ||
650 | |||
651 | #[test] | ||
652 | fn search_limit() { | ||
653 | let res = search_dependencies_of( | ||
654 | r#" | ||
655 | //- /main.rs crate:main deps:dep | ||
656 | //- /dep.rs crate:dep | ||
657 | pub mod fmt { | ||
658 | pub trait Display { | ||
659 | fn fmt(); | ||
660 | } | ||
661 | } | ||
662 | #[macro_export] | ||
663 | macro_rules! Fmt { | ||
664 | () => {}; | ||
665 | } | ||
666 | pub struct Fmt; | ||
667 | |||
668 | pub fn format() {} | ||
669 | pub fn no() {} | ||
670 | "#, | ||
671 | "main", | ||
672 | Query::new("").limit(2), | ||
673 | ); | ||
674 | assert_snapshot!(res, @r###" | ||
675 | dep::fmt (t) | ||
676 | dep::Fmt (t) | ||
677 | "###); | ||
678 | } | ||
679 | } | ||
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs index fc15948ad..b03ba939a 100644 --- a/crates/ra_hir_def/src/item_scope.rs +++ b/crates/ra_hir_def/src/item_scope.rs | |||
@@ -3,11 +3,12 @@ | |||
3 | 3 | ||
4 | use hir_expand::name::Name; | 4 | use hir_expand::name::Name; |
5 | use once_cell::sync::Lazy; | 5 | use once_cell::sync::Lazy; |
6 | use ra_db::CrateId; | ||
6 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
7 | 8 | ||
8 | use crate::{ | 9 | use crate::{ |
9 | per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId, | 10 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
10 | TraitId, | 11 | Lookup, MacroDefId, ModuleDefId, TraitId, |
11 | }; | 12 | }; |
12 | 13 | ||
13 | #[derive(Debug, Default, PartialEq, Eq)] | 14 | #[derive(Debug, Default, PartialEq, Eq)] |
@@ -203,4 +204,22 @@ impl ItemInNs { | |||
203 | ItemInNs::Macros(_) => None, | 204 | ItemInNs::Macros(_) => None, |
204 | } | 205 | } |
205 | } | 206 | } |
207 | |||
208 | /// Returns the crate defining this item (or `None` if `self` is built-in). | ||
209 | pub fn krate(&self, db: &dyn DefDatabase) -> Option<CrateId> { | ||
210 | Some(match self { | ||
211 | ItemInNs::Types(did) | ItemInNs::Values(did) => match did { | ||
212 | ModuleDefId::ModuleId(id) => id.krate, | ||
213 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate, | ||
214 | ModuleDefId::AdtId(id) => id.module(db).krate, | ||
215 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate, | ||
216 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate, | ||
217 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate, | ||
218 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate, | ||
219 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate, | ||
220 | ModuleDefId::BuiltinType(_) => return None, | ||
221 | }, | ||
222 | ItemInNs::Macros(id) => return id.krate, | ||
223 | }) | ||
224 | } | ||
206 | } | 225 | } |
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 5325a2760..edc59e5a8 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs | |||
@@ -43,6 +43,7 @@ pub mod child_by_source; | |||
43 | 43 | ||
44 | pub mod visibility; | 44 | pub mod visibility; |
45 | pub mod find_path; | 45 | pub mod find_path; |
46 | pub mod import_map; | ||
46 | 47 | ||
47 | #[cfg(test)] | 48 | #[cfg(test)] |
48 | mod test_db; | 49 | mod test_db; |
@@ -416,6 +417,7 @@ pub trait AsMacroCall { | |||
416 | fn as_call_id( | 417 | fn as_call_id( |
417 | &self, | 418 | &self, |
418 | db: &dyn db::DefDatabase, | 419 | db: &dyn db::DefDatabase, |
420 | krate: CrateId, | ||
419 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 421 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
420 | ) -> Option<MacroCallId>; | 422 | ) -> Option<MacroCallId>; |
421 | } | 423 | } |
@@ -424,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> { | |||
424 | fn as_call_id( | 426 | fn as_call_id( |
425 | &self, | 427 | &self, |
426 | db: &dyn db::DefDatabase, | 428 | db: &dyn db::DefDatabase, |
429 | krate: CrateId, | ||
427 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 430 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
428 | ) -> Option<MacroCallId> { | 431 | ) -> Option<MacroCallId> { |
429 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); | 432 | let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); |
430 | let h = Hygiene::new(db.upcast(), self.file_id); | 433 | let h = Hygiene::new(db.upcast(), self.file_id); |
431 | let path = path::ModPath::from_src(self.value.path()?, &h)?; | 434 | let path = path::ModPath::from_src(self.value.path()?, &h)?; |
432 | 435 | ||
433 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) | 436 | AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver) |
434 | } | 437 | } |
435 | } | 438 | } |
436 | 439 | ||
@@ -451,6 +454,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | |||
451 | fn as_call_id( | 454 | fn as_call_id( |
452 | &self, | 455 | &self, |
453 | db: &dyn db::DefDatabase, | 456 | db: &dyn db::DefDatabase, |
457 | krate: CrateId, | ||
454 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 458 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
455 | ) -> Option<MacroCallId> { | 459 | ) -> Option<MacroCallId> { |
456 | let def: MacroDefId = resolver(self.path.clone())?; | 460 | let def: MacroDefId = resolver(self.path.clone())?; |
@@ -460,13 +464,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> { | |||
460 | let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); | 464 | let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); |
461 | 465 | ||
462 | Some( | 466 | Some( |
463 | expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { | 467 | expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| { |
464 | resolver(path::ModPath::from_src(path, &hygiene)?) | 468 | resolver(path::ModPath::from_src(path, &hygiene)?) |
465 | })? | 469 | })? |
466 | .into(), | 470 | .into(), |
467 | ) | 471 | ) |
468 | } else { | 472 | } else { |
469 | Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) | 473 | Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into()) |
470 | } | 474 | } |
471 | } | 475 | } |
472 | } | 476 | } |
@@ -475,12 +479,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> { | |||
475 | fn as_call_id( | 479 | fn as_call_id( |
476 | &self, | 480 | &self, |
477 | db: &dyn db::DefDatabase, | 481 | db: &dyn db::DefDatabase, |
482 | krate: CrateId, | ||
478 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 483 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
479 | ) -> Option<MacroCallId> { | 484 | ) -> Option<MacroCallId> { |
480 | let def = resolver(self.path.clone())?; | 485 | let def = resolver(self.path.clone())?; |
481 | Some( | 486 | Some( |
482 | def.as_lazy_macro( | 487 | def.as_lazy_macro( |
483 | db.upcast(), | 488 | db.upcast(), |
489 | krate, | ||
484 | MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), | 490 | MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), |
485 | ) | 491 | ) |
486 | .into(), | 492 | .into(), |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 353a31ad4..976e5e585 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -571,16 +571,18 @@ impl DefCollector<'_> { | |||
571 | return false; | 571 | return false; |
572 | } | 572 | } |
573 | 573 | ||
574 | if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { | 574 | if let Some(call_id) = |
575 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | 575 | directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| { |
576 | self.db, | 576 | let resolved_res = self.def_map.resolve_path_fp_with_macro( |
577 | ResolveMode::Other, | 577 | self.db, |
578 | directive.module_id, | 578 | ResolveMode::Other, |
579 | &path, | 579 | directive.module_id, |
580 | BuiltinShadowMode::Module, | 580 | &path, |
581 | ); | 581 | BuiltinShadowMode::Module, |
582 | resolved_res.resolved_def.take_macros() | 582 | ); |
583 | }) { | 583 | resolved_res.resolved_def.take_macros() |
584 | }) | ||
585 | { | ||
584 | resolved.push((directive.module_id, call_id, directive.depth)); | 586 | resolved.push((directive.module_id, call_id, directive.depth)); |
585 | res = ReachedFixedPoint::No; | 587 | res = ReachedFixedPoint::No; |
586 | return false; |