diff options
189 files changed, 5636 insertions, 4414 deletions
diff --git a/Cargo.lock b/Cargo.lock index 39f43ba17..87cf1bf27 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -113,9 +113,9 @@ checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" | |||
113 | 113 | ||
114 | [[package]] | 114 | [[package]] |
115 | name = "camino" | 115 | name = "camino" |
116 | version = "1.0.1" | 116 | version = "1.0.2" |
117 | source = "registry+https://github.com/rust-lang/crates.io-index" | 117 | source = "registry+https://github.com/rust-lang/crates.io-index" |
118 | checksum = "9bb47ab72bdba43021afa16dc1ef4d80c980d366b17ed37ea8d2ebe2087075b9" | 118 | checksum = "cd065703998b183ed0b348a22555691373a9345a1431141e5778b48bb17e4703" |
119 | dependencies = [ | 119 | dependencies = [ |
120 | "serde", | 120 | "serde", |
121 | ] | 121 | ] |
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
168 | 168 | ||
169 | [[package]] | 169 | [[package]] |
170 | name = "chalk-derive" | 170 | name = "chalk-derive" |
171 | version = "0.59.0" | 171 | version = "0.60.0" |
172 | source = "registry+https://github.com/rust-lang/crates.io-index" | 172 | source = "registry+https://github.com/rust-lang/crates.io-index" |
173 | checksum = "4b9000fbcb67353dc8973ab9fd136277d321d85b79bd36b8756bb3ae0979a94a" | 173 | checksum = "ab0f74445d4fbeaf0217bc1d23978cc73b95b28e8a738b81894580dd646822d2" |
174 | dependencies = [ | 174 | dependencies = [ |
175 | "proc-macro2", | 175 | "proc-macro2", |
176 | "quote", | 176 | "quote", |
@@ -180,9 +180,9 @@ dependencies = [ | |||
180 | 180 | ||
181 | [[package]] | 181 | [[package]] |
182 | name = "chalk-ir" | 182 | name = "chalk-ir" |
183 | version = "0.59.0" | 183 | version = "0.60.0" |
184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 184 | source = "registry+https://github.com/rust-lang/crates.io-index" |
185 | checksum = "b23528d61b3557c676eccf508fa0771a38453b379f0b780154eaa7f70afe8dfc" | 185 | checksum = "294b1fc6210a5b3bd06c1d01dda48a581e2cafec80b8d659139ce45456644be2" |
186 | dependencies = [ | 186 | dependencies = [ |
187 | "bitflags", | 187 | "bitflags", |
188 | "chalk-derive", | 188 | "chalk-derive", |
@@ -191,9 +191,9 @@ dependencies = [ | |||
191 | 191 | ||
192 | [[package]] | 192 | [[package]] |
193 | name = "chalk-recursive" | 193 | name = "chalk-recursive" |
194 | version = "0.59.0" | 194 | version = "0.60.0" |
195 | source = "registry+https://github.com/rust-lang/crates.io-index" | 195 | source = "registry+https://github.com/rust-lang/crates.io-index" |
196 | checksum = "a8bdd37afc666b771de8b4429fe014363d0e74aae5cc26f320f60a3eab34d744" | 196 | checksum = "1b9386936070be4545bfa22b094b7065af79aa2aeaccc945438f1c5ffe74c30a" |
197 | dependencies = [ | 197 | dependencies = [ |
198 | "chalk-derive", | 198 | "chalk-derive", |
199 | "chalk-ir", | 199 | "chalk-ir", |
@@ -204,9 +204,9 @@ dependencies = [ | |||
204 | 204 | ||
205 | [[package]] | 205 | [[package]] |
206 | name = "chalk-solve" | 206 | name = "chalk-solve" |
207 | version = "0.59.0" | 207 | version = "0.60.0" |
208 | source = "registry+https://github.com/rust-lang/crates.io-index" | 208 | source = "registry+https://github.com/rust-lang/crates.io-index" |
209 | checksum = "4182c42ca319cb71c89898ebc3d2671d1fa7d928123b171b66f1797a2000b9c8" | 209 | checksum = "7c12a1ec7e850b50a049f27ef9cf5df3056bbd1acbb3eeb44d024e501a641f3a" |
210 | dependencies = [ | 210 | dependencies = [ |
211 | "chalk-derive", | 211 | "chalk-derive", |
212 | "chalk-ir", | 212 | "chalk-ir", |
@@ -232,15 +232,6 @@ dependencies = [ | |||
232 | ] | 232 | ] |
233 | 233 | ||
234 | [[package]] | 234 | [[package]] |
235 | name = "cmake" | ||
236 | version = "0.1.45" | ||
237 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
238 | checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" | ||
239 | dependencies = [ | ||
240 | "cc", | ||
241 | ] | ||
242 | |||
243 | [[package]] | ||
244 | name = "countme" | 235 | name = "countme" |
245 | version = "2.0.4" | 236 | version = "2.0.4" |
246 | source = "registry+https://github.com/rust-lang/crates.io-index" | 237 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -252,6 +243,12 @@ dependencies = [ | |||
252 | ] | 243 | ] |
253 | 244 | ||
254 | [[package]] | 245 | [[package]] |
246 | name = "cov-mark" | ||
247 | version = "1.1.0" | ||
248 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
249 | checksum = "9ffa3d3e0138386cd4361f63537765cac7ee40698028844635a54495a92f67f3" | ||
250 | |||
251 | [[package]] | ||
255 | name = "crc32fast" | 252 | name = "crc32fast" |
256 | version = "1.2.1" | 253 | version = "1.2.1" |
257 | source = "registry+https://github.com/rust-lang/crates.io-index" | 254 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -501,6 +498,7 @@ dependencies = [ | |||
501 | "anymap", | 498 | "anymap", |
502 | "base_db", | 499 | "base_db", |
503 | "cfg", | 500 | "cfg", |
501 | "cov-mark", | ||
504 | "drop_bomb", | 502 | "drop_bomb", |
505 | "either", | 503 | "either", |
506 | "expect-test", | 504 | "expect-test", |
@@ -547,6 +545,7 @@ dependencies = [ | |||
547 | "chalk-ir", | 545 | "chalk-ir", |
548 | "chalk-recursive", | 546 | "chalk-recursive", |
549 | "chalk-solve", | 547 | "chalk-solve", |
548 | "cov-mark", | ||
550 | "ena", | 549 | "ena", |
551 | "expect-test", | 550 | "expect-test", |
552 | "hir_def", | 551 | "hir_def", |
@@ -581,6 +580,7 @@ name = "ide" | |||
581 | version = "0.0.0" | 580 | version = "0.0.0" |
582 | dependencies = [ | 581 | dependencies = [ |
583 | "cfg", | 582 | "cfg", |
583 | "cov-mark", | ||
584 | "either", | 584 | "either", |
585 | "expect-test", | 585 | "expect-test", |
586 | "hir", | 586 | "hir", |
@@ -607,6 +607,7 @@ dependencies = [ | |||
607 | name = "ide_assists" | 607 | name = "ide_assists" |
608 | version = "0.0.0" | 608 | version = "0.0.0" |
609 | dependencies = [ | 609 | dependencies = [ |
610 | "cov-mark", | ||
610 | "either", | 611 | "either", |
611 | "expect-test", | 612 | "expect-test", |
612 | "hir", | 613 | "hir", |
@@ -625,6 +626,7 @@ name = "ide_completion" | |||
625 | version = "0.0.0" | 626 | version = "0.0.0" |
626 | dependencies = [ | 627 | dependencies = [ |
627 | "base_db", | 628 | "base_db", |
629 | "cov-mark", | ||
628 | "either", | 630 | "either", |
629 | "expect-test", | 631 | "expect-test", |
630 | "hir", | 632 | "hir", |
@@ -644,6 +646,7 @@ name = "ide_db" | |||
644 | version = "0.0.0" | 646 | version = "0.0.0" |
645 | dependencies = [ | 647 | dependencies = [ |
646 | "base_db", | 648 | "base_db", |
649 | "cov-mark", | ||
647 | "either", | 650 | "either", |
648 | "expect-test", | 651 | "expect-test", |
649 | "fst", | 652 | "fst", |
@@ -664,6 +667,7 @@ dependencies = [ | |||
664 | name = "ide_ssr" | 667 | name = "ide_ssr" |
665 | version = "0.0.0" | 668 | version = "0.0.0" |
666 | dependencies = [ | 669 | dependencies = [ |
670 | "cov-mark", | ||
667 | "expect-test", | 671 | "expect-test", |
668 | "hir", | 672 | "hir", |
669 | "ide_db", | 673 | "ide_db", |
@@ -687,9 +691,9 @@ dependencies = [ | |||
687 | 691 | ||
688 | [[package]] | 692 | [[package]] |
689 | name = "indexmap" | 693 | name = "indexmap" |
690 | version = "1.6.1" | 694 | version = "1.6.2" |
691 | source = "registry+https://github.com/rust-lang/crates.io-index" | 695 | source = "registry+https://github.com/rust-lang/crates.io-index" |
692 | checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b" | 696 | checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" |
693 | dependencies = [ | 697 | dependencies = [ |
694 | "autocfg", | 698 | "autocfg", |
695 | "hashbrown", | 699 | "hashbrown", |
@@ -789,9 +793,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | |||
789 | 793 | ||
790 | [[package]] | 794 | [[package]] |
791 | name = "libc" | 795 | name = "libc" |
792 | version = "0.2.87" | 796 | version = "0.2.88" |
793 | source = "registry+https://github.com/rust-lang/crates.io-index" | 797 | source = "registry+https://github.com/rust-lang/crates.io-index" |
794 | checksum = "265d751d31d6780a3f956bb5b8022feba2d94eeee5a84ba64f4212eedca42213" | 798 | checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" |
795 | 799 | ||
796 | [[package]] | 800 | [[package]] |
797 | name = "libloading" | 801 | name = "libloading" |
@@ -805,11 +809,11 @@ dependencies = [ | |||
805 | 809 | ||
806 | [[package]] | 810 | [[package]] |
807 | name = "libmimalloc-sys" | 811 | name = "libmimalloc-sys" |
808 | version = "0.1.20" | 812 | version = "0.1.21" |
809 | source = "registry+https://github.com/rust-lang/crates.io-index" | 813 | source = "registry+https://github.com/rust-lang/crates.io-index" |
810 | checksum = "e58f42b6424a0ed536678c65fd97cd64b4344bcf86251e284f7c0ce9eee40e64" | 814 | checksum = "2396cf99d2f58611cd69f0efeee4af3d2e2c7b61bed433515029163aa567e65c" |
811 | dependencies = [ | 815 | dependencies = [ |
812 | "cmake", | 816 | "cc", |
813 | ] | 817 | ] |
814 | 818 | ||
815 | [[package]] | 819 | [[package]] |
@@ -874,6 +878,7 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" | |||
874 | name = "mbe" | 878 | name = "mbe" |
875 | version = "0.0.0" | 879 | version = "0.0.0" |
876 | dependencies = [ | 880 | dependencies = [ |
881 | "cov-mark", | ||
877 | "log", | 882 | "log", |
878 | "parser", | 883 | "parser", |
879 | "profile", | 884 | "profile", |
@@ -921,9 +926,9 @@ dependencies = [ | |||
921 | 926 | ||
922 | [[package]] | 927 | [[package]] |
923 | name = "mimalloc" | 928 | name = "mimalloc" |
924 | version = "0.1.24" | 929 | version = "0.1.25" |
925 | source = "registry+https://github.com/rust-lang/crates.io-index" | 930 | source = "registry+https://github.com/rust-lang/crates.io-index" |
926 | checksum = "757efec188b3d2088949d912e01ea2fe87164ed6376b6c5d7dd4f3ce1668a93d" | 931 | checksum = "1e7c6b11afd1e5e689ac96b6d18b1fc763398fe3d7eed99e8773426bc2033dfb" |
927 | dependencies = [ | 932 | dependencies = [ |
928 | "libmimalloc-sys", | 933 | "libmimalloc-sys", |
929 | ] | 934 | ] |
@@ -1137,9 +1142,9 @@ dependencies = [ | |||
1137 | 1142 | ||
1138 | [[package]] | 1143 | [[package]] |
1139 | name = "pin-project-lite" | 1144 | name = "pin-project-lite" |
1140 | version = "0.2.5" | 1145 | version = "0.2.6" |
1141 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1142 | checksum = "0cf491442e4b033ed1c722cb9f0df5fcfcf4de682466c46469c36bc47dc5548a" | 1147 | checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" |
1143 | 1148 | ||
1144 | [[package]] | 1149 | [[package]] |
1145 | name = "proc-macro-hack" | 1150 | name = "proc-macro-hack" |
@@ -1479,18 +1484,18 @@ dependencies = [ | |||
1479 | 1484 | ||
1480 | [[package]] | 1485 | [[package]] |
1481 | name = "serde" | 1486 | name = "serde" |
1482 | version = "1.0.123" | 1487 | version = "1.0.124" |
1483 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1488 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1484 | checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" | 1489 | checksum = "bd761ff957cb2a45fbb9ab3da6512de9de55872866160b23c25f1a841e99d29f" |
1485 | dependencies = [ | 1490 | dependencies = [ |
1486 | "serde_derive", | 1491 | "serde_derive", |
1487 | ] | 1492 | ] |
1488 | 1493 | ||
1489 | [[package]] | 1494 | [[package]] |
1490 | name = "serde_derive" | 1495 | name = "serde_derive" |
1491 | version = "1.0.123" | 1496 | version = "1.0.124" |
1492 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1497 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1493 | checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" | 1498 | checksum = "1800f7693e94e186f5e25a28291ae1570da908aff7d97a095dec1e56ff99069b" |
1494 | dependencies = [ | 1499 | dependencies = [ |
1495 | "proc-macro2", | 1500 | "proc-macro2", |
1496 | "quote", | 1501 | "quote", |
@@ -1580,9 +1585,9 @@ dependencies = [ | |||
1580 | 1585 | ||
1581 | [[package]] | 1586 | [[package]] |
1582 | name = "syn" | 1587 | name = "syn" |
1583 | version = "1.0.60" | 1588 | version = "1.0.62" |
1584 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1589 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1585 | checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" | 1590 | checksum = "123a78a3596b24fee53a6464ce52d8ecbf62241e6294c7e7fe12086cd161f512" |
1586 | dependencies = [ | 1591 | dependencies = [ |
1587 | "proc-macro2", | 1592 | "proc-macro2", |
1588 | "quote", | 1593 | "quote", |
@@ -1606,6 +1611,7 @@ name = "syntax" | |||
1606 | version = "0.0.0" | 1611 | version = "0.0.0" |
1607 | dependencies = [ | 1612 | dependencies = [ |
1608 | "arrayvec", | 1613 | "arrayvec", |
1614 | "cov-mark", | ||
1609 | "expect-test", | 1615 | "expect-test", |
1610 | "indexmap", | 1616 | "indexmap", |
1611 | "itertools", | 1617 | "itertools", |
@@ -1640,7 +1646,6 @@ dependencies = [ | |||
1640 | "dissimilar", | 1646 | "dissimilar", |
1641 | "profile", | 1647 | "profile", |
1642 | "rustc-hash", | 1648 | "rustc-hash", |
1643 | "serde_json", | ||
1644 | "stdx", | 1649 | "stdx", |
1645 | "text-size", | 1650 | "text-size", |
1646 | ] | 1651 | ] |
@@ -1938,18 +1943,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3" | |||
1938 | 1943 | ||
1939 | [[package]] | 1944 | [[package]] |
1940 | name = "xflags" | 1945 | name = "xflags" |
1941 | version = "0.1.3" | 1946 | version = "0.2.1" |
1942 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1947 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1943 | checksum = "ddb4b07c0db813f8e2b5e1b2189ef56fcddb27a6f9ef71314dbf8cc50096a5db" | 1948 | checksum = "59ad6ce6a0b7224130015b4ebac796478ac04e0079f5d222a690efea06a9208a" |
1944 | dependencies = [ | 1949 | dependencies = [ |
1945 | "xflags-macros", | 1950 | "xflags-macros", |
1946 | ] | 1951 | ] |
1947 | 1952 | ||
1948 | [[package]] | 1953 | [[package]] |
1949 | name = "xflags-macros" | 1954 | name = "xflags-macros" |
1950 | version = "0.1.3" | 1955 | version = "0.2.1" |
1951 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1956 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1952 | checksum = "f8e168a99d6ce9d5dd0d0913f1bded279377843952dd8ff83f81b862a1dad0e1" | 1957 | checksum = "c8037d3ca14996158b03c0fa905d0834906ef0fc7044df72c1f5ff690e5e62c9" |
1953 | dependencies = [ | 1958 | dependencies = [ |
1954 | "proc-macro2", | 1959 | "proc-macro2", |
1955 | ] | 1960 | ] |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs deleted file mode 100644 index fc1a74641..000000000 --- a/crates/hir/src/code_model.rs +++ /dev/null | |||
@@ -1,2095 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | use std::{iter, sync::Arc}; | ||
3 | |||
4 | use arrayvec::ArrayVec; | ||
5 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | ||
6 | use either::Either; | ||
7 | use hir_def::{ | ||
8 | adt::{ReprKind, StructKind, VariantData}, | ||
9 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | ||
10 | import_map, | ||
11 | item_tree::ItemTreeNode, | ||
12 | lang_item::LangItemTarget, | ||
13 | path::ModPath, | ||
14 | per_ns::PerNs, | ||
15 | resolver::{HasResolver, Resolver}, | ||
16 | src::HasSource as _, | ||
17 | type_ref::TypeRef, | ||
18 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | ||
19 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | ||
20 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | ||
21 | TypeParamId, UnionId, | ||
22 | }; | ||
23 | use hir_def::{find_path::PrefixKind, item_scope::ItemInNs, visibility::Visibility}; | ||
24 | use hir_expand::{ | ||
25 | diagnostics::DiagnosticSink, | ||
26 | name::{name, AsName}, | ||
27 | MacroDefId, MacroDefKind, | ||
28 | }; | ||
29 | use hir_ty::{ | ||
30 | autoderef, | ||
31 | display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, | ||
32 | method_resolution, | ||
33 | traits::{FnTrait, Solution, SolutionVariables}, | ||
34 | AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, | ||
35 | InEnvironment, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, | ||
36 | TraitEnvironment, Ty, TyDefId, TyVariableKind, | ||
37 | }; | ||
38 | use rustc_hash::FxHashSet; | ||
39 | use stdx::{format_to, impl_from}; | ||
40 | use syntax::{ | ||
41 | ast::{self, AttrsOwner, NameOwner}, | ||
42 | AstNode, SmolStr, | ||
43 | }; | ||
44 | use tt::{Ident, Leaf, Literal, TokenTree}; | ||
45 | |||
46 | use crate::{ | ||
47 | db::{DefDatabase, HirDatabase}, | ||
48 | has_source::HasSource, | ||
49 | HirDisplay, InFile, Name, | ||
50 | }; | ||
51 | |||
52 | /// hir::Crate describes a single crate. It's the main interface with which | ||
53 | /// a crate's dependencies interact. Mostly, it should be just a proxy for the | ||
54 | /// root module. | ||
55 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
56 | pub struct Crate { | ||
57 | pub(crate) id: CrateId, | ||
58 | } | ||
59 | |||
60 | #[derive(Debug)] | ||
61 | pub struct CrateDependency { | ||
62 | pub krate: Crate, | ||
63 | pub name: Name, | ||
64 | } | ||
65 | |||
66 | impl Crate { | ||
67 | pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> { | ||
68 | db.crate_graph()[self.id] | ||
69 | .dependencies | ||
70 | .iter() | ||
71 | .map(|dep| { | ||
72 | let krate = Crate { id: dep.crate_id }; | ||
73 | let name = dep.as_name(); | ||
74 | CrateDependency { krate, name } | ||
75 | }) | ||
76 | .collect() | ||
77 | } | ||
78 | |||
79 | // FIXME: add `transitive_reverse_dependencies`. | ||
80 | pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> { | ||
81 | let crate_graph = db.crate_graph(); | ||
82 | crate_graph | ||
83 | .iter() | ||
84 | .filter(|&krate| { | ||
85 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
86 | }) | ||
87 | .map(|id| Crate { id }) | ||
88 | .collect() | ||
89 | } | ||
90 | |||
91 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { | ||
92 | let def_map = db.crate_def_map(self.id); | ||
93 | Module { id: def_map.module_id(def_map.root()) } | ||
94 | } | ||
95 | |||
96 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { | ||
97 | db.crate_graph()[self.id].root_file_id | ||
98 | } | ||
99 | |||
100 | pub fn edition(self, db: &dyn HirDatabase) -> Edition { | ||
101 | db.crate_graph()[self.id].edition | ||
102 | } | ||
103 | |||
104 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> { | ||
105 | db.crate_graph()[self.id].display_name.clone() | ||
106 | } | ||
107 | |||
108 | pub fn query_external_importables( | ||
109 | self, | ||
110 | db: &dyn DefDatabase, | ||
111 | query: import_map::Query, | ||
112 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
113 | import_map::search_dependencies(db, self.into(), query).into_iter().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 | |||
119 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | ||
120 | db.crate_graph().iter().map(|id| Crate { id }).collect() | ||
121 | } | ||
122 | |||
123 | /// Try to get the root URL of the documentation of a crate. | ||
124 | pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { | ||
125 | // Look for #![doc(html_root_url = "...")] | ||
126 | let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into())); | ||
127 | let doc_attr_q = attrs.by_key("doc"); | ||
128 | |||
129 | if !doc_attr_q.exists() { | ||
130 | return None; | ||
131 | } | ||
132 | |||
133 | let doc_url = doc_attr_q.tt_values().map(|tt| { | ||
134 | let name = tt.token_trees.iter() | ||
135 | .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url")) | ||
136 | .skip(2) | ||
137 | .next(); | ||
138 | |||
139 | match name { | ||
140 | Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), | ||
141 | _ => None | ||
142 | } | ||
143 | }).flat_map(|t| t).next(); | ||
144 | |||
145 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") | ||
146 | } | ||
147 | } | ||
148 | |||
149 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
150 | pub struct Module { | ||
151 | pub(crate) id: ModuleId, | ||
152 | } | ||
153 | |||
154 | /// The defs which can be visible in the module. | ||
155 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
156 | pub enum ModuleDef { | ||
157 | Module(Module), | ||
158 | Function(Function), | ||
159 | Adt(Adt), | ||
160 | // Can't be directly declared, but can be imported. | ||
161 | Variant(Variant), | ||
162 | Const(Const), | ||
163 | Static(Static), | ||
164 | Trait(Trait), | ||
165 | TypeAlias(TypeAlias), | ||
166 | BuiltinType(BuiltinType), | ||
167 | } | ||
168 | impl_from!( | ||
169 | Module, | ||
170 | Function, | ||
171 | Adt(Struct, Enum, Union), | ||
172 | Variant, | ||
173 | Const, | ||
174 | Static, | ||
175 | Trait, | ||
176 | TypeAlias, | ||
177 | BuiltinType | ||
178 | for ModuleDef | ||
179 | ); | ||
180 | |||
181 | impl From<VariantDef> for ModuleDef { | ||
182 | fn from(var: VariantDef) -> Self { | ||
183 | match var { | ||
184 | VariantDef::Struct(t) => Adt::from(t).into(), | ||
185 | VariantDef::Union(t) => Adt::from(t).into(), | ||
186 | VariantDef::Variant(t) => t.into(), | ||
187 | } | ||
188 | } | ||
189 | } | ||
190 | |||
191 | impl ModuleDef { | ||
192 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
193 | match self { | ||
194 | ModuleDef::Module(it) => it.parent(db), | ||
195 | ModuleDef::Function(it) => Some(it.module(db)), | ||
196 | ModuleDef::Adt(it) => Some(it.module(db)), | ||
197 | ModuleDef::Variant(it) => Some(it.module(db)), | ||
198 | ModuleDef::Const(it) => Some(it.module(db)), | ||
199 | ModuleDef::Static(it) => Some(it.module(db)), | ||
200 | ModuleDef::Trait(it) => Some(it.module(db)), | ||
201 | ModuleDef::TypeAlias(it) => Some(it.module(db)), | ||
202 | ModuleDef::BuiltinType(_) => None, | ||
203 | } | ||
204 | } | ||
205 | |||
206 | pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> { | ||
207 | let mut segments = Vec::new(); | ||
208 | segments.push(self.name(db)?.to_string()); | ||
209 | for m in self.module(db)?.path_to_root(db) { | ||
210 | segments.extend(m.name(db).map(|it| it.to_string())) | ||
211 | } | ||
212 | segments.reverse(); | ||
213 | Some(segments.join("::")) | ||
214 | } | ||
215 | |||
216 | pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> { | ||
217 | let module = match self { | ||
218 | ModuleDef::Module(it) => it.parent(db)?, | ||
219 | ModuleDef::Function(it) => return Some(it.visibility(db)), | ||
220 | ModuleDef::Adt(it) => it.module(db), | ||
221 | ModuleDef::Variant(it) => { | ||
222 | let parent = it.parent_enum(db); | ||
223 | let module = it.module(db); | ||
224 | return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))); | ||
225 | } | ||
226 | ModuleDef::Const(it) => return Some(it.visibility(db)), | ||
227 | ModuleDef::Static(it) => it.module(db), | ||
228 | ModuleDef::Trait(it) => it.module(db), | ||
229 | ModuleDef::TypeAlias(it) => return Some(it.visibility(db)), | ||
230 | ModuleDef::BuiltinType(_) => return None, | ||
231 | }; | ||
232 | |||
233 | module.visibility_of(db, self) | ||
234 | } | ||
235 | |||
236 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
237 | match self { | ||
238 | ModuleDef::Adt(it) => Some(it.name(db)), | ||
239 | ModuleDef::Trait(it) => Some(it.name(db)), | ||
240 | ModuleDef::Function(it) => Some(it.name(db)), | ||
241 | ModuleDef::Variant(it) => Some(it.name(db)), | ||
242 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | ||
243 | ModuleDef::Module(it) => it.name(db), | ||
244 | ModuleDef::Const(it) => it.name(db), | ||
245 | ModuleDef::Static(it) => it.name(db), | ||
246 | |||
247 | ModuleDef::BuiltinType(it) => Some(it.name()), | ||
248 | } | ||
249 | } | ||
250 | |||
251 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
252 | let id = match self { | ||
253 | ModuleDef::Adt(it) => match it { | ||
254 | Adt::Struct(it) => it.id.into(), | ||
255 | Adt::Enum(it) => it.id.into(), | ||
256 | Adt::Union(it) => it.id.into(), | ||
257 | }, | ||
258 | ModuleDef::Trait(it) => it.id.into(), | ||
259 | ModuleDef::Function(it) => it.id.into(), | ||
260 | ModuleDef::TypeAlias(it) => it.id.into(), | ||
261 | ModuleDef::Module(it) => it.id.into(), | ||
262 | ModuleDef::Const(it) => it.id.into(), | ||
263 | ModuleDef::Static(it) => it.id.into(), | ||
264 | _ => return, | ||
265 | }; | ||
266 | |||
267 | let module = match self.module(db) { | ||
268 | Some(it) => it, | ||
269 | None => return, | ||
270 | }; | ||
271 | |||
272 | hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) | ||
273 | } | ||
274 | } | ||
275 | |||
276 | impl Module { | ||
277 | /// Name of this module. | ||
278 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
279 | let def_map = self.id.def_map(db.upcast()); | ||
280 | let parent = def_map[self.id.local_id].parent?; | ||
281 | def_map[parent].children.iter().find_map(|(name, module_id)| { | ||
282 | if *module_id == self.id.local_id { | ||
283 | Some(name.clone()) | ||
284 | } else { | ||
285 | None | ||
286 | } | ||
287 | }) | ||
288 | } | ||
289 | |||
290 | /// Returns the crate this module is part of. | ||
291 | pub fn krate(self) -> Crate { | ||
292 | Crate { id: self.id.krate() } | ||
293 | } | ||
294 | |||
295 | /// Topmost parent of this module. Every module has a `crate_root`, but some | ||
296 | /// might be missing `krate`. This can happen if a module's file is not included | ||
297 | /// in the module tree of any target in `Cargo.toml`. | ||
298 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { | ||
299 | let def_map = db.crate_def_map(self.id.krate()); | ||
300 | Module { id: def_map.module_id(def_map.root()) } | ||
301 | } | ||
302 | |||
303 | /// Iterates over all child modules. | ||
304 | pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { | ||
305 | let def_map = self.id.def_map(db.upcast()); | ||
306 | let children = def_map[self.id.local_id] | ||
307 | .children | ||
308 | .iter() | ||
309 | .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) }) | ||
310 | .collect::<Vec<_>>(); | ||
311 | children.into_iter() | ||
312 | } | ||
313 | |||
314 | /// Finds a parent module. | ||
315 | pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> { | ||
316 | // FIXME: handle block expressions as modules (their parent is in a different DefMap) | ||
317 | let def_map = self.id.def_map(db.upcast()); | ||
318 | let parent_id = def_map[self.id.local_id].parent?; | ||
319 | Some(Module { id: def_map.module_id(parent_id) }) | ||
320 | } | ||
321 | |||
322 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { | ||
323 | let mut res = vec![self]; | ||
324 | let mut curr = self; | ||
325 | while let Some(next) = curr.parent(db) { | ||
326 | res.push(next); | ||
327 | curr = next | ||
328 | } | ||
329 | res | ||
330 | } | ||
331 | |||
332 | /// Returns a `ModuleScope`: a set of items, visible in this module. | ||
333 | pub fn scope( | ||
334 | self, | ||
335 | db: &dyn HirDatabase, | ||
336 | visible_from: Option<Module>, | ||
337 | ) -> Vec<(Name, ScopeDef)> { | ||
338 | self.id.def_map(db.upcast())[self.id.local_id] | ||
339 | .scope | ||
340 | .entries() | ||
341 | .filter_map(|(name, def)| { | ||
342 | if let Some(m) = visible_from { | ||
343 | let filtered = | ||
344 | def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id)); | ||
345 | if filtered.is_none() && !def.is_none() { | ||
346 | None | ||
347 | } else { | ||
348 | Some((name, filtered)) | ||
349 | } | ||
350 | } else { | ||
351 | Some((name, def)) | ||
352 | } | ||
353 | }) | ||
354 | .flat_map(|(name, def)| { | ||
355 | ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item)) | ||
356 | }) | ||
357 | .collect() | ||
358 | } | ||
359 | |||
360 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { | ||
361 | self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) | ||
362 | } | ||
363 | |||
364 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
365 | let _p = profile::span("Module::diagnostics").detail(|| { | ||
366 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) | ||
367 | }); | ||
368 | let crate_def_map = self.id.def_map(db.upcast()); | ||
369 | crate_def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | ||
370 | for decl in self.declarations(db) { | ||
371 | match decl { | ||
372 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
373 | crate::ModuleDef::Module(m) => { | ||
374 | // Only add diagnostics from inline modules | ||
375 | if crate_def_map[m.id.local_id].origin.is_inline() { | ||
376 | m.diagnostics(db, sink) | ||
377 | } | ||
378 | } | ||
379 | _ => { | ||
380 | decl.diagnostics(db, sink); | ||
381 | } | ||
382 | } | ||
383 | } | ||
384 | |||
385 | for impl_def in self.impl_defs(db) { | ||
386 | for item in impl_def.items(db) { | ||
387 | if let AssocItem::Function(f) = item { | ||
388 | f.diagnostics(db, sink); | ||
389 | } | ||
390 | } | ||
391 | } | ||
392 | } | ||
393 | |||
394 | pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> { | ||
395 | let def_map = self.id.def_map(db.upcast()); | ||
396 | def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect() | ||
397 | } | ||
398 | |||
399 | pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { | ||
400 | let def_map = self.id.def_map(db.upcast()); | ||
401 | def_map[self.id.local_id].scope.impls().map(Impl::from).collect() | ||
402 | } | ||
403 | |||
404 | /// Finds a path that can be used to refer to the given item from within | ||
405 | /// this module, if possible. | ||
406 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | ||
407 | hir_def::find_path::find_path(db, item.into(), self.into()) | ||
408 | } | ||
409 | |||
410 | /// Finds a path that can be used to refer to the given item from within | ||
411 | /// this module, if possible. This is used for returning import paths for use-statements. | ||
412 | pub fn find_use_path_prefixed( | ||
413 | self, | ||
414 | db: &dyn DefDatabase, | ||
415 | item: impl Into<ItemInNs>, | ||
416 | prefix_kind: PrefixKind, | ||
417 | ) -> Option<ModPath> { | ||
418 | hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind) | ||
419 | } | ||
420 | } | ||
421 | |||
422 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
423 | pub struct Field { | ||
424 | pub(crate) parent: VariantDef, | ||
425 | pub(crate) id: LocalFieldId, | ||
426 | } | ||
427 | |||
428 | #[derive(Debug, PartialEq, Eq)] | ||
429 | pub enum FieldSource { | ||
430 | Named(ast::RecordField), | ||
431 | Pos(ast::TupleField), | ||
432 | } | ||
433 | |||
434 | impl Field { | ||
435 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
436 | self.parent.variant_data(db).fields()[self.id].name.clone() | ||
437 | } | ||
438 | |||
439 | /// Returns the type as in the signature of the struct (i.e., with | ||
440 | /// placeholder types for type parameters). This is good for showing | ||
441 | /// signature help, but not so good to actually get the type of the field | ||
442 | /// when you actually have a variable of the struct. | ||
443 | pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type { | ||
444 | let var_id = self.parent.into(); | ||
445 | let generic_def_id: GenericDefId = match self.parent { | ||
446 | VariantDef::Struct(it) => it.id.into(), | ||
447 | VariantDef::Union(it) => it.id.into(), | ||
448 | VariantDef::Variant(it) => it.parent.id.into(), | ||
449 | }; | ||
450 | let substs = Substs::type_params(db, generic_def_id); | ||
451 | let ty = db.field_types(var_id)[self.id].clone().subst(&substs); | ||
452 | Type::new(db, self.parent.module(db).id.krate(), var_id, ty) | ||
453 | } | ||
454 | |||
455 | pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { | ||
456 | self.parent | ||
457 | } | ||
458 | } | ||
459 | |||
460 | impl HasVisibility for Field { | ||
461 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
462 | let variant_data = self.parent.variant_data(db); | ||
463 | let visibility = &variant_data.fields()[self.id].visibility; | ||
464 | let parent_id: hir_def::VariantId = self.parent.into(); | ||
465 | visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast())) | ||
466 | } | ||
467 | } | ||
468 | |||
469 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
470 | pub struct Struct { | ||
471 | pub(crate) id: StructId, | ||
472 | } | ||
473 | |||
474 | impl Struct { | ||
475 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
476 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
477 | } | ||
478 | |||
479 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
480 | Some(self.module(db).krate()) | ||
481 | } | ||
482 | |||
483 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
484 | db.struct_data(self.id).name.clone() | ||
485 | } | ||
486 | |||
487 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
488 | db.struct_data(self.id) | ||
489 | .variant_data | ||
490 | .fields() | ||
491 | .iter() | ||
492 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
493 | .collect() | ||
494 | } | ||
495 | |||
496 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
497 | Type::from_def( | ||
498 | db, | ||
499 | self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
500 | self.id, | ||
501 | ) | ||
502 | } | ||
503 | |||
504 | pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { | ||
505 | db.struct_data(self.id).repr.clone() | ||
506 | } | ||
507 | |||
508 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
509 | self.variant_data(db).kind() | ||
510 | } | ||
511 | |||
512 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
513 | db.struct_data(self.id).variant_data.clone() | ||
514 | } | ||
515 | } | ||
516 | |||
517 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
518 | pub struct Union { | ||
519 | pub(crate) id: UnionId, | ||
520 | } | ||
521 | |||
522 | impl Union { | ||
523 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
524 | db.union_data(self.id).name.clone() | ||
525 | } | ||
526 | |||
527 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
528 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
529 | } | ||
530 | |||
531 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
532 | Type::from_def( | ||
533 | db, | ||
534 | self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
535 | self.id, | ||
536 | ) | ||
537 | } | ||
538 | |||
539 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
540 | db.union_data(self.id) | ||
541 | .variant_data | ||
542 | .fields() | ||
543 | .iter() | ||
544 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
545 | .collect() | ||
546 | } | ||
547 | |||
548 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
549 | db.union_data(self.id).variant_data.clone() | ||
550 | } | ||
551 | } | ||
552 | |||
553 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
554 | pub struct Enum { | ||
555 | pub(crate) id: EnumId, | ||
556 | } | ||
557 | |||
558 | impl Enum { | ||
559 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
560 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
561 | } | ||
562 | |||
563 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
564 | Some(self.module(db).krate()) | ||
565 | } | ||
566 | |||
567 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
568 | db.enum_data(self.id).name.clone() | ||
569 | } | ||
570 | |||
571 | pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> { | ||
572 | db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() | ||
573 | } | ||
574 | |||
575 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
576 | Type::from_def( | ||
577 | db, | ||
578 | self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
579 | self.id, | ||
580 | ) | ||
581 | } | ||
582 | } | ||
583 | |||
584 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
585 | pub struct Variant { | ||
586 | pub(crate) parent: Enum, | ||
587 | pub(crate) id: LocalEnumVariantId, | ||
588 | } | ||
589 | |||
590 | impl Variant { | ||
591 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
592 | self.parent.module(db) | ||
593 | } | ||
594 | pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum { | ||
595 | self.parent | ||
596 | } | ||
597 | |||
598 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
599 | db.enum_data(self.parent.id).variants[self.id].name.clone() | ||
600 | } | ||
601 | |||
602 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
603 | self.variant_data(db) | ||
604 | .fields() | ||
605 | .iter() | ||
606 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
607 | .collect() | ||
608 | } | ||
609 | |||
610 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
611 | self.variant_data(db).kind() | ||
612 | } | ||
613 | |||
614 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
615 | db.enum_data(self.parent.id).variants[self.id].variant_data.clone() | ||
616 | } | ||
617 | } | ||
618 | |||
619 | /// A Data Type | ||
620 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
621 | pub enum Adt { | ||
622 | Struct(Struct), | ||
623 | Union(Union), | ||
624 | Enum(Enum), | ||
625 | } | ||
626 | impl_from!(Struct, Union, Enum for Adt); | ||
627 | |||
628 | impl Adt { | ||
629 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
630 | let subst = db.generic_defaults(self.into()); | ||
631 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
632 | } | ||
633 | |||
634 | /// Turns this ADT into a type. Any type parameters of the ADT will be | ||
635 | /// turned into unknown types, which is good for e.g. finding the most | ||
636 | /// general set of completions, but will not look very nice when printed. | ||
637 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
638 | let id = AdtId::from(self); | ||
639 | Type::from_def(db, id.module(db.upcast()).krate(), id) | ||
640 | } | ||
641 | |||
642 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
643 | match self { | ||
644 | Adt::Struct(s) => s.module(db), | ||
645 | Adt::Union(s) => s.module(db), | ||
646 | Adt::Enum(e) => e.module(db), | ||
647 | } | ||
648 | } | ||
649 | |||
650 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
651 | Some(self.module(db).krate()) | ||
652 | } | ||
653 | |||
654 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
655 | match self { | ||
656 | Adt::Struct(s) => s.name(db), | ||
657 | Adt::Union(u) => u.name(db), | ||
658 | Adt::Enum(e) => e.name(db), | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | |||
663 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
664 | pub enum VariantDef { | ||
665 | Struct(Struct), | ||
666 | Union(Union), | ||
667 | Variant(Variant), | ||
668 | } | ||
669 | impl_from!(Struct, Union, Variant for VariantDef); | ||
670 | |||
671 | impl VariantDef { | ||
672 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
673 | match self { | ||
674 | VariantDef::Struct(it) => it.fields(db), | ||
675 | VariantDef::Union(it) => it.fields(db), | ||
676 | VariantDef::Variant(it) => it.fields(db), | ||
677 | } | ||
678 | } | ||
679 | |||
680 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
681 | match self { | ||
682 | VariantDef::Struct(it) => it.module(db), | ||
683 | VariantDef::Union(it) => it.module(db), | ||
684 | VariantDef::Variant(it) => it.module(db), | ||
685 | } | ||
686 | } | ||
687 | |||
688 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
689 | match self { | ||
690 | VariantDef::Struct(s) => s.name(db), | ||
691 | VariantDef::Union(u) => u.name(db), | ||
692 | VariantDef::Variant(e) => e.name(db), | ||
693 | } | ||
694 | } | ||
695 | |||
696 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
697 | match self { | ||
698 | VariantDef::Struct(it) => it.variant_data(db), | ||
699 | VariantDef::Union(it) => it.variant_data(db), | ||
700 | VariantDef::Variant(it) => it.variant_data(db), | ||
701 | } | ||
702 | } | ||
703 | } | ||
704 | |||
705 | /// The defs which have a body. | ||
706 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
707 | pub enum DefWithBody { | ||
708 | Function(Function), | ||
709 | Static(Static), | ||
710 | Const(Const), | ||
711 | } | ||
712 | impl_from!(Function, Const, Static for DefWithBody); | ||
713 | |||
714 | impl DefWithBody { | ||
715 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
716 | match self { | ||
717 | DefWithBody::Const(c) => c.module(db), | ||
718 | DefWithBody::Function(f) => f.module(db), | ||
719 | DefWithBody::Static(s) => s.module(db), | ||
720 | } | ||
721 | } | ||
722 | |||
723 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
724 | match self { | ||
725 | DefWithBody::Function(f) => Some(f.name(db)), | ||
726 | DefWithBody::Static(s) => s.name(db), | ||
727 | DefWithBody::Const(c) => c.name(db), | ||
728 | } | ||
729 | } | ||
730 | } | ||
731 | |||
732 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
733 | pub struct Function { | ||
734 | pub(crate) id: FunctionId, | ||
735 | } | ||
736 | |||
737 | impl Function { | ||
738 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
739 | self.id.lookup(db.upcast()).module(db.upcast()).into() | ||
740 | } | ||
741 | |||
742 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
743 | Some(self.module(db).krate()) | ||
744 | } | ||
745 | |||
746 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
747 | db.function_data(self.id).name.clone() | ||
748 | } | ||
749 | |||
750 | /// Get this function's return type | ||
751 | pub fn ret_type(self, db: &dyn HirDatabase) -> Type { | ||
752 | let resolver = self.id.resolver(db.upcast()); | ||
753 | let ret_type = &db.function_data(self.id).ret_type; | ||
754 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
755 | let environment = TraitEnvironment::lower(db, &resolver); | ||
756 | Type { | ||
757 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
758 | ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment }, | ||
759 | } | ||
760 | } | ||
761 | |||
762 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { | ||
763 | if !db.function_data(self.id).has_self_param { | ||
764 | return None; | ||
765 | } | ||
766 | Some(SelfParam { func: self.id }) | ||
767 | } | ||
768 | |||
769 | pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> { | ||
770 | let resolver = self.id.resolver(db.upcast()); | ||
771 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
772 | let environment = TraitEnvironment::lower(db, &resolver); | ||
773 | db.function_data(self.id) | ||
774 | .params | ||
775 | .iter() | ||
776 | .map(|type_ref| { | ||
777 | let ty = Type { | ||
778 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
779 | ty: InEnvironment { | ||
780 | value: Ty::from_hir_ext(&ctx, type_ref).0, | ||
781 | environment: environment.clone(), | ||
782 | }, | ||
783 | }; | ||
784 | Param { ty } | ||
785 | }) | ||
786 | .collect() | ||
787 | } | ||
788 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { | ||
789 | if self.self_param(db).is_none() { | ||
790 | return None; | ||
791 | } | ||
792 | let mut res = self.assoc_fn_params(db); | ||
793 | res.remove(0); | ||
794 | Some(res) | ||
795 | } | ||
796 | |||
797 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
798 | db.function_data(self.id).is_unsafe | ||
799 | } | ||
800 | |||
801 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
802 | let krate = self.module(db).id.krate(); | ||
803 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | ||
804 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | ||
805 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | ||
806 | } | ||
807 | |||
808 | /// Whether this function declaration has a definition. | ||
809 | /// | ||
810 | /// This is false in the case of required (not provided) trait methods. | ||
811 | pub fn has_body(self, db: &dyn HirDatabase) -> bool { | ||
812 | db.function_data(self.id).has_body | ||
813 | } | ||
814 | |||
815 | /// A textual representation of the HIR of this function for debugging purposes. | ||
816 | pub fn debug_hir(self, db: &dyn HirDatabase) -> String { | ||
817 | let body = db.body(self.id.into()); | ||
818 | |||
819 | let mut result = String::new(); | ||
820 | format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); | ||
821 | for (id, expr) in body.exprs.iter() { | ||
822 | format_to!(result, "{:?}: {:?}\n", id, expr); | ||
823 | } | ||
824 | |||
825 | result | ||
826 | } | ||
827 | } | ||
828 | |||
829 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. | ||
830 | pub enum Access { | ||
831 | Shared, | ||
832 | Exclusive, | ||
833 | Owned, | ||
834 | } | ||
835 | |||
836 | impl From<Mutability> for Access { | ||
837 | fn from(mutability: Mutability) -> Access { | ||
838 | match mutability { | ||
839 | Mutability::Not => Access::Shared, | ||
840 | Mutability::Mut => Access::Exclusive, | ||
841 | } | ||
842 | } | ||
843 | } | ||
844 | |||
845 | #[derive(Debug)] | ||
846 | pub struct Param { | ||
847 | ty: Type, | ||
848 | } | ||
849 | |||
850 | impl Param { | ||
851 | pub fn ty(&self) -> &Type { | ||
852 | &self.ty | ||
853 | } | ||
854 | } | ||
855 | |||
856 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
857 | pub struct SelfParam { | ||
858 | func: FunctionId, | ||
859 | } | ||
860 | |||
861 | impl SelfParam { | ||
862 | pub fn access(self, db: &dyn HirDatabase) -> Access { | ||
863 | let func_data = db.function_data(self.func); | ||
864 | func_data | ||
865 | .params | ||
866 | .first() | ||
867 | .map(|param| match *param { | ||
868 | TypeRef::Reference(.., mutability) => match mutability { | ||
869 | hir_def::type_ref::Mutability::Shared => Access::Shared, | ||
870 | hir_def::type_ref::Mutability::Mut => Access::Exclusive, | ||
871 | }, | ||
872 | _ => Access::Owned, | ||
873 | }) | ||
874 | .unwrap_or(Access::Owned) | ||
875 | } | ||
876 | } | ||
877 | |||
878 | impl HasVisibility for Function { | ||
879 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
880 | let function_data = db.function_data(self.id); | ||
881 | let visibility = &function_data.visibility; | ||
882 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
883 | } | ||
884 | } | ||
885 | |||
886 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
887 | pub struct Const { | ||
888 | pub(crate) id: ConstId, | ||
889 | } | ||
890 | |||
891 | impl Const { | ||
892 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
893 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
894 | } | ||
895 | |||
896 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
897 | Some(self.module(db).krate()) | ||
898 | } | ||
899 | |||
900 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
901 | db.const_data(self.id).name.clone() | ||
902 | } | ||
903 | } | ||
904 | |||
905 | impl HasVisibility for Const { | ||
906 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
907 | let function_data = db.const_data(self.id); | ||
908 | let visibility = &function_data.visibility; | ||
909 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
910 | } | ||
911 | } | ||
912 | |||
913 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
914 | pub struct Static { | ||
915 | pub(crate) id: StaticId, | ||
916 | } | ||
917 | |||
918 | impl Static { | ||
919 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
920 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
921 | } | ||
922 | |||
923 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
924 | Some(self.module(db).krate()) | ||
925 | } | ||
926 | |||
927 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
928 | db.static_data(self.id).name.clone() | ||
929 | } | ||
930 | |||
931 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
932 | db.static_data(self.id).mutable | ||
933 | } | ||
934 | } | ||
935 | |||
936 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
937 | pub struct Trait { | ||
938 | pub(crate) id: TraitId, | ||
939 | } | ||
940 | |||
941 | impl Trait { | ||
942 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
943 | Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } | ||
944 | } | ||
945 | |||
946 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
947 | db.trait_data(self.id).name.clone() | ||
948 | } | ||
949 | |||
950 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
951 | db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() | ||
952 | } | ||
953 | |||
954 | pub fn is_auto(self, db: &dyn HirDatabase) -> bool { | ||
955 | db.trait_data(self.id).auto | ||
956 | } | ||
957 | } | ||
958 | |||
959 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
960 | pub struct TypeAlias { | ||
961 | pub(crate) id: TypeAliasId, | ||
962 | } | ||
963 | |||
964 | impl TypeAlias { | ||
965 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
966 | let subst = db.generic_defaults(self.id.into()); | ||
967 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
968 | } | ||
969 | |||
970 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
971 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
972 | } | ||
973 | |||
974 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
975 | Some(self.module(db).krate()) | ||
976 | } | ||
977 | |||
978 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
979 | db.type_alias_data(self.id).type_ref.clone() | ||
980 | } | ||
981 | |||
982 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
983 | Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id) | ||
984 | } | ||
985 | |||
986 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
987 | db.type_alias_data(self.id).name.clone() | ||
988 | } | ||
989 | } | ||
990 | |||
991 | impl HasVisibility for TypeAlias { | ||
992 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
993 | let function_data = db.type_alias_data(self.id); | ||
994 | let visibility = &function_data.visibility; | ||
995 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
996 | } | ||
997 | } | ||
998 | |||
999 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1000 | pub struct BuiltinType { | ||
1001 | pub(crate) inner: hir_def::builtin_type::BuiltinType, | ||
1002 | } | ||
1003 | |||
1004 | impl BuiltinType { | ||
1005 | pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type { | ||
1006 | let resolver = module.id.resolver(db.upcast()); | ||
1007 | Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner)) | ||
1008 | .expect("crate not present in resolver") | ||
1009 | } | ||
1010 | |||
1011 | pub fn name(self) -> Name { | ||
1012 | self.inner.as_name() | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1017 | pub struct MacroDef { | ||
1018 | pub(crate) id: MacroDefId, | ||
1019 | } | ||
1020 | |||
1021 | impl MacroDef { | ||
1022 | /// FIXME: right now, this just returns the root module of the crate that | ||
1023 | /// defines this macro. The reasons for this is that macros are expanded | ||
1024 | /// early, in `hir_expand`, where modules simply do not exist yet. | ||
1025 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
1026 | let krate = self.id.krate; | ||
1027 | let def_map = db.crate_def_map(krate); | ||
1028 | let module_id = def_map.root(); | ||
1029 | Some(Module { id: def_map.module_id(module_id) }) | ||
1030 | } | ||
1031 | |||
1032 | /// XXX: this parses the file | ||
1033 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1034 | self.source(db)?.value.name().map(|it| it.as_name()) | ||
1035 | } | ||
1036 | |||
1037 | /// Indicate it is a proc-macro | ||
1038 | pub fn is_proc_macro(&self) -> bool { | ||
1039 | matches!(self.id.kind, MacroDefKind::ProcMacro(_)) | ||
1040 | } | ||
1041 | |||
1042 | /// Indicate it is a derive macro | ||
1043 | pub fn is_derive_macro(&self) -> bool { | ||
1044 | matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_)) | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
1049 | /// We do not actively enforce this invariant. | ||
1050 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
1051 | pub enum AssocItem { | ||
1052 | Function(Function), | ||
1053 | Const(Const), | ||
1054 | TypeAlias(TypeAlias), | ||
1055 | } | ||
1056 | pub enum AssocItemContainer { | ||
1057 | Trait(Trait), | ||
1058 | Impl(Impl), | ||
1059 | } | ||
1060 | pub trait AsAssocItem { | ||
1061 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>; | ||
1062 | } | ||
1063 | |||
1064 | impl AsAssocItem for Function { | ||
1065 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1066 | as_assoc_item(db, AssocItem::Function, self.id) | ||
1067 | } | ||
1068 | } | ||
1069 | impl AsAssocItem for Const { | ||
1070 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1071 | as_assoc_item(db, AssocItem::Const, self.id) | ||
1072 | } | ||
1073 | } | ||
1074 | impl AsAssocItem for TypeAlias { | ||
1075 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1076 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
1077 | } | ||
1078 | } | ||
1079 | impl AsAssocItem for ModuleDef { | ||
1080 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1081 | match self { | ||
1082 | ModuleDef::Function(it) => it.as_assoc_item(db), | ||
1083 | ModuleDef::Const(it) => it.as_assoc_item(db), | ||
1084 | ModuleDef::TypeAlias(it) => it.as_assoc_item(db), | ||
1085 | _ => None, | ||
1086 | } | ||
1087 | } | ||
1088 | } | ||
1089 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
1090 | where | ||
1091 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
1092 | DEF: From<ID>, | ||
1093 | CTOR: FnOnce(DEF) -> AssocItem, | ||
1094 | AST: ItemTreeNode, | ||
1095 | { | ||
1096 | match id.lookup(db.upcast()).container { | ||
1097 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
1098 | AssocContainerId::ContainerId(_) => None, | ||
1099 | } | ||
1100 | } | ||
1101 | |||
1102 | impl AssocItem { | ||
1103 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1104 | match self { | ||
1105 | AssocItem::Function(it) => Some(it.name(db)), | ||
1106 | AssocItem::Const(it) => it.name(db), | ||
1107 | AssocItem::TypeAlias(it) => Some(it.name(db)), | ||
1108 | } | ||
1109 | } | ||
1110 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1111 | match self { | ||
1112 | AssocItem::Function(f) => f.module(db), | ||
1113 | AssocItem::Const(c) => c.module(db), | ||
1114 | AssocItem::TypeAlias(t) => t.module(db), | ||
1115 | } | ||
1116 | } | ||
1117 | pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { | ||
1118 | let container = match self { | ||
1119 | AssocItem::Function(it) => it.id.lookup(db.upcast()).container, | ||
1120 | AssocItem::Const(it) => it.id.lookup(db.upcast()).container, | ||
1121 | AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container, | ||
1122 | }; | ||
1123 | match container { | ||
1124 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
1125 | AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()), | ||
1126 | AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), | ||
1127 | } | ||
1128 | } | ||
1129 | |||
1130 | pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1131 | match self.container(db) { | ||
1132 | AssocItemContainer::Trait(t) => Some(t), | ||
1133 | _ => None, | ||
1134 | } | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | impl HasVisibility for AssocItem { | ||
1139 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1140 | match self { | ||
1141 | AssocItem::Function(f) => f.visibility(db), | ||
1142 | AssocItem::Const(c) => c.visibility(db), | ||
1143 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
1144 | } | ||
1145 | } | ||
1146 | } | ||
1147 | |||
1148 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
1149 | pub enum GenericDef { | ||
1150 | Function(Function), | ||
1151 | Adt(Adt), | ||
1152 | Trait(Trait), | ||
1153 | TypeAlias(TypeAlias), | ||
1154 | Impl(Impl), | ||
1155 | // enum variants cannot have generics themselves, but their parent enums | ||
1156 | // can, and this makes some code easier to write | ||
1157 | Variant(Variant), | ||
1158 | // consts can have type parameters from their parents (i.e. associated consts of traits) | ||
1159 | Const(Const), | ||
1160 | } | ||
1161 | impl_from!( | ||
1162 | Function, | ||
1163 | Adt(Struct, Enum, Union), | ||
1164 | Trait, | ||
1165 | TypeAlias, | ||
1166 | Impl, | ||
1167 | Variant, | ||
1168 | Const | ||
1169 | for GenericDef | ||
1170 | ); | ||
1171 | |||
1172 | impl GenericDef { | ||
1173 | pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> { | ||
1174 | let generics = db.generic_params(self.into()); | ||
1175 | let ty_params = generics | ||
1176 | .types | ||
1177 | .iter() | ||
1178 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1179 | .map(GenericParam::TypeParam); | ||
1180 | let lt_params = generics | ||
1181 | .lifetimes | ||
1182 | .iter() | ||
1183 | .map(|(local_id, _)| LifetimeParam { | ||
1184 | id: LifetimeParamId { parent: self.into(), local_id }, | ||
1185 | }) | ||
1186 | .map(GenericParam::LifetimeParam); | ||
1187 | let const_params = generics | ||
1188 | .consts | ||
1189 | .iter() | ||
1190 | .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } }) | ||
1191 | .map(GenericParam::ConstParam); | ||
1192 | ty_params.chain(lt_params).chain(const_params).collect() | ||
1193 | } | ||
1194 | |||
1195 | pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { | ||
1196 | let generics = db.generic_params(self.into()); | ||
1197 | generics | ||
1198 | .types | ||
1199 | .iter() | ||
1200 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1201 | .collect() | ||
1202 | } | ||
1203 | } | ||
1204 | |||
1205 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1206 | pub struct Local { | ||
1207 | pub(crate) parent: DefWithBodyId, | ||
1208 | pub(crate) pat_id: PatId, | ||
1209 | } | ||
1210 | |||
1211 | impl Local { | ||
1212 | pub fn is_param(self, db: &dyn HirDatabase) -> bool { | ||
1213 | let src = self.source(db); | ||
1214 | match src.value { | ||
1215 | Either::Left(bind_pat) => { | ||
1216 | bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind())) | ||
1217 | } | ||
1218 | Either::Right(_self_param) => true, | ||
1219 | } | ||
1220 | } | ||
1221 | |||
1222 | // FIXME: why is this an option? It shouldn't be? | ||
1223 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1224 | let body = db.body(self.parent.into()); | ||
1225 | match &body[self.pat_id] { | ||
1226 | Pat::Bind { name, .. } => Some(name.clone()), | ||
1227 | _ => None, | ||
1228 | } | ||
1229 | } | ||
1230 | |||
1231 | pub fn is_self(self, db: &dyn HirDatabase) -> bool { | ||
1232 | self.name(db) == Some(name![self]) | ||
1233 | } | ||
1234 | |||
1235 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
1236 | let body = db.body(self.parent.into()); | ||
1237 | match &body[self.pat_id] { | ||
1238 | Pat::Bind { mode, .. } => match mode { | ||
1239 | BindingAnnotation::Mutable | BindingAnnotation::RefMut => true, | ||
1240 | _ => false, | ||
1241 | }, | ||
1242 | _ => false, | ||
1243 | } | ||
1244 | } | ||
1245 | |||
1246 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1247 | self.parent.into() | ||
1248 | } | ||
1249 | |||
1250 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1251 | self.parent(db).module(db) | ||
1252 | } | ||
1253 | |||
1254 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1255 | let def = DefWithBodyId::from(self.parent); | ||
1256 | let infer = db.infer(def); | ||
1257 | let ty = infer[self.pat_id].clone(); | ||
1258 | let krate = def.module(db.upcast()).krate(); | ||
1259 | Type::new(db, krate, def, ty) | ||
1260 | } | ||
1261 | |||
1262 | pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> { | ||
1263 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1264 | let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm... | ||
1265 | let root = src.file_syntax(db.upcast()); | ||
1266 | src.map(|ast| { | ||
1267 | ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root)) | ||
1268 | }) | ||
1269 | } | ||
1270 | } | ||
1271 | |||
1272 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1273 | pub struct Label { | ||
1274 | pub(crate) parent: DefWithBodyId, | ||
1275 | pub(crate) label_id: LabelId, | ||
1276 | } | ||
1277 | |||
1278 | impl Label { | ||
1279 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1280 | self.parent(db).module(db) | ||
1281 | } | ||
1282 | |||
1283 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1284 | self.parent.into() | ||
1285 | } | ||
1286 | |||
1287 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1288 | let body = db.body(self.parent.into()); | ||
1289 | body[self.label_id].name.clone() | ||
1290 | } | ||
1291 | |||
1292 | pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> { | ||
1293 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1294 | let src = source_map.label_syntax(self.label_id); | ||
1295 | let root = src.file_syntax(db.upcast()); | ||
1296 | src.map(|ast| ast.to_node(&root)) | ||
1297 | } | ||
1298 | } | ||
1299 | |||
1300 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1301 | pub enum GenericParam { | ||
1302 | TypeParam(TypeParam), | ||
1303 | LifetimeParam(LifetimeParam), | ||
1304 | ConstParam(ConstParam), | ||
1305 | } | ||
1306 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); | ||
1307 | |||
1308 | impl GenericParam { | ||
1309 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1310 | match self { | ||
1311 | GenericParam::TypeParam(it) => it.module(db), | ||
1312 | GenericParam::LifetimeParam(it) => it.module(db), | ||
1313 | GenericParam::ConstParam(it) => it.module(db), | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1318 | match self { | ||
1319 | GenericParam::TypeParam(it) => it.name(db), | ||
1320 | GenericParam::LifetimeParam(it) => it.name(db), | ||
1321 | GenericParam::ConstParam(it) => it.name(db), | ||
1322 | } | ||
1323 | } | ||
1324 | } | ||
1325 | |||
1326 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1327 | pub struct TypeParam { | ||
1328 | pub(crate) id: TypeParamId, | ||
1329 | } | ||
1330 | |||
1331 | impl TypeParam { | ||
1332 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1333 | let params = db.generic_params(self.id.parent); | ||
1334 | params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing) | ||
1335 | } | ||
1336 | |||
1337 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1338 | self.id.parent.module(db.upcast()).into() | ||
1339 | } | ||
1340 | |||
1341 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1342 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1343 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1344 | let ty = Ty::Placeholder(self.id); | ||
1345 | Type { | ||
1346 | krate: self.id.parent.module(db.upcast()).krate(), | ||
1347 | ty: InEnvironment { value: ty, environment }, | ||
1348 | } | ||
1349 | } | ||
1350 | |||
1351 | pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { | ||
1352 | db.generic_predicates_for_param(self.id) | ||
1353 | .into_iter() | ||
1354 | .filter_map(|pred| match &pred.value { | ||
1355 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1356 | Some(Trait::from(trait_ref.trait_)) | ||
1357 | } | ||
1358 | _ => None, | ||
1359 | }) | ||
1360 | .collect() | ||
1361 | } | ||
1362 | |||
1363 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { | ||
1364 | let params = db.generic_defaults(self.id.parent); | ||
1365 | let local_idx = hir_ty::param_idx(db, self.id)?; | ||
1366 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1367 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1368 | let ty = params.get(local_idx)?.clone(); | ||
1369 | let subst = Substs::type_params(db, self.id.parent); | ||
1370 | let ty = ty.subst(&subst.prefix(local_idx)); | ||
1371 | Some(Type { | ||
1372 | krate: self.id.parent.module(db.upcast()).krate(), | ||
1373 | ty: InEnvironment { value: ty, environment }, | ||
1374 | }) | ||
1375 | } | ||
1376 | } | ||
1377 | |||
1378 | impl HirDisplay for TypeParam { | ||
1379 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1380 | write!(f, "{}", self.name(f.db))?; | ||
1381 | let bounds = f.db.generic_predicates_for_param(self.id); | ||
1382 | let substs = Substs::type_params(f.db, self.id.parent); | ||
1383 | let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); | ||
1384 | if !(predicates.is_empty() || f.omit_verbose_types()) { | ||
1385 | write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; | ||
1386 | } | ||
1387 | Ok(()) | ||
1388 | } | ||
1389 | } | ||
1390 | |||
1391 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1392 | pub struct LifetimeParam { | ||
1393 | pub(crate) id: LifetimeParamId, | ||
1394 | } | ||
1395 | |||
1396 | impl LifetimeParam { | ||
1397 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1398 | let params = db.generic_params(self.id.parent); | ||
1399 | params.lifetimes[self.id.local_id].name.clone() | ||
1400 | } | ||
1401 | |||
1402 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1403 | self.id.parent.module(db.upcast()).into() | ||
1404 | } | ||
1405 | |||
1406 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1407 | self.id.parent.into() | ||
1408 | } | ||
1409 | } | ||
1410 | |||
1411 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1412 | pub struct ConstParam { | ||
1413 | pub(crate) id: ConstParamId, | ||
1414 | } | ||
1415 | |||
1416 | impl ConstParam { | ||
1417 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1418 | let params = db.generic_params(self.id.parent); | ||
1419 | params.consts[self.id.local_id].name.clone() | ||
1420 | } | ||
1421 | |||
1422 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1423 | self.id.parent.module(db.upcast()).into() | ||
1424 | } | ||
1425 | |||
1426 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1427 | self.id.parent.into() | ||
1428 | } | ||
1429 | |||
1430 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1431 | let def = self.id.parent; | ||
1432 | let krate = def.module(db.upcast()).krate(); | ||
1433 | Type::new(db, krate, def, db.const_param_ty(self.id)) | ||
1434 | } | ||
1435 | } | ||
1436 | |||
1437 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1438 | pub struct Impl { | ||
1439 | pub(crate) id: ImplId, | ||
1440 | } | ||
1441 | |||
1442 | impl Impl { | ||
1443 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> { | ||
1444 | let inherent = db.inherent_impls_in_crate(krate.id); | ||
1445 | let trait_ = db.trait_impls_in_crate(krate.id); | ||
1446 | |||
1447 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | ||
1448 | } | ||
1449 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> { | ||
1450 | let impls = db.trait_impls_in_crate(krate.id); | ||
1451 | impls.for_trait(trait_.id).map(Self::from).collect() | ||
1452 | } | ||
1453 | |||
1454 | // FIXME: the return type is wrong. This should be a hir version of | ||
1455 | // `TraitRef` (ie, resolved `TypeRef`). | ||
1456 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1457 | db.impl_data(self.id).target_trait.clone() | ||
1458 | } | ||
1459 | |||
1460 | pub fn target_ty(self, db: &dyn HirDatabase) -> Type { | ||
1461 | let impl_data = db.impl_data(self.id); | ||
1462 | let resolver = self.id.resolver(db.upcast()); | ||
1463 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
1464 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1465 | let ty = Ty::from_hir(&ctx, &impl_data.target_type); | ||
1466 | Type { | ||
1467 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate(), | ||
1468 | ty: InEnvironment { value: ty, environment }, | ||
1469 | } | ||
1470 | } | ||
1471 | |||
1472 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
1473 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() | ||
1474 | } | ||
1475 | |||
1476 | pub fn is_negative(self, db: &dyn HirDatabase) -> bool { | ||
1477 | db.impl_data(self.id).is_negative | ||
1478 | } | ||
1479 | |||
1480 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1481 | self.id.lookup(db.upcast()).container.module(db.upcast()).into() | ||
1482 | } | ||
1483 | |||
1484 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { | ||
1485 | Crate { id: self.module(db).id.krate() } | ||
1486 | } | ||
1487 | |||
1488 | pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { | ||
1489 | let src = self.source(db)?; | ||
1490 | let item = src.file_id.is_builtin_derive(db.upcast())?; | ||
1491 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); | ||
1492 | |||
1493 | // FIXME: handle `cfg_attr` | ||
1494 | let attr = item | ||
1495 | .value | ||
1496 | .attrs() | ||
1497 | .filter_map(|it| { | ||
1498 | let path = ModPath::from_src(it.path()?, &hygenic)?; | ||
1499 | if path.as_ident()?.to_string() == "derive" { | ||
1500 | Some(it) | ||
1501 | } else { | ||
1502 | None | ||
1503 | } | ||
1504 | }) | ||
1505 | .last()?; | ||
1506 | |||
1507 | Some(item.with_value(attr)) | ||
1508 | } | ||
1509 | } | ||
1510 | |||
1511 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
1512 | pub struct Type { | ||
1513 | krate: CrateId, | ||
1514 | ty: InEnvironment<Ty>, | ||
1515 | } | ||
1516 | |||
1517 | impl Type { | ||
1518 | pub(crate) fn new_with_resolver( | ||
1519 | db: &dyn HirDatabase, | ||
1520 | resolver: &Resolver, | ||
1521 | ty: Ty, | ||
1522 | ) -> Option<Type> { | ||
1523 | let krate = resolver.krate()?; | ||
1524 | Some(Type::new_with_resolver_inner(db, krate, resolver, ty)) | ||
1525 | } | ||
1526 | pub(crate) fn new_with_resolver_inner( | ||
1527 | db: &dyn HirDatabase, | ||
1528 | krate: CrateId, | ||
1529 | resolver: &Resolver, | ||
1530 | ty: Ty, | ||
1531 | ) -> Type { | ||
1532 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1533 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1534 | } | ||
1535 | |||
1536 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | ||
1537 | let resolver = lexical_env.resolver(db.upcast()); | ||
1538 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1539 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1540 | } | ||
1541 | |||
1542 | fn from_def( | ||
1543 | db: &dyn HirDatabase, | ||
1544 | krate: CrateId, | ||
1545 | def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, | ||
1546 | ) -> Type { | ||
1547 | let substs = Substs::build_for_def(db, def).fill_with_unknown().build(); | ||
1548 | let ty = db.ty(def.into()).subst(&substs); | ||
1549 | Type::new(db, krate, def, ty) | ||
1550 | } | ||
1551 | |||
1552 | pub fn is_unit(&self) -> bool { | ||
1553 | matches!(self.ty.value, Ty::Tuple(0, ..)) | ||
1554 | } | ||
1555 | pub fn is_bool(&self) -> bool { | ||
1556 | matches!(self.ty.value, Ty::Scalar(Scalar::Bool)) | ||
1557 | } | ||
1558 | |||
1559 | pub fn is_mutable_reference(&self) -> bool { | ||
1560 | matches!(self.ty.value, Ty::Ref(Mutability::Mut, ..)) | ||
1561 | } | ||
1562 | |||
1563 | pub fn remove_ref(&self) -> Option<Type> { | ||
1564 | if let Ty::Ref(.., substs) = &self.ty.value { | ||
1565 | Some(self.derived(substs[0].clone())) | ||
1566 | } else { | ||
1567 | None | ||
1568 | } | ||
1569 | } | ||
1570 | |||
1571 | pub fn is_unknown(&self) -> bool { | ||
1572 | matches!(self.ty.value, Ty::Unknown) | ||
1573 | } | ||
1574 | |||
1575 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
1576 | /// This function is used in `.await` syntax completion. | ||
1577 | pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { | ||
1578 | // No special case for the type of async block, since Chalk can figure it out. | ||
1579 | |||
1580 | let krate = self.krate; | ||
1581 | |||
1582 | let std_future_trait = | ||
1583 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
1584 | let std_future_trait = match std_future_trait { | ||
1585 | Some(it) => it, | ||
1586 | None => return false, | ||
1587 | }; | ||
1588 | |||
1589 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1590 | method_resolution::implements_trait( | ||
1591 | &canonical_ty, | ||
1592 | db, | ||
1593 | self.ty.environment.clone(), | ||
1594 | krate, | ||
1595 | std_future_trait, | ||
1596 | ) | ||
1597 | } | ||
1598 | |||
1599 | /// Checks that particular type `ty` implements `std::ops::FnOnce`. | ||
1600 | /// | ||
1601 | /// This function can be used to check if a particular type is callable, since FnOnce is a | ||
1602 | /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. | ||
1603 | pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { | ||
1604 | let krate = self.krate; | ||
1605 | |||
1606 | let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { | ||
1607 | Some(it) => it, | ||
1608 | None => return false, | ||
1609 | }; | ||
1610 | |||
1611 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1612 | method_resolution::implements_trait_unique( | ||
1613 | &canonical_ty, | ||
1614 | db, | ||
1615 | self.ty.environment.clone(), | ||
1616 | krate, | ||
1617 | fnonce_trait, | ||
1618 | ) | ||
1619 | } | ||
1620 | |||
1621 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { | ||
1622 | let trait_ref = hir_ty::TraitRef { | ||
1623 | trait_: trait_.id, | ||
1624 | substs: Substs::build_for_def(db, trait_.id) | ||
1625 | .push(self.ty.value.clone()) | ||
1626 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1627 | .build(), | ||
1628 | }; | ||
1629 | |||
1630 | let goal = Canonical { | ||
1631 | value: hir_ty::InEnvironment::new( | ||
1632 | self.ty.environment.clone(), | ||
1633 | hir_ty::Obligation::Trait(trait_ref), | ||
1634 | ), | ||
1635 | kinds: Arc::new([]), | ||
1636 | }; | ||
1637 | |||
1638 | db.trait_solve(self.krate, goal).is_some() | ||
1639 | } | ||
1640 | |||
1641 | pub fn normalize_trait_assoc_type( | ||
1642 | &self, | ||
1643 | db: &dyn HirDatabase, | ||
1644 | trait_: Trait, | ||
1645 | args: &[Type], | ||
1646 | alias: TypeAlias, | ||
1647 | ) -> Option<Type> { | ||
1648 | let subst = Substs::build_for_def(db, trait_.id) | ||
1649 | .push(self.ty.value.clone()) | ||
1650 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1651 | .build(); | ||
1652 | let predicate = ProjectionPredicate { | ||
1653 | projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst }, | ||
1654 | ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)), | ||
1655 | }; | ||
1656 | let goal = Canonical { | ||
1657 | value: InEnvironment::new( | ||
1658 | self.ty.environment.clone(), | ||
1659 | Obligation::Projection(predicate), | ||
1660 | ), | ||
1661 | kinds: Arc::new([TyVariableKind::General]), | ||
1662 | }; | ||
1663 | |||
1664 | match db.trait_solve(self.krate, goal)? { | ||
1665 | Solution::Unique(SolutionVariables(subst)) => subst.value.first().cloned(), | ||
1666 | Solution::Ambig(_) => None, | ||
1667 | } | ||
1668 | .map(|ty| Type { | ||
1669 | krate: self.krate, | ||
1670 | ty: InEnvironment { value: ty, environment: Arc::clone(&self.ty.environment) }, | ||
1671 | }) | ||
1672 | } | ||
1673 | |||
1674 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { | ||
1675 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); | ||
1676 | let copy_trait = match lang_item { | ||
1677 | Some(LangItemTarget::TraitId(it)) => it, | ||
1678 | _ => return false, | ||
1679 | }; | ||
1680 | self.impls_trait(db, copy_trait.into(), &[]) | ||
1681 | } | ||
1682 | |||
1683 | pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { | ||
1684 | let def = match self.ty.value { | ||
1685 | Ty::FnDef(def, _) => Some(def), | ||
1686 | _ => None, | ||
1687 | }; | ||
1688 | |||
1689 | let sig = self.ty.value.callable_sig(db)?; | ||
1690 | Some(Callable { ty: self.clone(), sig, def, is_bound_method: false }) | ||
1691 | } | ||
1692 | |||
1693 | pub fn is_closure(&self) -> bool { | ||
1694 | matches!(&self.ty.value, Ty::Closure { .. }) | ||
1695 | } | ||
1696 | |||
1697 | pub fn is_fn(&self) -> bool { | ||
1698 | matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. }) | ||
1699 | } | ||
1700 | |||
1701 | pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { | ||
1702 | let adt_id = match self.ty.value { | ||
1703 | Ty::Adt(adt_id, ..) => adt_id, | ||
1704 | _ => return false, | ||
1705 | }; | ||
1706 | |||
1707 | let adt = adt_id.into(); | ||
1708 | match adt { | ||
1709 | Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), | ||
1710 | _ => false, | ||
1711 | } | ||
1712 | } | ||
1713 | |||
1714 | pub fn is_raw_ptr(&self) -> bool { | ||
1715 | matches!(&self.ty.value, Ty::Raw(..)) | ||
1716 | } | ||
1717 | |||
1718 | pub fn contains_unknown(&self) -> bool { | ||
1719 | return go(&self.ty.value); | ||
1720 | |||
1721 | fn go(ty: &Ty) -> bool { | ||
1722 | match ty { | ||
1723 | Ty::Unknown => true, | ||
1724 | _ => ty.substs().map_or(false, |substs| substs.iter().any(go)), | ||
1725 | } | ||
1726 | } | ||
1727 | } | ||
1728 | |||
1729 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | ||
1730 | let (variant_id, substs) = match self.ty.value { | ||
1731 | Ty::Adt(AdtId::StructId(s), ref substs) => (s.into(), substs), | ||
1732 | Ty::Adt(AdtId::UnionId(u), ref substs) => (u.into(), substs), | ||
1733 | _ => return Vec::new(), | ||
1734 | }; | ||
1735 | |||
1736 | db.field_types(variant_id) | ||
1737 | .iter() | ||
1738 | .map(|(local_id, ty)| { | ||
1739 | let def = Field { parent: variant_id.into(), id: local_id }; | ||
1740 | let ty = ty.clone().subst(substs); | ||
1741 | (def, self.derived(ty)) | ||
1742 | }) | ||
1743 | .collect() | ||
1744 | } | ||
1745 | |||
1746 | pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> { | ||
1747 | if let Ty::Tuple(_, substs) = &self.ty.value { | ||
1748 | substs.iter().map(|ty| self.derived(ty.clone())).collect() | ||
1749 | } else { | ||
1750 | Vec::new() | ||
1751 | } | ||
1752 | } | ||
1753 | |||
1754 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | ||
1755 | // There should be no inference vars in types passed here | ||
1756 | // FIXME check that? | ||
1757 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1758 | let environment = self.ty.environment.clone(); | ||
1759 | let ty = InEnvironment { value: canonical, environment }; | ||
1760 | autoderef(db, Some(self.krate), ty) | ||
1761 | .map(|canonical| canonical.value) | ||
1762 | .map(move |ty| self.derived(ty)) | ||
1763 | } | ||
1764 | |||
1765 | // This would be nicer if it just returned an iterator, but that runs into | ||
1766 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. | ||
1767 | pub fn iterate_assoc_items<T>( | ||
1768 | self, | ||
1769 | db: &dyn HirDatabase, | ||
1770 | krate: Crate, | ||
1771 | mut callback: impl FnMut(AssocItem) -> Option<T>, | ||
1772 | ) -> Option<T> { | ||
1773 | for krate in self.ty.value.def_crates(db, krate.id)? { | ||
1774 | let impls = db.inherent_impls_in_crate(krate); | ||
1775 | |||
1776 | for impl_def in impls.for_self_ty(&self.ty.value) { | ||
1777 | for &item in db.impl_data(*impl_def).items.iter() { | ||
1778 | if let Some(result) = callback(item.into()) { | ||
1779 | return Some(result); | ||
1780 | } | ||
1781 | } | ||
1782 | } | ||
1783 | } | ||
1784 | None | ||
1785 | } | ||
1786 | |||
1787 | pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { | ||
1788 | self.ty | ||
1789 | .value | ||
1790 | .strip_references() | ||
1791 | .substs() | ||
1792 | .into_iter() | ||
1793 | .flat_map(|substs| substs.iter()) | ||
1794 | .map(move |ty| self.derived(ty.clone())) | ||
1795 | } | ||
1796 | |||
1797 | pub fn iterate_method_candidates<T>( | ||
1798 | &self, | ||
1799 | db: &dyn HirDatabase, | ||
1800 | krate: Crate, | ||
1801 | traits_in_scope: &FxHashSet<TraitId>, | ||
1802 | name: Option<&Name>, | ||
1803 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1804 | ) -> Option<T> { | ||
1805 | // There should be no inference vars in types passed here | ||
1806 | // FIXME check that? | ||
1807 | // FIXME replace Unknown by bound vars here | ||
1808 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1809 | |||
1810 | let env = self.ty.environment.clone(); | ||
1811 | let krate = krate.id; | ||
1812 | |||
1813 | method_resolution::iterate_method_candidates( | ||
1814 | &canonical, | ||
1815 | db, | ||
1816 | env, | ||
1817 | krate, | ||
1818 | traits_in_scope, | ||
1819 | name, | ||
1820 | method_resolution::LookupMode::MethodCall, | ||
1821 | |ty, it| match it { | ||
1822 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1823 | _ => None, | ||
1824 | }, | ||
1825 | ) | ||
1826 | } | ||
1827 | |||
1828 | pub fn iterate_path_candidates<T>( | ||
1829 | &self, | ||
1830 | db: &dyn HirDatabase, | ||
1831 | krate: Crate, | ||
1832 | traits_in_scope: &FxHashSet<TraitId>, | ||
1833 | name: Option<&Name>, | ||
1834 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1835 | ) -> Option<T> { | ||
1836 | // There should be no inference vars in types passed here | ||
1837 | // FIXME check that? | ||
1838 | // FIXME replace Unknown by bound vars here | ||
1839 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1840 | |||
1841 | let env = self.ty.environment.clone(); | ||
1842 | let krate = krate.id; | ||
1843 | |||
1844 | method_resolution::iterate_method_candidates( | ||
1845 | &canonical, | ||
1846 | db, | ||
1847 | env, | ||
1848 | krate, | ||
1849 | traits_in_scope, | ||
1850 | name, | ||
1851 | method_resolution::LookupMode::Path, | ||
1852 | |ty, it| callback(ty, it.into()), | ||
1853 | ) | ||
1854 | } | ||
1855 | |||
1856 | pub fn as_adt(&self) -> Option<Adt> { | ||
1857 | let (adt, _subst) = self.ty.value.as_adt()?; | ||
1858 | Some(adt.into()) | ||
1859 | } | ||
1860 | |||
1861 | pub fn as_dyn_trait(&self) -> Option<Trait> { | ||
1862 | self.ty.value.dyn_trait().map(Into::into) | ||
1863 | } | ||
1864 | |||
1865 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | ||
1866 | self.ty.value.impl_trait_bounds(db).map(|it| { | ||
1867 | it.into_iter() | ||
1868 | .filter_map(|pred| match pred { | ||
1869 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1870 | Some(Trait::from(trait_ref.trait_)) | ||
1871 | } | ||
1872 | _ => None, | ||
1873 | }) | ||
1874 | .collect() | ||
1875 | }) | ||
1876 | } | ||
1877 | |||
1878 | pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1879 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | ||
1880 | } | ||
1881 | |||
1882 | // FIXME: provide required accessors such that it becomes implementable from outside. | ||
1883 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | ||
1884 | let rref = other.remove_ref(); | ||
1885 | self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value)) | ||
1886 | } | ||
1887 | |||
1888 | fn derived(&self, ty: Ty) -> Type { | ||
1889 | Type { | ||
1890 | krate: self.krate, | ||
1891 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, | ||
1892 | } | ||
1893 | } | ||
1894 | |||
1895 | pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { | ||
1896 | // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. | ||
1897 | // We need a different order here. | ||
1898 | |||
1899 | fn walk_substs( | ||
1900 | db: &dyn HirDatabase, | ||
1901 | type_: &Type, | ||
1902 | substs: &Substs, | ||
1903 | cb: &mut impl FnMut(Type), | ||
1904 | ) { | ||
1905 | for ty in substs.iter() { | ||
1906 | walk_type(db, &type_.derived(ty.clone()), cb); | ||
1907 | } | ||
1908 | } | ||
1909 | |||
1910 | fn walk_bounds( | ||
1911 | db: &dyn HirDatabase, | ||
1912 | type_: &Type, | ||
1913 | bounds: &[GenericPredicate], | ||
1914 | cb: &mut impl FnMut(Type), | ||
1915 | ) { | ||
1916 | for pred in bounds { | ||
1917 | match pred { | ||
1918 | GenericPredicate::Implemented(trait_ref) => { | ||
1919 | cb(type_.clone()); | ||
1920 | walk_substs(db, type_, &trait_ref.substs, cb); | ||
1921 | } | ||
1922 | _ => (), | ||
1923 | } | ||
1924 | } | ||
1925 | } | ||
1926 | |||
1927 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | ||
1928 | let ty = type_.ty.value.strip_references(); | ||
1929 | match ty { | ||
1930 | Ty::Adt(..) => { | ||
1931 | cb(type_.derived(ty.clone())); | ||
1932 | } | ||
1933 | Ty::AssociatedType(..) => { | ||
1934 | if let Some(_) = ty.associated_type_parent_trait(db) { | ||
1935 | cb(type_.derived(ty.clone())); | ||
1936 | } | ||
1937 | } | ||
1938 | Ty::OpaqueType(..) => { | ||
1939 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1940 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1941 | } | ||
1942 | } | ||
1943 | Ty::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
1944 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1945 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1946 | } | ||
1947 | |||
1948 | walk_substs(db, type_, &opaque_ty.parameters, cb); | ||
1949 | } | ||
1950 | Ty::Placeholder(_) => { | ||
1951 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1952 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1953 | } | ||
1954 | } | ||
1955 | Ty::Dyn(bounds) => { | ||
1956 | walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); | ||
1957 | } | ||
1958 | |||
1959 | _ => {} | ||
1960 | } | ||
1961 | if let Some(substs) = ty.substs() { | ||
1962 | walk_substs(db, type_, &substs, cb); | ||
1963 | } | ||
1964 | } | ||
1965 | |||
1966 | walk_type(db, self, &mut cb); | ||
1967 | } | ||
1968 | } | ||
1969 | |||
1970 | impl HirDisplay for Type { | ||
1971 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1972 | self.ty.value.hir_fmt(f) | ||
1973 | } | ||
1974 | } | ||
1975 | |||
1976 | // FIXME: closures | ||
1977 | #[derive(Debug)] | ||
1978 | pub struct Callable { | ||
1979 | ty: Type, | ||
1980 | sig: CallableSig, | ||
1981 | def: Option<CallableDefId>, | ||
1982 | pub(crate) is_bound_method: bool, | ||
1983 | } | ||
1984 | |||
1985 | pub enum CallableKind { | ||
1986 | Function(Function), | ||
1987 | TupleStruct(Struct), | ||
1988 | TupleEnumVariant(Variant), | ||
1989 | Closure, | ||
1990 | } | ||
1991 | |||
1992 | impl Callable { | ||
1993 | pub fn kind(&self) -> CallableKind { | ||
1994 | match self.def { | ||
1995 | Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), | ||
1996 | Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), | ||
1997 | Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), | ||
1998 | None => CallableKind::Closure, | ||
1999 | } | ||
2000 | } | ||
2001 | pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> { | ||
2002 | let func = match self.def { | ||
2003 | Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it, | ||
2004 | _ => return None, | ||
2005 | }; | ||
2006 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2007 | let param_list = src.value.param_list()?; | ||
2008 | param_list.self_param() | ||
2009 | } | ||
2010 | pub fn n_params(&self) -> usize { | ||
2011 | self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } | ||
2012 | } | ||
2013 | pub fn params( | ||
2014 | &self, | ||
2015 | db: &dyn HirDatabase, | ||
2016 | ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> { | ||
2017 | let types = self | ||
2018 | .sig | ||
2019 | .params() | ||
2020 | .iter() | ||
2021 | .skip(if self.is_bound_method { 1 } else { 0 }) | ||
2022 | .map(|ty| self.ty.derived(ty.clone())); | ||
2023 | let patterns = match self.def { | ||
2024 | Some(CallableDefId::FunctionId(func)) => { | ||
2025 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2026 | src.value.param_list().map(|param_list| { | ||
2027 | param_list | ||
2028 | .self_param() | ||
2029 | .map(|it| Some(Either::Left(it))) | ||
2030 | .filter(|_| !self.is_bound_method) | ||
2031 | .into_iter() | ||
2032 | .chain(param_list.params().map(|it| it.pat().map(Either::Right))) | ||
2033 | }) | ||
2034 | } | ||
2035 | _ => None, | ||
2036 | }; | ||
2037 | patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() | ||
2038 | } | ||
2039 | pub fn return_type(&self) -> Type { | ||
2040 | self.ty.derived(self.sig.ret().clone()) | ||
2041 | } | ||
2042 | } | ||
2043 | |||
2044 | /// For IDE only | ||
2045 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
2046 | pub enum ScopeDef { | ||
2047 | ModuleDef(ModuleDef), | ||
2048 | MacroDef(MacroDef), | ||
2049 | GenericParam(GenericParam), | ||
2050 | ImplSelfType(Impl), | ||
2051 | AdtSelfType(Adt), | ||
2052 | Local(Local), | ||
2053 | Unknown, | ||
2054 | } | ||
2055 | |||
2056 | impl ScopeDef { | ||
2057 | pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> { | ||
2058 | let mut items = ArrayVec::new(); | ||
2059 | |||
2060 | match (def.take_types(), def.take_values()) { | ||
2061 | (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())), | ||
2062 | (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())), | ||
2063 | (Some(m1), Some(m2)) => { | ||
2064 | // Some items, like unit structs and enum variants, are | ||
2065 | // returned as both a type and a value. Here we want | ||
2066 | // to de-duplicate them. | ||
2067 | if m1 != m2 { | ||
2068 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2069 | items.push(ScopeDef::ModuleDef(m2.into())); | ||
2070 | } else { | ||
2071 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2072 | } | ||
2073 | } | ||
2074 | (None, None) => {} | ||
2075 | }; | ||
2076 | |||
2077 | if let Some(macro_def_id) = def.take_macros() { | ||
2078 | items.push(ScopeDef::MacroDef(macro_def_id.into())); | ||
2079 | } | ||
2080 | |||
2081 | if items.is_empty() { | ||
2082 | items.push(ScopeDef::Unknown); | ||
2083 | } | ||
2084 | |||
2085 | items | ||
2086 | } | ||
2087 | } | ||
2088 | |||
2089 | pub trait HasVisibility { | ||
2090 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility; | ||
2091 | fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { | ||
2092 | let vis = self.visibility(db); | ||
2093 | vis.is_visible_from(db.upcast(), module.id) | ||
2094 | } | ||
2095 | } | ||
diff --git a/crates/hir/src/from_id.rs b/crates/hir/src/from_id.rs index b5814da11..179b9d51e 100644 --- a/crates/hir/src/from_id.rs +++ b/crates/hir/src/from_id.rs | |||
@@ -11,9 +11,8 @@ use hir_def::{ | |||
11 | }; | 11 | }; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
14 | code_model::{BuiltinType, GenericParam}, | 14 | Adt, AssocItem, BuiltinType, DefWithBody, Field, GenericDef, GenericParam, Label, Local, |
15 | Adt, AssocItem, DefWithBody, Field, GenericDef, Label, Local, MacroDef, ModuleDef, Variant, | 15 | MacroDef, ModuleDef, Variant, VariantDef, |
16 | VariantDef, | ||
17 | }; | 16 | }; |
18 | 17 | ||
19 | macro_rules! from_id { | 18 | macro_rules! from_id { |
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 69fcdab07..4ef38c0f0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -20,49 +20,2118 @@ | |||
20 | #![recursion_limit = "512"] | 20 | #![recursion_limit = "512"] |
21 | 21 | ||
22 | mod semantics; | 22 | mod semantics; |
23 | pub mod db; | ||
24 | mod source_analyzer; | 23 | mod source_analyzer; |
25 | 24 | ||
26 | pub mod diagnostics; | ||
27 | |||
28 | mod from_id; | 25 | mod from_id; |
29 | mod code_model; | ||
30 | mod attrs; | 26 | mod attrs; |
31 | mod has_source; | 27 | mod has_source; |
32 | 28 | ||
29 | pub mod diagnostics; | ||
30 | pub mod db; | ||
31 | |||
32 | use std::{iter, sync::Arc}; | ||
33 | |||
34 | use arrayvec::ArrayVec; | ||
35 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | ||
36 | use either::Either; | ||
37 | use hir_def::{ | ||
38 | adt::{ReprKind, VariantData}, | ||
39 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | ||
40 | item_tree::ItemTreeNode, | ||
41 | lang_item::LangItemTarget, | ||
42 | per_ns::PerNs, | ||
43 | resolver::{HasResolver, Resolver}, | ||
44 | src::HasSource as _, | ||
45 | AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, | ||
46 | DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, | ||
47 | LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | ||
48 | TypeParamId, UnionId, | ||
49 | }; | ||
50 | use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | ||
51 | use hir_ty::{ | ||
52 | autoderef, | ||
53 | display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, | ||
54 | method_resolution, | ||
55 | traits::{FnTrait, Solution, SolutionVariables}, | ||
56 | AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, | ||
57 | InEnvironment, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, TraitEnvironment, | ||
58 | Ty, TyDefId, TyVariableKind, | ||
59 | }; | ||
60 | use rustc_hash::FxHashSet; | ||
61 | use stdx::{format_to, impl_from}; | ||
62 | use syntax::{ | ||
63 | ast::{self, AttrsOwner, NameOwner}, | ||
64 | AstNode, SmolStr, | ||
65 | }; | ||
66 | use tt::{Ident, Leaf, Literal, TokenTree}; | ||
67 | |||
68 | use crate::db::{DefDatabase, HirDatabase}; | ||
69 | |||
33 | pub use crate::{ | 70 | pub use crate::{ |
34 | attrs::{HasAttrs, Namespace}, | 71 | attrs::{HasAttrs, Namespace}, |
35 | code_model::{ | ||
36 | Access, Adt, AsAssocItem, AssocItem, AssocItemContainer, BuiltinType, Callable, | ||
37 | CallableKind, Const, ConstParam, Crate, CrateDependency, DefWithBody, Enum, Field, | ||
38 | FieldSource, Function, GenericDef, GenericParam, HasVisibility, Impl, Label, LifetimeParam, | ||
39 | Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, Trait, Type, TypeAlias, | ||
40 | TypeParam, Union, Variant, VariantDef, | ||
41 | }, | ||
42 | has_source::HasSource, | 72 | has_source::HasSource, |
43 | semantics::{PathResolution, Semantics, SemanticsScope}, | 73 | semantics::{PathResolution, Semantics, SemanticsScope}, |
44 | }; | 74 | }; |
45 | 75 | ||
46 | pub use hir_def::{ | 76 | // Be careful with these re-exports. |
47 | adt::StructKind, | 77 | // |
48 | attr::{Attrs, Documentation}, | 78 | // `hir` is the boundary between the compiler and the IDE. It should try hard to |
49 | body::scope::ExprScopes, | 79 | // isolate the compiler from the ide, to allow the two to be refactored |
50 | find_path::PrefixKind, | 80 | // independently. Re-exporting something from the compiler is the sure way to |
51 | import_map, | 81 | // breach the boundary. |
52 | item_scope::ItemInNs, | 82 | // |
53 | nameres::ModuleSource, | 83 | // Generally, a refactoring which *removes* a name from this list is a good |
54 | path::{ModPath, PathKind}, | 84 | // idea! |
55 | type_ref::{Mutability, TypeRef}, | 85 | pub use { |
56 | visibility::Visibility, | 86 | hir_def::{ |
57 | }; | 87 | adt::StructKind, |
58 | pub use hir_expand::{ | 88 | attr::{Attrs, Documentation}, |
59 | name::{known, AsName, Name}, | 89 | body::scope::ExprScopes, |
60 | ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, | 90 | find_path::PrefixKind, |
61 | MacroFile, Origin, | 91 | import_map, |
92 | item_scope::ItemInNs, | ||
93 | nameres::ModuleSource, | ||
94 | path::{ModPath, PathKind}, | ||
95 | type_ref::{Mutability, TypeRef}, | ||
96 | visibility::Visibility, | ||
97 | }, | ||
98 | hir_expand::{ | ||
99 | name::{known, Name}, | ||
100 | ExpandResult, HirFileId, InFile, MacroCallId, MacroCallLoc, /* FIXME */ MacroDefId, | ||
101 | MacroFile, Origin, | ||
102 | }, | ||
103 | hir_ty::display::HirDisplay, | ||
62 | }; | 104 | }; |
63 | pub use hir_ty::display::HirDisplay; | ||
64 | 105 | ||
65 | // These are negative re-exports: pub using these names is forbidden, they | 106 | // These are negative re-exports: pub using these names is forbidden, they |
66 | // should remain private to hir internals. | 107 | // should remain private to hir internals. |
67 | #[allow(unused)] | 108 | #[allow(unused)] |
68 | use {hir_def::path::Path, hir_expand::hygiene::Hygiene}; | 109 | use { |
110 | hir_def::path::Path, | ||
111 | hir_expand::{hygiene::Hygiene, name::AsName}, | ||
112 | }; | ||
113 | |||
114 | /// hir::Crate describes a single crate. It's the main interface with which | ||
115 | /// a crate's dependencies interact. Mostly, it should be just a proxy for the | ||
116 | /// root module. | ||
117 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
118 | pub struct Crate { | ||
119 | pub(crate) id: CrateId, | ||
120 | } | ||
121 | |||
122 | #[derive(Debug)] | ||
123 | pub struct CrateDependency { | ||
124 | pub krate: Crate, | ||
125 | pub name: Name, | ||
126 | } | ||
127 | |||
128 | impl Crate { | ||
129 | pub fn dependencies(self, db: &dyn HirDatabase) -> Vec<CrateDependency> { | ||
130 | db.crate_graph()[self.id] | ||
131 | .dependencies | ||
132 | .iter() | ||
133 | .map(|dep| { | ||
134 | let krate = Crate { id: dep.crate_id }; | ||
135 | let name = dep.as_name(); | ||
136 | CrateDependency { krate, name } | ||
137 | }) | ||
138 | .collect() | ||
139 | } | ||
140 | |||
141 | // FIXME: add `transitive_reverse_dependencies`. | ||
142 | pub fn reverse_dependencies(self, db: &dyn HirDatabase) -> Vec<Crate> { | ||
143 | let crate_graph = db.crate_graph(); | ||
144 | crate_graph | ||
145 | .iter() | ||
146 | .filter(|&krate| { | ||
147 | crate_graph[krate].dependencies.iter().any(|it| it.crate_id == self.id) | ||
148 | }) | ||
149 | .map(|id| Crate { id }) | ||
150 | .collect() | ||
151 | } | ||
152 | |||
153 | pub fn root_module(self, db: &dyn HirDatabase) -> Module { | ||
154 | let def_map = db.crate_def_map(self.id); | ||
155 | Module { id: def_map.module_id(def_map.root()) } | ||
156 | } | ||
157 | |||
158 | pub fn root_file(self, db: &dyn HirDatabase) -> FileId { | ||
159 | db.crate_graph()[self.id].root_file_id | ||
160 | } | ||
161 | |||
162 | pub fn edition(self, db: &dyn HirDatabase) -> Edition { | ||
163 | db.crate_graph()[self.id].edition | ||
164 | } | ||
165 | |||
166 | pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateDisplayName> { | ||
167 | db.crate_graph()[self.id].display_name.clone() | ||
168 | } | ||
169 | |||
170 | pub fn query_external_importables( | ||
171 | self, | ||
172 | db: &dyn DefDatabase, | ||
173 | query: import_map::Query, | ||
174 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | ||
175 | import_map::search_dependencies(db, self.into(), query).into_iter().map(|item| match item { | ||
176 | ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()), | ||
177 | ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()), | ||
178 | }) | ||
179 | } | ||
180 | |||
181 | pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { | ||
182 | db.crate_graph().iter().map(|id| Crate { id }).collect() | ||
183 | } | ||
184 | |||
185 | /// Try to get the root URL of the documentation of a crate. | ||
186 | pub fn get_html_root_url(self: &Crate, db: &dyn HirDatabase) -> Option<String> { | ||
187 | // Look for #![doc(html_root_url = "...")] | ||
188 | let attrs = db.attrs(AttrDefId::ModuleId(self.root_module(db).into())); | ||
189 | let doc_attr_q = attrs.by_key("doc"); | ||
190 | |||
191 | if !doc_attr_q.exists() { | ||
192 | return None; | ||
193 | } | ||
194 | |||
195 | let doc_url = doc_attr_q.tt_values().map(|tt| { | ||
196 | let name = tt.token_trees.iter() | ||
197 | .skip_while(|tt| !matches!(tt, TokenTree::Leaf(Leaf::Ident(Ident{text: ref ident, ..})) if ident == "html_root_url")) | ||
198 | .skip(2) | ||
199 | .next(); | ||
200 | |||
201 | match name { | ||
202 | Some(TokenTree::Leaf(Leaf::Literal(Literal{ref text, ..}))) => Some(text), | ||
203 | _ => None | ||
204 | } | ||
205 | }).flat_map(|t| t).next(); | ||
206 | |||
207 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") | ||
208 | } | ||
209 | } | ||
210 | |||
211 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
212 | pub struct Module { | ||
213 | pub(crate) id: ModuleId, | ||
214 | } | ||
215 | |||
216 | /// The defs which can be visible in the module. | ||
217 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
218 | pub enum ModuleDef { | ||
219 | Module(Module), | ||
220 | Function(Function), | ||
221 | Adt(Adt), | ||
222 | // Can't be directly declared, but can be imported. | ||
223 | Variant(Variant), | ||
224 | Const(Const), | ||
225 | Static(Static), | ||
226 | Trait(Trait), | ||
227 | TypeAlias(TypeAlias), | ||
228 | BuiltinType(BuiltinType), | ||
229 | } | ||
230 | impl_from!( | ||
231 | Module, | ||
232 | Function, | ||
233 | Adt(Struct, Enum, Union), | ||
234 | Variant, | ||
235 | Const, | ||
236 | Static, | ||
237 | Trait, | ||
238 | TypeAlias, | ||
239 | BuiltinType | ||
240 | for ModuleDef | ||
241 | ); | ||
242 | |||
243 | impl From<VariantDef> for ModuleDef { | ||
244 | fn from(var: VariantDef) -> Self { | ||
245 | match var { | ||
246 | VariantDef::Struct(t) => Adt::from(t).into(), | ||
247 | VariantDef::Union(t) => Adt::from(t).into(), | ||
248 | VariantDef::Variant(t) => t.into(), | ||
249 | } | ||
250 | } | ||
251 | } | ||
252 | |||
253 | impl ModuleDef { | ||
254 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
255 | match self { | ||
256 | ModuleDef::Module(it) => it.parent(db), | ||
257 | ModuleDef::Function(it) => Some(it.module(db)), | ||
258 | ModuleDef::Adt(it) => Some(it.module(db)), | ||
259 | ModuleDef::Variant(it) => Some(it.module(db)), | ||
260 | ModuleDef::Const(it) => Some(it.module(db)), | ||
261 | ModuleDef::Static(it) => Some(it.module(db)), | ||
262 | ModuleDef::Trait(it) => Some(it.module(db)), | ||
263 | ModuleDef::TypeAlias(it) => Some(it.module(db)), | ||
264 | ModuleDef::BuiltinType(_) => None, | ||
265 | } | ||
266 | } | ||
267 | |||
268 | pub fn canonical_path(&self, db: &dyn HirDatabase) -> Option<String> { | ||
269 | let mut segments = Vec::new(); | ||
270 | segments.push(self.name(db)?.to_string()); | ||
271 | for m in self.module(db)?.path_to_root(db) { | ||
272 | segments.extend(m.name(db).map(|it| it.to_string())) | ||
273 | } | ||
274 | segments.reverse(); | ||
275 | Some(segments.join("::")) | ||
276 | } | ||
277 | |||
278 | pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> { | ||
279 | let module = match self { | ||
280 | ModuleDef::Module(it) => it.parent(db)?, | ||
281 | ModuleDef::Function(it) => return Some(it.visibility(db)), | ||
282 | ModuleDef::Adt(it) => it.module(db), | ||
283 | ModuleDef::Variant(it) => { | ||
284 | let parent = it.parent_enum(db); | ||
285 | let module = it.module(db); | ||
286 | return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))); | ||
287 | } | ||
288 | ModuleDef::Const(it) => return Some(it.visibility(db)), | ||
289 | ModuleDef::Static(it) => it.module(db), | ||
290 | ModuleDef::Trait(it) => it.module(db), | ||
291 | ModuleDef::TypeAlias(it) => return Some(it.visibility(db)), | ||
292 | ModuleDef::BuiltinType(_) => return None, | ||
293 | }; | ||
294 | |||
295 | module.visibility_of(db, self) | ||
296 | } | ||
297 | |||
298 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
299 | match self { | ||
300 | ModuleDef::Adt(it) => Some(it.name(db)), | ||
301 | ModuleDef::Trait(it) => Some(it.name(db)), | ||
302 | ModuleDef::Function(it) => Some(it.name(db)), | ||
303 | ModuleDef::Variant(it) => Some(it.name(db)), | ||
304 | ModuleDef::TypeAlias(it) => Some(it.name(db)), | ||
305 | ModuleDef::Module(it) => it.name(db), | ||
306 | ModuleDef::Const(it) => it.name(db), | ||
307 | ModuleDef::Static(it) => it.name(db), | ||
308 | |||
309 | ModuleDef::BuiltinType(it) => Some(it.name()), | ||
310 | } | ||
311 | } | ||
312 | |||
313 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
314 | let id = match self { | ||
315 | ModuleDef::Adt(it) => match it { | ||
316 | Adt::Struct(it) => it.id.into(), | ||
317 | Adt::Enum(it) => it.id.into(), | ||
318 | Adt::Union(it) => it.id.into(), | ||
319 | }, | ||
320 | ModuleDef::Trait(it) => it.id.into(), | ||
321 | ModuleDef::Function(it) => it.id.into(), | ||
322 | ModuleDef::TypeAlias(it) => it.id.into(), | ||
323 | ModuleDef::Module(it) => it.id.into(), | ||
324 | ModuleDef::Const(it) => it.id.into(), | ||
325 | ModuleDef::Static(it) => it.id.into(), | ||
326 | _ => return, | ||
327 | }; | ||
328 | |||
329 | let module = match self.module(db) { | ||
330 | Some(it) => it, | ||
331 | None => return, | ||
332 | }; | ||
333 | |||
334 | hir_ty::diagnostics::validate_module_item(db, module.id.krate(), id, sink) | ||
335 | } | ||
336 | } | ||
337 | |||
338 | impl Module { | ||
339 | /// Name of this module. | ||
340 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
341 | let def_map = self.id.def_map(db.upcast()); | ||
342 | let parent = def_map[self.id.local_id].parent?; | ||
343 | def_map[parent].children.iter().find_map(|(name, module_id)| { | ||
344 | if *module_id == self.id.local_id { | ||
345 | Some(name.clone()) | ||
346 | } else { | ||
347 | None | ||
348 | } | ||
349 | }) | ||
350 | } | ||
351 | |||
352 | /// Returns the crate this module is part of. | ||
353 | pub fn krate(self) -> Crate { | ||
354 | Crate { id: self.id.krate() } | ||
355 | } | ||
356 | |||
357 | /// Topmost parent of this module. Every module has a `crate_root`, but some | ||
358 | /// might be missing `krate`. This can happen if a module's file is not included | ||
359 | /// in the module tree of any target in `Cargo.toml`. | ||
360 | pub fn crate_root(self, db: &dyn HirDatabase) -> Module { | ||
361 | let def_map = db.crate_def_map(self.id.krate()); | ||
362 | Module { id: def_map.module_id(def_map.root()) } | ||
363 | } | ||
364 | |||
365 | /// Iterates over all child modules. | ||
366 | pub fn children(self, db: &dyn HirDatabase) -> impl Iterator<Item = Module> { | ||
367 | let def_map = self.id.def_map(db.upcast()); | ||
368 | let children = def_map[self.id.local_id] | ||
369 | .children | ||
370 | .iter() | ||
371 | .map(|(_, module_id)| Module { id: def_map.module_id(*module_id) }) | ||
372 | .collect::<Vec<_>>(); | ||
373 | children.into_iter() | ||
374 | } | ||
375 | |||
376 | /// Finds a parent module. | ||
377 | pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> { | ||
378 | // FIXME: handle block expressions as modules (their parent is in a different DefMap) | ||
379 | let def_map = self.id.def_map(db.upcast()); | ||
380 | let parent_id = def_map[self.id.local_id].parent?; | ||
381 | Some(Module { id: def_map.module_id(parent_id) }) | ||
382 | } | ||
383 | |||
384 | pub fn path_to_root(self, db: &dyn HirDatabase) -> Vec<Module> { | ||
385 | let mut res = vec![self]; | ||
386 | let mut curr = self; | ||
387 | while let Some(next) = curr.parent(db) { | ||
388 | res.push(next); | ||
389 | curr = next | ||
390 | } | ||
391 | res | ||
392 | } | ||
393 | |||
394 | /// Returns a `ModuleScope`: a set of items, visible in this module. | ||
395 | pub fn scope( | ||
396 | self, | ||
397 | db: &dyn HirDatabase, | ||
398 | visible_from: Option<Module>, | ||
399 | ) -> Vec<(Name, ScopeDef)> { | ||
400 | self.id.def_map(db.upcast())[self.id.local_id] | ||
401 | .scope | ||
402 | .entries() | ||
403 | .filter_map(|(name, def)| { | ||
404 | if let Some(m) = visible_from { | ||
405 | let filtered = | ||
406 | def.filter_visibility(|vis| vis.is_visible_from(db.upcast(), m.id)); | ||
407 | if filtered.is_none() && !def.is_none() { | ||
408 | None | ||
409 | } else { | ||
410 | Some((name, filtered)) | ||
411 | } | ||
412 | } else { | ||
413 | Some((name, def)) | ||
414 | } | ||
415 | }) | ||
416 | .flat_map(|(name, def)| { | ||
417 | ScopeDef::all_items(def).into_iter().map(move |item| (name.clone(), item)) | ||
418 | }) | ||
419 | .collect() | ||
420 | } | ||
421 | |||
422 | pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> { | ||
423 | self.id.def_map(db.upcast())[self.id.local_id].scope.visibility_of(def.clone().into()) | ||
424 | } | ||
425 | |||
426 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
427 | let _p = profile::span("Module::diagnostics").detail(|| { | ||
428 | format!("{:?}", self.name(db).map_or("<unknown>".into(), |name| name.to_string())) | ||
429 | }); | ||
430 | let def_map = self.id.def_map(db.upcast()); | ||
431 | def_map.add_diagnostics(db.upcast(), self.id.local_id, sink); | ||
432 | for decl in self.declarations(db) { | ||
433 | match decl { | ||
434 | crate::ModuleDef::Function(f) => f.diagnostics(db, sink), | ||
435 | crate::ModuleDef::Module(m) => { | ||
436 | // Only add diagnostics from inline modules | ||
437 | if def_map[m.id.local_id].origin.is_inline() { | ||
438 | m.diagnostics(db, sink) | ||
439 | } | ||
440 | } | ||
441 | _ => { | ||
442 | decl.diagnostics(db, sink); | ||
443 | } | ||
444 | } | ||
445 | } | ||
446 | |||
447 | for impl_def in self.impl_defs(db) { | ||
448 | for item in impl_def.items(db) { | ||
449 | if let AssocItem::Function(f) = item { | ||
450 | f.diagnostics(db, sink); | ||
451 | } | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | |||
456 | pub fn declarations(self, db: &dyn HirDatabase) -> Vec<ModuleDef> { | ||
457 | let def_map = self.id.def_map(db.upcast()); | ||
458 | def_map[self.id.local_id].scope.declarations().map(ModuleDef::from).collect() | ||
459 | } | ||
460 | |||
461 | pub fn impl_defs(self, db: &dyn HirDatabase) -> Vec<Impl> { | ||
462 | let def_map = self.id.def_map(db.upcast()); | ||
463 | def_map[self.id.local_id].scope.impls().map(Impl::from).collect() | ||
464 | } | ||
465 | |||
466 | /// Finds a path that can be used to refer to the given item from within | ||
467 | /// this module, if possible. | ||
468 | pub fn find_use_path(self, db: &dyn DefDatabase, item: impl Into<ItemInNs>) -> Option<ModPath> { | ||
469 | hir_def::find_path::find_path(db, item.into(), self.into()) | ||
470 | } | ||
471 | |||
472 | /// Finds a path that can be used to refer to the given item from within | ||
473 | /// this module, if possible. This is used for returning import paths for use-statements. | ||
474 | pub fn find_use_path_prefixed( | ||
475 | self, | ||
476 | db: &dyn DefDatabase, | ||
477 | item: impl Into<ItemInNs>, | ||
478 | prefix_kind: PrefixKind, | ||
479 | ) -> Option<ModPath> { | ||
480 | hir_def::find_path::find_path_prefixed(db, item.into(), self.into(), prefix_kind) | ||
481 | } | ||
482 | } | ||
483 | |||
484 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
485 | pub struct Field { | ||
486 | pub(crate) parent: VariantDef, | ||
487 | pub(crate) id: LocalFieldId, | ||
488 | } | ||
489 | |||
490 | #[derive(Debug, PartialEq, Eq)] | ||
491 | pub enum FieldSource { | ||
492 | Named(ast::RecordField), | ||
493 | Pos(ast::TupleField), | ||
494 | } | ||
495 | |||
496 | impl Field { | ||
497 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
498 | self.parent.variant_data(db).fields()[self.id].name.clone() | ||
499 | } | ||
500 | |||
501 | /// Returns the type as in the signature of the struct (i.e., with | ||
502 | /// placeholder types for type parameters). This is good for showing | ||
503 | /// signature help, but not so good to actually get the type of the field | ||
504 | /// when you actually have a variable of the struct. | ||
505 | pub fn signature_ty(&self, db: &dyn HirDatabase) -> Type { | ||
506 | let var_id = self.parent.into(); | ||
507 | let generic_def_id: GenericDefId = match self.parent { | ||
508 | VariantDef::Struct(it) => it.id.into(), | ||
509 | VariantDef::Union(it) => it.id.into(), | ||
510 | VariantDef::Variant(it) => it.parent.id.into(), | ||
511 | }; | ||
512 | let substs = Substs::type_params(db, generic_def_id); | ||
513 | let ty = db.field_types(var_id)[self.id].clone().subst(&substs); | ||
514 | Type::new(db, self.parent.module(db).id.krate(), var_id, ty) | ||
515 | } | ||
516 | |||
517 | pub fn parent_def(&self, _db: &dyn HirDatabase) -> VariantDef { | ||
518 | self.parent | ||
519 | } | ||
520 | } | ||
521 | |||
522 | impl HasVisibility for Field { | ||
523 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
524 | let variant_data = self.parent.variant_data(db); | ||
525 | let visibility = &variant_data.fields()[self.id].visibility; | ||
526 | let parent_id: hir_def::VariantId = self.parent.into(); | ||
527 | visibility.resolve(db.upcast(), &parent_id.resolver(db.upcast())) | ||
528 | } | ||
529 | } | ||
530 | |||
531 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
532 | pub struct Struct { | ||
533 | pub(crate) id: StructId, | ||
534 | } | ||
535 | |||
536 | impl Struct { | ||
537 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
538 | Module { id: self.id.lookup(db.upcast()).container } | ||
539 | } | ||
540 | |||
541 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
542 | Some(self.module(db).krate()) | ||
543 | } | ||
544 | |||
545 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
546 | db.struct_data(self.id).name.clone() | ||
547 | } | ||
548 | |||
549 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
550 | db.struct_data(self.id) | ||
551 | .variant_data | ||
552 | .fields() | ||
553 | .iter() | ||
554 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
555 | .collect() | ||
556 | } | ||
557 | |||
558 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
559 | Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id) | ||
560 | } | ||
561 | |||
562 | pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { | ||
563 | db.struct_data(self.id).repr.clone() | ||
564 | } | ||
565 | |||
566 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
567 | self.variant_data(db).kind() | ||
568 | } | ||
569 | |||
570 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
571 | db.struct_data(self.id).variant_data.clone() | ||
572 | } | ||
573 | } | ||
574 | |||
575 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
576 | pub struct Union { | ||
577 | pub(crate) id: UnionId, | ||
578 | } | ||
579 | |||
580 | impl Union { | ||
581 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
582 | db.union_data(self.id).name.clone() | ||
583 | } | ||
584 | |||
585 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
586 | Module { id: self.id.lookup(db.upcast()).container } | ||
587 | } | ||
588 | |||
589 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
590 | Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id) | ||
591 | } | ||
592 | |||
593 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
594 | db.union_data(self.id) | ||
595 | .variant_data | ||
596 | .fields() | ||
597 | .iter() | ||
598 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
599 | .collect() | ||
600 | } | ||
601 | |||
602 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
603 | db.union_data(self.id).variant_data.clone() | ||
604 | } | ||
605 | } | ||
606 | |||
607 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
608 | pub struct Enum { | ||
609 | pub(crate) id: EnumId, | ||
610 | } | ||
611 | |||
612 | impl Enum { | ||
613 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
614 | Module { id: self.id.lookup(db.upcast()).container } | ||
615 | } | ||
616 | |||
617 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
618 | Some(self.module(db).krate()) | ||
619 | } | ||
620 | |||
621 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
622 | db.enum_data(self.id).name.clone() | ||
623 | } | ||
624 | |||
625 | pub fn variants(self, db: &dyn HirDatabase) -> Vec<Variant> { | ||
626 | db.enum_data(self.id).variants.iter().map(|(id, _)| Variant { parent: self, id }).collect() | ||
627 | } | ||
628 | |||
629 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
630 | Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id) | ||
631 | } | ||
632 | } | ||
633 | |||
634 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
635 | pub struct Variant { | ||
636 | pub(crate) parent: Enum, | ||
637 | pub(crate) id: LocalEnumVariantId, | ||
638 | } | ||
639 | |||
640 | impl Variant { | ||
641 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
642 | self.parent.module(db) | ||
643 | } | ||
644 | pub fn parent_enum(self, _db: &dyn HirDatabase) -> Enum { | ||
645 | self.parent | ||
646 | } | ||
647 | |||
648 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
649 | db.enum_data(self.parent.id).variants[self.id].name.clone() | ||
650 | } | ||
651 | |||
652 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
653 | self.variant_data(db) | ||
654 | .fields() | ||
655 | .iter() | ||
656 | .map(|(id, _)| Field { parent: self.into(), id }) | ||
657 | .collect() | ||
658 | } | ||
659 | |||
660 | pub fn kind(self, db: &dyn HirDatabase) -> StructKind { | ||
661 | self.variant_data(db).kind() | ||
662 | } | ||
663 | |||
664 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
665 | db.enum_data(self.parent.id).variants[self.id].variant_data.clone() | ||
666 | } | ||
667 | } | ||
668 | |||
669 | /// A Data Type | ||
670 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
671 | pub enum Adt { | ||
672 | Struct(Struct), | ||
673 | Union(Union), | ||
674 | Enum(Enum), | ||
675 | } | ||
676 | impl_from!(Struct, Union, Enum for Adt); | ||
677 | |||
678 | impl Adt { | ||
679 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
680 | let subst = db.generic_defaults(self.into()); | ||
681 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
682 | } | ||
683 | |||
684 | /// Turns this ADT into a type. Any type parameters of the ADT will be | ||
685 | /// turned into unknown types, which is good for e.g. finding the most | ||
686 | /// general set of completions, but will not look very nice when printed. | ||
687 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
688 | let id = AdtId::from(self); | ||
689 | Type::from_def(db, id.module(db.upcast()).krate(), id) | ||
690 | } | ||
691 | |||
692 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
693 | match self { | ||
694 | Adt::Struct(s) => s.module(db), | ||
695 | Adt::Union(s) => s.module(db), | ||
696 | Adt::Enum(e) => e.module(db), | ||
697 | } | ||
698 | } | ||
699 | |||
700 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
701 | Some(self.module(db).krate()) | ||
702 | } | ||
703 | |||
704 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
705 | match self { | ||
706 | Adt::Struct(s) => s.name(db), | ||
707 | Adt::Union(u) => u.name(db), | ||
708 | Adt::Enum(e) => e.name(db), | ||
709 | } | ||
710 | } | ||
711 | } | ||
712 | |||
713 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
714 | pub enum VariantDef { | ||
715 | Struct(Struct), | ||
716 | Union(Union), | ||
717 | Variant(Variant), | ||
718 | } | ||
719 | impl_from!(Struct, Union, Variant for VariantDef); | ||
720 | |||
721 | impl VariantDef { | ||
722 | pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { | ||
723 | match self { | ||
724 | VariantDef::Struct(it) => it.fields(db), | ||
725 | VariantDef::Union(it) => it.fields(db), | ||
726 | VariantDef::Variant(it) => it.fields(db), | ||
727 | } | ||
728 | } | ||
729 | |||
730 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
731 | match self { | ||
732 | VariantDef::Struct(it) => it.module(db), | ||
733 | VariantDef::Union(it) => it.module(db), | ||
734 | VariantDef::Variant(it) => it.module(db), | ||
735 | } | ||
736 | } | ||
737 | |||
738 | pub fn name(&self, db: &dyn HirDatabase) -> Name { | ||
739 | match self { | ||
740 | VariantDef::Struct(s) => s.name(db), | ||
741 | VariantDef::Union(u) => u.name(db), | ||
742 | VariantDef::Variant(e) => e.name(db), | ||
743 | } | ||
744 | } | ||
745 | |||
746 | pub(crate) fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | ||
747 | match self { | ||
748 | VariantDef::Struct(it) => it.variant_data(db), | ||
749 | VariantDef::Union(it) => it.variant_data(db), | ||
750 | VariantDef::Variant(it) => it.variant_data(db), | ||
751 | } | ||
752 | } | ||
753 | } | ||
754 | |||
755 | /// The defs which have a body. | ||
756 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
757 | pub enum DefWithBody { | ||
758 | Function(Function), | ||
759 | Static(Static), | ||
760 | Const(Const), | ||
761 | } | ||
762 | impl_from!(Function, Const, Static for DefWithBody); | ||
763 | |||
764 | impl DefWithBody { | ||
765 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
766 | match self { | ||
767 | DefWithBody::Const(c) => c.module(db), | ||
768 | DefWithBody::Function(f) => f.module(db), | ||
769 | DefWithBody::Static(s) => s.module(db), | ||
770 | } | ||
771 | } | ||
772 | |||
773 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
774 | match self { | ||
775 | DefWithBody::Function(f) => Some(f.name(db)), | ||
776 | DefWithBody::Static(s) => s.name(db), | ||
777 | DefWithBody::Const(c) => c.name(db), | ||
778 | } | ||
779 | } | ||
780 | } | ||
781 | |||
782 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
783 | pub struct Function { | ||
784 | pub(crate) id: FunctionId, | ||
785 | } | ||
786 | |||
787 | impl Function { | ||
788 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
789 | self.id.lookup(db.upcast()).module(db.upcast()).into() | ||
790 | } | ||
791 | |||
792 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
793 | Some(self.module(db).krate()) | ||
794 | } | ||
795 | |||
796 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
797 | db.function_data(self.id).name.clone() | ||
798 | } | ||
799 | |||
800 | /// Get this function's return type | ||
801 | pub fn ret_type(self, db: &dyn HirDatabase) -> Type { | ||
802 | let resolver = self.id.resolver(db.upcast()); | ||
803 | let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate(); | ||
804 | let ret_type = &db.function_data(self.id).ret_type; | ||
805 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
806 | let ty = Ty::from_hir_ext(&ctx, ret_type).0; | ||
807 | Type::new_with_resolver_inner(db, krate, &resolver, ty) | ||
808 | } | ||
809 | |||
810 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { | ||
811 | if !db.function_data(self.id).has_self_param { | ||
812 | return None; | ||
813 | } | ||
814 | Some(SelfParam { func: self.id }) | ||
815 | } | ||
816 | |||
817 | pub fn assoc_fn_params(self, db: &dyn HirDatabase) -> Vec<Param> { | ||
818 | let resolver = self.id.resolver(db.upcast()); | ||
819 | let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate(); | ||
820 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
821 | let environment = TraitEnvironment::lower(db, &resolver); | ||
822 | db.function_data(self.id) | ||
823 | .params | ||
824 | .iter() | ||
825 | .map(|type_ref| { | ||
826 | let ty = Type { | ||
827 | krate, | ||
828 | ty: InEnvironment { | ||
829 | value: Ty::from_hir_ext(&ctx, type_ref).0, | ||
830 | environment: environment.clone(), | ||
831 | }, | ||
832 | }; | ||
833 | Param { ty } | ||
834 | }) | ||
835 | .collect() | ||
836 | } | ||
837 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { | ||
838 | if self.self_param(db).is_none() { | ||
839 | return None; | ||
840 | } | ||
841 | let mut res = self.assoc_fn_params(db); | ||
842 | res.remove(0); | ||
843 | Some(res) | ||
844 | } | ||
845 | |||
846 | pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool { | ||
847 | db.function_data(self.id).is_unsafe | ||
848 | } | ||
849 | |||
850 | pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { | ||
851 | let krate = self.module(db).id.krate(); | ||
852 | hir_def::diagnostics::validate_body(db.upcast(), self.id.into(), sink); | ||
853 | hir_ty::diagnostics::validate_module_item(db, krate, self.id.into(), sink); | ||
854 | hir_ty::diagnostics::validate_body(db, self.id.into(), sink); | ||
855 | } | ||
856 | |||
857 | /// Whether this function declaration has a definition. | ||
858 | /// | ||
859 | /// This is false in the case of required (not provided) trait methods. | ||
860 | pub fn has_body(self, db: &dyn HirDatabase) -> bool { | ||
861 | db.function_data(self.id).has_body | ||
862 | } | ||
863 | |||
864 | /// A textual representation of the HIR of this function for debugging purposes. | ||
865 | pub fn debug_hir(self, db: &dyn HirDatabase) -> String { | ||
866 | let body = db.body(self.id.into()); | ||
867 | |||
868 | let mut result = String::new(); | ||
869 | format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); | ||
870 | for (id, expr) in body.exprs.iter() { | ||
871 | format_to!(result, "{:?}: {:?}\n", id, expr); | ||
872 | } | ||
873 | |||
874 | result | ||
875 | } | ||
876 | } | ||
877 | |||
878 | // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. | ||
879 | pub enum Access { | ||
880 | Shared, | ||
881 | Exclusive, | ||
882 | Owned, | ||
883 | } | ||
884 | |||
885 | impl From<hir_ty::Mutability> for Access { | ||
886 | fn from(mutability: hir_ty::Mutability) -> Access { | ||
887 | match mutability { | ||
888 | hir_ty::Mutability::Not => Access::Shared, | ||
889 | hir_ty::Mutability::Mut => Access::Exclusive, | ||
890 | } | ||
891 | } | ||
892 | } | ||
893 | |||
894 | #[derive(Debug)] | ||
895 | pub struct Param { | ||
896 | ty: Type, | ||
897 | } | ||
898 | |||
899 | impl Param { | ||
900 | pub fn ty(&self) -> &Type { | ||
901 | &self.ty | ||
902 | } | ||
903 | } | ||
904 | |||
905 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
906 | pub struct SelfParam { | ||
907 | func: FunctionId, | ||
908 | } | ||
909 | |||
910 | impl SelfParam { | ||
911 | pub fn access(self, db: &dyn HirDatabase) -> Access { | ||
912 | let func_data = db.function_data(self.func); | ||
913 | func_data | ||
914 | .params | ||
915 | .first() | ||
916 | .map(|param| match *param { | ||
917 | TypeRef::Reference(.., mutability) => match mutability { | ||
918 | hir_def::type_ref::Mutability::Shared => Access::Shared, | ||
919 | hir_def::type_ref::Mutability::Mut => Access::Exclusive, | ||
920 | }, | ||
921 | _ => Access::Owned, | ||
922 | }) | ||
923 | .unwrap_or(Access::Owned) | ||
924 | } | ||
925 | } | ||
926 | |||
927 | impl HasVisibility for Function { | ||
928 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
929 | let function_data = db.function_data(self.id); | ||
930 | let visibility = &function_data.visibility; | ||
931 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
932 | } | ||
933 | } | ||
934 | |||
935 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
936 | pub struct Const { | ||
937 | pub(crate) id: ConstId, | ||
938 | } | ||
939 | |||
940 | impl Const { | ||
941 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
942 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
943 | } | ||
944 | |||
945 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
946 | Some(self.module(db).krate()) | ||
947 | } | ||
948 | |||
949 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
950 | db.const_data(self.id).name.clone() | ||
951 | } | ||
952 | } | ||
953 | |||
954 | impl HasVisibility for Const { | ||
955 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
956 | let function_data = db.const_data(self.id); | ||
957 | let visibility = &function_data.visibility; | ||
958 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
959 | } | ||
960 | } | ||
961 | |||
962 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
963 | pub struct Static { | ||
964 | pub(crate) id: StaticId, | ||
965 | } | ||
966 | |||
967 | impl Static { | ||
968 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
969 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
970 | } | ||
971 | |||
972 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
973 | Some(self.module(db).krate()) | ||
974 | } | ||
975 | |||
976 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
977 | db.static_data(self.id).name.clone() | ||
978 | } | ||
979 | |||
980 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
981 | db.static_data(self.id).mutable | ||
982 | } | ||
983 | } | ||
984 | |||
985 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
986 | pub struct Trait { | ||
987 | pub(crate) id: TraitId, | ||
988 | } | ||
989 | |||
990 | impl Trait { | ||
991 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
992 | Module { id: self.id.lookup(db.upcast()).container } | ||
993 | } | ||
994 | |||
995 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
996 | db.trait_data(self.id).name.clone() | ||
997 | } | ||
998 | |||
999 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
1000 | db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() | ||
1001 | } | ||
1002 | |||
1003 | pub fn is_auto(self, db: &dyn HirDatabase) -> bool { | ||
1004 | db.trait_data(self.id).auto | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1009 | pub struct TypeAlias { | ||
1010 | pub(crate) id: TypeAliasId, | ||
1011 | } | ||
1012 | |||
1013 | impl TypeAlias { | ||
1014 | pub fn has_non_default_type_params(self, db: &dyn HirDatabase) -> bool { | ||
1015 | let subst = db.generic_defaults(self.id.into()); | ||
1016 | subst.iter().any(|ty| &ty.value == &Ty::Unknown) | ||
1017 | } | ||
1018 | |||
1019 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1020 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | ||
1021 | } | ||
1022 | |||
1023 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | ||
1024 | Some(self.module(db).krate()) | ||
1025 | } | ||
1026 | |||
1027 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1028 | db.type_alias_data(self.id).type_ref.clone() | ||
1029 | } | ||
1030 | |||
1031 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1032 | Type::from_def(db, self.id.lookup(db.upcast()).module(db.upcast()).krate(), self.id) | ||
1033 | } | ||
1034 | |||
1035 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1036 | db.type_alias_data(self.id).name.clone() | ||
1037 | } | ||
1038 | } | ||
1039 | |||
1040 | impl HasVisibility for TypeAlias { | ||
1041 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1042 | let function_data = db.type_alias_data(self.id); | ||
1043 | let visibility = &function_data.visibility; | ||
1044 | visibility.resolve(db.upcast(), &self.id.resolver(db.upcast())) | ||
1045 | } | ||
1046 | } | ||
1047 | |||
1048 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1049 | pub struct BuiltinType { | ||
1050 | pub(crate) inner: hir_def::builtin_type::BuiltinType, | ||
1051 | } | ||
1052 | |||
1053 | impl BuiltinType { | ||
1054 | pub fn ty(self, db: &dyn HirDatabase, module: Module) -> Type { | ||
1055 | let resolver = module.id.resolver(db.upcast()); | ||
1056 | Type::new_with_resolver(db, &resolver, Ty::builtin(self.inner)) | ||
1057 | .expect("crate not present in resolver") | ||
1058 | } | ||
1059 | |||
1060 | pub fn name(self) -> Name { | ||
1061 | self.inner.as_name() | ||
1062 | } | ||
1063 | } | ||
1064 | |||
1065 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1066 | pub struct MacroDef { | ||
1067 | pub(crate) id: MacroDefId, | ||
1068 | } | ||
1069 | |||
1070 | impl MacroDef { | ||
1071 | /// FIXME: right now, this just returns the root module of the crate that | ||
1072 | /// defines this macro. The reasons for this is that macros are expanded | ||
1073 | /// early, in `hir_expand`, where modules simply do not exist yet. | ||
1074 | pub fn module(self, db: &dyn HirDatabase) -> Option<Module> { | ||
1075 | let krate = self.id.krate; | ||
1076 | let def_map = db.crate_def_map(krate); | ||
1077 | let module_id = def_map.root(); | ||
1078 | Some(Module { id: def_map.module_id(module_id) }) | ||
1079 | } | ||
1080 | |||
1081 | /// XXX: this parses the file | ||
1082 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1083 | self.source(db)?.value.name().map(|it| it.as_name()) | ||
1084 | } | ||
1085 | |||
1086 | /// Indicate it is a proc-macro | ||
1087 | pub fn is_proc_macro(&self) -> bool { | ||
1088 | matches!(self.id.kind, MacroDefKind::ProcMacro(_)) | ||
1089 | } | ||
1090 | |||
1091 | /// Indicate it is a derive macro | ||
1092 | pub fn is_derive_macro(&self) -> bool { | ||
1093 | matches!(self.id.kind, MacroDefKind::ProcMacro(_) | MacroDefKind::BuiltInDerive(_)) | ||
1094 | } | ||
1095 | } | ||
1096 | |||
1097 | /// Invariant: `inner.as_assoc_item(db).is_some()` | ||
1098 | /// We do not actively enforce this invariant. | ||
1099 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
1100 | pub enum AssocItem { | ||
1101 | Function(Function), | ||
1102 | Const(Const), | ||
1103 | TypeAlias(TypeAlias), | ||
1104 | } | ||
1105 | #[derive(Debug)] | ||
1106 | pub enum AssocItemContainer { | ||
1107 | Trait(Trait), | ||
1108 | Impl(Impl), | ||
1109 | } | ||
1110 | pub trait AsAssocItem { | ||
1111 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem>; | ||
1112 | } | ||
1113 | |||
1114 | impl AsAssocItem for Function { | ||
1115 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1116 | as_assoc_item(db, AssocItem::Function, self.id) | ||
1117 | } | ||
1118 | } | ||
1119 | impl AsAssocItem for Const { | ||
1120 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1121 | as_assoc_item(db, AssocItem::Const, self.id) | ||
1122 | } | ||
1123 | } | ||
1124 | impl AsAssocItem for TypeAlias { | ||
1125 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1126 | as_assoc_item(db, AssocItem::TypeAlias, self.id) | ||
1127 | } | ||
1128 | } | ||
1129 | impl AsAssocItem for ModuleDef { | ||
1130 | fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> { | ||
1131 | match self { | ||
1132 | ModuleDef::Function(it) => it.as_assoc_item(db), | ||
1133 | ModuleDef::Const(it) => it.as_assoc_item(db), | ||
1134 | ModuleDef::TypeAlias(it) => it.as_assoc_item(db), | ||
1135 | _ => None, | ||
1136 | } | ||
1137 | } | ||
1138 | } | ||
1139 | fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> | ||
1140 | where | ||
1141 | ID: Lookup<Data = AssocItemLoc<AST>>, | ||
1142 | DEF: From<ID>, | ||
1143 | CTOR: FnOnce(DEF) -> AssocItem, | ||
1144 | AST: ItemTreeNode, | ||
1145 | { | ||
1146 | match id.lookup(db.upcast()).container { | ||
1147 | AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), | ||
1148 | AssocContainerId::ModuleId(_) => None, | ||
1149 | } | ||
1150 | } | ||
1151 | |||
1152 | impl AssocItem { | ||
1153 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1154 | match self { | ||
1155 | AssocItem::Function(it) => Some(it.name(db)), | ||
1156 | AssocItem::Const(it) => it.name(db), | ||
1157 | AssocItem::TypeAlias(it) => Some(it.name(db)), | ||
1158 | } | ||
1159 | } | ||
1160 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1161 | match self { | ||
1162 | AssocItem::Function(f) => f.module(db), | ||
1163 | AssocItem::Const(c) => c.module(db), | ||
1164 | AssocItem::TypeAlias(t) => t.module(db), | ||
1165 | } | ||
1166 | } | ||
1167 | pub fn container(self, db: &dyn HirDatabase) -> AssocItemContainer { | ||
1168 | let container = match self { | ||
1169 | AssocItem::Function(it) => it.id.lookup(db.upcast()).container, | ||
1170 | AssocItem::Const(it) => it.id.lookup(db.upcast()).container, | ||
1171 | AssocItem::TypeAlias(it) => it.id.lookup(db.upcast()).container, | ||
1172 | }; | ||
1173 | match container { | ||
1174 | AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), | ||
1175 | AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()), | ||
1176 | AssocContainerId::ModuleId(_) => panic!("invalid AssocItem"), | ||
1177 | } | ||
1178 | } | ||
1179 | |||
1180 | pub fn containing_trait(self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1181 | match self.container(db) { | ||
1182 | AssocItemContainer::Trait(t) => Some(t), | ||
1183 | _ => None, | ||
1184 | } | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | impl HasVisibility for AssocItem { | ||
1189 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility { | ||
1190 | match self { | ||
1191 | AssocItem::Function(f) => f.visibility(db), | ||
1192 | AssocItem::Const(c) => c.visibility(db), | ||
1193 | AssocItem::TypeAlias(t) => t.visibility(db), | ||
1194 | } | ||
1195 | } | ||
1196 | } | ||
1197 | |||
1198 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] | ||
1199 | pub enum GenericDef { | ||
1200 | Function(Function), | ||
1201 | Adt(Adt), | ||
1202 | Trait(Trait), | ||
1203 | TypeAlias(TypeAlias), | ||
1204 | Impl(Impl), | ||
1205 | // enum variants cannot have generics themselves, but their parent enums | ||
1206 | // can, and this makes some code easier to write | ||
1207 | Variant(Variant), | ||
1208 | // consts can have type parameters from their parents (i.e. associated consts of traits) | ||
1209 | Const(Const), | ||
1210 | } | ||
1211 | impl_from!( | ||
1212 | Function, | ||
1213 | Adt(Struct, Enum, Union), | ||
1214 | Trait, | ||
1215 | TypeAlias, | ||
1216 | Impl, | ||
1217 | Variant, | ||
1218 | Const | ||
1219 | for GenericDef | ||
1220 | ); | ||
1221 | |||
1222 | impl GenericDef { | ||
1223 | pub fn params(self, db: &dyn HirDatabase) -> Vec<GenericParam> { | ||
1224 | let generics = db.generic_params(self.into()); | ||
1225 | let ty_params = generics | ||
1226 | .types | ||
1227 | .iter() | ||
1228 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1229 | .map(GenericParam::TypeParam); | ||
1230 | let lt_params = generics | ||
1231 | .lifetimes | ||
1232 | .iter() | ||
1233 | .map(|(local_id, _)| LifetimeParam { | ||
1234 | id: LifetimeParamId { parent: self.into(), local_id }, | ||
1235 | }) | ||
1236 | .map(GenericParam::LifetimeParam); | ||
1237 | let const_params = generics | ||
1238 | .consts | ||
1239 | .iter() | ||
1240 | .map(|(local_id, _)| ConstParam { id: ConstParamId { parent: self.into(), local_id } }) | ||
1241 | .map(GenericParam::ConstParam); | ||
1242 | ty_params.chain(lt_params).chain(const_params).collect() | ||
1243 | } | ||
1244 | |||
1245 | pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> { | ||
1246 | let generics = db.generic_params(self.into()); | ||
1247 | generics | ||
1248 | .types | ||
1249 | .iter() | ||
1250 | .map(|(local_id, _)| TypeParam { id: TypeParamId { parent: self.into(), local_id } }) | ||
1251 | .collect() | ||
1252 | } | ||
1253 | } | ||
1254 | |||
1255 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1256 | pub struct Local { | ||
1257 | pub(crate) parent: DefWithBodyId, | ||
1258 | pub(crate) pat_id: PatId, | ||
1259 | } | ||
1260 | |||
1261 | impl Local { | ||
1262 | pub fn is_param(self, db: &dyn HirDatabase) -> bool { | ||
1263 | let src = self.source(db); | ||
1264 | match src.value { | ||
1265 | Either::Left(bind_pat) => { | ||
1266 | bind_pat.syntax().ancestors().any(|it| ast::Param::can_cast(it.kind())) | ||
1267 | } | ||
1268 | Either::Right(_self_param) => true, | ||
1269 | } | ||
1270 | } | ||
1271 | |||
1272 | // FIXME: why is this an option? It shouldn't be? | ||
1273 | pub fn name(self, db: &dyn HirDatabase) -> Option<Name> { | ||
1274 | let body = db.body(self.parent.into()); | ||
1275 | match &body[self.pat_id] { | ||
1276 | Pat::Bind { name, .. } => Some(name.clone()), | ||
1277 | _ => None, | ||
1278 | } | ||
1279 | } | ||
1280 | |||
1281 | pub fn is_self(self, db: &dyn HirDatabase) -> bool { | ||
1282 | self.name(db) == Some(name![self]) | ||
1283 | } | ||
1284 | |||
1285 | pub fn is_mut(self, db: &dyn HirDatabase) -> bool { | ||
1286 | let body = db.body(self.parent.into()); | ||
1287 | matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. }) | ||
1288 | } | ||
1289 | |||
1290 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1291 | self.parent.into() | ||
1292 | } | ||
1293 | |||
1294 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1295 | self.parent(db).module(db) | ||
1296 | } | ||
1297 | |||
1298 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1299 | let def = DefWithBodyId::from(self.parent); | ||
1300 | let infer = db.infer(def); | ||
1301 | let ty = infer[self.pat_id].clone(); | ||
1302 | let krate = def.module(db.upcast()).krate(); | ||
1303 | Type::new(db, krate, def, ty) | ||
1304 | } | ||
1305 | |||
1306 | pub fn source(self, db: &dyn HirDatabase) -> InFile<Either<ast::IdentPat, ast::SelfParam>> { | ||
1307 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1308 | let src = source_map.pat_syntax(self.pat_id).unwrap(); // Hmm... | ||
1309 | let root = src.file_syntax(db.upcast()); | ||
1310 | src.map(|ast| { | ||
1311 | ast.map_left(|it| it.cast().unwrap().to_node(&root)).map_right(|it| it.to_node(&root)) | ||
1312 | }) | ||
1313 | } | ||
1314 | } | ||
1315 | |||
1316 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1317 | pub struct Label { | ||
1318 | pub(crate) parent: DefWithBodyId, | ||
1319 | pub(crate) label_id: LabelId, | ||
1320 | } | ||
1321 | |||
1322 | impl Label { | ||
1323 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1324 | self.parent(db).module(db) | ||
1325 | } | ||
1326 | |||
1327 | pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { | ||
1328 | self.parent.into() | ||
1329 | } | ||
1330 | |||
1331 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1332 | let body = db.body(self.parent.into()); | ||
1333 | body[self.label_id].name.clone() | ||
1334 | } | ||
1335 | |||
1336 | pub fn source(self, db: &dyn HirDatabase) -> InFile<ast::Label> { | ||
1337 | let (_body, source_map) = db.body_with_source_map(self.parent.into()); | ||
1338 | let src = source_map.label_syntax(self.label_id); | ||
1339 | let root = src.file_syntax(db.upcast()); | ||
1340 | src.map(|ast| ast.to_node(&root)) | ||
1341 | } | ||
1342 | } | ||
1343 | |||
1344 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1345 | pub enum GenericParam { | ||
1346 | TypeParam(TypeParam), | ||
1347 | LifetimeParam(LifetimeParam), | ||
1348 | ConstParam(ConstParam), | ||
1349 | } | ||
1350 | impl_from!(TypeParam, LifetimeParam, ConstParam for GenericParam); | ||
1351 | |||
1352 | impl GenericParam { | ||
1353 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1354 | match self { | ||
1355 | GenericParam::TypeParam(it) => it.module(db), | ||
1356 | GenericParam::LifetimeParam(it) => it.module(db), | ||
1357 | GenericParam::ConstParam(it) => it.module(db), | ||
1358 | } | ||
1359 | } | ||
1360 | |||
1361 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1362 | match self { | ||
1363 | GenericParam::TypeParam(it) => it.name(db), | ||
1364 | GenericParam::LifetimeParam(it) => it.name(db), | ||
1365 | GenericParam::ConstParam(it) => it.name(db), | ||
1366 | } | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1371 | pub struct TypeParam { | ||
1372 | pub(crate) id: TypeParamId, | ||
1373 | } | ||
1374 | |||
1375 | impl TypeParam { | ||
1376 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1377 | let params = db.generic_params(self.id.parent); | ||
1378 | params.types[self.id.local_id].name.clone().unwrap_or_else(Name::missing) | ||
1379 | } | ||
1380 | |||
1381 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1382 | self.id.parent.module(db.upcast()).into() | ||
1383 | } | ||
1384 | |||
1385 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1386 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1387 | let krate = self.id.parent.module(db.upcast()).krate(); | ||
1388 | let ty = Ty::Placeholder(self.id); | ||
1389 | Type::new_with_resolver_inner(db, krate, &resolver, ty) | ||
1390 | } | ||
1391 | |||
1392 | pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { | ||
1393 | db.generic_predicates_for_param(self.id) | ||
1394 | .into_iter() | ||
1395 | .filter_map(|pred| match &pred.value { | ||
1396 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1397 | Some(Trait::from(trait_ref.trait_)) | ||
1398 | } | ||
1399 | _ => None, | ||
1400 | }) | ||
1401 | .collect() | ||
1402 | } | ||
1403 | |||
1404 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { | ||
1405 | let params = db.generic_defaults(self.id.parent); | ||
1406 | let local_idx = hir_ty::param_idx(db, self.id)?; | ||
1407 | let resolver = self.id.parent.resolver(db.upcast()); | ||
1408 | let krate = self.id.parent.module(db.upcast()).krate(); | ||
1409 | let ty = params.get(local_idx)?.clone(); | ||
1410 | let subst = Substs::type_params(db, self.id.parent); | ||
1411 | let ty = ty.subst(&subst.prefix(local_idx)); | ||
1412 | Some(Type::new_with_resolver_inner(db, krate, &resolver, ty)) | ||
1413 | } | ||
1414 | } | ||
1415 | |||
1416 | impl HirDisplay for TypeParam { | ||
1417 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1418 | write!(f, "{}", self.name(f.db))?; | ||
1419 | let bounds = f.db.generic_predicates_for_param(self.id); | ||
1420 | let substs = Substs::type_params(f.db, self.id.parent); | ||
1421 | let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); | ||
1422 | if !(predicates.is_empty() || f.omit_verbose_types()) { | ||
1423 | write_bounds_like_dyn_trait_with_prefix(":", &predicates, f)?; | ||
1424 | } | ||
1425 | Ok(()) | ||
1426 | } | ||
1427 | } | ||
1428 | |||
1429 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1430 | pub struct LifetimeParam { | ||
1431 | pub(crate) id: LifetimeParamId, | ||
1432 | } | ||
1433 | |||
1434 | impl LifetimeParam { | ||
1435 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1436 | let params = db.generic_params(self.id.parent); | ||
1437 | params.lifetimes[self.id.local_id].name.clone() | ||
1438 | } | ||
1439 | |||
1440 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1441 | self.id.parent.module(db.upcast()).into() | ||
1442 | } | ||
1443 | |||
1444 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1445 | self.id.parent.into() | ||
1446 | } | ||
1447 | } | ||
1448 | |||
1449 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
1450 | pub struct ConstParam { | ||
1451 | pub(crate) id: ConstParamId, | ||
1452 | } | ||
1453 | |||
1454 | impl ConstParam { | ||
1455 | pub fn name(self, db: &dyn HirDatabase) -> Name { | ||
1456 | let params = db.generic_params(self.id.parent); | ||
1457 | params.consts[self.id.local_id].name.clone() | ||
1458 | } | ||
1459 | |||
1460 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1461 | self.id.parent.module(db.upcast()).into() | ||
1462 | } | ||
1463 | |||
1464 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | ||
1465 | self.id.parent.into() | ||
1466 | } | ||
1467 | |||
1468 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1469 | let def = self.id.parent; | ||
1470 | let krate = def.module(db.upcast()).krate(); | ||
1471 | Type::new(db, krate, def, db.const_param_ty(self.id)) | ||
1472 | } | ||
1473 | } | ||
1474 | |||
1475 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
1476 | pub struct Impl { | ||
1477 | pub(crate) id: ImplId, | ||
1478 | } | ||
1479 | |||
1480 | impl Impl { | ||
1481 | pub fn all_in_crate(db: &dyn HirDatabase, krate: Crate) -> Vec<Impl> { | ||
1482 | let inherent = db.inherent_impls_in_crate(krate.id); | ||
1483 | let trait_ = db.trait_impls_in_crate(krate.id); | ||
1484 | |||
1485 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | ||
1486 | } | ||
1487 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> { | ||
1488 | let impls = db.trait_impls_in_crate(krate.id); | ||
1489 | impls.for_trait(trait_.id).map(Self::from).collect() | ||
1490 | } | ||
1491 | |||
1492 | // FIXME: the return type is wrong. This should be a hir version of | ||
1493 | // `TraitRef` (ie, resolved `TypeRef`). | ||
1494 | pub fn target_trait(self, db: &dyn HirDatabase) -> Option<TypeRef> { | ||
1495 | db.impl_data(self.id).target_trait.clone() | ||
1496 | } | ||
1497 | |||
1498 | pub fn target_ty(self, db: &dyn HirDatabase) -> Type { | ||
1499 | let impl_data = db.impl_data(self.id); | ||
1500 | let resolver = self.id.resolver(db.upcast()); | ||
1501 | let krate = self.id.lookup(db.upcast()).container.krate(); | ||
1502 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
1503 | let ty = Ty::from_hir(&ctx, &impl_data.target_type); | ||
1504 | Type::new_with_resolver_inner(db, krate, &resolver, ty) | ||
1505 | } | ||
1506 | |||
1507 | pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> { | ||
1508 | db.impl_data(self.id).items.iter().map(|it| (*it).into()).collect() | ||
1509 | } | ||
1510 | |||
1511 | pub fn is_negative(self, db: &dyn HirDatabase) -> bool { | ||
1512 | db.impl_data(self.id).is_negative | ||
1513 | } | ||
1514 | |||
1515 | pub fn module(self, db: &dyn HirDatabase) -> Module { | ||
1516 | self.id.lookup(db.upcast()).container.into() | ||
1517 | } | ||
1518 | |||
1519 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { | ||
1520 | Crate { id: self.module(db).id.krate() } | ||
1521 | } | ||
1522 | |||
1523 | pub fn is_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> { | ||
1524 | let src = self.source(db)?; | ||
1525 | let item = src.file_id.is_builtin_derive(db.upcast())?; | ||
1526 | let hygenic = hir_expand::hygiene::Hygiene::new(db.upcast(), item.file_id); | ||
1527 | |||
1528 | // FIXME: handle `cfg_attr` | ||
1529 | let attr = item | ||
1530 | .value | ||
1531 | .attrs() | ||
1532 | .filter_map(|it| { | ||
1533 | let path = ModPath::from_src(it.path()?, &hygenic)?; | ||
1534 | if path.as_ident()?.to_string() == "derive" { | ||
1535 | Some(it) | ||
1536 | } else { | ||
1537 | None | ||
1538 | } | ||
1539 | }) | ||
1540 | .last()?; | ||
1541 | |||
1542 | Some(item.with_value(attr)) | ||
1543 | } | ||
1544 | } | ||
1545 | |||
1546 | #[derive(Clone, PartialEq, Eq, Debug)] | ||
1547 | pub struct Type { | ||
1548 | krate: CrateId, | ||
1549 | ty: InEnvironment<Ty>, | ||
1550 | } | ||
1551 | |||
1552 | impl Type { | ||
1553 | pub(crate) fn new_with_resolver( | ||
1554 | db: &dyn HirDatabase, | ||
1555 | resolver: &Resolver, | ||
1556 | ty: Ty, | ||
1557 | ) -> Option<Type> { | ||
1558 | let krate = resolver.krate()?; | ||
1559 | Some(Type::new_with_resolver_inner(db, krate, resolver, ty)) | ||
1560 | } | ||
1561 | pub(crate) fn new_with_resolver_inner( | ||
1562 | db: &dyn HirDatabase, | ||
1563 | krate: CrateId, | ||
1564 | resolver: &Resolver, | ||
1565 | ty: Ty, | ||
1566 | ) -> Type { | ||
1567 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1568 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1569 | } | ||
1570 | |||
1571 | fn new(db: &dyn HirDatabase, krate: CrateId, lexical_env: impl HasResolver, ty: Ty) -> Type { | ||
1572 | let resolver = lexical_env.resolver(db.upcast()); | ||
1573 | let environment = TraitEnvironment::lower(db, &resolver); | ||
1574 | Type { krate, ty: InEnvironment { value: ty, environment } } | ||
1575 | } | ||
1576 | |||
1577 | fn from_def( | ||
1578 | db: &dyn HirDatabase, | ||
1579 | krate: CrateId, | ||
1580 | def: impl HasResolver + Into<TyDefId> + Into<GenericDefId>, | ||
1581 | ) -> Type { | ||
1582 | let substs = Substs::build_for_def(db, def).fill_with_unknown().build(); | ||
1583 | let ty = db.ty(def.into()).subst(&substs); | ||
1584 | Type::new(db, krate, def, ty) | ||
1585 | } | ||
1586 | |||
1587 | pub fn is_unit(&self) -> bool { | ||
1588 | matches!(self.ty.value, Ty::Tuple(0, ..)) | ||
1589 | } | ||
1590 | pub fn is_bool(&self) -> bool { | ||
1591 | matches!(self.ty.value, Ty::Scalar(Scalar::Bool)) | ||
1592 | } | ||
1593 | |||
1594 | pub fn is_mutable_reference(&self) -> bool { | ||
1595 | matches!(self.ty.value, Ty::Ref(hir_ty::Mutability::Mut, ..)) | ||
1596 | } | ||
1597 | |||
1598 | pub fn remove_ref(&self) -> Option<Type> { | ||
1599 | match &self.ty.value { | ||
1600 | Ty::Ref(.., substs) => Some(self.derived(substs[0].clone())), | ||
1601 | _ => None, | ||
1602 | } | ||
1603 | } | ||
1604 | |||
1605 | pub fn is_unknown(&self) -> bool { | ||
1606 | matches!(self.ty.value, Ty::Unknown) | ||
1607 | } | ||
1608 | |||
1609 | /// Checks that particular type `ty` implements `std::future::Future`. | ||
1610 | /// This function is used in `.await` syntax completion. | ||
1611 | pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { | ||
1612 | // No special case for the type of async block, since Chalk can figure it out. | ||
1613 | |||
1614 | let krate = self.krate; | ||
1615 | |||
1616 | let std_future_trait = | ||
1617 | db.lang_item(krate, "future_trait".into()).and_then(|it| it.as_trait()); | ||
1618 | let std_future_trait = match std_future_trait { | ||
1619 | Some(it) => it, | ||
1620 | None => return false, | ||
1621 | }; | ||
1622 | |||
1623 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1624 | method_resolution::implements_trait( | ||
1625 | &canonical_ty, | ||
1626 | db, | ||
1627 | self.ty.environment.clone(), | ||
1628 | krate, | ||
1629 | std_future_trait, | ||
1630 | ) | ||
1631 | } | ||
1632 | |||
1633 | /// Checks that particular type `ty` implements `std::ops::FnOnce`. | ||
1634 | /// | ||
1635 | /// This function can be used to check if a particular type is callable, since FnOnce is a | ||
1636 | /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. | ||
1637 | pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { | ||
1638 | let krate = self.krate; | ||
1639 | |||
1640 | let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { | ||
1641 | Some(it) => it, | ||
1642 | None => return false, | ||
1643 | }; | ||
1644 | |||
1645 | let canonical_ty = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1646 | method_resolution::implements_trait_unique( | ||
1647 | &canonical_ty, | ||
1648 | db, | ||
1649 | self.ty.environment.clone(), | ||
1650 | krate, | ||
1651 | fnonce_trait, | ||
1652 | ) | ||
1653 | } | ||
1654 | |||
1655 | pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool { | ||
1656 | let trait_ref = hir_ty::TraitRef { | ||
1657 | trait_: trait_.id, | ||
1658 | substs: Substs::build_for_def(db, trait_.id) | ||
1659 | .push(self.ty.value.clone()) | ||
1660 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1661 | .build(), | ||
1662 | }; | ||
1663 | |||
1664 | let goal = Canonical { | ||
1665 | value: hir_ty::InEnvironment::new( | ||
1666 | self.ty.environment.clone(), | ||
1667 | hir_ty::Obligation::Trait(trait_ref), | ||
1668 | ), | ||
1669 | kinds: Arc::new([]), | ||
1670 | }; | ||
1671 | |||
1672 | db.trait_solve(self.krate, goal).is_some() | ||
1673 | } | ||
1674 | |||
1675 | pub fn normalize_trait_assoc_type( | ||
1676 | &self, | ||
1677 | db: &dyn HirDatabase, | ||
1678 | trait_: Trait, | ||
1679 | args: &[Type], | ||
1680 | alias: TypeAlias, | ||
1681 | ) -> Option<Type> { | ||
1682 | let subst = Substs::build_for_def(db, trait_.id) | ||
1683 | .push(self.ty.value.clone()) | ||
1684 | .fill(args.iter().map(|t| t.ty.value.clone())) | ||
1685 | .build(); | ||
1686 | let predicate = ProjectionPredicate { | ||
1687 | projection_ty: ProjectionTy { associated_ty: alias.id, parameters: subst }, | ||
1688 | ty: Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)), | ||
1689 | }; | ||
1690 | let goal = Canonical { | ||
1691 | value: InEnvironment::new( | ||
1692 | self.ty.environment.clone(), | ||
1693 | Obligation::Projection(predicate), | ||
1694 | ), | ||
1695 | kinds: Arc::new([TyVariableKind::General]), | ||
1696 | }; | ||
1697 | |||
1698 | match db.trait_solve(self.krate, goal)? { | ||
1699 | Solution::Unique(SolutionVariables(subst)) => { | ||
1700 | subst.value.first().map(|ty| self.derived(ty.clone())) | ||
1701 | } | ||
1702 | Solution::Ambig(_) => None, | ||
1703 | } | ||
1704 | } | ||
1705 | |||
1706 | pub fn is_copy(&self, db: &dyn HirDatabase) -> bool { | ||
1707 | let lang_item = db.lang_item(self.krate, SmolStr::new("copy")); | ||
1708 | let copy_trait = match lang_item { | ||
1709 | Some(LangItemTarget::TraitId(it)) => it, | ||
1710 | _ => return false, | ||
1711 | }; | ||
1712 | self.impls_trait(db, copy_trait.into(), &[]) | ||
1713 | } | ||
1714 | |||
1715 | pub fn as_callable(&self, db: &dyn HirDatabase) -> Option<Callable> { | ||
1716 | let def = match self.ty.value { | ||
1717 | Ty::FnDef(def, _) => Some(def), | ||
1718 | _ => None, | ||
1719 | }; | ||
1720 | |||
1721 | let sig = self.ty.value.callable_sig(db)?; | ||
1722 | Some(Callable { ty: self.clone(), sig, def, is_bound_method: false }) | ||
1723 | } | ||
1724 | |||
1725 | pub fn is_closure(&self) -> bool { | ||
1726 | matches!(&self.ty.value, Ty::Closure { .. }) | ||
1727 | } | ||
1728 | |||
1729 | pub fn is_fn(&self) -> bool { | ||
1730 | matches!(&self.ty.value, Ty::FnDef(..) | Ty::Function { .. }) | ||
1731 | } | ||
1732 | |||
1733 | pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { | ||
1734 | let adt_id = match self.ty.value { | ||
1735 | Ty::Adt(hir_ty::AdtId(adt_id), ..) => adt_id, | ||
1736 | _ => return false, | ||
1737 | }; | ||
1738 | |||
1739 | let adt = adt_id.into(); | ||
1740 | match adt { | ||
1741 | Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), | ||
1742 | _ => false, | ||
1743 | } | ||
1744 | } | ||
1745 | |||
1746 | pub fn is_raw_ptr(&self) -> bool { | ||
1747 | matches!(&self.ty.value, Ty::Raw(..)) | ||
1748 | } | ||
1749 | |||
1750 | pub fn contains_unknown(&self) -> bool { | ||
1751 | return go(&self.ty.value); | ||
1752 | |||
1753 | fn go(ty: &Ty) -> bool { | ||
1754 | match ty { | ||
1755 | Ty::Unknown => true, | ||
1756 | _ => ty.substs().map_or(false, |substs| substs.iter().any(go)), | ||
1757 | } | ||
1758 | } | ||
1759 | } | ||
1760 | |||
1761 | pub fn fields(&self, db: &dyn HirDatabase) -> Vec<(Field, Type)> { | ||
1762 | let (variant_id, substs) = match self.ty.value { | ||
1763 | Ty::Adt(hir_ty::AdtId(AdtId::StructId(s)), ref substs) => (s.into(), substs), | ||
1764 | Ty::Adt(hir_ty::AdtId(AdtId::UnionId(u)), ref substs) => (u.into(), substs), | ||
1765 | _ => return Vec::new(), | ||
1766 | }; | ||
1767 | |||
1768 | db.field_types(variant_id) | ||
1769 | .iter() | ||
1770 | .map(|(local_id, ty)| { | ||
1771 | let def = Field { parent: variant_id.into(), id: local_id }; | ||
1772 | let ty = ty.clone().subst(substs); | ||
1773 | (def, self.derived(ty)) | ||
1774 | }) | ||
1775 | .collect() | ||
1776 | } | ||
1777 | |||
1778 | pub fn tuple_fields(&self, _db: &dyn HirDatabase) -> Vec<Type> { | ||
1779 | if let Ty::Tuple(_, substs) = &self.ty.value { | ||
1780 | substs.iter().map(|ty| self.derived(ty.clone())).collect() | ||
1781 | } else { | ||
1782 | Vec::new() | ||
1783 | } | ||
1784 | } | ||
1785 | |||
1786 | pub fn autoderef<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator<Item = Type> + 'a { | ||
1787 | // There should be no inference vars in types passed here | ||
1788 | // FIXME check that? | ||
1789 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1790 | let environment = self.ty.environment.clone(); | ||
1791 | let ty = InEnvironment { value: canonical, environment }; | ||
1792 | autoderef(db, Some(self.krate), ty) | ||
1793 | .map(|canonical| canonical.value) | ||
1794 | .map(move |ty| self.derived(ty)) | ||
1795 | } | ||
1796 | |||
1797 | // This would be nicer if it just returned an iterator, but that runs into | ||
1798 | // lifetime problems, because we need to borrow temp `CrateImplDefs`. | ||
1799 | pub fn iterate_assoc_items<T>( | ||
1800 | self, | ||
1801 | db: &dyn HirDatabase, | ||
1802 | krate: Crate, | ||
1803 | mut callback: impl FnMut(AssocItem) -> Option<T>, | ||
1804 | ) -> Option<T> { | ||
1805 | for krate in self.ty.value.def_crates(db, krate.id)? { | ||
1806 | let impls = db.inherent_impls_in_crate(krate); | ||
1807 | |||
1808 | for impl_def in impls.for_self_ty(&self.ty.value) { | ||
1809 | for &item in db.impl_data(*impl_def).items.iter() { | ||
1810 | if let Some(result) = callback(item.into()) { | ||
1811 | return Some(result); | ||
1812 | } | ||
1813 | } | ||
1814 | } | ||
1815 | } | ||
1816 | None | ||
1817 | } | ||
1818 | |||
1819 | pub fn type_parameters(&self) -> impl Iterator<Item = Type> + '_ { | ||
1820 | self.ty | ||
1821 | .value | ||
1822 | .strip_references() | ||
1823 | .substs() | ||
1824 | .into_iter() | ||
1825 | .flat_map(|substs| substs.iter()) | ||
1826 | .map(move |ty| self.derived(ty.clone())) | ||
1827 | } | ||
1828 | |||
1829 | pub fn iterate_method_candidates<T>( | ||
1830 | &self, | ||
1831 | db: &dyn HirDatabase, | ||
1832 | krate: Crate, | ||
1833 | traits_in_scope: &FxHashSet<TraitId>, | ||
1834 | name: Option<&Name>, | ||
1835 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | ||
1836 | ) -> Option<T> { | ||
1837 | // There should be no inference vars in types passed here | ||
1838 | // FIXME check that? | ||
1839 | // FIXME replace Unknown by bound vars here | ||
1840 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1841 | |||
1842 | let env = self.ty.environment.clone(); | ||
1843 | let krate = krate.id; | ||
1844 | |||
1845 | method_resolution::iterate_method_candidates( | ||
1846 | &canonical, | ||
1847 | db, | ||
1848 | env, | ||
1849 | krate, | ||
1850 | traits_in_scope, | ||
1851 | name, | ||
1852 | method_resolution::LookupMode::MethodCall, | ||
1853 | |ty, it| match it { | ||
1854 | AssocItemId::FunctionId(f) => callback(ty, f.into()), | ||
1855 | _ => None, | ||
1856 | }, | ||
1857 | ) | ||
1858 | } | ||
1859 | |||
1860 | pub fn iterate_path_candidates<T>( | ||
1861 | &self, | ||
1862 | db: &dyn HirDatabase, | ||
1863 | krate: Crate, | ||
1864 | traits_in_scope: &FxHashSet<TraitId>, | ||
1865 | name: Option<&Name>, | ||
1866 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
1867 | ) -> Option<T> { | ||
1868 | // There should be no inference vars in types passed here | ||
1869 | // FIXME check that? | ||
1870 | // FIXME replace Unknown by bound vars here | ||
1871 | let canonical = Canonical { value: self.ty.value.clone(), kinds: Arc::new([]) }; | ||
1872 | |||
1873 | let env = self.ty.environment.clone(); | ||
1874 | let krate = krate.id; | ||
1875 | |||
1876 | method_resolution::iterate_method_candidates( | ||
1877 | &canonical, | ||
1878 | db, | ||
1879 | env, | ||
1880 | krate, | ||
1881 | traits_in_scope, | ||
1882 | name, | ||
1883 | method_resolution::LookupMode::Path, | ||
1884 | |ty, it| callback(ty, it.into()), | ||
1885 | ) | ||
1886 | } | ||
1887 | |||
1888 | pub fn as_adt(&self) -> Option<Adt> { | ||
1889 | let (adt, _subst) = self.ty.value.as_adt()?; | ||
1890 | Some(adt.into()) | ||
1891 | } | ||
1892 | |||
1893 | pub fn as_dyn_trait(&self) -> Option<Trait> { | ||
1894 | self.ty.value.dyn_trait().map(Into::into) | ||
1895 | } | ||
1896 | |||
1897 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | ||
1898 | self.ty.value.impl_trait_bounds(db).map(|it| { | ||
1899 | it.into_iter() | ||
1900 | .filter_map(|pred| match pred { | ||
1901 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1902 | Some(Trait::from(trait_ref.trait_)) | ||
1903 | } | ||
1904 | _ => None, | ||
1905 | }) | ||
1906 | .collect() | ||
1907 | }) | ||
1908 | } | ||
1909 | |||
1910 | pub fn as_associated_type_parent_trait(&self, db: &dyn HirDatabase) -> Option<Trait> { | ||
1911 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | ||
1912 | } | ||
1913 | |||
1914 | // FIXME: provide required accessors such that it becomes implementable from outside. | ||
1915 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | ||
1916 | let rref = other.remove_ref(); | ||
1917 | self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value)) | ||
1918 | } | ||
1919 | |||
1920 | fn derived(&self, ty: Ty) -> Type { | ||
1921 | Type { | ||
1922 | krate: self.krate, | ||
1923 | ty: InEnvironment { value: ty, environment: self.ty.environment.clone() }, | ||
1924 | } | ||
1925 | } | ||
1926 | |||
1927 | pub fn walk(&self, db: &dyn HirDatabase, mut cb: impl FnMut(Type)) { | ||
1928 | // TypeWalk::walk for a Ty at first visits parameters and only after that the Ty itself. | ||
1929 | // We need a different order here. | ||
1930 | |||
1931 | fn walk_substs( | ||
1932 | db: &dyn HirDatabase, | ||
1933 | type_: &Type, | ||
1934 | substs: &Substs, | ||
1935 | cb: &mut impl FnMut(Type), | ||
1936 | ) { | ||
1937 | for ty in substs.iter() { | ||
1938 | walk_type(db, &type_.derived(ty.clone()), cb); | ||
1939 | } | ||
1940 | } | ||
1941 | |||
1942 | fn walk_bounds( | ||
1943 | db: &dyn HirDatabase, | ||
1944 | type_: &Type, | ||
1945 | bounds: &[GenericPredicate], | ||
1946 | cb: &mut impl FnMut(Type), | ||
1947 | ) { | ||
1948 | for pred in bounds { | ||
1949 | match pred { | ||
1950 | GenericPredicate::Implemented(trait_ref) => { | ||
1951 | cb(type_.clone()); | ||
1952 | walk_substs(db, type_, &trait_ref.substs, cb); | ||
1953 | } | ||
1954 | _ => (), | ||
1955 | } | ||
1956 | } | ||
1957 | } | ||
1958 | |||
1959 | fn walk_type(db: &dyn HirDatabase, type_: &Type, cb: &mut impl FnMut(Type)) { | ||
1960 | let ty = type_.ty.value.strip_references(); | ||
1961 | match ty { | ||
1962 | Ty::Adt(..) => { | ||
1963 | cb(type_.derived(ty.clone())); | ||
1964 | } | ||
1965 | Ty::AssociatedType(..) => { | ||
1966 | if let Some(_) = ty.associated_type_parent_trait(db) { | ||
1967 | cb(type_.derived(ty.clone())); | ||
1968 | } | ||
1969 | } | ||
1970 | Ty::OpaqueType(..) => { | ||
1971 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1972 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1973 | } | ||
1974 | } | ||
1975 | Ty::Alias(AliasTy::Opaque(opaque_ty)) => { | ||
1976 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1977 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1978 | } | ||
1979 | |||
1980 | walk_substs(db, type_, &opaque_ty.parameters, cb); | ||
1981 | } | ||
1982 | Ty::Placeholder(_) => { | ||
1983 | if let Some(bounds) = ty.impl_trait_bounds(db) { | ||
1984 | walk_bounds(db, &type_.derived(ty.clone()), &bounds, cb); | ||
1985 | } | ||
1986 | } | ||
1987 | Ty::Dyn(bounds) => { | ||
1988 | walk_bounds(db, &type_.derived(ty.clone()), bounds.as_ref(), cb); | ||
1989 | } | ||
1990 | |||
1991 | _ => {} | ||
1992 | } | ||
1993 | if let Some(substs) = ty.substs() { | ||
1994 | walk_substs(db, type_, &substs, cb); | ||
1995 | } | ||
1996 | } | ||
1997 | |||
1998 | walk_type(db, self, &mut cb); | ||
1999 | } | ||
2000 | } | ||
2001 | |||
2002 | impl HirDisplay for Type { | ||
2003 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
2004 | self.ty.value.hir_fmt(f) | ||
2005 | } | ||
2006 | } | ||
2007 | |||
2008 | // FIXME: closures | ||
2009 | #[derive(Debug)] | ||
2010 | pub struct Callable { | ||
2011 | ty: Type, | ||
2012 | sig: CallableSig, | ||
2013 | def: Option<CallableDefId>, | ||
2014 | pub(crate) is_bound_method: bool, | ||
2015 | } | ||
2016 | |||
2017 | pub enum CallableKind { | ||
2018 | Function(Function), | ||
2019 | TupleStruct(Struct), | ||
2020 | TupleEnumVariant(Variant), | ||
2021 | Closure, | ||
2022 | } | ||
2023 | |||
2024 | impl Callable { | ||
2025 | pub fn kind(&self) -> CallableKind { | ||
2026 | match self.def { | ||
2027 | Some(CallableDefId::FunctionId(it)) => CallableKind::Function(it.into()), | ||
2028 | Some(CallableDefId::StructId(it)) => CallableKind::TupleStruct(it.into()), | ||
2029 | Some(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), | ||
2030 | None => CallableKind::Closure, | ||
2031 | } | ||
2032 | } | ||
2033 | pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option<ast::SelfParam> { | ||
2034 | let func = match self.def { | ||
2035 | Some(CallableDefId::FunctionId(it)) if self.is_bound_method => it, | ||
2036 | _ => return None, | ||
2037 | }; | ||
2038 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2039 | let param_list = src.value.param_list()?; | ||
2040 | param_list.self_param() | ||
2041 | } | ||
2042 | pub fn n_params(&self) -> usize { | ||
2043 | self.sig.params().len() - if self.is_bound_method { 1 } else { 0 } | ||
2044 | } | ||
2045 | pub fn params( | ||
2046 | &self, | ||
2047 | db: &dyn HirDatabase, | ||
2048 | ) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> { | ||
2049 | let types = self | ||
2050 | .sig | ||
2051 | .params() | ||
2052 | .iter() | ||
2053 | .skip(if self.is_bound_method { 1 } else { 0 }) | ||
2054 | .map(|ty| self.ty.derived(ty.clone())); | ||
2055 | let patterns = match self.def { | ||
2056 | Some(CallableDefId::FunctionId(func)) => { | ||
2057 | let src = func.lookup(db.upcast()).source(db.upcast()); | ||
2058 | src.value.param_list().map(|param_list| { | ||
2059 | param_list | ||
2060 | .self_param() | ||
2061 | .map(|it| Some(Either::Left(it))) | ||
2062 | .filter(|_| !self.is_bound_method) | ||
2063 | .into_iter() | ||
2064 | .chain(param_list.params().map(|it| it.pat().map(Either::Right))) | ||
2065 | }) | ||
2066 | } | ||
2067 | _ => None, | ||
2068 | }; | ||
2069 | patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect() | ||
2070 | } | ||
2071 | pub fn return_type(&self) -> Type { | ||
2072 | self.ty.derived(self.sig.ret().clone()) | ||
2073 | } | ||
2074 | } | ||
2075 | |||
2076 | /// For IDE only | ||
2077 | #[derive(Debug, PartialEq, Eq, Hash)] | ||
2078 | pub enum ScopeDef { | ||
2079 | ModuleDef(ModuleDef), | ||
2080 | MacroDef(MacroDef), | ||
2081 | GenericParam(GenericParam), | ||
2082 | ImplSelfType(Impl), | ||
2083 | AdtSelfType(Adt), | ||
2084 | Local(Local), | ||
2085 | Unknown, | ||
2086 | } | ||
2087 | |||
2088 | impl ScopeDef { | ||
2089 | pub fn all_items(def: PerNs) -> ArrayVec<[Self; 3]> { | ||
2090 | let mut items = ArrayVec::new(); | ||
2091 | |||
2092 | match (def.take_types(), def.take_values()) { | ||
2093 | (Some(m1), None) => items.push(ScopeDef::ModuleDef(m1.into())), | ||
2094 | (None, Some(m2)) => items.push(ScopeDef::ModuleDef(m2.into())), | ||
2095 | (Some(m1), Some(m2)) => { | ||
2096 | // Some items, like unit structs and enum variants, are | ||
2097 | // returned as both a type and a value. Here we want | ||
2098 | // to de-duplicate them. | ||
2099 | if m1 != m2 { | ||
2100 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2101 | items.push(ScopeDef::ModuleDef(m2.into())); | ||
2102 | } else { | ||
2103 | items.push(ScopeDef::ModuleDef(m1.into())); | ||
2104 | } | ||
2105 | } | ||
2106 | (None, None) => {} | ||
2107 | }; | ||
2108 | |||
2109 | if let Some(macro_def_id) = def.take_macros() { | ||
2110 | items.push(ScopeDef::MacroDef(macro_def_id.into())); | ||
2111 | } | ||
2112 | |||
2113 | if items.is_empty() { | ||
2114 | items.push(ScopeDef::Unknown); | ||
2115 | } | ||
2116 | |||
2117 | items | ||
2118 | } | ||
2119 | } | ||
2120 | |||
2121 | impl From<ItemInNs> for ScopeDef { | ||
2122 | fn from(item: ItemInNs) -> Self { | ||
2123 | match item { | ||
2124 | ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), | ||
2125 | ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), | ||
2126 | ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), | ||
2127 | } | ||
2128 | } | ||
2129 | } | ||
2130 | |||
2131 | pub trait HasVisibility { | ||
2132 | fn visibility(&self, db: &dyn HirDatabase) -> Visibility; | ||
2133 | fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { | ||
2134 | let vis = self.visibility(db); | ||
2135 | vis.is_visible_from(db.upcast(), module.id) | ||
2136 | } | ||
2137 | } | ||
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 144851f83..945638cc5 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -20,12 +20,11 @@ use syntax::{ | |||
20 | }; | 20 | }; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | code_model::Access, | ||
24 | db::HirDatabase, | 23 | db::HirDatabase, |
25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 24 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
26 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, | 25 | source_analyzer::{resolve_hir_path, SourceAnalyzer}, |
27 | AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, Label, | 26 | Access, AssocItem, Callable, ConstParam, Crate, Field, Function, HirFileId, Impl, InFile, |
28 | LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, | 27 | Label, LifetimeParam, Local, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, |
29 | TypeAlias, TypeParam, VariantDef, | 28 | TypeAlias, TypeParam, VariantDef, |
30 | }; | 29 | }; |
31 | 30 | ||
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 64ce4add1..d546512cb 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs | |||
@@ -28,9 +28,8 @@ use syntax::{ | |||
28 | }; | 28 | }; |
29 | 29 | ||
30 | use crate::{ | 30 | use crate::{ |
31 | code_model::BuiltinType, db::HirDatabase, semantics::PathResolution, Adt, Const, Field, | 31 | db::HirDatabase, semantics::PathResolution, Adt, BuiltinType, Const, Field, Function, Local, |
32 | Function, Local, MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, | 32 | MacroDef, ModuleDef, Static, Struct, Trait, Type, TypeAlias, TypeParam, Variant, |
33 | Variant, | ||
34 | }; | 33 | }; |
35 | use base_db::CrateId; | 34 | use base_db::CrateId; |
36 | 35 | ||
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml index 535221294..c2d99280f 100644 --- a/crates/hir_def/Cargo.toml +++ b/crates/hir_def/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | log = "0.4.8" | 14 | log = "0.4.8" |
14 | once_cell = "1.3.1" | 15 | once_cell = "1.3.1" |
15 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
@@ -27,10 +28,10 @@ base_db = { path = "../base_db", version = "0.0.0" } | |||
27 | syntax = { path = "../syntax", version = "0.0.0" } | 28 | syntax = { path = "../syntax", version = "0.0.0" } |
28 | profile = { path = "../profile", version = "0.0.0" } | 29 | profile = { path = "../profile", version = "0.0.0" } |
29 | hir_expand = { path = "../hir_expand", version = "0.0.0" } | 30 | hir_expand = { path = "../hir_expand", version = "0.0.0" } |
30 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
31 | mbe = { path = "../mbe", version = "0.0.0" } | 31 | mbe = { path = "../mbe", version = "0.0.0" } |
32 | cfg = { path = "../cfg", version = "0.0.0" } | 32 | cfg = { path = "../cfg", version = "0.0.0" } |
33 | tt = { path = "../tt", version = "0.0.0" } | 33 | tt = { path = "../tt", version = "0.0.0" } |
34 | 34 | ||
35 | [dev-dependencies] | 35 | [dev-dependencies] |
36 | test_utils = { path = "../test_utils" } | ||
36 | expect-test = "1.1" | 37 | expect-test = "1.1" |
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs index ed36c3109..efbde17d8 100644 --- a/crates/hir_def/src/adt.rs +++ b/crates/hir_def/src/adt.rs | |||
@@ -21,8 +21,7 @@ use crate::{ | |||
21 | trace::Trace, | 21 | trace::Trace, |
22 | type_ref::TypeRef, | 22 | type_ref::TypeRef, |
23 | visibility::RawVisibility, | 23 | visibility::RawVisibility, |
24 | EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, | 24 | EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId, |
25 | VariantId, | ||
26 | }; | 25 | }; |
27 | use cfg::CfgOptions; | 26 | use cfg::CfgOptions; |
28 | 27 | ||
@@ -92,10 +91,10 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> { | |||
92 | impl StructData { | 91 | impl StructData { |
93 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 92 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
94 | let loc = id.lookup(db); | 93 | let loc = id.lookup(db); |
95 | let krate = loc.container.module(db).krate; | 94 | let krate = loc.container.krate; |
96 | let item_tree = db.item_tree(loc.id.file_id); | 95 | let item_tree = db.item_tree(loc.id.file_id); |
97 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 96 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
98 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | 97 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
99 | 98 | ||
100 | let strukt = &item_tree[loc.id.value]; | 99 | let strukt = &item_tree[loc.id.value]; |
101 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); | 100 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); |
@@ -107,10 +106,10 @@ impl StructData { | |||
107 | } | 106 | } |
108 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 107 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
109 | let loc = id.lookup(db); | 108 | let loc = id.lookup(db); |
110 | let krate = loc.container.module(db).krate; | 109 | let krate = loc.container.krate; |
111 | let item_tree = db.item_tree(loc.id.file_id); | 110 | let item_tree = db.item_tree(loc.id.file_id); |
112 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); | 111 | let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); |
113 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | 112 | let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone(); |
114 | 113 | ||
115 | let union = &item_tree[loc.id.value]; | 114 | let union = &item_tree[loc.id.value]; |
116 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); | 115 | let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); |
@@ -126,7 +125,7 @@ impl StructData { | |||
126 | impl EnumData { | 125 | impl EnumData { |
127 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { | 126 | pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { |
128 | let loc = e.lookup(db); | 127 | let loc = e.lookup(db); |
129 | let krate = loc.container.module(db).krate; | 128 | let krate = loc.container.krate; |
130 | let item_tree = db.item_tree(loc.id.file_id); | 129 | let item_tree = db.item_tree(loc.id.file_id); |
131 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); | 130 | let cfg_options = db.crate_graph()[krate].cfg_options.clone(); |
132 | 131 | ||
@@ -168,7 +167,7 @@ impl HasChildSource<LocalEnumVariantId> for EnumId { | |||
168 | ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { | 167 | ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { |
169 | let src = self.lookup(db).source(db); | 168 | let src = self.lookup(db).source(db); |
170 | let mut trace = Trace::new_for_map(); | 169 | let mut trace = Trace::new_for_map(); |
171 | lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); | 170 | lower_enum(db, &mut trace, &src, self.lookup(db).container); |
172 | src.with_value(trace.into_map()) | 171 | src.with_value(trace.into_map()) |
173 | } | 172 | } |
174 | } | 173 | } |
@@ -238,10 +237,10 @@ impl HasChildSource<LocalFieldId> for VariantId { | |||
238 | // I don't really like the fact that we call into parent source | 237 | // I don't really like the fact that we call into parent source |
239 | // here, this might add to more queries then necessary. | 238 | // here, this might add to more queries then necessary. |
240 | let src = it.parent.child_source(db); | 239 | let src = it.parent.child_source(db); |
241 | (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db)) | 240 | (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container) |
242 | } | 241 | } |
243 | VariantId::StructId(it) => { | 242 | VariantId::StructId(it) => { |
244 | (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db)) | 243 | (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container) |
245 | } | 244 | } |
246 | VariantId::UnionId(it) => ( | 245 | VariantId::UnionId(it) => ( |
247 | it.lookup(db).source(db).map(|it| { | 246 | it.lookup(db).source(db).map(|it| { |
@@ -249,7 +248,7 @@ impl HasChildSource<LocalFieldId> for VariantId { | |||
249 | .map(ast::StructKind::Record) | 248 | .map(ast::StructKind::Record) |
250 | .unwrap_or(ast::StructKind::Unit) | 249 | .unwrap_or(ast::StructKind::Unit) |
251 | }), | 250 | }), |
252 | it.lookup(db).container.module(db), | 251 | it.lookup(db).container, |
253 | ), | 252 | ), |
254 | }; | 253 | }; |
255 | let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); | 254 | let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); |
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index fe4c3fa28..97cdbbb9e 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -13,7 +13,6 @@ use syntax::{ | |||
13 | ast::{self, AstNode, AttrsOwner}, | 13 | ast::{self, AstNode, AttrsOwner}, |
14 | match_ast, AstToken, SmolStr, SyntaxNode, | 14 | match_ast, AstToken, SmolStr, SyntaxNode, |
15 | }; | 15 | }; |
16 | use test_utils::mark; | ||
17 | use tt::Subtree; | 16 | use tt::Subtree; |
18 | 17 | ||
19 | use crate::{ | 18 | use crate::{ |
@@ -177,7 +176,7 @@ impl RawAttrs { | |||
177 | if cfg_options.check(&cfg) == Some(false) { | 176 | if cfg_options.check(&cfg) == Some(false) { |
178 | None | 177 | None |
179 | } else { | 178 | } else { |
180 | mark::hit!(cfg_attr_active); | 179 | cov_mark::hit!(cfg_attr_active); |
181 | 180 | ||
182 | let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; | 181 | let attr = ast::Attr::parse(&format!("#[{}]", attr)).ok()?; |
183 | let hygiene = Hygiene::new_unhygienic(); // FIXME | 182 | let hygiene = Hygiene::new_unhygienic(); // FIXME |
@@ -268,7 +267,7 @@ impl Attrs { | |||
268 | db: &dyn DefDatabase, | 267 | db: &dyn DefDatabase, |
269 | e: EnumId, | 268 | e: EnumId, |
270 | ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { | 269 | ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { |
271 | let krate = e.lookup(db).container.module(db).krate; | 270 | let krate = e.lookup(db).container.krate; |
272 | let src = e.child_source(db); | 271 | let src = e.child_source(db); |
273 | let mut res = ArenaMap::default(); | 272 | let mut res = ArenaMap::default(); |
274 | 273 | ||
@@ -367,7 +366,7 @@ fn inner_attributes( | |||
367 | // Excerpt from the reference: | 366 | // Excerpt from the reference: |
368 | // Block expressions accept outer and inner attributes, but only when they are the outer | 367 | // Block expressions accept outer and inner attributes, but only when they are the outer |
369 | // expression of an expression statement or the final expression of another block expression. | 368 | // expression of an expression statement or the final expression of another block expression. |
370 | ast::BlockExpr(it) => return None, | 369 | ast::BlockExpr(_it) => return None, |
371 | _ => return None, | 370 | _ => return None, |
372 | } | 371 | } |
373 | }; | 372 | }; |
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs index ff4b4a0cf..19c4eb521 100644 --- a/crates/hir_def/src/body.rs +++ b/crates/hir_def/src/body.rs | |||
@@ -20,7 +20,6 @@ use la_arena::{Arena, ArenaMap}; | |||
20 | use profile::Count; | 20 | use profile::Count; |
21 | use rustc_hash::FxHashMap; | 21 | use rustc_hash::FxHashMap; |
22 | use syntax::{ast, AstNode, AstPtr}; | 22 | use syntax::{ast, AstNode, AstPtr}; |
23 | use test_utils::mark; | ||
24 | 23 | ||
25 | pub(crate) use lower::LowerCtx; | 24 | pub(crate) use lower::LowerCtx; |
26 | 25 | ||
@@ -29,11 +28,10 @@ use crate::{ | |||
29 | db::DefDatabase, | 28 | db::DefDatabase, |
30 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, | 29 | expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, |
31 | item_scope::BuiltinShadowMode, | 30 | item_scope::BuiltinShadowMode, |
32 | item_scope::ItemScope, | ||
33 | nameres::DefMap, | 31 | nameres::DefMap, |
34 | path::{ModPath, Path}, | 32 | path::{ModPath, Path}, |
35 | src::HasSource, | 33 | src::HasSource, |
36 | AsMacroCall, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, | 34 | AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, |
37 | }; | 35 | }; |
38 | 36 | ||
39 | /// A subset of Expander that only deals with cfg attributes. We only need it to | 37 | /// A subset of Expander that only deals with cfg attributes. We only need it to |
@@ -87,11 +85,11 @@ impl Expander { | |||
87 | module: ModuleId, | 85 | module: ModuleId, |
88 | ) -> Expander { | 86 | ) -> Expander { |
89 | let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); | 87 | let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); |
90 | let crate_def_map = module.def_map(db); | 88 | let def_map = module.def_map(db); |
91 | let ast_id_map = db.ast_id_map(current_file_id); | 89 | let ast_id_map = db.ast_id_map(current_file_id); |
92 | Expander { | 90 | Expander { |
93 | cfg_expander, | 91 | cfg_expander, |
94 | def_map: crate_def_map, | 92 | def_map, |
95 | current_file_id, | 93 | current_file_id, |
96 | ast_id_map, | 94 | ast_id_map, |
97 | module: module.local_id, | 95 | module: module.local_id, |
@@ -105,7 +103,7 @@ impl Expander { | |||
105 | macro_call: ast::MacroCall, | 103 | macro_call: ast::MacroCall, |
106 | ) -> ExpandResult<Option<(Mark, T)>> { | 104 | ) -> ExpandResult<Option<(Mark, T)>> { |
107 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { | 105 | if self.recursion_limit + 1 > EXPANSION_RECURSION_LIMIT { |
108 | mark::hit!(your_stack_belongs_to_me); | 106 | cov_mark::hit!(your_stack_belongs_to_me); |
109 | return ExpandResult::str_err("reached recursion limit during macro expansion".into()); | 107 | return ExpandResult::str_err("reached recursion limit during macro expansion".into()); |
110 | } | 108 | } |
111 | 109 | ||
@@ -227,7 +225,8 @@ pub struct Body { | |||
227 | pub params: Vec<PatId>, | 225 | pub params: Vec<PatId>, |
228 | /// The `ExprId` of the actual body expression. | 226 | /// The `ExprId` of the actual body expression. |
229 | pub body_expr: ExprId, | 227 | pub body_expr: ExprId, |
230 | pub item_scope: ItemScope, | 228 | /// Block expressions in this body that may contain inner items. |
229 | pub block_scopes: Vec<BlockId>, | ||
231 | _c: Count<Self>, | 230 | _c: Count<Self>, |
232 | } | 231 | } |
233 | 232 | ||
@@ -296,7 +295,7 @@ impl Body { | |||
296 | } | 295 | } |
297 | }; | 296 | }; |
298 | let expander = Expander::new(db, file_id, module); | 297 | let expander = Expander::new(db, file_id, module); |
299 | let (body, source_map) = Body::new(db, def, expander, params, body); | 298 | let (body, source_map) = Body::new(db, expander, params, body); |
300 | (Arc::new(body), Arc::new(source_map)) | 299 | (Arc::new(body), Arc::new(source_map)) |
301 | } | 300 | } |
302 | 301 | ||
@@ -306,12 +305,11 @@ impl Body { | |||
306 | 305 | ||
307 | fn new( | 306 | fn new( |
308 | db: &dyn DefDatabase, | 307 | db: &dyn DefDatabase, |
309 | def: DefWithBodyId, | ||
310 | expander: Expander, | 308 | expander: Expander, |
311 | params: Option<ast::ParamList>, | 309 | params: Option<ast::ParamList>, |
312 | body: Option<ast::Expr>, | 310 | body: Option<ast::Expr>, |
313 | ) -> (Body, BodySourceMap) { | 311 | ) -> (Body, BodySourceMap) { |
314 | lower::lower(db, def, expander, params, body) | 312 | lower::lower(db, expander, params, body) |
315 | } | 313 | } |
316 | } | 314 | } |
317 | 315 | ||
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 40beb2f7a..4d79ab72c 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -1,13 +1,13 @@ | |||
1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` | 1 | //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` |
2 | //! representation. | 2 | //! representation. |
3 | 3 | ||
4 | use std::{any::type_name, mem, sync::Arc}; | 4 | use std::{mem, sync::Arc}; |
5 | 5 | ||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{ | 7 | use hir_expand::{ |
8 | hygiene::Hygiene, | 8 | hygiene::Hygiene, |
9 | name::{name, AsName, Name}, | 9 | name::{name, AsName, Name}, |
10 | ExpandError, HirFileId, MacroDefId, MacroDefKind, | 10 | ExpandError, HirFileId, |
11 | }; | 11 | }; |
12 | use la_arena::Arena; | 12 | use la_arena::Arena; |
13 | use profile::Count; | 13 | use profile::Count; |
@@ -19,7 +19,6 @@ use syntax::{ | |||
19 | }, | 19 | }, |
20 | AstNode, AstPtr, SyntaxNodePtr, | 20 | AstNode, AstPtr, SyntaxNodePtr, |
21 | }; | 21 | }; |
22 | use test_utils::mark; | ||
23 | 22 | ||
24 | use crate::{ | 23 | use crate::{ |
25 | adt::StructKind, | 24 | adt::StructKind, |
@@ -33,11 +32,10 @@ use crate::{ | |||
33 | Statement, | 32 | Statement, |
34 | }, | 33 | }, |
35 | item_scope::BuiltinShadowMode, | 34 | item_scope::BuiltinShadowMode, |
36 | item_tree::{ItemTree, ItemTreeId, ItemTreeNode}, | 35 | item_tree::ItemTree, |
37 | path::{GenericArgs, Path}, | 36 | path::{GenericArgs, Path}, |
38 | type_ref::{Mutability, Rawness, TypeRef}, | 37 | type_ref::{Mutability, Rawness, TypeRef}, |
39 | AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, | 38 | AdtId, BlockLoc, ModuleDefId, |
40 | ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, | ||
41 | }; | 39 | }; |
42 | 40 | ||
43 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 41 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
@@ -61,7 +59,6 @@ impl LowerCtx { | |||
61 | 59 | ||
62 | pub(super) fn lower( | 60 | pub(super) fn lower( |
63 | db: &dyn DefDatabase, | 61 | db: &dyn DefDatabase, |
64 | def: DefWithBodyId, | ||
65 | expander: Expander, | 62 | expander: Expander, |
66 | params: Option<ast::ParamList>, | 63 | params: Option<ast::ParamList>, |
67 | body: Option<ast::Expr>, | 64 | body: Option<ast::Expr>, |
@@ -69,7 +66,6 @@ pub(super) fn lower( | |||
69 | let item_tree = db.item_tree(expander.current_file_id); | 66 | let item_tree = db.item_tree(expander.current_file_id); |
70 | ExprCollector { | 67 | ExprCollector { |
71 | db, | 68 | db, |
72 | def, | ||
73 | source_map: BodySourceMap::default(), | 69 | source_map: BodySourceMap::default(), |
74 | body: Body { | 70 | body: Body { |
75 | exprs: Arena::default(), | 71 | exprs: Arena::default(), |
@@ -77,7 +73,7 @@ pub(super) fn lower( | |||
77 | labels: Arena::default(), | 73 | labels: Arena::default(), |
78 | params: Vec::new(), | 74 | params: Vec::new(), |
79 | body_expr: dummy_expr_id(), | 75 | body_expr: dummy_expr_id(), |
80 | item_scope: Default::default(), | 76 | block_scopes: Vec::new(), |
81 | _c: Count::new(), | 77 | _c: Count::new(), |
82 | }, | 78 | }, |
83 | item_trees: { | 79 | item_trees: { |
@@ -92,7 +88,6 @@ pub(super) fn lower( | |||
92 | 88 | ||
93 | struct ExprCollector<'a> { | 89 | struct ExprCollector<'a> { |
94 | db: &'a dyn DefDatabase, | 90 | db: &'a dyn DefDatabase, |
95 | def: DefWithBodyId, | ||
96 | expander: Expander, | 91 | expander: Expander, |
97 | body: Body, | 92 | body: Body, |
98 | source_map: BodySourceMap, | 93 | source_map: BodySourceMap, |
@@ -286,7 +281,7 @@ impl ExprCollector<'_> { | |||
286 | None => self.collect_expr_opt(condition.expr()), | 281 | None => self.collect_expr_opt(condition.expr()), |
287 | // if let -- desugar to match | 282 | // if let -- desugar to match |
288 | Some(pat) => { | 283 | Some(pat) => { |
289 | mark::hit!(infer_resolve_while_let); | 284 | cov_mark::hit!(infer_resolve_while_let); |
290 | let pat = self.collect_pat(pat); | 285 | let pat = self.collect_pat(pat); |
291 | let match_expr = self.collect_expr_opt(condition.expr()); | 286 | let match_expr = self.collect_expr_opt(condition.expr()); |
292 | let placeholder_pat = self.missing_pat(); | 287 | let placeholder_pat = self.missing_pat(); |
@@ -606,32 +601,6 @@ impl ExprCollector<'_> { | |||
606 | } | 601 | } |
607 | } | 602 | } |
608 | 603 | ||
609 | fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> { | ||
610 | let id = self.expander.ast_id(ast); | ||
611 | let tree = &self.item_trees[&id.file_id]; | ||
612 | |||
613 | // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes | ||
614 | |||
615 | // Root file (non-macro). | ||
616 | let item_tree_id = tree | ||
617 | .all_inner_items() | ||
618 | .chain(tree.top_level_items().iter().copied()) | ||
619 | .filter_map(|mod_item| mod_item.downcast::<N>()) | ||
620 | .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast()) | ||
621 | .or_else(|| { | ||
622 | log::debug!( | ||
623 | "couldn't find inner {} item for {:?} (AST: `{}` - {:?})", | ||
624 | type_name::<N>(), | ||
625 | id, | ||
626 | ast.syntax(), | ||
627 | ast.syntax(), | ||
628 | ); | ||
629 | None | ||
630 | })?; | ||
631 | |||
632 | Some(ItemTreeId::new(id.file_id, item_tree_id)) | ||
633 | } | ||
634 | |||
635 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { | 604 | fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { |
636 | if let Some(expr) = expr { | 605 | if let Some(expr) = expr { |
637 | self.collect_expr(expr) | 606 | self.collect_expr(expr) |
@@ -663,7 +632,6 @@ impl ExprCollector<'_> { | |||
663 | match expansion { | 632 | match expansion { |
664 | Some(expansion) => { | 633 | Some(expansion) => { |
665 | let statements: ast::MacroStmts = expansion; | 634 | let statements: ast::MacroStmts = expansion; |
666 | this.collect_stmts_items(statements.statements()); | ||
667 | 635 | ||
668 | statements.statements().for_each(|stmt| { | 636 | statements.statements().for_each(|stmt| { |
669 | if let Some(mut r) = this.collect_stmt(stmt) { | 637 | if let Some(mut r) = this.collect_stmt(stmt) { |
@@ -701,6 +669,8 @@ impl ExprCollector<'_> { | |||
701 | let block_loc = | 669 | let block_loc = |
702 | BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; | 670 | BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; |
703 | let block_id = self.db.intern_block(block_loc); | 671 | let block_id = self.db.intern_block(block_loc); |
672 | self.body.block_scopes.push(block_id); | ||
673 | |||
704 | let opt_def_map = self.db.block_def_map(block_id); | 674 | let opt_def_map = self.db.block_def_map(block_id); |
705 | let has_def_map = opt_def_map.is_some(); | 675 | let has_def_map = opt_def_map.is_some(); |
706 | let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); | 676 | let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); |
@@ -708,7 +678,6 @@ impl ExprCollector<'_> { | |||
708 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); | 678 | let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); |
709 | let prev_local_module = mem::replace(&mut self.expander.module, module); | 679 | let prev_local_module = mem::replace(&mut self.expander.module, module); |
710 | 680 | ||
711 | self.collect_stmts_items(block.statements()); | ||
712 | let statements = | 681 | let statements = |
713 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); | 682 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); |
714 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); | 683 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); |
@@ -723,108 +692,6 @@ impl ExprCollector<'_> { | |||
723 | expr_id | 692 | expr_id |
724 | } | 693 | } |
725 | 694 | ||
726 | fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) { | ||
727 | let container = ContainerId::DefWithBodyId(self.def); | ||
728 | |||
729 | let items = stmts | ||
730 | .filter_map(|stmt| match stmt { | ||
731 | ast::Stmt::Item(it) => Some(it), | ||
732 | ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None, | ||
733 | }) | ||
734 | .filter_map(|item| { | ||
735 | let (def, name): (ModuleDefId, Option<ast::Name>) = match item { | ||
736 | ast::Item::Fn(def) => { | ||
737 | let id = self.find_inner_item(&def)?; | ||
738 | ( | ||
739 | FunctionLoc { container: container.into(), id }.intern(self.db).into(), | ||
740 | def.name(), | ||
741 | ) | ||
742 | } | ||
743 | ast::Item::TypeAlias(def) => { | ||
744 | let id = self.find_inner_item(&def)?; | ||
745 | ( | ||
746 | TypeAliasLoc { container: container.into(), id }.intern(self.db).into(), | ||
747 | def.name(), | ||
748 | ) | ||
749 | } | ||
750 | ast::Item::Const(def) => { | ||
751 | let id = self.find_inner_item(&def)?; | ||
752 | ( | ||
753 | ConstLoc { container: container.into(), id }.intern(self.db).into(), | ||
754 | def.name(), | ||
755 | ) | ||
756 | } | ||
757 | ast::Item::Static(def) => { | ||
758 | let id = self.find_inner_item(&def)?; | ||
759 | (StaticLoc { container, id }.intern(self.db).into(), def.name()) | ||
760 | } | ||
761 | ast::Item::Struct(def) => { | ||
762 | let id = self.find_inner_item(&def)?; | ||
763 | (StructLoc { container, id }.intern(self.db).into(), def.name()) | ||
764 | } | ||
765 | ast::Item::Enum(def) => { | ||
766 | let id = self.find_inner_item(&def)?; | ||
767 | (EnumLoc { container, id }.intern(self.db).into(), def.name()) | ||
768 | } | ||
769 | ast::Item::Union(def) => { | ||
770 | let id = self.find_inner_item(&def)?; | ||
771 | (UnionLoc { container, id }.intern(self.db).into(), def.name()) | ||
772 | } | ||
773 | ast::Item::Trait(def) => { | ||
774 | let id = self.find_inner_item(&def)?; | ||
775 | (TraitLoc { container, id }.intern(self.db).into(), def.name()) | ||
776 | } | ||
777 | ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks | ||
778 | ast::Item::Impl(_) | ||
779 | | ast::Item::Use(_) | ||
780 | | ast::Item::ExternCrate(_) | ||
781 | | ast::Item::Module(_) | ||
782 | | ast::Item::MacroCall(_) => return None, | ||
783 | ast::Item::MacroRules(def) => { | ||
784 | return Some(Either::Right(ast::Macro::from(def))); | ||
785 | } | ||
786 | ast::Item::MacroDef(def) => { | ||
787 | return Some(Either::Right(ast::Macro::from(def))); | ||
788 | } | ||
789 | }; | ||
790 | |||
791 | Some(Either::Left((def, name))) | ||
792 | }) | ||
793 | .collect::<Vec<_>>(); | ||
794 | |||
795 | for either in items { | ||
796 | match either { | ||
797 | Either::Left((def, name)) => { | ||
798 | self.body.item_scope.define_def(def); | ||
799 | if let Some(name) = name { | ||
800 | let vis = crate::visibility::Visibility::Public; // FIXME determine correctly | ||
801 | let has_constructor = match def { | ||
802 | ModuleDefId::AdtId(AdtId::StructId(s)) => { | ||
803 | self.db.struct_data(s).variant_data.kind() != StructKind::Record | ||
804 | } | ||
805 | _ => true, | ||
806 | }; | ||
807 | self.body.item_scope.push_res( | ||
808 | name.as_name(), | ||
809 | crate::per_ns::PerNs::from_def(def, vis, has_constructor), | ||
810 | ); | ||
811 | } | ||
812 | } | ||
813 | Either::Right(e) => { | ||
814 | let mac = MacroDefId { | ||
815 | krate: self.expander.def_map.krate(), | ||
816 | ast_id: Some(self.expander.ast_id(&e)), | ||
817 | kind: MacroDefKind::Declarative, | ||
818 | local_inner: false, | ||
819 | }; | ||
820 | if let Some(name) = e.name() { | ||
821 | self.body.item_scope.define_legacy_macro(name.as_name(), mac); | ||
822 | } | ||
823 | } | ||
824 | } | ||
825 | } | ||
826 | } | ||
827 | |||
828 | fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { | 695 | fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { |
829 | if let Some(block) = expr { | 696 | if let Some(block) = expr { |
830 | self.collect_block(block) | 697 | self.collect_block(block) |
diff --git a/crates/hir_def/src/body/scope.rs b/crates/hir_def/src/body/scope.rs index 210b4a617..1bbb54fc6 100644 --- a/crates/hir_def/src/body/scope.rs +++ b/crates/hir_def/src/body/scope.rs | |||
@@ -186,7 +186,7 @@ mod tests { | |||
186 | use base_db::{fixture::WithFixture, FileId, SourceDatabase}; | 186 | use base_db::{fixture::WithFixture, FileId, SourceDatabase}; |
187 | use hir_expand::{name::AsName, InFile}; | 187 | use hir_expand::{name::AsName, InFile}; |
188 | use syntax::{algo::find_node_at_offset, ast, AstNode}; | 188 | use syntax::{algo::find_node_at_offset, ast, AstNode}; |
189 | use test_utils::{assert_eq_text, extract_offset, mark}; | 189 | use test_utils::{assert_eq_text, extract_offset}; |
190 | 190 | ||
191 | use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; | 191 | use crate::{db::DefDatabase, test_db::TestDB, FunctionId, ModuleDefId}; |
192 | 192 | ||
@@ -454,7 +454,7 @@ fn foo() { | |||
454 | 454 | ||
455 | #[test] | 455 | #[test] |
456 | fn while_let_desugaring() { | 456 | fn while_let_desugaring() { |
457 | mark::check!(infer_resolve_while_let); | 457 | cov_mark::check!(infer_resolve_while_let); |
458 | do_check_local_name( | 458 | do_check_local_name( |
459 | r#" | 459 | r#" |
460 | fn test() { | 460 | fn test() { |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index bb43569d7..991a32b15 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -2,7 +2,6 @@ mod block; | |||
2 | 2 | ||
3 | use base_db::{fixture::WithFixture, SourceDatabase}; | 3 | use base_db::{fixture::WithFixture, SourceDatabase}; |
4 | use expect_test::Expect; | 4 | use expect_test::Expect; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{test_db::TestDB, ModuleDefId}; | 6 | use crate::{test_db::TestDB, ModuleDefId}; |
8 | 7 | ||
@@ -48,7 +47,7 @@ fn check_at(ra_fixture: &str, expect: Expect) { | |||
48 | 47 | ||
49 | #[test] | 48 | #[test] |
50 | fn your_stack_belongs_to_me() { | 49 | fn your_stack_belongs_to_me() { |
51 | mark::check!(your_stack_belongs_to_me); | 50 | cov_mark::check!(your_stack_belongs_to_me); |
52 | lower( | 51 | lower( |
53 | " | 52 | " |
54 | macro_rules! n_nuple { | 53 | macro_rules! n_nuple { |
diff --git a/crates/hir_def/src/body/tests/block.rs b/crates/hir_def/src/body/tests/block.rs index 8bca72a17..3b6ba4cde 100644 --- a/crates/hir_def/src/body/tests/block.rs +++ b/crates/hir_def/src/body/tests/block.rs | |||
@@ -165,16 +165,16 @@ fn macro_resolve() { | |||
165 | check_at( | 165 | check_at( |
166 | r#" | 166 | r#" |
167 | //- /lib.rs crate:lib deps:core | 167 | //- /lib.rs crate:lib deps:core |
168 | use core::mark; | 168 | use core::cov_mark; |
169 | 169 | ||
170 | fn f() { | 170 | fn f() { |
171 | fn nested() { | 171 | fn nested() { |
172 | mark::hit!(Hit); | 172 | cov_mark::hit!(Hit); |
173 | $0 | 173 | $0 |
174 | } | 174 | } |
175 | } | 175 | } |
176 | //- /core.rs crate:core | 176 | //- /core.rs crate:core |
177 | pub mod mark { | 177 | pub mod cov_mark { |
178 | #[macro_export] | 178 | #[macro_export] |
179 | macro_rules! _hit { | 179 | macro_rules! _hit { |
180 | ($name:ident) => { | 180 | ($name:ident) => { |
@@ -193,8 +193,8 @@ pub mod mark { | |||
193 | nested: v | 193 | nested: v |
194 | 194 | ||
195 | crate | 195 | crate |
196 | cov_mark: t | ||
196 | f: v | 197 | f: v |
197 | mark: t | ||
198 | "#]], | 198 | "#]], |
199 | ); | 199 | ); |
200 | } | 200 | } |
@@ -264,7 +264,7 @@ fn main() { | |||
264 | fn underscore_import() { | 264 | fn underscore_import() { |
265 | // This used to panic, because the default (private) visibility inside block expressions would | 265 | // This used to panic, because the default (private) visibility inside block expressions would |
266 | // point into the containing `DefMap`, which visibilities should never be able to do. | 266 | // point into the containing `DefMap`, which visibilities should never be able to do. |
267 | mark::check!(adjust_vis_in_block_def_map); | 267 | cov_mark::check!(adjust_vis_in_block_def_map); |
268 | check_at( | 268 | check_at( |
269 | r#" | 269 | r#" |
270 | mod m { | 270 | mod m { |
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs index 65d85c86a..2a331dcaf 100644 --- a/crates/hir_def/src/child_by_source.rs +++ b/crates/hir_def/src/child_by_source.rs | |||
@@ -17,13 +17,16 @@ use crate::{ | |||
17 | }; | 17 | }; |
18 | 18 | ||
19 | pub trait ChildBySource { | 19 | pub trait ChildBySource { |
20 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap; | ||
21 | } | ||
22 | |||
23 | impl ChildBySource for TraitId { | ||
24 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 20 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { |
25 | let mut res = DynMap::default(); | 21 | let mut res = DynMap::default(); |
22 | self.child_by_source_to(db, &mut res); | ||
23 | res | ||
24 | } | ||
25 | fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap); | ||
26 | } | ||
26 | 27 | ||
28 | impl ChildBySource for TraitId { | ||
29 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { | ||
27 | let data = db.trait_data(*self); | 30 | let data = db.trait_data(*self); |
28 | for (_name, item) in data.items.iter() { | 31 | for (_name, item) in data.items.iter() { |
29 | match *item { | 32 | match *item { |
@@ -41,15 +44,11 @@ impl ChildBySource for TraitId { | |||
41 | } | 44 | } |
42 | } | 45 | } |
43 | } | 46 | } |
44 | |||
45 | res | ||
46 | } | 47 | } |
47 | } | 48 | } |
48 | 49 | ||
49 | impl ChildBySource for ImplId { | 50 | impl ChildBySource for ImplId { |
50 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 51 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
51 | let mut res = DynMap::default(); | ||
52 | |||
53 | let data = db.impl_data(*self); | 52 | let data = db.impl_data(*self); |
54 | for &item in data.items.iter() { | 53 | for &item in data.items.iter() { |
55 | match item { | 54 | match item { |
@@ -67,25 +66,21 @@ impl ChildBySource for ImplId { | |||
67 | } | 66 | } |
68 | } | 67 | } |
69 | } | 68 | } |
70 | |||
71 | res | ||
72 | } | 69 | } |
73 | } | 70 | } |
74 | 71 | ||
75 | impl ChildBySource for ModuleId { | 72 | impl ChildBySource for ModuleId { |
76 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 73 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
77 | let crate_def_map = self.def_map(db); | 74 | let def_map = self.def_map(db); |
78 | let module_data = &crate_def_map[self.local_id]; | 75 | let module_data = &def_map[self.local_id]; |
79 | module_data.scope.child_by_source(db) | 76 | module_data.scope.child_by_source_to(db, res); |
80 | } | 77 | } |
81 | } | 78 | } |
82 | 79 | ||
83 | impl ChildBySource for ItemScope { | 80 | impl ChildBySource for ItemScope { |
84 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 81 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
85 | let mut res = DynMap::default(); | 82 | self.declarations().for_each(|item| add_module_def(db, res, item)); |
86 | self.declarations().for_each(|item| add_module_def(db, &mut res, item)); | 83 | self.impls().for_each(|imp| add_impl(db, res, imp)); |
87 | self.impls().for_each(|imp| add_impl(db, &mut res, imp)); | ||
88 | return res; | ||
89 | 84 | ||
90 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { | 85 | fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { |
91 | match item { | 86 | match item { |
@@ -134,9 +129,7 @@ impl ChildBySource for ItemScope { | |||
134 | } | 129 | } |
135 | 130 | ||
136 | impl ChildBySource for VariantId { | 131 | impl ChildBySource for VariantId { |
137 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 132 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
138 | let mut res = DynMap::default(); | ||
139 | |||
140 | let arena_map = self.child_source(db); | 133 | let arena_map = self.child_source(db); |
141 | let arena_map = arena_map.as_ref(); | 134 | let arena_map = arena_map.as_ref(); |
142 | for (local_id, source) in arena_map.value.iter() { | 135 | for (local_id, source) in arena_map.value.iter() { |
@@ -150,28 +143,27 @@ impl ChildBySource for VariantId { | |||
150 | } | 143 | } |
151 | } | 144 | } |
152 | } | 145 | } |
153 | res | ||
154 | } | 146 | } |
155 | } | 147 | } |
156 | 148 | ||
157 | impl ChildBySource for EnumId { | 149 | impl ChildBySource for EnumId { |
158 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 150 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
159 | let mut res = DynMap::default(); | ||
160 | |||
161 | let arena_map = self.child_source(db); | 151 | let arena_map = self.child_source(db); |
162 | let arena_map = arena_map.as_ref(); | 152 | let arena_map = arena_map.as_ref(); |
163 | for (local_id, source) in arena_map.value.iter() { | 153 | for (local_id, source) in arena_map.value.iter() { |
164 | let id = EnumVariantId { parent: *self, local_id }; | 154 | let id = EnumVariantId { parent: *self, local_id }; |
165 | res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) | 155 | res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) |
166 | } | 156 | } |
167 | |||
168 | res | ||
169 | } | 157 | } |
170 | } | 158 | } |
171 | 159 | ||
172 | impl ChildBySource for DefWithBodyId { | 160 | impl ChildBySource for DefWithBodyId { |
173 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 161 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
174 | let body = db.body(*self); | 162 | let body = db.body(*self); |
175 | body.item_scope.child_by_source(db) | 163 | for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) { |
164 | // All block expressions are merged into the same map, because they logically all add | ||
165 | // inner items to the containing `DefWithBodyId`. | ||
166 | def_map[def_map.root()].scope.child_by_source_to(db, res); | ||
167 | } | ||
176 | } | 168 | } |
177 | } | 169 | } |
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs index d3380e0f4..aea53d527 100644 --- a/crates/hir_def/src/data.rs +++ b/crates/hir_def/src/data.rs | |||
@@ -97,7 +97,7 @@ impl TraitData { | |||
97 | let tr_def = &item_tree[tr_loc.id.value]; | 97 | let tr_def = &item_tree[tr_loc.id.value]; |
98 | let name = tr_def.name.clone(); | 98 | let name = tr_def.name.clone(); |
99 | let auto = tr_def.auto; | 99 | let auto = tr_def.auto; |
100 | let module_id = tr_loc.container.module(db); | 100 | let module_id = tr_loc.container; |
101 | let container = AssocContainerId::TraitId(tr); | 101 | let container = AssocContainerId::TraitId(tr); |
102 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); | 102 | let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); |
103 | 103 | ||
@@ -147,7 +147,7 @@ impl ImplData { | |||
147 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); | 147 | let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); |
148 | let target_type = item_tree[impl_def.target_type].clone(); | 148 | let target_type = item_tree[impl_def.target_type].clone(); |
149 | let is_negative = impl_def.is_negative; | 149 | let is_negative = impl_def.is_negative; |
150 | let module_id = impl_loc.container.module(db); | 150 | let module_id = impl_loc.container; |
151 | let container = AssocContainerId::ImplId(id); | 151 | let container = AssocContainerId::ImplId(id); |
152 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); | 152 | let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); |
153 | 153 | ||
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index 6c01f1ed0..cca5a086b 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -133,7 +133,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
133 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; | 133 | fn import_map(&self, krate: CrateId) -> Arc<ImportMap>; |
134 | } | 134 | } |
135 | 135 | ||
136 | fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<DefMap> { | 136 | fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> { |
137 | let _p = profile::span("crate_def_map:wait"); | 137 | let _p = profile::span("crate_def_map:wait"); |
138 | db.crate_def_map_query(krate) | 138 | db.crate_def_map_query(krate) |
139 | } | 139 | } |
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 3a98ffbaa..de08e2737 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -4,7 +4,6 @@ use std::iter; | |||
4 | 4 | ||
5 | use hir_expand::name::{known, AsName, Name}; | 5 | use hir_expand::name::{known, AsName, Name}; |
6 | use rustc_hash::FxHashSet; | 6 | use rustc_hash::FxHashSet; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::nameres::DefMap; | 8 | use crate::nameres::DefMap; |
10 | use crate::{ | 9 | use crate::{ |
@@ -215,7 +214,7 @@ fn find_path_inner( | |||
215 | best_path_len - 1, | 214 | best_path_len - 1, |
216 | prefixed, | 215 | prefixed, |
217 | )?; | 216 | )?; |
218 | mark::hit!(partially_imported); | 217 | cov_mark::hit!(partially_imported); |
219 | path.push_segment(info.path.segments.last().unwrap().clone()); | 218 | path.push_segment(info.path.segments.last().unwrap().clone()); |
220 | Some(path) | 219 | Some(path) |
221 | }) | 220 | }) |
@@ -235,7 +234,7 @@ fn find_path_inner( | |||
235 | // that correctly (FIXME). | 234 | // that correctly (FIXME). |
236 | if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) { | 235 | if let Some(item_module) = item.as_module_def_id().and_then(|did| did.module(db)) { |
237 | if item_module.def_map(db).block_id().is_some() && prefixed.is_some() { | 236 | if item_module.def_map(db).block_id().is_some() && prefixed.is_some() { |
238 | mark::hit!(prefixed_in_block_expression); | 237 | cov_mark::hit!(prefixed_in_block_expression); |
239 | prefixed = Some(PrefixKind::Plain); | 238 | prefixed = Some(PrefixKind::Plain); |
240 | } | 239 | } |
241 | } | 240 | } |
@@ -252,18 +251,18 @@ fn find_path_inner( | |||
252 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { | 251 | fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath { |
253 | if old_path.starts_with_std() && new_path.can_start_with_std() { | 252 | if old_path.starts_with_std() && new_path.can_start_with_std() { |
254 | if prefer_no_std { | 253 | if prefer_no_std { |
255 | mark::hit!(prefer_no_std_paths); | 254 | cov_mark::hit!(prefer_no_std_paths); |
256 | new_path | 255 | new_path |
257 | } else { | 256 | } else { |
258 | mark::hit!(prefer_std_paths); | 257 | cov_mark::hit!(prefer_std_paths); |
259 | old_path | 258 | old_path |
260 | } | 259 | } |
261 | } else if new_path.starts_with_std() && old_path.can_start_with_std() { | 260 | } else if new_path.starts_with_std() && old_path.can_start_with_std() { |
262 | if prefer_no_std { | 261 | if prefer_no_std { |
263 | mark::hit!(prefer_no_std_paths); | 262 | cov_mark::hit!(prefer_no_std_paths); |
264 | old_path | 263 | old_path |
265 | } else { | 264 | } else { |
266 | mark::hit!(prefer_std_paths); | 265 | cov_mark::hit!(prefer_std_paths); |
267 | new_path | 266 | new_path |
268 | } | 267 | } |
269 | } else if new_path.len() < old_path.len() { | 268 | } else if new_path.len() < old_path.len() { |
@@ -364,7 +363,6 @@ mod tests { | |||
364 | use base_db::fixture::WithFixture; | 363 | use base_db::fixture::WithFixture; |
365 | use hir_expand::hygiene::Hygiene; | 364 | use hir_expand::hygiene::Hygiene; |
366 | use syntax::ast::AstNode; | 365 | use syntax::ast::AstNode; |
367 | use test_utils::mark; | ||
368 | 366 | ||
369 | use crate::test_db::TestDB; | 367 | use crate::test_db::TestDB; |
370 | 368 | ||
@@ -522,7 +520,7 @@ mod tests { | |||
522 | 520 | ||
523 | #[test] | 521 | #[test] |
524 | fn partially_imported() { | 522 | fn partially_imported() { |
525 | mark::check!(partially_imported); | 523 | cov_mark::check!(partially_imported); |
526 | // Tests that short paths are used even for external items, when parts of the path are | 524 | // Tests that short paths are used even for external items, when parts of the path are |
527 | // already in scope. | 525 | // already in scope. |
528 | let code = r#" | 526 | let code = r#" |
@@ -686,7 +684,7 @@ mod tests { | |||
686 | 684 | ||
687 | #[test] | 685 | #[test] |
688 | fn prefer_std_paths_over_alloc() { | 686 | fn prefer_std_paths_over_alloc() { |
689 | mark::check!(prefer_std_paths); | 687 | cov_mark::check!(prefer_std_paths); |
690 | let code = r#" | 688 | let code = r#" |
691 | //- /main.rs crate:main deps:alloc,std | 689 | //- /main.rs crate:main deps:alloc,std |
692 | $0 | 690 | $0 |
@@ -712,7 +710,7 @@ mod tests { | |||
712 | 710 | ||
713 | #[test] | 711 | #[test] |
714 | fn prefer_core_paths_over_std() { | 712 | fn prefer_core_paths_over_std() { |
715 | mark::check!(prefer_no_std_paths); | 713 | cov_mark::check!(prefer_no_std_paths); |
716 | let code = r#" | 714 | let code = r#" |
717 | //- /main.rs crate:main deps:core,std | 715 | //- /main.rs crate:main deps:core,std |
718 | #![no_std] | 716 | #![no_std] |
@@ -842,7 +840,7 @@ mod tests { | |||
842 | 840 | ||
843 | #[test] | 841 | #[test] |
844 | fn inner_items_from_inner_module() { | 842 | fn inner_items_from_inner_module() { |
845 | mark::check!(prefixed_in_block_expression); | 843 | cov_mark::check!(prefixed_in_block_expression); |
846 | check_found_path( | 844 | check_found_path( |
847 | r#" | 845 | r#" |
848 | fn main() { | 846 | fn main() { |
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs index 3ace3be1f..a056ab797 100644 --- a/crates/hir_def/src/generics.rs +++ b/crates/hir_def/src/generics.rs | |||
@@ -421,8 +421,7 @@ impl HasChildSource<LocalConstParamId> for GenericDefId { | |||
421 | } | 421 | } |
422 | 422 | ||
423 | impl ChildBySource for GenericDefId { | 423 | impl ChildBySource for GenericDefId { |
424 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { | 424 | fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) { |
425 | let mut res = DynMap::default(); | ||
426 | let (_, sm) = GenericParams::new(db, *self); | 425 | let (_, sm) = GenericParams::new(db, *self); |
427 | 426 | ||
428 | let sm = sm.as_ref(); | 427 | let sm = sm.as_ref(); |
@@ -440,6 +439,5 @@ impl ChildBySource for GenericDefId { | |||
440 | let id = ConstParamId { parent: *self, local_id }; | 439 | let id = ConstParamId { parent: *self, local_id }; |
441 | res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); | 440 | res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); |
442 | } | 441 | } |
443 | res | ||
444 | } | 442 | } |
445 | } | 443 | } |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 0a3dc7956..369bc3350 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -8,7 +8,6 @@ use hir_expand::name::Name; | |||
8 | use indexmap::{map::Entry, IndexMap}; | 8 | use indexmap::{map::Entry, IndexMap}; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use rustc_hash::{FxHashSet, FxHasher}; | 10 | use rustc_hash::{FxHashSet, FxHasher}; |
11 | use test_utils::mark; | ||
12 | 11 | ||
13 | use crate::{ | 12 | use crate::{ |
14 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, | 13 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, |
@@ -193,7 +192,7 @@ impl ImportMap { | |||
193 | // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` | 192 | // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` |
194 | // qualifier, ergo no need to store it for imports in import_map | 193 | // qualifier, ergo no need to store it for imports in import_map |
195 | AssocItemId::TypeAliasId(_) => { | 194 | AssocItemId::TypeAliasId(_) => { |
196 | mark::hit!(type_aliases_ignored); | 195 | cov_mark::hit!(type_aliases_ignored); |
197 | continue; | 196 | continue; |
198 | } | 197 | } |
199 | }; | 198 | }; |
@@ -388,7 +387,7 @@ pub fn search_dependencies<'a>( | |||
388 | db: &'a dyn DefDatabase, | 387 | db: &'a dyn DefDatabase, |
389 | krate: CrateId, | 388 | krate: CrateId, |
390 | query: Query, | 389 | query: Query, |
391 | ) -> Vec<ItemInNs> { | 390 | ) -> FxHashSet<ItemInNs> { |
392 | let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); | 391 | let _p = profile::span("search_dependencies").detail(|| format!("{:?}", query)); |
393 | 392 | ||
394 | let graph = db.crate_graph(); | 393 | let graph = db.crate_graph(); |
@@ -403,41 +402,42 @@ pub fn search_dependencies<'a>( | |||
403 | } | 402 | } |
404 | 403 | ||
405 | let mut stream = op.union(); | 404 | let mut stream = op.union(); |
406 | let mut res = Vec::new(); | 405 | |
406 | let mut all_indexed_values = FxHashSet::default(); | ||
407 | while let Some((_, indexed_values)) = stream.next() { | 407 | while let Some((_, indexed_values)) = stream.next() { |
408 | for indexed_value in indexed_values { | 408 | all_indexed_values.extend(indexed_values.iter().copied()); |
409 | let import_map = &import_maps[indexed_value.index]; | 409 | } |
410 | let importables = &import_map.importables[indexed_value.value as usize..]; | ||
411 | 410 | ||
412 | let common_importable_data = &import_map.map[&importables[0]]; | 411 | let mut res = FxHashSet::default(); |
413 | if !query.import_matches(common_importable_data, true) { | 412 | for indexed_value in all_indexed_values { |
414 | continue; | 413 | let import_map = &import_maps[indexed_value.index]; |
415 | } | 414 | let importables = &import_map.importables[indexed_value.value as usize..]; |
416 | 415 | ||
417 | // Path shared by the importable items in this group. | 416 | let common_importable_data = &import_map.map[&importables[0]]; |
418 | let common_importables_path_fst = fst_path(&common_importable_data.path); | 417 | if !query.import_matches(common_importable_data, true) { |
419 | // Add the items from this `ModPath` group. Those are all subsequent items in | 418 | continue; |
420 | // `importables` whose paths match `path`. | 419 | } |
421 | let iter = importables | 420 | |
422 | .iter() | 421 | // Path shared by the importable items in this group. |
423 | .copied() | 422 | let common_importables_path_fst = fst_path(&common_importable_data.path); |
424 | .take_while(|item| { | 423 | // Add the items from this `ModPath` group. Those are all subsequent items in |
425 | common_importables_path_fst == fst_path(&import_map.map[item].path) | 424 | // `importables` whose paths match `path`. |
426 | }) | 425 | let iter = importables |
427 | .filter(|&item| match item_import_kind(item) { | 426 | .iter() |
428 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), | 427 | .copied() |
429 | None => true, | 428 | .take_while(|item| common_importables_path_fst == fst_path(&import_map.map[item].path)) |
430 | }) | 429 | .filter(|&item| match item_import_kind(item) { |
431 | .filter(|item| { | 430 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), |
432 | !query.case_sensitive // we've already checked the common importables path case-insensitively | 431 | None => true, |
432 | }) | ||
433 | .filter(|item| { | ||
434 | !query.case_sensitive // we've already checked the common importables path case-insensitively | ||
433 | || query.import_matches(&import_map.map[item], false) | 435 | || query.import_matches(&import_map.map[item], false) |
434 | }); | 436 | }); |
435 | res.extend(iter); | 437 | res.extend(iter); |
436 | 438 | ||
437 | if res.len() >= query.limit { | 439 | if res.len() >= query.limit { |
438 | res.truncate(query.limit); | 440 | return res; |
439 | return res; | ||
440 | } | ||
441 | } | 441 | } |
442 | } | 442 | } |
443 | 443 | ||
@@ -462,7 +462,6 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> { | |||
462 | mod tests { | 462 | mod tests { |
463 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; | 463 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; |
464 | use expect_test::{expect, Expect}; | 464 | use expect_test::{expect, Expect}; |
465 | use test_utils::mark; | ||
466 | 465 | ||
467 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; | 466 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; |
468 | 467 | ||
@@ -800,7 +799,7 @@ mod tests { | |||
800 | 799 | ||
801 | #[test] | 800 | #[test] |
802 | fn fuzzy_import_trait_and_assoc_items() { | 801 | fn fuzzy_import_trait_and_assoc_items() { |
803 | mark::check!(type_aliases_ignored); | 802 | cov_mark::check!(type_aliases_ignored); |
804 | let ra_fixture = r#" | 803 | let ra_fixture = r#" |
805 | //- /main.rs crate:main deps:dep | 804 | //- /main.rs crate:main deps:dep |
806 | //- /dep.rs crate:dep | 805 | //- /dep.rs crate:dep |
@@ -821,10 +820,10 @@ mod tests { | |||
821 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), | 820 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), |
822 | expect![[r#" | 821 | expect![[r#" |
823 | dep::fmt (t) | 822 | dep::fmt (t) |
823 | dep::fmt::Display::format_method (a) | ||
824 | dep::fmt::Display (t) | 824 | dep::fmt::Display (t) |
825 | dep::fmt::Display::FMT_CONST (a) | 825 | dep::fmt::Display::FMT_CONST (a) |
826 | dep::fmt::Display::format_function (a) | 826 | dep::fmt::Display::format_function (a) |
827 | dep::fmt::Display::format_method (a) | ||
828 | "#]], | 827 | "#]], |
829 | ); | 828 | ); |
830 | } | 829 | } |
@@ -850,9 +849,9 @@ mod tests { | |||
850 | "main", | 849 | "main", |
851 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), | 850 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy).assoc_items_only(), |
852 | expect![[r#" | 851 | expect![[r#" |
852 | dep::fmt::Display::format_method (a) | ||
853 | dep::fmt::Display::FMT_CONST (a) | 853 | dep::fmt::Display::FMT_CONST (a) |
854 | dep::fmt::Display::format_function (a) | 854 | dep::fmt::Display::format_function (a) |
855 | dep::fmt::Display::format_method (a) | ||
856 | "#]], | 855 | "#]], |
857 | ); | 856 | ); |
858 | 857 | ||
@@ -911,12 +910,12 @@ mod tests { | |||
911 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), | 910 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), |
912 | expect![[r#" | 911 | expect![[r#" |
913 | dep::fmt (t) | 912 | dep::fmt (t) |
914 | dep::Fmt (t) | 913 | dep::format (f) |
915 | dep::Fmt (v) | 914 | dep::Fmt (v) |
916 | dep::Fmt (m) | ||
917 | dep::fmt::Display (t) | 915 | dep::fmt::Display (t) |
916 | dep::Fmt (t) | ||
918 | dep::fmt::Display::fmt (a) | 917 | dep::fmt::Display::fmt (a) |
919 | dep::format (f) | 918 | dep::Fmt (m) |
920 | "#]], | 919 | "#]], |
921 | ); | 920 | ); |
922 | 921 | ||
@@ -926,10 +925,10 @@ mod tests { | |||
926 | Query::new("fmt".to_string()).search_mode(SearchMode::Equals), | 925 | Query::new("fmt".to_string()).search_mode(SearchMode::Equals), |
927 | expect![[r#" | 926 | expect![[r#" |
928 | dep::fmt (t) | 927 | dep::fmt (t) |
929 | dep::Fmt (t) | ||
930 | dep::Fmt (v) | 928 | dep::Fmt (v) |
931 | dep::Fmt (m) | 929 | dep::Fmt (t) |
932 | dep::fmt::Display::fmt (a) | 930 | dep::fmt::Display::fmt (a) |
931 | dep::Fmt (m) | ||
933 | "#]], | 932 | "#]], |
934 | ); | 933 | ); |
935 | 934 | ||
@@ -939,11 +938,11 @@ mod tests { | |||
939 | Query::new("fmt".to_string()).search_mode(SearchMode::Contains), | 938 | Query::new("fmt".to_string()).search_mode(SearchMode::Contains), |
940 | expect![[r#" | 939 | expect![[r#" |
941 | dep::fmt (t) | 940 | dep::fmt (t) |
942 | dep::Fmt (t) | ||
943 | dep::Fmt (v) | 941 | dep::Fmt (v) |
944 | dep::Fmt (m) | ||
945 | dep::fmt::Display (t) | 942 | dep::fmt::Display (t) |
943 | dep::Fmt (t) | ||
946 | dep::fmt::Display::fmt (a) | 944 | dep::fmt::Display::fmt (a) |
945 | dep::Fmt (m) | ||
947 | "#]], | 946 | "#]], |
948 | ); | 947 | ); |
949 | } | 948 | } |
@@ -980,11 +979,11 @@ mod tests { | |||
980 | Query::new("fmt".to_string()), | 979 | Query::new("fmt".to_string()), |
981 | expect![[r#" | 980 | expect![[r#" |
982 | dep::fmt (t) | 981 | dep::fmt (t) |
983 | dep::Fmt (t) | ||
984 | dep::Fmt (v) | 982 | dep::Fmt (v) |
985 | dep::Fmt (m) | ||
986 | dep::fmt::Display (t) | 983 | dep::fmt::Display (t) |
984 | dep::Fmt (t) | ||
987 | dep::fmt::Display::fmt (a) | 985 | dep::fmt::Display::fmt (a) |
986 | dep::Fmt (m) | ||
988 | "#]], | 987 | "#]], |
989 | ); | 988 | ); |
990 | 989 | ||
@@ -994,10 +993,10 @@ mod tests { | |||
994 | Query::new("fmt".to_string()).name_only(), | 993 | Query::new("fmt".to_string()).name_only(), |
995 | expect![[r#" | 994 | expect![[r#" |
996 | dep::fmt (t) | 995 | dep::fmt (t) |
997 | dep::Fmt (t) | ||
998 | dep::Fmt (v) | 996 | dep::Fmt (v) |
999 | dep::Fmt (m) | 997 | dep::Fmt (t) |
1000 | dep::fmt::Display::fmt (a) | 998 | dep::fmt::Display::fmt (a) |
999 | dep::Fmt (m) | ||
1001 | "#]], | 1000 | "#]], |
1002 | ); | 1001 | ); |
1003 | } | 1002 | } |
@@ -1018,9 +1017,9 @@ mod tests { | |||
1018 | Query::new("FMT".to_string()), | 1017 | Query::new("FMT".to_string()), |
1019 | expect![[r#" | 1018 | expect![[r#" |
1020 | dep::fmt (t) | 1019 | dep::fmt (t) |
1020 | dep::FMT (v) | ||
1021 | dep::fmt (v) | 1021 | dep::fmt (v) |
1022 | dep::FMT (t) | 1022 | dep::FMT (t) |
1023 | dep::FMT (v) | ||
1024 | "#]], | 1023 | "#]], |
1025 | ); | 1024 | ); |
1026 | 1025 | ||
@@ -1060,6 +1059,8 @@ mod tests { | |||
1060 | expect![[r#" | 1059 | expect![[r#" |
1061 | dep::fmt (t) | 1060 | dep::fmt (t) |
1062 | dep::Fmt (t) | 1061 | dep::Fmt (t) |
1062 | dep::Fmt (m) | ||
1063 | dep::Fmt (v) | ||
1063 | "#]], | 1064 | "#]], |
1064 | ); | 1065 | ); |
1065 | } | 1066 | } |
@@ -1080,9 +1081,9 @@ mod tests { | |||
1080 | Query::new("FMT".to_string()), | 1081 | Query::new("FMT".to_string()), |
1081 | expect![[r#" | 1082 | expect![[r#" |
1082 | dep::fmt (t) | 1083 | dep::fmt (t) |
1084 | dep::FMT (v) | ||
1083 | dep::fmt (v) | 1085 | dep::fmt (v) |
1084 | dep::FMT (t) | 1086 | dep::FMT (t) |
1085 | dep::FMT (v) | ||
1086 | "#]], | 1087 | "#]], |
1087 | ); | 1088 | ); |
1088 | 1089 | ||
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 4e5daa2ff..aafd73b60 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -9,7 +9,6 @@ use hir_expand::MacroDefKind; | |||
9 | use once_cell::sync::Lazy; | 9 | use once_cell::sync::Lazy; |
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use stdx::format_to; | 11 | use stdx::format_to; |
12 | use test_utils::mark; | ||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, | 14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, |
@@ -169,37 +168,6 @@ impl ItemScope { | |||
169 | self.unnamed_trait_imports.insert(tr, vis); | 168 | self.unnamed_trait_imports.insert(tr, vis); |
170 | } | 169 | } |
171 | 170 | ||
172 | pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool { | ||
173 | let mut changed = false; | ||
174 | |||
175 | if let Some(types) = def.types { | ||
176 | self.types.entry(name.clone()).or_insert_with(|| { | ||
177 | changed = true; | ||
178 | types | ||
179 | }); | ||
180 | } | ||
181 | if let Some(values) = def.values { | ||
182 | self.values.entry(name.clone()).or_insert_with(|| { | ||
183 | changed = true; | ||
184 | values | ||
185 | }); | ||
186 | } | ||
187 | if let Some(macros) = def.macros { | ||
188 | self.macros.entry(name.clone()).or_insert_with(|| { | ||
189 | changed = true; | ||
190 | macros | ||
191 | }); | ||
192 | } | ||
193 | |||
194 | if def.is_none() { | ||
195 | if self.unresolved.insert(name) { | ||
196 | changed = true; | ||
197 | } | ||
198 | } | ||
199 | |||
200 | changed | ||
201 | } | ||
202 | |||
203 | pub(crate) fn push_res_with_import( | 171 | pub(crate) fn push_res_with_import( |
204 | &mut self, | 172 | &mut self, |
205 | glob_imports: &mut PerNsGlobImports, | 173 | glob_imports: &mut PerNsGlobImports, |
@@ -237,7 +205,7 @@ impl ItemScope { | |||
237 | if $glob_imports.$field.contains(&$lookup) | 205 | if $glob_imports.$field.contains(&$lookup) |
238 | && matches!($def_import_type, ImportType::Named) => | 206 | && matches!($def_import_type, ImportType::Named) => |
239 | { | 207 | { |
240 | mark::hit!(import_shadowed); | 208 | cov_mark::hit!(import_shadowed); |
241 | $glob_imports.$field.remove(&$lookup); | 209 | $glob_imports.$field.remove(&$lookup); |
242 | if let Some(fld) = $def.$field { | 210 | if let Some(fld) = $def.$field { |
243 | entry.insert(fld); | 211 | entry.insert(fld); |
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs index 3233b1957..6bb334573 100644 --- a/crates/hir_def/src/item_tree.rs +++ b/crates/hir_def/src/item_tree.rs | |||
@@ -25,7 +25,6 @@ use profile::Count; | |||
25 | use rustc_hash::FxHashMap; | 25 | use rustc_hash::FxHashMap; |
26 | use smallvec::SmallVec; | 26 | use smallvec::SmallVec; |
27 | use syntax::{ast, match_ast, SyntaxKind}; | 27 | use syntax::{ast, match_ast, SyntaxKind}; |
28 | use test_utils::mark; | ||
29 | 28 | ||
30 | use crate::{ | 29 | use crate::{ |
31 | attr::{Attrs, RawAttrs}, | 30 | attr::{Attrs, RawAttrs}, |
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs index 8f2f0b340..240fdacf9 100644 --- a/crates/hir_def/src/item_tree/lower.rs +++ b/crates/hir_def/src/item_tree/lower.rs | |||
@@ -466,7 +466,7 @@ impl Ctx { | |||
466 | .collect() | 466 | .collect() |
467 | }) | 467 | }) |
468 | .unwrap_or_else(|| { | 468 | .unwrap_or_else(|| { |
469 | mark::hit!(name_res_works_for_broken_modules); | 469 | cov_mark::hit!(name_res_works_for_broken_modules); |
470 | Box::new([]) as Box<[_]> | 470 | Box::new([]) as Box<[_]> |
471 | }), | 471 | }), |
472 | } | 472 | } |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index 4498d94bb..6d11c5be4 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -108,7 +108,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>; | |||
108 | 108 | ||
109 | #[derive(Debug)] | 109 | #[derive(Debug)] |
110 | pub struct ItemLoc<N: ItemTreeNode> { | 110 | pub struct ItemLoc<N: ItemTreeNode> { |
111 | pub container: ContainerId, | 111 | pub container: ModuleId, |
112 | pub id: ItemTreeId<N>, | 112 | pub id: ItemTreeId<N>, |
113 | } | 113 | } |
114 | 114 | ||
@@ -279,18 +279,12 @@ pub struct ConstParamId { | |||
279 | pub type LocalConstParamId = Idx<generics::ConstParamData>; | 279 | pub type LocalConstParamId = Idx<generics::ConstParamData>; |
280 | 280 | ||
281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
282 | pub enum ContainerId { | ||
283 | ModuleId(ModuleId), | ||
284 | DefWithBodyId(DefWithBodyId), | ||
285 | } | ||
286 | |||
287 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
288 | pub enum AssocContainerId { | 282 | pub enum AssocContainerId { |
289 | ContainerId(ContainerId), | 283 | ModuleId(ModuleId), |
290 | ImplId(ImplId), | 284 | ImplId(ImplId), |
291 | TraitId(TraitId), | 285 | TraitId(TraitId), |
292 | } | 286 | } |
293 | impl_from!(ContainerId for AssocContainerId); | 287 | impl_from!(ModuleId for AssocContainerId); |
294 | 288 | ||
295 | /// A Data Type | 289 | /// A Data Type |
296 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] | 290 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] |
@@ -447,21 +441,12 @@ pub trait HasModule { | |||
447 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; | 441 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; |
448 | } | 442 | } |
449 | 443 | ||
450 | impl HasModule for ContainerId { | ||
451 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | ||
452 | match *self { | ||
453 | ContainerId::ModuleId(it) => it, | ||
454 | ContainerId::DefWithBodyId(it) => it.module(db), | ||
455 | } | ||
456 | } | ||
457 | } | ||
458 | |||
459 | impl HasModule for AssocContainerId { | 444 | impl HasModule for AssocContainerId { |
460 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | 445 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { |
461 | match *self { | 446 | match *self { |
462 | AssocContainerId::ContainerId(it) => it.module(db), | 447 | AssocContainerId::ModuleId(it) => it, |
463 | AssocContainerId::ImplId(it) => it.lookup(db).container.module(db), | 448 | AssocContainerId::ImplId(it) => it.lookup(db).container, |
464 | AssocContainerId::TraitId(it) => it.lookup(db).container.module(db), | 449 | AssocContainerId::TraitId(it) => it.lookup(db).container, |
465 | } | 450 | } |
466 | } | 451 | } |
467 | } | 452 | } |
@@ -479,16 +464,15 @@ impl HasModule for AdtId { | |||
479 | AdtId::UnionId(it) => it.lookup(db).container, | 464 | AdtId::UnionId(it) => it.lookup(db).container, |
480 | AdtId::EnumId(it) => it.lookup(db).container, | 465 | AdtId::EnumId(it) => it.lookup(db).container, |
481 | } | 466 | } |
482 | .module(db) | ||
483 | } | 467 | } |
484 | } | 468 | } |
485 | 469 | ||
486 | impl HasModule for VariantId { | 470 | impl HasModule for VariantId { |
487 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | 471 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { |
488 | match self { | 472 | match self { |
489 | VariantId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), | 473 | VariantId::EnumVariantId(it) => it.parent.lookup(db).container, |
490 | VariantId::StructId(it) => it.lookup(db).container.module(db), | 474 | VariantId::StructId(it) => it.lookup(db).container, |
491 | VariantId::UnionId(it) => it.lookup(db).container.module(db), | 475 | VariantId::UnionId(it) => it.lookup(db).container, |
492 | } | 476 | } |
493 | } | 477 | } |
494 | } | 478 | } |
@@ -518,18 +502,18 @@ impl HasModule for GenericDefId { | |||
518 | match self { | 502 | match self { |
519 | GenericDefId::FunctionId(it) => it.lookup(db).module(db), | 503 | GenericDefId::FunctionId(it) => it.lookup(db).module(db), |
520 | GenericDefId::AdtId(it) => it.module(db), | 504 | GenericDefId::AdtId(it) => it.module(db), |
521 | GenericDefId::TraitId(it) => it.lookup(db).container.module(db), | 505 | GenericDefId::TraitId(it) => it.lookup(db).container, |
522 | GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), | 506 | GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), |
523 | GenericDefId::ImplId(it) => it.lookup(db).container.module(db), | 507 | GenericDefId::ImplId(it) => it.lookup(db).container, |
524 | GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), | 508 | GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container, |
525 | GenericDefId::ConstId(it) => it.lookup(db).module(db), | 509 | GenericDefId::ConstId(it) => it.lookup(db).module(db), |
526 | } | 510 | } |
527 | } | 511 | } |
528 | } | 512 | } |
529 | 513 | ||
530 | impl HasModule for StaticLoc { | 514 | impl HasModule for StaticLoc { |
531 | fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { | 515 | fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId { |
532 | self.container.module(db) | 516 | self.container |
533 | } | 517 | } |
534 | } | 518 | } |
535 | 519 | ||
@@ -542,10 +526,10 @@ impl ModuleDefId { | |||
542 | ModuleDefId::ModuleId(id) => *id, | 526 | ModuleDefId::ModuleId(id) => *id, |
543 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db), | 527 | ModuleDefId::FunctionId(id) => id.lookup(db).module(db), |
544 | ModuleDefId::AdtId(id) => id.module(db), | 528 | ModuleDefId::AdtId(id) => id.module(db), |
545 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db), | 529 | ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container, |
546 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), | 530 | ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), |
547 | ModuleDefId::StaticId(id) => id.lookup(db).container.module(db), | 531 | ModuleDefId::StaticId(id) => id.lookup(db).container, |
548 | ModuleDefId::TraitId(id) => id.lookup(db).container.module(db), | 532 | ModuleDefId::TraitId(id) => id.lookup(db).container, |
549 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), | 533 | ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), |
550 | ModuleDefId::BuiltinType(_) => return None, | 534 | ModuleDefId::BuiltinType(_) => return None, |
551 | }) | 535 | }) |
@@ -559,12 +543,12 @@ impl AttrDefId { | |||
559 | AttrDefId::FieldId(it) => it.parent.module(db).krate, | 543 | AttrDefId::FieldId(it) => it.parent.module(db).krate, |
560 | AttrDefId::AdtId(it) => it.module(db).krate, | 544 | AttrDefId::AdtId(it) => it.module(db).krate, |
561 | AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, | 545 | AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, |
562 | AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db).krate, | 546 | AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.krate, |
563 | AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, | 547 | AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, |
564 | AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, | 548 | AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, |
565 | AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, | 549 | AttrDefId::TraitId(it) => it.lookup(db).container.krate, |
566 | AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, | 550 | AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, |
567 | AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, | 551 | AttrDefId::ImplId(it) => it.lookup(db).container.krate, |
568 | AttrDefId::GenericParamId(it) => { | 552 | AttrDefId::GenericParamId(it) => { |
569 | match it { | 553 | match it { |
570 | GenericParamId::TypeParamId(it) => it.parent, | 554 | GenericParamId::TypeParamId(it) => it.parent, |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index e51d89b43..9ed48c506 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -18,7 +18,6 @@ use hir_expand::{ | |||
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
20 | use syntax::ast; | 20 | use syntax::ast; |
21 | use test_utils::mark; | ||
22 | use tt::{Leaf, TokenTree}; | 21 | use tt::{Leaf, TokenTree}; |
23 | 22 | ||
24 | use crate::{ | 23 | use crate::{ |
@@ -38,9 +37,9 @@ use crate::{ | |||
38 | path::{ImportAlias, ModPath, PathKind}, | 37 | path::{ImportAlias, ModPath, PathKind}, |
39 | per_ns::PerNs, | 38 | per_ns::PerNs, |
40 | visibility::{RawVisibility, Visibility}, | 39 | visibility::{RawVisibility, Visibility}, |
41 | AdtId, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, | 40 | AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern, |
42 | ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, | 41 | LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, |
43 | UnionLoc, UnresolvedMacro, | 42 | UnresolvedMacro, |
44 | }; | 43 | }; |
45 | 44 | ||
46 | const GLOB_RECURSION_LIMIT: usize = 100; | 45 | const GLOB_RECURSION_LIMIT: usize = 100; |
@@ -462,7 +461,7 @@ impl DefCollector<'_> { | |||
462 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); | 461 | let res = self.def_map.resolve_name_in_extern_prelude(&extern_crate.name); |
463 | 462 | ||
464 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { | 463 | if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { |
465 | mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); | 464 | cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); |
466 | self.import_all_macros_exported(current_module_id, m.krate); | 465 | self.import_all_macros_exported(current_module_id, m.krate); |
467 | } | 466 | } |
468 | } | 467 | } |
@@ -571,10 +570,10 @@ impl DefCollector<'_> { | |||
571 | match def.take_types() { | 570 | match def.take_types() { |
572 | Some(ModuleDefId::ModuleId(m)) => { | 571 | Some(ModuleDefId::ModuleId(m)) => { |
573 | if import.is_prelude { | 572 | if import.is_prelude { |
574 | mark::hit!(std_prelude); | 573 | cov_mark::hit!(std_prelude); |
575 | self.def_map.prelude = Some(m); | 574 | self.def_map.prelude = Some(m); |
576 | } else if m.krate != self.def_map.krate { | 575 | } else if m.krate != self.def_map.krate { |
577 | mark::hit!(glob_across_crates); | 576 | cov_mark::hit!(glob_across_crates); |
578 | // glob import from other crate => we can just import everything once | 577 | // glob import from other crate => we can just import everything once |
579 | let item_map = m.def_map(self.db); | 578 | let item_map = m.def_map(self.db); |
580 | let scope = &item_map[m.local_id].scope; | 579 | let scope = &item_map[m.local_id].scope; |
@@ -626,7 +625,7 @@ impl DefCollector<'_> { | |||
626 | } | 625 | } |
627 | } | 626 | } |
628 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { | 627 | Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { |
629 | mark::hit!(glob_enum); | 628 | cov_mark::hit!(glob_enum); |
630 | // glob import from enum => just import all the variants | 629 | // glob import from enum => just import all the variants |
631 | 630 | ||
632 | // XXX: urgh, so this works by accident! Here, we look at | 631 | // XXX: urgh, so this works by accident! Here, we look at |
@@ -675,7 +674,7 @@ impl DefCollector<'_> { | |||
675 | 674 | ||
676 | self.update(module_id, &[(name, def)], vis, ImportType::Named); | 675 | self.update(module_id, &[(name, def)], vis, ImportType::Named); |
677 | } | 676 | } |
678 | None => mark::hit!(bogus_paths), | 677 | None => cov_mark::hit!(bogus_paths), |
679 | } | 678 | } |
680 | } | 679 | } |
681 | } | 680 | } |
@@ -738,7 +737,7 @@ impl DefCollector<'_> { | |||
738 | if max_vis == old_vis { | 737 | if max_vis == old_vis { |
739 | false | 738 | false |
740 | } else { | 739 | } else { |
741 | mark::hit!(upgrade_underscore_visibility); | 740 | cov_mark::hit!(upgrade_underscore_visibility); |
742 | true | 741 | true |
743 | } | 742 | } |
744 | } | 743 | } |
@@ -866,7 +865,7 @@ impl DefCollector<'_> { | |||
866 | depth: usize, | 865 | depth: usize, |
867 | ) { | 866 | ) { |
868 | if depth > EXPANSION_DEPTH_LIMIT { | 867 | if depth > EXPANSION_DEPTH_LIMIT { |
869 | mark::hit!(macro_expansion_overflow); | 868 | cov_mark::hit!(macro_expansion_overflow); |
870 | log::warn!("macro expansion is too deep"); | 869 | log::warn!("macro expansion is too deep"); |
871 | return; | 870 | return; |
872 | } | 871 | } |
@@ -1009,7 +1008,7 @@ impl ModCollector<'_, '_> { | |||
1009 | // Prelude module is always considered to be `#[macro_use]`. | 1008 | // Prelude module is always considered to be `#[macro_use]`. |
1010 | if let Some(prelude_module) = self.def_collector.def_map.prelude { | 1009 | if let Some(prelude_module) = self.def_collector.def_map.prelude { |
1011 | if prelude_module.krate != self.def_collector.def_map.krate { | 1010 | if prelude_module.krate != self.def_collector.def_map.krate { |
1012 | mark::hit!(prelude_is_macro_use); | 1011 | cov_mark::hit!(prelude_is_macro_use); |
1013 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); | 1012 | self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); |
1014 | } | 1013 | } |
1015 | } | 1014 | } |
@@ -1043,7 +1042,6 @@ impl ModCollector<'_, '_> { | |||
1043 | } | 1042 | } |
1044 | } | 1043 | } |
1045 | let module = self.def_collector.def_map.module_id(self.module_id); | 1044 | let module = self.def_collector.def_map.module_id(self.module_id); |
1046 | let container = ContainerId::ModuleId(module); | ||
1047 | 1045 | ||
1048 | let mut def = None; | 1046 | let mut def = None; |
1049 | match item { | 1047 | match item { |
@@ -1110,9 +1108,9 @@ impl ModCollector<'_, '_> { | |||
1110 | } | 1108 | } |
1111 | ModItem::Impl(imp) => { | 1109 | ModItem::Impl(imp) => { |
1112 | let module = self.def_collector.def_map.module_id(self.module_id); | 1110 | let module = self.def_collector.def_map.module_id(self.module_id); |
1113 | let container = ContainerId::ModuleId(module); | 1111 | let impl_id = |
1114 | let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } | 1112 | ImplLoc { container: module, id: ItemTreeId::new(self.file_id, imp) } |
1115 | .intern(self.def_collector.db); | 1113 | .intern(self.def_collector.db); |
1116 | self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) | 1114 | self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) |
1117 | } | 1115 | } |
1118 | ModItem::Function(id) => { | 1116 | ModItem::Function(id) => { |
@@ -1122,7 +1120,7 @@ impl ModCollector<'_, '_> { | |||
1122 | 1120 | ||
1123 | def = Some(DefData { | 1121 | def = Some(DefData { |
1124 | id: FunctionLoc { | 1122 | id: FunctionLoc { |
1125 | container: container.into(), | 1123 | container: module.into(), |
1126 | id: ItemTreeId::new(self.file_id, id), | 1124 | id: ItemTreeId::new(self.file_id, id), |
1127 | } | 1125 | } |
1128 | .intern(self.def_collector.db) | 1126 | .intern(self.def_collector.db) |
@@ -1141,7 +1139,7 @@ impl ModCollector<'_, '_> { | |||
1141 | self.collect_derives(&attrs, it.ast_id.upcast()); | 1139 | self.collect_derives(&attrs, it.ast_id.upcast()); |
1142 | 1140 | ||
1143 | def = Some(DefData { | 1141 | def = Some(DefData { |
1144 | id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1142 | id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1145 | .intern(self.def_collector.db) | 1143 | .intern(self.def_collector.db) |
1146 | .into(), | 1144 | .into(), |
1147 | name: &it.name, | 1145 | name: &it.name, |
@@ -1158,7 +1156,7 @@ impl ModCollector<'_, '_> { | |||
1158 | self.collect_derives(&attrs, it.ast_id.upcast()); | 1156 | self.collect_derives(&attrs, it.ast_id.upcast()); |
1159 | 1157 | ||
1160 | def = Some(DefData { | 1158 | def = Some(DefData { |
1161 | id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1159 | id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1162 | .intern(self.def_collector.db) | 1160 | .intern(self.def_collector.db) |
1163 | .into(), | 1161 | .into(), |
1164 | name: &it.name, | 1162 | name: &it.name, |
@@ -1175,7 +1173,7 @@ impl ModCollector<'_, '_> { | |||
1175 | self.collect_derives(&attrs, it.ast_id.upcast()); | 1173 | self.collect_derives(&attrs, it.ast_id.upcast()); |
1176 | 1174 | ||
1177 | def = Some(DefData { | 1175 | def = Some(DefData { |
1178 | id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1176 | id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1179 | .intern(self.def_collector.db) | 1177 | .intern(self.def_collector.db) |
1180 | .into(), | 1178 | .into(), |
1181 | name: &it.name, | 1179 | name: &it.name, |
@@ -1189,7 +1187,7 @@ impl ModCollector<'_, '_> { | |||
1189 | if let Some(name) = &it.name { | 1187 | if let Some(name) = &it.name { |
1190 | def = Some(DefData { | 1188 | def = Some(DefData { |
1191 | id: ConstLoc { | 1189 | id: ConstLoc { |
1192 | container: container.into(), | 1190 | container: module.into(), |
1193 | id: ItemTreeId::new(self.file_id, id), | 1191 | id: ItemTreeId::new(self.file_id, id), |
1194 | } | 1192 | } |
1195 | .intern(self.def_collector.db) | 1193 | .intern(self.def_collector.db) |
@@ -1204,7 +1202,7 @@ impl ModCollector<'_, '_> { | |||
1204 | let it = &self.item_tree[id]; | 1202 | let it = &self.item_tree[id]; |
1205 | 1203 | ||
1206 | def = Some(DefData { | 1204 | def = Some(DefData { |
1207 | id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1205 | id: StaticLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1208 | .intern(self.def_collector.db) | 1206 | .intern(self.def_collector.db) |
1209 | .into(), | 1207 | .into(), |
1210 | name: &it.name, | 1208 | name: &it.name, |
@@ -1216,7 +1214,7 @@ impl ModCollector<'_, '_> { | |||
1216 | let it = &self.item_tree[id]; | 1214 | let it = &self.item_tree[id]; |
1217 | 1215 | ||
1218 | def = Some(DefData { | 1216 | def = Some(DefData { |
1219 | id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } | 1217 | id: TraitLoc { container: module, id: ItemTreeId::new(self.file_id, id) } |
1220 | .intern(self.def_collector.db) | 1218 | .intern(self.def_collector.db) |
1221 | .into(), | 1219 | .into(), |
1222 | name: &it.name, | 1220 | name: &it.name, |
@@ -1229,7 +1227,7 @@ impl ModCollector<'_, '_> { | |||
1229 | 1227 | ||
1230 | def = Some(DefData { | 1228 | def = Some(DefData { |
1231 | id: TypeAliasLoc { | 1229 | id: TypeAliasLoc { |
1232 | container: container.into(), | 1230 | container: module.into(), |
1233 | id: ItemTreeId::new(self.file_id, id), | 1231 | id: ItemTreeId::new(self.file_id, id), |
1234 | } | 1232 | } |
1235 | .intern(self.def_collector.db) | 1233 | .intern(self.def_collector.db) |
diff --git a/crates/hir_def/src/nameres/mod_resolution.rs b/crates/hir_def/src/nameres/mod_resolution.rs index af3262439..d5de9899c 100644 --- a/crates/hir_def/src/nameres/mod_resolution.rs +++ b/crates/hir_def/src/nameres/mod_resolution.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | use base_db::{AnchoredPath, FileId}; | 2 | use base_db::{AnchoredPath, FileId}; |
3 | use hir_expand::name::Name; | 3 | use hir_expand::name::Name; |
4 | use syntax::SmolStr; | 4 | use syntax::SmolStr; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{db::DefDatabase, HirFileId}; | 6 | use crate::{db::DefDatabase, HirFileId}; |
8 | 7 | ||
@@ -28,7 +27,7 @@ impl ModDir { | |||
28 | let depth = self.depth + 1; | 27 | let depth = self.depth + 1; |
29 | if depth > MOD_DEPTH_LIMIT { | 28 | if depth > MOD_DEPTH_LIMIT { |
30 | log::error!("MOD_DEPTH_LIMIT exceeded"); | 29 | log::error!("MOD_DEPTH_LIMIT exceeded"); |
31 | mark::hit!(circular_mods); | 30 | cov_mark::hit!(circular_mods); |
32 | return None; | 31 | return None; |
33 | } | 32 | } |
34 | Some(ModDir { dir_path, root_non_dir_owner, depth }) | 33 | Some(ModDir { dir_path, root_non_dir_owner, depth }) |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index dd1db0094..8258dcffb 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -13,7 +13,6 @@ | |||
13 | use base_db::Edition; | 13 | use base_db::Edition; |
14 | use hir_expand::name; | 14 | use hir_expand::name; |
15 | use hir_expand::name::Name; | 15 | use hir_expand::name::Name; |
16 | use test_utils::mark; | ||
17 | 16 | ||
18 | use crate::{ | 17 | use crate::{ |
19 | db::DefDatabase, | 18 | db::DefDatabase, |
@@ -63,7 +62,7 @@ impl ResolvePathResult { | |||
63 | impl DefMap { | 62 | impl DefMap { |
64 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { | 63 | pub(super) fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { |
65 | if name == &name!(self) { | 64 | if name == &name!(self) { |
66 | mark::hit!(extern_crate_self_as); | 65 | cov_mark::hit!(extern_crate_self_as); |
67 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); | 66 | return PerNs::types(self.module_id(self.root).into(), Visibility::Public); |
68 | } | 67 | } |
69 | self.extern_prelude | 68 | self.extern_prelude |
@@ -101,7 +100,7 @@ impl DefMap { | |||
101 | // DefMap they're written in, so we restrict them when that happens. | 100 | // DefMap they're written in, so we restrict them when that happens. |
102 | if let Visibility::Module(m) = vis { | 101 | if let Visibility::Module(m) = vis { |
103 | if self.block_id() != m.block { | 102 | if self.block_id() != m.block { |
104 | mark::hit!(adjust_vis_in_block_def_map); | 103 | cov_mark::hit!(adjust_vis_in_block_def_map); |
105 | vis = Visibility::Module(self.module_id(self.root())); | 104 | vis = Visibility::Module(self.module_id(self.root())); |
106 | log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); | 105 | log::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); |
107 | } | 106 | } |
@@ -169,12 +168,12 @@ impl DefMap { | |||
169 | let mut curr_per_ns: PerNs = match path.kind { | 168 | let mut curr_per_ns: PerNs = match path.kind { |
170 | PathKind::DollarCrate(krate) => { | 169 | PathKind::DollarCrate(krate) => { |
171 | if krate == self.krate { | 170 | if krate == self.krate { |
172 | mark::hit!(macro_dollar_crate_self); | 171 | cov_mark::hit!(macro_dollar_crate_self); |
173 | PerNs::types(self.crate_root(db).into(), Visibility::Public) | 172 | PerNs::types(self.crate_root(db).into(), Visibility::Public) |
174 | } else { | 173 | } else { |
175 | let def_map = db.crate_def_map(krate); | 174 | let def_map = db.crate_def_map(krate); |
176 | let module = def_map.module_id(def_map.root); | 175 | let module = def_map.module_id(def_map.root); |
177 | mark::hit!(macro_dollar_crate_other); | 176 | cov_mark::hit!(macro_dollar_crate_other); |
178 | PerNs::types(module.into(), Visibility::Public) | 177 | PerNs::types(module.into(), Visibility::Public) |
179 | } | 178 | } |
180 | } | 179 | } |
@@ -310,7 +309,7 @@ impl DefMap { | |||
310 | } | 309 | } |
311 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { | 310 | ModuleDefId::AdtId(AdtId::EnumId(e)) => { |
312 | // enum variant | 311 | // enum variant |
313 | mark::hit!(can_import_enum_variant); | 312 | cov_mark::hit!(can_import_enum_variant); |
314 | let enum_data = db.enum_data(e); | 313 | let enum_data = db.enum_data(e); |
315 | match enum_data.variant(&segment) { | 314 | match enum_data.variant(&segment) { |
316 | Some(local_id) => { | 315 | Some(local_id) => { |
diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index bd3e2701b..de3aa4f9a 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs | |||
@@ -9,7 +9,6 @@ use std::sync::Arc; | |||
9 | 9 | ||
10 | use base_db::{fixture::WithFixture, SourceDatabase}; | 10 | use base_db::{fixture::WithFixture, SourceDatabase}; |
11 | use expect_test::{expect, Expect}; | 11 | use expect_test::{expect, Expect}; |
12 | use test_utils::mark; | ||
13 | 12 | ||
14 | use crate::{db::DefDatabase, test_db::TestDB}; | 13 | use crate::{db::DefDatabase, test_db::TestDB}; |
15 | 14 | ||
@@ -136,7 +135,7 @@ mod m { | |||
136 | 135 | ||
137 | #[test] | 136 | #[test] |
138 | fn bogus_paths() { | 137 | fn bogus_paths() { |
139 | mark::check!(bogus_paths); | 138 | cov_mark::check!(bogus_paths); |
140 | check( | 139 | check( |
141 | r#" | 140 | r#" |
142 | //- /lib.rs | 141 | //- /lib.rs |
@@ -243,7 +242,7 @@ pub struct Baz; | |||
243 | 242 | ||
244 | #[test] | 243 | #[test] |
245 | fn std_prelude() { | 244 | fn std_prelude() { |
246 | mark::check!(std_prelude); | 245 | cov_mark::check!(std_prelude); |
247 | check( | 246 | check( |
248 | r#" | 247 | r#" |
249 | //- /main.rs crate:main deps:test_crate | 248 | //- /main.rs crate:main deps:test_crate |
@@ -267,7 +266,7 @@ pub enum Foo { Bar, Baz }; | |||
267 | 266 | ||
268 | #[test] | 267 | #[test] |
269 | fn can_import_enum_variant() { | 268 | fn can_import_enum_variant() { |
270 | mark::check!(can_import_enum_variant); | 269 | cov_mark::check!(can_import_enum_variant); |
271 | check( | 270 | check( |
272 | r#" | 271 | r#" |
273 | enum E { V } | 272 | enum E { V } |
@@ -628,7 +627,7 @@ use crate::reex::*; | |||
628 | 627 | ||
629 | #[test] | 628 | #[test] |
630 | fn underscore_pub_crate_reexport() { | 629 | fn underscore_pub_crate_reexport() { |
631 | mark::check!(upgrade_underscore_visibility); | 630 | cov_mark::check!(upgrade_underscore_visibility); |
632 | check( | 631 | check( |
633 | r#" | 632 | r#" |
634 | //- /main.rs crate:main deps:lib | 633 | //- /main.rs crate:main deps:lib |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index e8e72e5ef..d5ef8ceb5 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use base_db::fixture::WithFixture; | 1 | use base_db::fixture::WithFixture; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use crate::test_db::TestDB; | 3 | use crate::test_db::TestDB; |
5 | 4 | ||
@@ -63,7 +62,7 @@ fn unresolved_extern_crate() { | |||
63 | 62 | ||
64 | #[test] | 63 | #[test] |
65 | fn extern_crate_self_as() { | 64 | fn extern_crate_self_as() { |
66 | mark::check!(extern_crate_self_as); | 65 | cov_mark::check!(extern_crate_self_as); |
67 | check_diagnostics( | 66 | check_diagnostics( |
68 | r" | 67 | r" |
69 | //- /lib.rs | 68 | //- /lib.rs |
@@ -140,7 +139,7 @@ fn inactive_item() { | |||
140 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. | 139 | /// Tests that `cfg` attributes behind `cfg_attr` is handled properly. |
141 | #[test] | 140 | #[test] |
142 | fn inactive_via_cfg_attr() { | 141 | fn inactive_via_cfg_attr() { |
143 | mark::check!(cfg_attr_active); | 142 | cov_mark::check!(cfg_attr_active); |
144 | check_diagnostics( | 143 | check_diagnostics( |
145 | r#" | 144 | r#" |
146 | //- /lib.rs | 145 | //- /lib.rs |
diff --git a/crates/hir_def/src/nameres/tests/globs.rs b/crates/hir_def/src/nameres/tests/globs.rs index 2ae836e3c..17426d54d 100644 --- a/crates/hir_def/src/nameres/tests/globs.rs +++ b/crates/hir_def/src/nameres/tests/globs.rs | |||
@@ -148,7 +148,7 @@ pub(crate) struct PubCrateStruct; | |||
148 | 148 | ||
149 | #[test] | 149 | #[test] |
150 | fn glob_across_crates() { | 150 | fn glob_across_crates() { |
151 | mark::check!(glob_across_crates); | 151 | cov_mark::check!(glob_across_crates); |
152 | check( | 152 | check( |
153 | r#" | 153 | r#" |
154 | //- /main.rs crate:main deps:test_crate | 154 | //- /main.rs crate:main deps:test_crate |
@@ -184,7 +184,7 @@ struct Foo; | |||
184 | 184 | ||
185 | #[test] | 185 | #[test] |
186 | fn glob_enum() { | 186 | fn glob_enum() { |
187 | mark::check!(glob_enum); | 187 | cov_mark::check!(glob_enum); |
188 | check( | 188 | check( |
189 | r#" | 189 | r#" |
190 | enum Foo { Bar, Baz } | 190 | enum Foo { Bar, Baz } |
@@ -201,7 +201,7 @@ use self::Foo::*; | |||
201 | 201 | ||
202 | #[test] | 202 | #[test] |
203 | fn glob_enum_group() { | 203 | fn glob_enum_group() { |
204 | mark::check!(glob_enum_group); | 204 | cov_mark::check!(glob_enum_group); |
205 | check( | 205 | check( |
206 | r#" | 206 | r#" |
207 | enum Foo { Bar, Baz } | 207 | enum Foo { Bar, Baz } |
@@ -218,7 +218,7 @@ use self::Foo::{*}; | |||
218 | 218 | ||
219 | #[test] | 219 | #[test] |
220 | fn glob_shadowed_def() { | 220 | fn glob_shadowed_def() { |
221 | mark::check!(import_shadowed); | 221 | cov_mark::check!(import_shadowed); |
222 | check( | 222 | check( |
223 | r#" | 223 | r#" |
224 | //- /lib.rs | 224 | //- /lib.rs |
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 36ed5e8ce..f65a655bf 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -210,7 +210,7 @@ macro_rules! bar { | |||
210 | 210 | ||
211 | #[test] | 211 | #[test] |
212 | fn macro_rules_from_other_crates_are_visible_with_macro_use() { | 212 | fn macro_rules_from_other_crates_are_visible_with_macro_use() { |
213 | mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); | 213 | cov_mark::check!(macro_rules_from_other_crates_are_visible_with_macro_use); |
214 | check( | 214 | check( |
215 | r#" | 215 | r#" |
216 | //- /main.rs crate:main deps:foo | 216 | //- /main.rs crate:main deps:foo |
@@ -260,7 +260,7 @@ mod priv_mod { | |||
260 | 260 | ||
261 | #[test] | 261 | #[test] |
262 | fn prelude_is_macro_use() { | 262 | fn prelude_is_macro_use() { |
263 | mark::check!(prelude_is_macro_use); | 263 | cov_mark::check!(prelude_is_macro_use); |
264 | check( | 264 | check( |
265 | r#" | 265 | r#" |
266 | //- /main.rs crate:main deps:foo | 266 | //- /main.rs crate:main deps:foo |
@@ -550,7 +550,7 @@ mod m { | |||
550 | 550 | ||
551 | #[test] | 551 | #[test] |
552 | fn macro_dollar_crate_is_correct_in_item() { | 552 | fn macro_dollar_crate_is_correct_in_item() { |
553 | mark::check!(macro_dollar_crate_self); | 553 | cov_mark::check!(macro_dollar_crate_self); |
554 | check( | 554 | check( |
555 | r#" | 555 | r#" |
556 | //- /main.rs crate:main deps:foo | 556 | //- /main.rs crate:main deps:foo |
@@ -608,7 +608,7 @@ struct Baz; | |||
608 | 608 | ||
609 | #[test] | 609 | #[test] |
610 | fn macro_dollar_crate_is_correct_in_indirect_deps() { | 610 | fn macro_dollar_crate_is_correct_in_indirect_deps() { |
611 | mark::check!(macro_dollar_crate_other); | 611 | cov_mark::check!(macro_dollar_crate_other); |
612 | // From std | 612 | // From std |
613 | check( | 613 | check( |
614 | r#" | 614 | r#" |
@@ -686,7 +686,7 @@ pub trait Clone {} | |||
686 | 686 | ||
687 | #[test] | 687 | #[test] |
688 | fn macro_expansion_overflow() { | 688 | fn macro_expansion_overflow() { |
689 | mark::check!(macro_expansion_overflow); | 689 | cov_mark::check!(macro_expansion_overflow); |
690 | check( | 690 | check( |
691 | r#" | 691 | r#" |
692 | macro_rules! a { | 692 | macro_rules! a { |
diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index e80b593aa..dfbbad1f9 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs | |||
@@ -2,7 +2,7 @@ use super::*; | |||
2 | 2 | ||
3 | #[test] | 3 | #[test] |
4 | fn name_res_works_for_broken_modules() { | 4 | fn name_res_works_for_broken_modules() { |
5 | mark::check!(name_res_works_for_broken_modules); | 5 | cov_mark::check!(name_res_works_for_broken_modules); |
6 | check( | 6 | check( |
7 | r" | 7 | r" |
8 | //- /lib.rs | 8 | //- /lib.rs |
@@ -774,7 +774,7 @@ struct X; | |||
774 | 774 | ||
775 | #[test] | 775 | #[test] |
776 | fn circular_mods() { | 776 | fn circular_mods() { |
777 | mark::check!(circular_mods); | 777 | cov_mark::check!(circular_mods); |
778 | compute_crate_def_map( | 778 | compute_crate_def_map( |
779 | r#" | 779 | r#" |
780 | //- /lib.rs | 780 | //- /lib.rs |
diff --git a/crates/hir_def/src/path/lower/lower_use.rs b/crates/hir_def/src/path/lower/lower_use.rs index d584b0b70..e2965b033 100644 --- a/crates/hir_def/src/path/lower/lower_use.rs +++ b/crates/hir_def/src/path/lower/lower_use.rs | |||
@@ -6,7 +6,6 @@ use std::iter; | |||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{hygiene::Hygiene, name::AsName}; | 7 | use hir_expand::{hygiene::Hygiene, name::AsName}; |
8 | use syntax::ast::{self, NameOwner}; | 8 | use syntax::ast::{self, NameOwner}; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | use crate::path::{ImportAlias, ModPath, PathKind}; | 10 | use crate::path::{ImportAlias, ModPath, PathKind}; |
12 | 11 | ||
@@ -54,7 +53,7 @@ pub(crate) fn lower_use_tree( | |||
54 | // FIXME: report errors somewhere | 53 | // FIXME: report errors somewhere |
55 | // We get here if we do | 54 | // We get here if we do |
56 | } else if is_glob { | 55 | } else if is_glob { |
57 | mark::hit!(glob_enum_group); | 56 | cov_mark::hit!(glob_enum_group); |
58 | if let Some(prefix) = prefix { | 57 | if let Some(prefix) = prefix { |
59 | cb(prefix, &tree, is_glob, None) | 58 | cb(prefix, &tree, is_glob, None) |
60 | } | 59 | } |
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs index e85f85e49..42736171e 100644 --- a/crates/hir_def/src/resolver.rs +++ b/crates/hir_def/src/resolver.rs | |||
@@ -19,10 +19,10 @@ use crate::{ | |||
19 | path::{ModPath, PathKind}, | 19 | path::{ModPath, PathKind}, |
20 | per_ns::PerNs, | 20 | per_ns::PerNs, |
21 | visibility::{RawVisibility, Visibility}, | 21 | visibility::{RawVisibility, Visibility}, |
22 | AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId, | 22 | AdtId, AssocContainerId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, |
23 | EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, | 23 | FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, LocalModuleId, |
24 | LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, | 24 | Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, |
25 | TypeParamId, VariantId, | 25 | VariantId, |
26 | }; | 26 | }; |
27 | 27 | ||
28 | #[derive(Debug, Clone, Default)] | 28 | #[derive(Debug, Clone, Default)] |
@@ -34,7 +34,7 @@ pub struct Resolver { | |||
34 | // FIXME how to store these best | 34 | // FIXME how to store these best |
35 | #[derive(Debug, Clone)] | 35 | #[derive(Debug, Clone)] |
36 | struct ModuleItemMap { | 36 | struct ModuleItemMap { |
37 | crate_def_map: Arc<DefMap>, | 37 | def_map: Arc<DefMap>, |
38 | module_id: LocalModuleId, | 38 | module_id: LocalModuleId, |
39 | } | 39 | } |
40 | 40 | ||
@@ -337,11 +337,21 @@ impl Resolver { | |||
337 | let mut traits = FxHashSet::default(); | 337 | let mut traits = FxHashSet::default(); |
338 | for scope in &self.scopes { | 338 | for scope in &self.scopes { |
339 | if let Scope::ModuleScope(m) = scope { | 339 | if let Scope::ModuleScope(m) = scope { |
340 | if let Some(prelude) = m.crate_def_map.prelude() { | 340 | if let Some(prelude) = m.def_map.prelude() { |
341 | let prelude_def_map = prelude.def_map(db); | 341 | let prelude_def_map = prelude.def_map(db); |
342 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | 342 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); |
343 | } | 343 | } |
344 | traits.extend(m.crate_def_map[m.module_id].scope.traits()); | 344 | traits.extend(m.def_map[m.module_id].scope.traits()); |
345 | |||
346 | // Add all traits that are in scope because of the containing DefMaps | ||
347 | m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| { | ||
348 | if let Some(prelude) = def_map.prelude() { | ||
349 | let prelude_def_map = prelude.def_map(db); | ||
350 | traits.extend(prelude_def_map[prelude.local_id].scope.traits()); | ||
351 | } | ||
352 | traits.extend(def_map[module].scope.traits()); | ||
353 | None::<()> | ||
354 | }); | ||
345 | } | 355 | } |
346 | } | 356 | } |
347 | traits | 357 | traits |
@@ -349,7 +359,7 @@ impl Resolver { | |||
349 | 359 | ||
350 | fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> { | 360 | fn module_scope(&self) -> Option<(&DefMap, LocalModuleId)> { |
351 | self.scopes.iter().rev().find_map(|scope| match scope { | 361 | self.scopes.iter().rev().find_map(|scope| match scope { |
352 | Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)), | 362 | Scope::ModuleScope(m) => Some((&*m.def_map, m.module_id)), |
353 | 363 | ||
354 | _ => None, | 364 | _ => None, |
355 | }) | 365 | }) |
@@ -413,21 +423,21 @@ impl Scope { | |||
413 | // def: m.module.into(), | 423 | // def: m.module.into(), |
414 | // }), | 424 | // }), |
415 | // ); | 425 | // ); |
416 | m.crate_def_map[m.module_id].scope.entries().for_each(|(name, def)| { | 426 | m.def_map[m.module_id].scope.entries().for_each(|(name, def)| { |
417 | f(name.clone(), ScopeDef::PerNs(def)); | 427 | f(name.clone(), ScopeDef::PerNs(def)); |
418 | }); | 428 | }); |
419 | m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { | 429 | m.def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { |
420 | let scope = PerNs::macros(macro_, Visibility::Public); | 430 | let scope = PerNs::macros(macro_, Visibility::Public); |
421 | seen.insert((name.clone(), scope)); | 431 | seen.insert((name.clone(), scope)); |
422 | f(name.clone(), ScopeDef::PerNs(scope)); | 432 | f(name.clone(), ScopeDef::PerNs(scope)); |
423 | }); | 433 | }); |
424 | m.crate_def_map.extern_prelude().for_each(|(name, &def)| { | 434 | m.def_map.extern_prelude().for_each(|(name, &def)| { |
425 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); | 435 | f(name.clone(), ScopeDef::PerNs(PerNs::types(def, Visibility::Public))); |
426 | }); | 436 | }); |
427 | BUILTIN_SCOPE.iter().for_each(|(name, &def)| { | 437 | BUILTIN_SCOPE.iter().for_each(|(name, &def)| { |
428 | f(name.clone(), ScopeDef::PerNs(def)); | 438 | f(name.clone(), ScopeDef::PerNs(def)); |
429 | }); | 439 | }); |
430 | if let Some(prelude) = m.crate_def_map.prelude() { | 440 | if let Some(prelude) = m.def_map.prelude() { |
431 | let prelude_def_map = prelude.def_map(db); | 441 | let prelude_def_map = prelude.def_map(db); |
432 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { | 442 | prelude_def_map[prelude.local_id].scope.entries().for_each(|(name, def)| { |
433 | let seen_tuple = (name.clone(), def); | 443 | let seen_tuple = (name.clone(), def); |
@@ -513,8 +523,8 @@ impl Resolver { | |||
513 | self.push_scope(Scope::ImplDefScope(impl_def)) | 523 | self.push_scope(Scope::ImplDefScope(impl_def)) |
514 | } | 524 | } |
515 | 525 | ||
516 | fn push_module_scope(self, crate_def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver { | 526 | fn push_module_scope(self, def_map: Arc<DefMap>, module_id: LocalModuleId) -> Resolver { |
517 | self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id })) | 527 | self.push_scope(Scope::ModuleScope(ModuleItemMap { def_map, module_id })) |
518 | } | 528 | } |
519 | 529 | ||
520 | fn push_expr_scope( | 530 | fn push_expr_scope( |
@@ -534,7 +544,7 @@ impl ModuleItemMap { | |||
534 | path: &ModPath, | 544 | path: &ModPath, |
535 | ) -> Option<ResolveValueResult> { | 545 | ) -> Option<ResolveValueResult> { |
536 | let (module_def, idx) = | 546 | let (module_def, idx) = |
537 | self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); | 547 | self.def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); |
538 | match idx { | 548 | match idx { |
539 | None => { | 549 | None => { |
540 | let value = to_value_ns(module_def)?; | 550 | let value = to_value_ns(module_def)?; |
@@ -564,7 +574,7 @@ impl ModuleItemMap { | |||
564 | path: &ModPath, | 574 | path: &ModPath, |
565 | ) -> Option<(TypeNs, Option<usize>)> { | 575 | ) -> Option<(TypeNs, Option<usize>)> { |
566 | let (module_def, idx) = | 576 | let (module_def, idx) = |
567 | self.crate_def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); | 577 | self.def_map.resolve_path(db, self.module_id, &path, BuiltinShadowMode::Other); |
568 | let res = to_type_ns(module_def)?; | 578 | let res = to_type_ns(module_def)?; |
569 | Some((res, idx)) | 579 | Some((res, idx)) |
570 | } | 580 | } |
@@ -678,19 +688,10 @@ impl HasResolver for DefWithBodyId { | |||
678 | } | 688 | } |
679 | } | 689 | } |
680 | 690 | ||
681 | impl HasResolver for ContainerId { | ||
682 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { | ||
683 | match self { | ||
684 | ContainerId::ModuleId(it) => it.resolver(db), | ||
685 | ContainerId::DefWithBodyId(it) => it.module(db).resolver(db), | ||
686 | } | ||
687 | } | ||
688 | } | ||
689 | |||
690 | impl HasResolver for AssocContainerId { | 691 | impl HasResolver for AssocContainerId { |
691 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { | 692 | fn resolver(self, db: &dyn DefDatabase) -> Resolver { |
692 | match self { | 693 | match self { |
693 | AssocContainerId::ContainerId(it) => it.resolver(db), | 694 | AssocContainerId::ModuleId(it) => it.resolver(db), |
694 | AssocContainerId::TraitId(it) => it.resolver(db), | 695 | AssocContainerId::TraitId(it) => it.resolver(db), |
695 | AssocContainerId::ImplId(it) => it.resolver(db), | 696 | AssocContainerId::ImplId(it) => it.resolver(db), |
696 | } | 697 | } |
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml index 5271110d2..76cb03126 100644 --- a/crates/hir_expand/Cargo.toml +++ b/crates/hir_expand/Cargo.toml | |||
@@ -21,4 +21,6 @@ parser = { path = "../parser", version = "0.0.0" } | |||
21 | profile = { path = "../profile", version = "0.0.0" } | 21 | profile = { path = "../profile", version = "0.0.0" } |
22 | tt = { path = "../tt", version = "0.0.0" } | 22 | tt = { path = "../tt", version = "0.0.0" } |
23 | mbe = { path = "../mbe", version = "0.0.0" } | 23 | mbe = { path = "../mbe", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | 24 | |
25 | [dev-dependencies] | ||
26 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index d1302d749..b9c93f56f 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
14 | arrayvec = "0.5.1" | 15 | arrayvec = "0.5.1" |
15 | smallvec = "1.2.0" | 16 | smallvec = "1.2.0" |
@@ -17,9 +18,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 18 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 19 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 20 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.59", default-features = false } | 21 | chalk-solve = { version = "0.60", default-features = false } |
21 | chalk-ir = "0.59" | 22 | chalk-ir = "0.60" |
22 | chalk-recursive = "0.59" | 23 | chalk-recursive = "0.60" |
23 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 24 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
24 | 25 | ||
25 | stdx = { path = "../stdx", version = "0.0.0" } | 26 | stdx = { path = "../stdx", version = "0.0.0" } |
@@ -28,9 +29,9 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" } | |||
28 | base_db = { path = "../base_db", version = "0.0.0" } | 29 | base_db = { path = "../base_db", version = "0.0.0" } |
29 | profile = { path = "../profile", version = "0.0.0" } | 30 | profile = { path = "../profile", version = "0.0.0" } |
30 | syntax = { path = "../syntax", version = "0.0.0" } | 31 | syntax = { path = "../syntax", version = "0.0.0" } |
31 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
32 | 32 | ||
33 | [dev-dependencies] | 33 | [dev-dependencies] |
34 | test_utils = { path = "../test_utils" } | ||
34 | expect-test = "1.1" | 35 | expect-test = "1.1" |
35 | tracing = "0.1" | 36 | tracing = "0.1" |
36 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 37 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } |
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index b3af82444..06714409f 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs | |||
@@ -130,7 +130,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
130 | ) -> chalk_ir::ProgramClauses<chalk::Interner>; | 130 | ) -> chalk_ir::ProgramClauses<chalk::Interner>; |
131 | } | 131 | } |
132 | 132 | ||
133 | fn infer_wait(db: &impl HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { | 133 | fn infer_wait(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<InferenceResult> { |
134 | let _p = profile::span("infer:wait").detail(|| match def { | 134 | let _p = profile::span("infer:wait").detail(|| match def { |
135 | DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), | 135 | DefWithBodyId::FunctionId(it) => db.function_data(it).name.to_string(), |
136 | DefWithBodyId::StaticId(it) => { | 136 | DefWithBodyId::StaticId(it) => { |
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs index 6773ddea3..3605ca581 100644 --- a/crates/hir_ty/src/diagnostics/decl_check.rs +++ b/crates/hir_ty/src/diagnostics/decl_check.rs | |||
@@ -28,7 +28,6 @@ use syntax::{ | |||
28 | ast::{self, NameOwner}, | 28 | ast::{self, NameOwner}, |
29 | AstNode, AstPtr, | 29 | AstNode, AstPtr, |
30 | }; | 30 | }; |
31 | use test_utils::mark; | ||
32 | 31 | ||
33 | use crate::{ | 32 | use crate::{ |
34 | db::HirDatabase, | 33 | db::HirDatabase, |
@@ -93,16 +92,21 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
93 | fn validate_func(&mut self, func: FunctionId) { | 92 | fn validate_func(&mut self, func: FunctionId) { |
94 | let data = self.db.function_data(func); | 93 | let data = self.db.function_data(func); |
95 | if data.is_extern { | 94 | if data.is_extern { |
96 | mark::hit!(extern_func_incorrect_case_ignored); | 95 | cov_mark::hit!(extern_func_incorrect_case_ignored); |
97 | return; | 96 | return; |
98 | } | 97 | } |
99 | 98 | ||
100 | let body = self.db.body(func.into()); | 99 | let body = self.db.body(func.into()); |
101 | 100 | ||
102 | // Recursively validate inner scope items, such as static variables and constants. | 101 | // Recursively validate inner scope items, such as static variables and constants. |
103 | for (item_id, _) in body.item_scope.values() { | 102 | let db = self.db; |
104 | let mut validator = DeclValidator::new(self.db, self.krate, self.sink); | 103 | for block_def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) { |
105 | validator.validate_item(item_id); | 104 | for (_, module) in block_def_map.modules() { |
105 | for (def_id, _) in module.scope.values() { | ||
106 | let mut validator = DeclValidator::new(self.db, self.krate, self.sink); | ||
107 | validator.validate_item(def_id); | ||
108 | } | ||
109 | } | ||
106 | } | 110 | } |
107 | 111 | ||
108 | // Check whether non-snake case identifiers are allowed for this function. | 112 | // Check whether non-snake case identifiers are allowed for this function. |
@@ -625,7 +629,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
625 | fn validate_static(&mut self, static_id: StaticId) { | 629 | fn validate_static(&mut self, static_id: StaticId) { |
626 | let data = self.db.static_data(static_id); | 630 | let data = self.db.static_data(static_id); |
627 | if data.is_extern { | 631 | if data.is_extern { |
628 | mark::hit!(extern_static_incorrect_case_ignored); | 632 | cov_mark::hit!(extern_static_incorrect_case_ignored); |
629 | return; | 633 | return; |
630 | } | 634 | } |
631 | 635 | ||
@@ -673,8 +677,6 @@ impl<'a, 'b> DeclValidator<'a, 'b> { | |||
673 | 677 | ||
674 | #[cfg(test)] | 678 | #[cfg(test)] |
675 | mod tests { | 679 | mod tests { |
676 | use test_utils::mark; | ||
677 | |||
678 | use crate::diagnostics::tests::check_diagnostics; | 680 | use crate::diagnostics::tests::check_diagnostics; |
679 | 681 | ||
680 | #[test] | 682 | #[test] |
@@ -889,8 +891,8 @@ fn main() { | |||
889 | 891 | ||
890 | #[test] | 892 | #[test] |
891 | fn ignores_extern_items() { | 893 | fn ignores_extern_items() { |
892 | mark::check!(extern_func_incorrect_case_ignored); | 894 | cov_mark::check!(extern_func_incorrect_case_ignored); |
893 | mark::check!(extern_static_incorrect_case_ignored); | 895 | cov_mark::check!(extern_static_incorrect_case_ignored); |
894 | check_diagnostics( | 896 | check_diagnostics( |
895 | r#" | 897 | r#" |
896 | extern { | 898 | extern { |
diff --git a/crates/hir_ty/src/diagnostics/expr.rs b/crates/hir_ty/src/diagnostics/expr.rs index 66a88e2b6..2751cd304 100644 --- a/crates/hir_ty/src/diagnostics/expr.rs +++ b/crates/hir_ty/src/diagnostics/expr.rs | |||
@@ -2,9 +2,7 @@ | |||
2 | 2 | ||
3 | use std::sync::Arc; | 3 | use std::sync::Arc; |
4 | 4 | ||
5 | use hir_def::{ | 5 | use hir_def::{expr::Statement, path::path, resolver::HasResolver, AssocItemId, DefWithBodyId}; |
6 | expr::Statement, path::path, resolver::HasResolver, AdtId, AssocItemId, DefWithBodyId, | ||
7 | }; | ||
8 | use hir_expand::{diagnostics::DiagnosticSink, name}; | 6 | use hir_expand::{diagnostics::DiagnosticSink, name}; |
9 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
10 | use syntax::{ast, AstPtr}; | 8 | use syntax::{ast, AstPtr}; |
@@ -17,7 +15,7 @@ use crate::{ | |||
17 | MissingPatFields, RemoveThisSemicolon, | 15 | MissingPatFields, RemoveThisSemicolon, |
18 | }, | 16 | }, |
19 | utils::variant_data, | 17 | utils::variant_data, |
20 | InferenceResult, Ty, | 18 | AdtId, InferenceResult, Ty, |
21 | }; | 19 | }; |
22 | 20 | ||
23 | pub(crate) use hir_def::{ | 21 | pub(crate) use hir_def::{ |
@@ -382,10 +380,14 @@ impl<'a, 'b> ExprValidator<'a, 'b> { | |||
382 | }; | 380 | }; |
383 | 381 | ||
384 | let (params, required) = match mismatch.expected { | 382 | let (params, required) = match mismatch.expected { |
385 | Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_result_enum => { | 383 | Ty::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref parameters) |
384 | if enum_id == core_result_enum => | ||
385 | { | ||
386 | (parameters, "Ok".to_string()) | 386 | (parameters, "Ok".to_string()) |
387 | } | 387 | } |
388 | Ty::Adt(AdtId::EnumId(enum_id), ref parameters) if enum_id == core_option_enum => { | 388 | Ty::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ref parameters) |
389 | if enum_id == core_option_enum => | ||
390 | { | ||
389 | (parameters, "Some".to_string()) | 391 | (parameters, "Some".to_string()) |
390 | } | 392 | } |
391 | _ => return, | 393 | _ => return, |
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 86fee0050..04d39c571 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -222,12 +222,12 @@ use hir_def::{ | |||
222 | adt::VariantData, | 222 | adt::VariantData, |
223 | body::Body, | 223 | body::Body, |
224 | expr::{Expr, Literal, Pat, PatId}, | 224 | expr::{Expr, Literal, Pat, PatId}, |
225 | AdtId, EnumVariantId, StructId, VariantId, | 225 | EnumVariantId, StructId, VariantId, |
226 | }; | 226 | }; |
227 | use la_arena::Idx; | 227 | use la_arena::Idx; |
228 | use smallvec::{smallvec, SmallVec}; | 228 | use smallvec::{smallvec, SmallVec}; |
229 | 229 | ||
230 | use crate::{db::HirDatabase, InferenceResult, Ty}; | 230 | use crate::{db::HirDatabase, AdtId, InferenceResult, Ty}; |
231 | 231 | ||
232 | #[derive(Debug, Clone, Copy)] | 232 | #[derive(Debug, Clone, Copy)] |
233 | /// Either a pattern from the source code being analyzed, represented as | 233 | /// Either a pattern from the source code being analyzed, represented as |
@@ -627,7 +627,7 @@ pub(super) fn is_useful( | |||
627 | // - `!` type | 627 | // - `!` type |
628 | // In those cases, no match arm is useful. | 628 | // In those cases, no match arm is useful. |
629 | match cx.infer[cx.match_expr].strip_references() { | 629 | match cx.infer[cx.match_expr].strip_references() { |
630 | Ty::Adt(AdtId::EnumId(enum_id), ..) => { | 630 | Ty::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), ..) => { |
631 | if cx.db.enum_data(*enum_id).variants.is_empty() { | 631 | if cx.db.enum_data(*enum_id).variants.is_empty() { |
632 | return Ok(Usefulness::NotUseful); | 632 | return Ok(Usefulness::NotUseful); |
633 | } | 633 | } |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index d4a8b48e6..ab51cb0a6 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -2,19 +2,20 @@ | |||
2 | 2 | ||
3 | use std::{borrow::Cow, fmt}; | 3 | use std::{borrow::Cow, fmt}; |
4 | 4 | ||
5 | use crate::{ | ||
6 | db::HirDatabase, primitive, utils::generics, AliasTy, CallableDefId, CallableSig, | ||
7 | GenericPredicate, Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar, Substs, | ||
8 | TraitRef, Ty, | ||
9 | }; | ||
10 | use arrayvec::ArrayVec; | 5 | use arrayvec::ArrayVec; |
11 | use chalk_ir::Mutability; | 6 | use chalk_ir::Mutability; |
12 | use hir_def::{ | 7 | use hir_def::{ |
13 | db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, AdtId, | 8 | db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, |
14 | AssocContainerId, HasModule, Lookup, ModuleId, TraitId, | 9 | AssocContainerId, Lookup, ModuleId, TraitId, |
15 | }; | 10 | }; |
16 | use hir_expand::name::Name; | 11 | use hir_expand::name::Name; |
17 | 12 | ||
13 | use crate::{ | ||
14 | db::HirDatabase, primitive, utils::generics, AdtId, AliasTy, CallableDefId, CallableSig, | ||
15 | GenericPredicate, Lifetime, Obligation, OpaqueTy, OpaqueTyId, ProjectionTy, Scalar, Substs, | ||
16 | TraitRef, Ty, | ||
17 | }; | ||
18 | |||
18 | pub struct HirFormatter<'a> { | 19 | pub struct HirFormatter<'a> { |
19 | pub db: &'a dyn HirDatabase, | 20 | pub db: &'a dyn HirDatabase, |
20 | fmt: &'a mut dyn fmt::Write, | 21 | fmt: &'a mut dyn fmt::Write, |
@@ -400,13 +401,13 @@ impl HirDisplay for Ty { | |||
400 | write!(f, " -> {}", ret_display)?; | 401 | write!(f, " -> {}", ret_display)?; |
401 | } | 402 | } |
402 | } | 403 | } |
403 | Ty::Adt(def_id, parameters) => { | 404 | Ty::Adt(AdtId(def_id), parameters) => { |
404 | match f.display_target { | 405 | match f.display_target { |
405 | DisplayTarget::Diagnostics | DisplayTarget::Test => { | 406 | DisplayTarget::Diagnostics | DisplayTarget::Test => { |
406 | let name = match *def_id { | 407 | let name = match *def_id { |
407 | AdtId::StructId(it) => f.db.struct_data(it).name.clone(), | 408 | hir_def::AdtId::StructId(it) => f.db.struct_data(it).name.clone(), |
408 | AdtId::UnionId(it) => f.db.union_data(it).name.clone(), | 409 | hir_def::AdtId::UnionId(it) => f.db.union_data(it).name.clone(), |
409 | AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), | 410 | hir_def::AdtId::EnumId(it) => f.db.enum_data(it).name.clone(), |
410 | }; | 411 | }; |
411 | write!(f, "{}", name)?; | 412 | write!(f, "{}", name)?; |
412 | } | 413 | } |
@@ -610,7 +611,7 @@ impl HirDisplay for CallableSig { | |||
610 | } | 611 | } |
611 | 612 | ||
612 | fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { | 613 | fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { |
613 | let krate = trait_.lookup(db).container.module(db).krate(); | 614 | let krate = trait_.lookup(db).container.krate(); |
614 | let fn_traits = [ | 615 | let fn_traits = [ |
615 | db.lang_item(krate, "fn".into()), | 616 | db.lang_item(krate, "fn".into()), |
616 | db.lang_item(krate, "fn_mut".into()), | 617 | db.lang_item(krate, "fn_mut".into()), |
diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index cf0a3add4..7e8846f27 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs | |||
@@ -6,7 +6,6 @@ | |||
6 | 6 | ||
7 | use chalk_ir::{Mutability, TyVariableKind}; | 7 | use chalk_ir::{Mutability, TyVariableKind}; |
8 | use hir_def::lang_item::LangItemTarget; | 8 | use hir_def::lang_item::LangItemTarget; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty}; | 10 | use crate::{autoderef, traits::Solution, Obligation, Substs, TraitRef, Ty}; |
12 | 11 | ||
@@ -35,7 +34,7 @@ impl<'a> InferenceContext<'a> { | |||
35 | ty1.clone() | 34 | ty1.clone() |
36 | } else { | 35 | } else { |
37 | if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) { | 36 | if let (Ty::FnDef(..), Ty::FnDef(..)) = (ty1, ty2) { |
38 | mark::hit!(coerce_fn_reification); | 37 | cov_mark::hit!(coerce_fn_reification); |
39 | // Special case: two function types. Try to coerce both to | 38 | // Special case: two function types. Try to coerce both to |
40 | // pointers to have a chance at getting a match. See | 39 | // pointers to have a chance at getting a match. See |
41 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 | 40 | // https://github.com/rust-lang/rust/blob/7b805396bf46dce972692a6846ce2ad8481c5f85/src/librustc_typeck/check/coercion.rs#L877-L916 |
@@ -45,7 +44,7 @@ impl<'a> InferenceContext<'a> { | |||
45 | let ptr_ty2 = Ty::fn_ptr(sig2); | 44 | let ptr_ty2 = Ty::fn_ptr(sig2); |
46 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) | 45 | self.coerce_merge_branch(&ptr_ty1, &ptr_ty2) |
47 | } else { | 46 | } else { |
48 | mark::hit!(coerce_merge_fail_fallback); | 47 | cov_mark::hit!(coerce_merge_fail_fallback); |
49 | ty1.clone() | 48 | ty1.clone() |
50 | } | 49 | } |
51 | } | 50 | } |
diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index cf1f1038a..262177ffb 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs | |||
@@ -8,11 +8,10 @@ use hir_def::{ | |||
8 | expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, | 8 | expr::{Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, |
9 | path::{GenericArg, GenericArgs}, | 9 | path::{GenericArg, GenericArgs}, |
10 | resolver::resolver_for_expr, | 10 | resolver::resolver_for_expr, |
11 | AdtId, AssocContainerId, FieldId, Lookup, | 11 | AssocContainerId, FieldId, Lookup, |
12 | }; | 12 | }; |
13 | use hir_expand::name::{name, Name}; | 13 | use hir_expand::name::{name, Name}; |
14 | use syntax::ast::RangeOp; | 14 | use syntax::ast::RangeOp; |
15 | use test_utils::mark; | ||
16 | 15 | ||
17 | use crate::{ | 16 | use crate::{ |
18 | autoderef, | 17 | autoderef, |
@@ -21,8 +20,8 @@ use crate::{ | |||
21 | primitive::{self, UintTy}, | 20 | primitive::{self, UintTy}, |
22 | traits::{FnTrait, InEnvironment}, | 21 | traits::{FnTrait, InEnvironment}, |
23 | utils::{generics, variant_data, Generics}, | 22 | utils::{generics, variant_data, Generics}, |
24 | Binders, CallableDefId, FnPointer, FnSig, Obligation, OpaqueTyId, Rawness, Scalar, Substs, | 23 | AdtId, Binders, CallableDefId, FnPointer, FnSig, Obligation, OpaqueTyId, Rawness, Scalar, |
25 | TraitRef, Ty, | 24 | Substs, TraitRef, Ty, |
26 | }; | 25 | }; |
27 | 26 | ||
28 | use super::{ | 27 | use super::{ |
@@ -429,14 +428,14 @@ impl<'a> InferenceContext<'a> { | |||
429 | Ty::Tuple(_, substs) => { | 428 | Ty::Tuple(_, substs) => { |
430 | name.as_tuple_index().and_then(|idx| substs.0.get(idx).cloned()) | 429 | name.as_tuple_index().and_then(|idx| substs.0.get(idx).cloned()) |
431 | } | 430 | } |
432 | Ty::Adt(AdtId::StructId(s), parameters) => { | 431 | Ty::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { |
433 | self.db.struct_data(s).variant_data.field(name).map(|local_id| { | 432 | self.db.struct_data(s).variant_data.field(name).map(|local_id| { |
434 | let field = FieldId { parent: s.into(), local_id }; | 433 | let field = FieldId { parent: s.into(), local_id }; |
435 | self.write_field_resolution(tgt_expr, field); | 434 | self.write_field_resolution(tgt_expr, field); |
436 | self.db.field_types(s.into())[field.local_id].clone().subst(¶meters) | 435 | self.db.field_types(s.into())[field.local_id].clone().subst(¶meters) |
437 | }) | 436 | }) |
438 | } | 437 | } |
439 | Ty::Adt(AdtId::UnionId(u), parameters) => { | 438 | Ty::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { |
440 | self.db.union_data(u).variant_data.field(name).map(|local_id| { | 439 | self.db.union_data(u).variant_data.field(name).map(|local_id| { |
441 | let field = FieldId { parent: u.into(), local_id }; | 440 | let field = FieldId { parent: u.into(), local_id }; |
442 | self.write_field_resolution(tgt_expr, field); | 441 | self.write_field_resolution(tgt_expr, field); |
@@ -498,7 +497,7 @@ impl<'a> InferenceContext<'a> { | |||
498 | _ => (), | 497 | _ => (), |
499 | } | 498 | } |
500 | sb = sb.fill(repeat_with(|| self.table.new_type_var())); | 499 | sb = sb.fill(repeat_with(|| self.table.new_type_var())); |
501 | Ty::Adt(box_, sb.build()) | 500 | Ty::adt_ty(box_, sb.build()) |
502 | } else { | 501 | } else { |
503 | Ty::Unknown | 502 | Ty::Unknown |
504 | } | 503 | } |
@@ -565,7 +564,7 @@ impl<'a> InferenceContext<'a> { | |||
565 | let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); | 564 | let ret = op::binary_op_return_ty(*op, lhs_ty.clone(), rhs_ty.clone()); |
566 | 565 | ||
567 | if ret == Ty::Unknown { | 566 | if ret == Ty::Unknown { |
568 | mark::hit!(infer_expr_inner_binary_operator_overload); | 567 | cov_mark::hit!(infer_expr_inner_binary_operator_overload); |
569 | 568 | ||
570 | self.resolve_associated_type_with_params( | 569 | self.resolve_associated_type_with_params( |
571 | lhs_ty, | 570 | lhs_ty, |
@@ -586,31 +585,31 @@ impl<'a> InferenceContext<'a> { | |||
586 | let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect)); | 585 | let rhs_ty = rhs.map(|e| self.infer_expr(e, &rhs_expect)); |
587 | match (range_type, lhs_ty, rhs_ty) { | 586 | match (range_type, lhs_ty, rhs_ty) { |
588 | (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { | 587 | (RangeOp::Exclusive, None, None) => match self.resolve_range_full() { |
589 | Some(adt) => Ty::Adt(adt, Substs::empty()), | 588 | Some(adt) => Ty::adt_ty(adt, Substs::empty()), |
590 | None => Ty::Unknown, | 589 | None => Ty::Unknown, |
591 | }, | 590 | }, |
592 | (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() { | 591 | (RangeOp::Exclusive, None, Some(ty)) => match self.resolve_range_to() { |
593 | Some(adt) => Ty::Adt(adt, Substs::single(ty)), | 592 | Some(adt) => Ty::adt_ty(adt, Substs::single(ty)), |
594 | None => Ty::Unknown, | 593 | None => Ty::Unknown, |
595 | }, | 594 | }, |
596 | (RangeOp::Inclusive, None, Some(ty)) => { | 595 | (RangeOp::Inclusive, None, Some(ty)) => { |
597 | match self.resolve_range_to_inclusive() { | 596 | match self.resolve_range_to_inclusive() { |
598 | Some(adt) => Ty::Adt(adt, Substs::single(ty)), | 597 | Some(adt) => Ty::adt_ty(adt, Substs::single(ty)), |
599 | None => Ty::Unknown, | 598 | None => Ty::Unknown, |
600 | } | 599 | } |
601 | } | 600 | } |
602 | (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() { | 601 | (RangeOp::Exclusive, Some(_), Some(ty)) => match self.resolve_range() { |
603 | Some(adt) => Ty::Adt(adt, Substs::single(ty)), | 602 | Some(adt) => Ty::adt_ty(adt, Substs::single(ty)), |
604 | None => Ty::Unknown, | 603 | None => Ty::Unknown, |
605 | }, | 604 | }, |
606 | (RangeOp::Inclusive, Some(_), Some(ty)) => { | 605 | (RangeOp::Inclusive, Some(_), Some(ty)) => { |
607 | match self.resolve_range_inclusive() { | 606 | match self.resolve_range_inclusive() { |
608 | Some(adt) => Ty::Adt(adt, Substs::single(ty)), | 607 | Some(adt) => Ty::adt_ty(adt, Substs::single(ty)), |
609 | None => Ty::Unknown, | 608 | None => Ty::Unknown, |
610 | } | 609 | } |
611 | } | 610 | } |
612 | (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() { | 611 | (RangeOp::Exclusive, Some(ty), None) => match self.resolve_range_from() { |
613 | Some(adt) => Ty::Adt(adt, Substs::single(ty)), | 612 | Some(adt) => Ty::adt_ty(adt, Substs::single(ty)), |
614 | None => Ty::Unknown, | 613 | None => Ty::Unknown, |
615 | }, | 614 | }, |
616 | (RangeOp::Inclusive, _, None) => Ty::Unknown, | 615 | (RangeOp::Inclusive, _, None) => Ty::Unknown, |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index eb099311c..a0ac8d80f 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -10,7 +10,6 @@ use hir_def::{ | |||
10 | FieldId, | 10 | FieldId, |
11 | }; | 11 | }; |
12 | use hir_expand::name::Name; | 12 | use hir_expand::name::Name; |
13 | use test_utils::mark; | ||
14 | 13 | ||
15 | use super::{BindingMode, Expectation, InferenceContext}; | 14 | use super::{BindingMode, Expectation, InferenceContext}; |
16 | use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty}; | 15 | use crate::{lower::lower_to_chalk_mutability, utils::variant_data, Substs, Ty}; |
@@ -108,7 +107,7 @@ impl<'a> InferenceContext<'a> { | |||
108 | } | 107 | } |
109 | } | 108 | } |
110 | } else if let Pat::Ref { .. } = &body[pat] { | 109 | } else if let Pat::Ref { .. } = &body[pat] { |
111 | mark::hit!(match_ergonomics_ref); | 110 | cov_mark::hit!(match_ergonomics_ref); |
112 | // When you encounter a `&pat` pattern, reset to Move. | 111 | // When you encounter a `&pat` pattern, reset to Move. |
113 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` | 112 | // This is so that `w` is by value: `let (_, &w) = &(1, &2);` |
114 | default_bm = BindingMode::Move; | 113 | default_bm = BindingMode::Move; |
@@ -237,7 +236,7 @@ impl<'a> InferenceContext<'a> { | |||
237 | }; | 236 | }; |
238 | 237 | ||
239 | let inner_ty = self.infer_pat(*inner, inner_expected, default_bm); | 238 | let inner_ty = self.infer_pat(*inner, inner_expected, default_bm); |
240 | Ty::Adt(box_adt, Substs::single(inner_ty)) | 239 | Ty::adt_ty(box_adt, Substs::single(inner_ty)) |
241 | } | 240 | } |
242 | None => Ty::Unknown, | 241 | None => Ty::Unknown, |
243 | }, | 242 | }, |
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index 5d541104e..ae3554bac 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -260,7 +260,7 @@ impl<'a> InferenceContext<'a> { | |||
260 | })); | 260 | })); |
261 | Some(trait_substs) | 261 | Some(trait_substs) |
262 | } | 262 | } |
263 | AssocContainerId::ContainerId(_) => None, | 263 | AssocContainerId::ModuleId(_) => None, |
264 | }; | 264 | }; |
265 | 265 | ||
266 | self.write_assoc_resolution(id, item); | 266 | self.write_assoc_resolution(id, item); |
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index 99a89a7f3..54fcfed10 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs | |||
@@ -5,8 +5,6 @@ use std::borrow::Cow; | |||
5 | use chalk_ir::{FloatTy, IntTy, TyVariableKind}; | 5 | use chalk_ir::{FloatTy, IntTy, TyVariableKind}; |
6 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; | 6 | use ena::unify::{InPlaceUnificationTable, NoError, UnifyKey, UnifyValue}; |
7 | 7 | ||
8 | use test_utils::mark; | ||
9 | |||
10 | use super::{InferenceContext, Obligation}; | 8 | use super::{InferenceContext, Obligation}; |
11 | use crate::{ | 9 | use crate::{ |
12 | BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar, | 10 | BoundVar, Canonical, DebruijnIndex, GenericPredicate, InEnvironment, InferenceVar, Scalar, |
@@ -387,7 +385,7 @@ impl InferenceTable { | |||
387 | // more than once | 385 | // more than once |
388 | for i in 0..3 { | 386 | for i in 0..3 { |
389 | if i > 0 { | 387 | if i > 0 { |
390 | mark::hit!(type_var_resolves_to_int_var); | 388 | cov_mark::hit!(type_var_resolves_to_int_var); |
391 | } | 389 | } |
392 | match &*ty { | 390 | match &*ty { |
393 | Ty::InferenceVar(tv, _) => { | 391 | Ty::InferenceVar(tv, _) => { |
@@ -416,7 +414,7 @@ impl InferenceTable { | |||
416 | Ty::InferenceVar(tv, kind) => { | 414 | Ty::InferenceVar(tv, kind) => { |
417 | let inner = tv.to_inner(); | 415 | let inner = tv.to_inner(); |
418 | if tv_stack.contains(&inner) { | 416 | if tv_stack.contains(&inner) { |
419 | mark::hit!(type_var_cycles_resolve_as_possible); | 417 | cov_mark::hit!(type_var_cycles_resolve_as_possible); |
420 | // recursive type | 418 | // recursive type |
421 | return self.type_variable_table.fallback_value(tv, kind); | 419 | return self.type_variable_table.fallback_value(tv, kind); |
422 | } | 420 | } |
@@ -443,7 +441,7 @@ impl InferenceTable { | |||
443 | Ty::InferenceVar(tv, kind) => { | 441 | Ty::InferenceVar(tv, kind) => { |
444 | let inner = tv.to_inner(); | 442 | let inner = tv.to_inner(); |
445 | if tv_stack.contains(&inner) { | 443 | if tv_stack.contains(&inner) { |
446 | mark::hit!(type_var_cycles_resolve_completely); | 444 | cov_mark::hit!(type_var_cycles_resolve_completely); |
447 | // recursive type | 445 | // recursive type |
448 | return self.type_variable_table.fallback_value(tv, kind); | 446 | return self.type_variable_table.fallback_value(tv, kind); |
449 | } | 447 | } |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index c2a20c480..e77f24e4e 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -27,9 +27,9 @@ use std::{iter, mem, ops::Deref, sync::Arc}; | |||
27 | 27 | ||
28 | use base_db::salsa; | 28 | use base_db::salsa; |
29 | use hir_def::{ | 29 | use hir_def::{ |
30 | builtin_type::BuiltinType, expr::ExprId, type_ref::Rawness, AdtId, AssocContainerId, | 30 | builtin_type::BuiltinType, expr::ExprId, type_ref::Rawness, AssocContainerId, DefWithBodyId, |
31 | DefWithBodyId, FunctionId, GenericDefId, HasModule, LifetimeParamId, Lookup, TraitId, | 31 | FunctionId, GenericDefId, HasModule, LifetimeParamId, Lookup, TraitId, TypeAliasId, |
32 | TypeAliasId, TypeParamId, | 32 | TypeParamId, |
33 | }; | 33 | }; |
34 | use itertools::Itertools; | 34 | use itertools::Itertools; |
35 | 35 | ||
@@ -47,7 +47,9 @@ pub use lower::{ | |||
47 | }; | 47 | }; |
48 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; | 48 | pub use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; |
49 | 49 | ||
50 | pub use chalk_ir::{BoundVar, DebruijnIndex, Mutability, Scalar, TyVariableKind}; | 50 | pub use chalk_ir::{AdtId, BoundVar, DebruijnIndex, Mutability, Scalar, TyVariableKind}; |
51 | |||
52 | pub(crate) use crate::traits::chalk::Interner; | ||
51 | 53 | ||
52 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 54 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
53 | pub enum Lifetime { | 55 | pub enum Lifetime { |
@@ -131,7 +133,7 @@ pub enum AliasTy { | |||
131 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] | 133 | #[derive(Clone, PartialEq, Eq, Debug, Hash)] |
132 | pub enum Ty { | 134 | pub enum Ty { |
133 | /// Structures, enumerations and unions. | 135 | /// Structures, enumerations and unions. |
134 | Adt(AdtId, Substs), | 136 | Adt(AdtId<Interner>, Substs), |
135 | 137 | ||
136 | /// Represents an associated item like `Iterator::Item`. This is used | 138 | /// Represents an associated item like `Iterator::Item`. This is used |
137 | /// when we have tried to normalize a projection like `T::Item` but | 139 | /// when we have tried to normalize a projection like `T::Item` but |
@@ -602,6 +604,10 @@ impl Ty { | |||
602 | Ty::Tuple(0, Substs::empty()) | 604 | Ty::Tuple(0, Substs::empty()) |
603 | } | 605 | } |
604 | 606 | ||
607 | pub fn adt_ty(adt: hir_def::AdtId, substs: Substs) -> Ty { | ||
608 | Ty::Adt(AdtId(adt), substs) | ||
609 | } | ||
610 | |||
605 | pub fn fn_ptr(sig: CallableSig) -> Self { | 611 | pub fn fn_ptr(sig: CallableSig) -> Self { |
606 | Ty::Function(FnPointer { | 612 | Ty::Function(FnPointer { |
607 | num_args: sig.params().len(), | 613 | num_args: sig.params().len(), |
@@ -650,9 +656,9 @@ impl Ty { | |||
650 | t | 656 | t |
651 | } | 657 | } |
652 | 658 | ||
653 | pub fn as_adt(&self) -> Option<(AdtId, &Substs)> { | 659 | pub fn as_adt(&self) -> Option<(hir_def::AdtId, &Substs)> { |
654 | match self { | 660 | match self { |
655 | Ty::Adt(adt_def, parameters) => Some((*adt_def, parameters)), | 661 | Ty::Adt(AdtId(adt), parameters) => Some((*adt, parameters)), |
656 | _ => None, | 662 | _ => None, |
657 | } | 663 | } |
658 | } | 664 | } |
@@ -666,7 +672,7 @@ impl Ty { | |||
666 | 672 | ||
667 | pub fn as_generic_def(&self) -> Option<GenericDefId> { | 673 | pub fn as_generic_def(&self) -> Option<GenericDefId> { |
668 | match *self { | 674 | match *self { |
669 | Ty::Adt(adt, ..) => Some(adt.into()), | 675 | Ty::Adt(AdtId(adt), ..) => Some(adt.into()), |
670 | Ty::FnDef(callable, ..) => Some(callable.into()), | 676 | Ty::FnDef(callable, ..) => Some(callable.into()), |
671 | Ty::AssociatedType(type_alias, ..) => Some(type_alias.into()), | 677 | Ty::AssociatedType(type_alias, ..) => Some(type_alias.into()), |
672 | Ty::ForeignType(type_alias, ..) => Some(type_alias.into()), | 678 | Ty::ForeignType(type_alias, ..) => Some(type_alias.into()), |
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs index 1b5843d48..5fa83567b 100644 --- a/crates/hir_ty/src/lower.rs +++ b/crates/hir_ty/src/lower.rs | |||
@@ -24,7 +24,6 @@ use hir_expand::name::Name; | |||
24 | use la_arena::ArenaMap; | 24 | use la_arena::ArenaMap; |
25 | use smallvec::SmallVec; | 25 | use smallvec::SmallVec; |
26 | use stdx::impl_from; | 26 | use stdx::impl_from; |
27 | use test_utils::mark; | ||
28 | 27 | ||
29 | use crate::{ | 28 | use crate::{ |
30 | db::HirDatabase, | 29 | db::HirDatabase, |
@@ -760,7 +759,7 @@ fn assoc_type_bindings_from_type_bound<'a>( | |||
760 | 759 | ||
761 | impl ReturnTypeImplTrait { | 760 | impl ReturnTypeImplTrait { |
762 | fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { | 761 | fn from_hir(ctx: &TyLoweringContext, bounds: &[TypeBound]) -> Self { |
763 | mark::hit!(lower_rpit); | 762 | cov_mark::hit!(lower_rpit); |
764 | let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)); | 763 | let self_ty = Ty::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)); |
765 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { | 764 | let predicates = ctx.with_shifted_in(DebruijnIndex::ONE, |ctx| { |
766 | bounds | 765 | bounds |
@@ -935,7 +934,7 @@ impl TraitEnvironment { | |||
935 | // add `Self: Trait<T1, T2, ...>` to the environment in trait | 934 | // add `Self: Trait<T1, T2, ...>` to the environment in trait |
936 | // function default implementations (and hypothetical code | 935 | // function default implementations (and hypothetical code |
937 | // inside consts or type aliases) | 936 | // inside consts or type aliases) |
938 | test_utils::mark::hit!(trait_self_implements_self); | 937 | cov_mark::hit!(trait_self_implements_self); |
939 | let substs = Substs::type_params(db, trait_id); | 938 | let substs = Substs::type_params(db, trait_id); |
940 | let trait_ref = TraitRef { trait_: trait_id, substs }; | 939 | let trait_ref = TraitRef { trait_: trait_id, substs }; |
941 | let pred = GenericPredicate::Implemented(trait_ref); | 940 | let pred = GenericPredicate::Implemented(trait_ref); |
@@ -1100,7 +1099,7 @@ fn type_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) - | |||
1100 | fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { | 1099 | fn type_for_adt(db: &dyn HirDatabase, adt: AdtId) -> Binders<Ty> { |
1101 | let generics = generics(db.upcast(), adt.into()); | 1100 | let generics = generics(db.upcast(), adt.into()); |
1102 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); | 1101 | let substs = Substs::bound_vars(&generics, DebruijnIndex::INNERMOST); |
1103 | Binders::new(substs.len(), Ty::Adt(adt, substs)) | 1102 | Binders::new(substs.len(), Ty::adt_ty(adt, substs)) |
1104 | } | 1103 | } |
1105 | 1104 | ||
1106 | fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { | 1105 | fn type_for_type_alias(db: &dyn HirDatabase, t: TypeAliasId) -> Binders<Ty> { |
@@ -1131,8 +1130,8 @@ impl CallableDefId { | |||
1131 | let db = db.upcast(); | 1130 | let db = db.upcast(); |
1132 | match self { | 1131 | match self { |
1133 | CallableDefId::FunctionId(f) => f.lookup(db).module(db), | 1132 | CallableDefId::FunctionId(f) => f.lookup(db).module(db), |
1134 | CallableDefId::StructId(s) => s.lookup(db).container.module(db), | 1133 | CallableDefId::StructId(s) => s.lookup(db).container, |
1135 | CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container.module(db), | 1134 | CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container, |
1136 | } | 1135 | } |
1137 | .krate() | 1136 | .krate() |
1138 | } | 1137 | } |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index f301a8477..ccc12c075 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -8,8 +8,8 @@ use arrayvec::ArrayVec; | |||
8 | use base_db::CrateId; | 8 | use base_db::CrateId; |
9 | use chalk_ir::Mutability; | 9 | use chalk_ir::Mutability; |
10 | use hir_def::{ | 10 | use hir_def::{ |
11 | lang_item::LangItemTarget, AdtId, AssocContainerId, AssocItemId, FunctionId, GenericDefId, | 11 | lang_item::LangItemTarget, AssocContainerId, AssocItemId, FunctionId, GenericDefId, HasModule, |
12 | HasModule, ImplId, Lookup, ModuleId, TraitId, TypeAliasId, | 12 | ImplId, Lookup, ModuleId, TraitId, TypeAliasId, |
13 | }; | 13 | }; |
14 | use hir_expand::name::Name; | 14 | use hir_expand::name::Name; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 15 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -19,8 +19,8 @@ use crate::{ | |||
19 | db::HirDatabase, | 19 | db::HirDatabase, |
20 | primitive::{self, FloatTy, IntTy, UintTy}, | 20 | primitive::{self, FloatTy, IntTy, UintTy}, |
21 | utils::all_super_traits, | 21 | utils::all_super_traits, |
22 | Canonical, DebruijnIndex, FnPointer, FnSig, InEnvironment, Scalar, Substs, TraitEnvironment, | 22 | AdtId, Canonical, DebruijnIndex, FnPointer, FnSig, InEnvironment, Scalar, Substs, |
23 | TraitRef, Ty, TypeWalk, | 23 | TraitEnvironment, TraitRef, Ty, TypeWalk, |
24 | }; | 24 | }; |
25 | 25 | ||
26 | /// This is used as a key for indexing impls. | 26 | /// This is used as a key for indexing impls. |
@@ -32,7 +32,7 @@ pub enum TyFingerprint { | |||
32 | Never, | 32 | Never, |
33 | RawPtr(Mutability), | 33 | RawPtr(Mutability), |
34 | Scalar(Scalar), | 34 | Scalar(Scalar), |
35 | Adt(AdtId), | 35 | Adt(hir_def::AdtId), |
36 | Dyn(TraitId), | 36 | Dyn(TraitId), |
37 | Tuple(usize), | 37 | Tuple(usize), |
38 | ForeignType(TypeAliasId), | 38 | ForeignType(TypeAliasId), |
@@ -50,7 +50,7 @@ impl TyFingerprint { | |||
50 | &Ty::Slice(..) => TyFingerprint::Slice, | 50 | &Ty::Slice(..) => TyFingerprint::Slice, |
51 | &Ty::Array(..) => TyFingerprint::Array, | 51 | &Ty::Array(..) => TyFingerprint::Array, |
52 | &Ty::Scalar(scalar) => TyFingerprint::Scalar(scalar), | 52 | &Ty::Scalar(scalar) => TyFingerprint::Scalar(scalar), |
53 | &Ty::Adt(adt, _) => TyFingerprint::Adt(adt), | 53 | &Ty::Adt(AdtId(adt), _) => TyFingerprint::Adt(adt), |
54 | &Ty::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality), | 54 | &Ty::Tuple(cardinality, _) => TyFingerprint::Tuple(cardinality), |
55 | &Ty::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability), | 55 | &Ty::Raw(mutability, ..) => TyFingerprint::RawPtr(mutability), |
56 | &Ty::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id), | 56 | &Ty::ForeignType(alias_id, ..) => TyFingerprint::ForeignType(alias_id), |
@@ -231,7 +231,7 @@ impl Ty { | |||
231 | let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect()); | 231 | let mod_to_crate_ids = |module: ModuleId| Some(std::iter::once(module.krate()).collect()); |
232 | 232 | ||
233 | let lang_item_targets = match self { | 233 | let lang_item_targets = match self { |
234 | Ty::Adt(def_id, _) => { | 234 | Ty::Adt(AdtId(def_id), _) => { |
235 | return mod_to_crate_ids(def_id.module(db.upcast())); | 235 | return mod_to_crate_ids(def_id.module(db.upcast())); |
236 | } | 236 | } |
237 | Ty::ForeignType(type_alias_id) => { | 237 | Ty::ForeignType(type_alias_id) => { |
@@ -267,7 +267,7 @@ impl Ty { | |||
267 | LangItemTarget::ImplDefId(it) => Some(it), | 267 | LangItemTarget::ImplDefId(it) => Some(it), |
268 | _ => None, | 268 | _ => None, |
269 | }) | 269 | }) |
270 | .map(|it| it.lookup(db.upcast()).container.module(db.upcast()).krate()) | 270 | .map(|it| it.lookup(db.upcast()).container.krate()) |
271 | .collect(); | 271 | .collect(); |
272 | Some(res) | 272 | Some(res) |
273 | } | 273 | } |
@@ -588,7 +588,7 @@ fn iterate_inherent_methods( | |||
588 | // already happens in `is_valid_candidate` above; if not, we | 588 | // already happens in `is_valid_candidate` above; if not, we |
589 | // check it here | 589 | // check it here |
590 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { | 590 | if receiver_ty.is_none() && inherent_impl_substs(db, impl_def, self_ty).is_none() { |
591 | test_utils::mark::hit!(impl_self_type_match_without_receiver); | 591 | cov_mark::hit!(impl_self_type_match_without_receiver); |
592 | continue; | 592 | continue; |
593 | } | 593 | } |
594 | if callback(&self_ty.value, item) { | 594 | if callback(&self_ty.value, item) { |
@@ -715,7 +715,7 @@ fn transform_receiver_ty( | |||
715 | .fill_with_unknown() | 715 | .fill_with_unknown() |
716 | .build() | 716 | .build() |
717 | } | 717 | } |
718 | AssocContainerId::ContainerId(_) => unreachable!(), | 718 | AssocContainerId::ModuleId(_) => unreachable!(), |
719 | }; | 719 | }; |
720 | let sig = db.callable_item_signature(function_id.into()); | 720 | let sig = db.callable_item_signature(function_id.into()); |
721 | Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) | 721 | Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) |
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 7386a4e7b..fc770ea60 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs | |||
@@ -13,7 +13,7 @@ use std::{env, sync::Arc}; | |||
13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
14 | use expect_test::Expect; | 14 | use expect_test::Expect; |
15 | use hir_def::{ | 15 | use hir_def::{ |
16 | body::{BodySourceMap, SyntheticSyntax}, | 16 | body::{Body, BodySourceMap, SyntheticSyntax}, |
17 | child_by_source::ChildBySource, | 17 | child_by_source::ChildBySource, |
18 | db::DefDatabase, | 18 | db::DefDatabase, |
19 | item_scope::ItemScope, | 19 | item_scope::ItemScope, |
@@ -234,13 +234,13 @@ fn visit_module( | |||
234 | let def = it.into(); | 234 | let def = it.into(); |
235 | cb(def); | 235 | cb(def); |
236 | let body = db.body(def); | 236 | let body = db.body(def); |
237 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 237 | visit_body(db, &body, cb); |
238 | } | 238 | } |
239 | AssocItemId::ConstId(it) => { | 239 | AssocItemId::ConstId(it) => { |
240 | let def = it.into(); | 240 | let def = it.into(); |
241 | cb(def); | 241 | cb(def); |
242 | let body = db.body(def); | 242 | let body = db.body(def); |
243 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 243 | visit_body(db, &body, cb); |
244 | } | 244 | } |
245 | AssocItemId::TypeAliasId(_) => (), | 245 | AssocItemId::TypeAliasId(_) => (), |
246 | } | 246 | } |
@@ -259,19 +259,19 @@ fn visit_module( | |||
259 | let def = it.into(); | 259 | let def = it.into(); |
260 | cb(def); | 260 | cb(def); |
261 | let body = db.body(def); | 261 | let body = db.body(def); |
262 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 262 | visit_body(db, &body, cb); |
263 | } | 263 | } |
264 | ModuleDefId::ConstId(it) => { | 264 | ModuleDefId::ConstId(it) => { |
265 | let def = it.into(); | 265 | let def = it.into(); |
266 | cb(def); | 266 | cb(def); |
267 | let body = db.body(def); | 267 | let body = db.body(def); |
268 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 268 | visit_body(db, &body, cb); |
269 | } | 269 | } |
270 | ModuleDefId::StaticId(it) => { | 270 | ModuleDefId::StaticId(it) => { |
271 | let def = it.into(); | 271 | let def = it.into(); |
272 | cb(def); | 272 | cb(def); |
273 | let body = db.body(def); | 273 | let body = db.body(def); |
274 | visit_scope(db, crate_def_map, &body.item_scope, cb); | 274 | visit_body(db, &body, cb); |
275 | } | 275 | } |
276 | ModuleDefId::TraitId(it) => { | 276 | ModuleDefId::TraitId(it) => { |
277 | let trait_data = db.trait_data(it); | 277 | let trait_data = db.trait_data(it); |
@@ -288,6 +288,14 @@ fn visit_module( | |||
288 | } | 288 | } |
289 | } | 289 | } |
290 | } | 290 | } |
291 | |||
292 | fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) { | ||
293 | for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) { | ||
294 | for (mod_id, _) in def_map.modules() { | ||
295 | visit_module(db, &def_map, mod_id, cb); | ||
296 | } | ||
297 | } | ||
298 | } | ||
291 | } | 299 | } |
292 | 300 | ||
293 | fn ellipsize(mut text: String, max_len: usize) -> String { | 301 | fn ellipsize(mut text: String, max_len: usize) -> String { |
diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index 7bc6c79f3..63d9d4e0b 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches}; |
5 | 4 | ||
@@ -381,7 +380,7 @@ fn infer_match_second_coerce() { | |||
381 | 380 | ||
382 | #[test] | 381 | #[test] |
383 | fn coerce_merge_one_by_one1() { | 382 | fn coerce_merge_one_by_one1() { |
384 | mark::check!(coerce_merge_fail_fallback); | 383 | cov_mark::check!(coerce_merge_fail_fallback); |
385 | 384 | ||
386 | check_infer( | 385 | check_infer( |
387 | r" | 386 | r" |
@@ -589,7 +588,7 @@ fn coerce_fn_item_to_fn_ptr() { | |||
589 | 588 | ||
590 | #[test] | 589 | #[test] |
591 | fn coerce_fn_items_in_match_arms() { | 590 | fn coerce_fn_items_in_match_arms() { |
592 | mark::check!(coerce_fn_reification); | 591 | cov_mark::check!(coerce_fn_reification); |
593 | 592 | ||
594 | check_infer_with_mismatches( | 593 | check_infer_with_mismatches( |
595 | r" | 594 | r" |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index a9901d7b8..4e3f9a9b6 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -913,7 +913,7 @@ fn test() { S2.into(); } | |||
913 | 913 | ||
914 | #[test] | 914 | #[test] |
915 | fn method_resolution_overloaded_method() { | 915 | fn method_resolution_overloaded_method() { |
916 | test_utils::mark::check!(impl_self_type_match_without_receiver); | 916 | cov_mark::check!(impl_self_type_match_without_receiver); |
917 | check_types( | 917 | check_types( |
918 | r#" | 918 | r#" |
919 | struct Wrapper<T>(T); | 919 | struct Wrapper<T>(T); |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 2053d8f56..5da19ba5f 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_infer_with_mismatches}; | 3 | use super::{check_infer, check_infer_with_mismatches}; |
5 | 4 | ||
@@ -197,7 +196,7 @@ fn infer_pattern_match_ergonomics() { | |||
197 | 196 | ||
198 | #[test] | 197 | #[test] |
199 | fn infer_pattern_match_ergonomics_ref() { | 198 | fn infer_pattern_match_ergonomics_ref() { |
200 | mark::check!(match_ergonomics_ref); | 199 | cov_mark::check!(match_ergonomics_ref); |
201 | check_infer( | 200 | check_infer( |
202 | r#" | 201 | r#" |
203 | fn test() { | 202 | fn test() { |
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index cffe8630b..69314e245 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_types}; | 3 | use super::{check_infer, check_types}; |
5 | 4 | ||
@@ -87,8 +86,8 @@ fn bug_651() { | |||
87 | 86 | ||
88 | #[test] | 87 | #[test] |
89 | fn recursive_vars() { | 88 | fn recursive_vars() { |
90 | mark::check!(type_var_cycles_resolve_completely); | 89 | cov_mark::check!(type_var_cycles_resolve_completely); |
91 | mark::check!(type_var_cycles_resolve_as_possible); | 90 | cov_mark::check!(type_var_cycles_resolve_as_possible); |
92 | check_infer( | 91 | check_infer( |
93 | r#" | 92 | r#" |
94 | fn test() { | 93 | fn test() { |
@@ -166,7 +165,7 @@ fn infer_std_crash_1() { | |||
166 | 165 | ||
167 | #[test] | 166 | #[test] |
168 | fn infer_std_crash_2() { | 167 | fn infer_std_crash_2() { |
169 | mark::check!(type_var_resolves_to_int_var); | 168 | cov_mark::check!(type_var_resolves_to_int_var); |
170 | // caused "equating two type variables, ...", taken from std | 169 | // caused "equating two type variables, ...", taken from std |
171 | check_infer( | 170 | check_infer( |
172 | r#" | 171 | r#" |
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 2947857a5..f5069eba5 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_types}; | 3 | use super::{check_infer, check_types}; |
5 | 4 | ||
@@ -2314,7 +2313,7 @@ fn generic_default_depending_on_other_type_arg_forward() { | |||
2314 | 2313 | ||
2315 | #[test] | 2314 | #[test] |
2316 | fn infer_operator_overload() { | 2315 | fn infer_operator_overload() { |
2317 | mark::check!(infer_expr_inner_binary_operator_overload); | 2316 | cov_mark::check!(infer_expr_inner_binary_operator_overload); |
2318 | 2317 | ||
2319 | check_infer( | 2318 | check_infer( |
2320 | r#" | 2319 | r#" |
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index 1298e5a88..e185b1c0a 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use expect_test::expect; | 1 | use expect_test::expect; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use super::{check_infer, check_infer_with_mismatches, check_types}; | 3 | use super::{check_infer, check_infer_with_mismatches, check_types}; |
5 | 4 | ||
@@ -319,7 +318,7 @@ fn infer_from_bound_2() { | |||
319 | 318 | ||
320 | #[test] | 319 | #[test] |
321 | fn trait_default_method_self_bound_implements_trait() { | 320 | fn trait_default_method_self_bound_implements_trait() { |
322 | mark::check!(trait_self_implements_self); | 321 | cov_mark::check!(trait_self_implements_self); |
323 | check_infer( | 322 | check_infer( |
324 | r#" | 323 | r#" |
325 | trait Trait { | 324 | trait Trait { |
@@ -1189,7 +1188,7 @@ fn impl_trait() { | |||
1189 | 1188 | ||
1190 | #[test] | 1189 | #[test] |
1191 | fn simple_return_pos_impl_trait() { | 1190 | fn simple_return_pos_impl_trait() { |
1192 | mark::check!(lower_rpit); | 1191 | cov_mark::check!(lower_rpit); |
1193 | check_infer( | 1192 | check_infer( |
1194 | r#" | 1193 | r#" |
1195 | trait Trait<T> { | 1194 | trait Trait<T> { |
@@ -3175,6 +3174,39 @@ fn f() { | |||
3175 | } | 3174 | } |
3176 | 3175 | ||
3177 | #[test] | 3176 | #[test] |
3177 | fn trait_in_scope_with_inner_item() { | ||
3178 | check_infer( | ||
3179 | r#" | ||
3180 | mod m { | ||
3181 | pub trait Tr { | ||
3182 | fn method(&self) -> u8 { 0 } | ||
3183 | } | ||
3184 | |||
3185 | impl Tr for () {} | ||
3186 | } | ||
3187 | |||
3188 | use m::Tr; | ||
3189 | |||
3190 | fn f() { | ||
3191 | fn inner() { | ||
3192 | ().method(); | ||
3193 | //^^^^^^^^^^^ u8 | ||
3194 | } | ||
3195 | } | ||
3196 | "#, | ||
3197 | expect![[r#" | ||
3198 | 46..50 'self': &Self | ||
3199 | 58..63 '{ 0 }': u8 | ||
3200 | 60..61 '0': u8 | ||
3201 | 115..185 '{ ... } }': () | ||
3202 | 132..183 '{ ... }': () | ||
3203 | 142..144 '()': () | ||
3204 | 142..153 '().method()': u8 | ||
3205 | "#]], | ||
3206 | ); | ||
3207 | } | ||
3208 | |||
3209 | #[test] | ||
3178 | fn inner_use_in_block() { | 3210 | fn inner_use_in_block() { |
3179 | check_types( | 3211 | check_types( |
3180 | r#" | 3212 | r#" |
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index e513fa8f4..565672b6b 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs | |||
@@ -315,9 +315,8 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
315 | let id = from_chalk(self.db, trait_id); | 315 | let id = from_chalk(self.db, trait_id); |
316 | self.db.trait_data(id).name.to_string() | 316 | self.db.trait_data(id).name.to_string() |
317 | } | 317 | } |
318 | fn adt_name(&self, adt_id: chalk_ir::AdtId<Interner>) -> String { | 318 | fn adt_name(&self, chalk_ir::AdtId(adt_id): AdtId) -> String { |
319 | let id = from_chalk(self.db, adt_id); | 319 | match adt_id { |
320 | match id { | ||
321 | hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(), | 320 | hir_def::AdtId::StructId(id) => self.db.struct_data(id).name.to_string(), |
322 | hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(), | 321 | hir_def::AdtId::EnumId(id) => self.db.enum_data(id).name.to_string(), |
323 | hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(), | 322 | hir_def::AdtId::UnionId(id) => self.db.union_data(id).name.to_string(), |
@@ -425,7 +424,7 @@ pub(crate) fn trait_datum_query( | |||
425 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 424 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
426 | let flags = rust_ir::TraitFlags { | 425 | let flags = rust_ir::TraitFlags { |
427 | auto: trait_data.auto, | 426 | auto: trait_data.auto, |
428 | upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate() != krate, | 427 | upstream: trait_.lookup(db.upcast()).container.krate() != krate, |
429 | non_enumerable: true, | 428 | non_enumerable: true, |
430 | coinductive: false, // only relevant for Chalk testing | 429 | coinductive: false, // only relevant for Chalk testing |
431 | // FIXME: set these flags correctly | 430 | // FIXME: set these flags correctly |
@@ -488,8 +487,8 @@ pub(crate) fn struct_datum_query( | |||
488 | struct_id: AdtId, | 487 | struct_id: AdtId, |
489 | ) -> Arc<StructDatum> { | 488 | ) -> Arc<StructDatum> { |
490 | debug!("struct_datum {:?}", struct_id); | 489 | debug!("struct_datum {:?}", struct_id); |
491 | let adt_id = from_chalk(db, struct_id); | 490 | let type_ctor = Ty::Adt(struct_id, Substs::empty()); |
492 | let type_ctor = Ty::Adt(adt_id, Substs::empty()); | 491 | let chalk_ir::AdtId(adt_id) = struct_id; |
493 | debug!("struct {:?} = {:?}", struct_id, type_ctor); | 492 | debug!("struct {:?} = {:?}", struct_id, type_ctor); |
494 | let num_params = generics(db.upcast(), adt_id.into()).len(); | 493 | let num_params = generics(db.upcast(), adt_id.into()).len(); |
495 | let upstream = adt_id.module(db.upcast()).krate() != krate; | 494 | let upstream = adt_id.module(db.upcast()).krate() != krate; |
@@ -549,7 +548,7 @@ fn impl_def_datum( | |||
549 | let generic_params = generics(db.upcast(), impl_id.into()); | 548 | let generic_params = generics(db.upcast(), impl_id.into()); |
550 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); | 549 | let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); |
551 | let trait_ = trait_ref.trait_; | 550 | let trait_ = trait_ref.trait_; |
552 | let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate() == krate { | 551 | let impl_type = if impl_id.lookup(db.upcast()).container.krate() == krate { |
553 | rust_ir::ImplType::Local | 552 | rust_ir::ImplType::Local |
554 | } else { | 553 | } else { |
555 | rust_ir::ImplType::External | 554 | rust_ir::ImplType::External |
@@ -684,10 +683,9 @@ pub(crate) fn fn_def_variance_query( | |||
684 | pub(crate) fn adt_variance_query( | 683 | pub(crate) fn adt_variance_query( |
685 | db: &dyn HirDatabase, | 684 | db: &dyn HirDatabase, |
686 | _krate: CrateId, | 685 | _krate: CrateId, |
687 | adt_id: AdtId, | 686 | chalk_ir::AdtId(adt_id): AdtId, |
688 | ) -> Variances { | 687 | ) -> Variances { |
689 | let adt: crate::AdtId = from_chalk(db, adt_id); | 688 | let generic_params = generics(db.upcast(), adt_id.into()); |
690 | let generic_params = generics(db.upcast(), adt.into()); | ||
691 | Variances::from_iter( | 689 | Variances::from_iter( |
692 | &Interner, | 690 | &Interner, |
693 | std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), | 691 | std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index db1760e6c..3a08b67e9 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -86,7 +86,7 @@ impl ToChalk for Ty { | |||
86 | 86 | ||
87 | Ty::Adt(adt_id, substs) => { | 87 | Ty::Adt(adt_id, substs) => { |
88 | let substitution = substs.to_chalk(db); | 88 | let substitution = substs.to_chalk(db); |
89 | chalk_ir::TyKind::Adt(chalk_ir::AdtId(adt_id), substitution).intern(&Interner) | 89 | chalk_ir::TyKind::Adt(adt_id, substitution).intern(&Interner) |
90 | } | 90 | } |
91 | Ty::Alias(AliasTy::Projection(proj_ty)) => { | 91 | Ty::Alias(AliasTy::Projection(proj_ty)) => { |
92 | let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db); | 92 | let associated_ty_id = TypeAliasAsAssocType(proj_ty.associated_ty).to_chalk(db); |
@@ -183,7 +183,7 @@ impl ToChalk for Ty { | |||
183 | Ty::Dyn(predicates) | 183 | Ty::Dyn(predicates) |
184 | } | 184 | } |
185 | 185 | ||
186 | chalk_ir::TyKind::Adt(struct_id, subst) => Ty::Adt(struct_id.0, from_chalk(db, subst)), | 186 | chalk_ir::TyKind::Adt(adt_id, subst) => Ty::Adt(adt_id, from_chalk(db, subst)), |
187 | chalk_ir::TyKind::AssociatedType(type_id, subst) => Ty::AssociatedType( | 187 | chalk_ir::TyKind::AssociatedType(type_id, subst) => Ty::AssociatedType( |
188 | from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0, | 188 | from_chalk::<TypeAliasAsAssocType, _>(db, type_id).0, |
189 | from_chalk(db, subst), | 189 | from_chalk(db, subst), |
@@ -325,18 +325,6 @@ impl ToChalk for hir_def::ImplId { | |||
325 | } | 325 | } |
326 | } | 326 | } |
327 | 327 | ||
328 | impl ToChalk for hir_def::AdtId { | ||
329 | type Chalk = AdtId; | ||
330 | |||
331 | fn to_chalk(self, _db: &dyn HirDatabase) -> Self::Chalk { | ||
332 | chalk_ir::AdtId(self.into()) | ||
333 | } | ||
334 | |||
335 | fn from_chalk(_db: &dyn HirDatabase, id: AdtId) -> Self { | ||
336 | id.0 | ||
337 | } | ||
338 | } | ||
339 | |||
340 | impl ToChalk for CallableDefId { | 328 | impl ToChalk for CallableDefId { |
341 | type Chalk = FnDefId; | 329 | type Chalk = FnDefId; |
342 | 330 | ||
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index 65b79df0d..7351e4e54 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs | |||
@@ -259,6 +259,6 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic | |||
259 | match container { | 259 | match container { |
260 | AssocContainerId::ImplId(it) => Some(it.into()), | 260 | AssocContainerId::ImplId(it) => Some(it.into()), |
261 | AssocContainerId::TraitId(it) => Some(it.into()), | 261 | AssocContainerId::TraitId(it) => Some(it.into()), |
262 | AssocContainerId::ContainerId(_) => None, | 262 | AssocContainerId::ModuleId(_) => None, |
263 | } | 263 | } |
264 | } | 264 | } |
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index f6aaaeda4..107bd8432 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | either = "1.5.3" | 14 | either = "1.5.3" |
14 | indexmap = "1.4.0" | 15 | indexmap = "1.4.0" |
15 | itertools = "0.10.0" | 16 | itertools = "0.10.0" |
@@ -26,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
26 | ide_db = { path = "../ide_db", version = "0.0.0" } | 27 | ide_db = { path = "../ide_db", version = "0.0.0" } |
27 | cfg = { path = "../cfg", version = "0.0.0" } | 28 | cfg = { path = "../cfg", version = "0.0.0" } |
28 | profile = { path = "../profile", version = "0.0.0" } | 29 | profile = { path = "../profile", version = "0.0.0" } |
29 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
30 | ide_assists = { path = "../ide_assists", version = "0.0.0" } | 30 | ide_assists = { path = "../ide_assists", version = "0.0.0" } |
31 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } | 31 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } |
32 | ide_completion = { path = "../ide_completion", version = "0.0.0" } | 32 | ide_completion = { path = "../ide_completion", version = "0.0.0" } |
@@ -36,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" } | |||
36 | hir = { path = "../hir", version = "0.0.0" } | 36 | hir = { path = "../hir", version = "0.0.0" } |
37 | 37 | ||
38 | [dev-dependencies] | 38 | [dev-dependencies] |
39 | test_utils = { path = "../test_utils" } | ||
39 | expect-test = "1.1" | 40 | expect-test = "1.1" |
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs index 84b8883de..2df9266b4 100644 --- a/crates/ide/src/display/short_label.rs +++ b/crates/ide/src/display/short_label.rs | |||
@@ -71,11 +71,7 @@ impl ShortLabel for ast::TypeAlias { | |||
71 | 71 | ||
72 | impl ShortLabel for ast::Const { | 72 | impl ShortLabel for ast::Const { |
73 | fn short_label(&self) -> Option<String> { | 73 | fn short_label(&self) -> Option<String> { |
74 | let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; | 74 | short_label_from_ty(self, self.ty(), "const ") |
75 | if let Some(expr) = self.body() { | ||
76 | format_to!(new_buf, " = {}", expr.syntax()); | ||
77 | } | ||
78 | Some(new_buf) | ||
79 | } | 75 | } |
80 | } | 76 | } |
81 | 77 | ||
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index a9454cfa3..ea45086ce 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -1,3 +1,4 @@ | |||
1 | use either::Either; | ||
1 | use hir::{ | 2 | use hir::{ |
2 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, | 3 | Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, |
3 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, | 4 | HirDisplay, Module, ModuleDef, ModuleSource, Semantics, |
@@ -11,7 +12,6 @@ use ide_db::{ | |||
11 | use itertools::Itertools; | 12 | use itertools::Itertools; |
12 | use stdx::format_to; | 13 | use stdx::format_to; |
13 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; | 14 | use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; |
14 | use test_utils::mark; | ||
15 | 15 | ||
16 | use crate::{ | 16 | use crate::{ |
17 | display::{macro_label, ShortLabel, TryToNav}, | 17 | display::{macro_label, ShortLabel, TryToNav}, |
@@ -193,8 +193,8 @@ fn runnable_action( | |||
193 | ModuleDef::Function(func) => { | 193 | ModuleDef::Function(func) => { |
194 | let src = func.source(sema.db)?; | 194 | let src = func.source(sema.db)?; |
195 | if src.file_id != file_id.into() { | 195 | if src.file_id != file_id.into() { |
196 | mark::hit!(hover_macro_generated_struct_fn_doc_comment); | 196 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_comment); |
197 | mark::hit!(hover_macro_generated_struct_fn_doc_attr); | 197 | cov_mark::hit!(hover_macro_generated_struct_fn_doc_attr); |
198 | return None; | 198 | return None; |
199 | } | 199 | } |
200 | 200 | ||
@@ -367,7 +367,7 @@ fn hover_for_definition( | |||
367 | .and_then(|fd| hover_for_builtin(fd, it)) | 367 | .and_then(|fd| hover_for_builtin(fd, it)) |
368 | .or_else(|| Some(Markup::fenced_block(&it.name()))), | 368 | .or_else(|| Some(Markup::fenced_block(&it.name()))), |
369 | }, | 369 | }, |
370 | Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), | 370 | Definition::Local(it) => hover_for_local(it, db), |
371 | Definition::SelfType(impl_def) => { | 371 | Definition::SelfType(impl_def) => { |
372 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { | 372 | impl_def.target_ty(db).as_adt().and_then(|adt| match adt { |
373 | Adt::Struct(it) => from_def_source(db, it, mod_path), | 373 | Adt::Struct(it) => from_def_source(db, it, mod_path), |
@@ -406,6 +406,29 @@ fn hover_for_definition( | |||
406 | } | 406 | } |
407 | } | 407 | } |
408 | 408 | ||
409 | fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> { | ||
410 | let ty = it.ty(db); | ||
411 | let ty = ty.display(db); | ||
412 | let is_mut = if it.is_mut(db) { "mut " } else { "" }; | ||
413 | let desc = match it.source(db).value { | ||
414 | Either::Left(ident) => { | ||
415 | let name = it.name(db).unwrap(); | ||
416 | let let_kw = if ident | ||
417 | .syntax() | ||
418 | .parent() | ||
419 | .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION) | ||
420 | { | ||
421 | "let " | ||
422 | } else { | ||
423 | "" | ||
424 | }; | ||
425 | format!("{}{}{}: {}", let_kw, is_mut, name, ty) | ||
426 | } | ||
427 | Either::Right(_) => format!("{}self: {}", is_mut, ty), | ||
428 | }; | ||
429 | hover_markup(None, Some(desc), None) | ||
430 | } | ||
431 | |||
409 | fn hover_for_keyword( | 432 | fn hover_for_keyword( |
410 | sema: &Semantics<RootDatabase>, | 433 | sema: &Semantics<RootDatabase>, |
411 | links_in_hover: bool, | 434 | links_in_hover: bool, |
@@ -575,7 +598,7 @@ fn main() { | |||
575 | *iter* | 598 | *iter* |
576 | 599 | ||
577 | ```rust | 600 | ```rust |
578 | Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> | 601 | let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> |
579 | ``` | 602 | ``` |
580 | "#]], | 603 | "#]], |
581 | ); | 604 | ); |
@@ -799,7 +822,7 @@ fn main() { | |||
799 | ``` | 822 | ``` |
800 | 823 | ||
801 | ```rust | 824 | ```rust |
802 | const foo: u32 = 123 | 825 | const foo: u32 |
803 | ``` | 826 | ``` |
804 | "#]], | 827 | "#]], |
805 | ); | 828 | ); |
@@ -832,7 +855,7 @@ fn main() { | |||
832 | *zz* | 855 | *zz* |
833 | 856 | ||
834 | ```rust | 857 | ```rust |
835 | Test<i32, u8> | 858 | let zz: Test<i32, u8> |
836 | ``` | 859 | ``` |
837 | "#]], | 860 | "#]], |
838 | ); | 861 | ); |
@@ -871,7 +894,7 @@ fn main() { let b$0ar = Some(12); } | |||
871 | *bar* | 894 | *bar* |
872 | 895 | ||
873 | ```rust | 896 | ```rust |
874 | Option<i32> | 897 | let bar: Option<i32> |
875 | ``` | 898 | ``` |
876 | "#]], | 899 | "#]], |
877 | ); | 900 | ); |
@@ -939,7 +962,7 @@ fn main() { | |||
939 | *foo* | 962 | *foo* |
940 | 963 | ||
941 | ```rust | 964 | ```rust |
942 | i32 | 965 | foo: i32 |
943 | ``` | 966 | ``` |
944 | "#]], | 967 | "#]], |
945 | ) | 968 | ) |
@@ -953,7 +976,7 @@ fn main() { | |||
953 | *foo* | 976 | *foo* |
954 | 977 | ||
955 | ```rust | 978 | ```rust |
956 | i32 | 979 | foo: i32 |
957 | ``` | 980 | ``` |
958 | "#]], | 981 | "#]], |
959 | ) | 982 | ) |
@@ -967,7 +990,7 @@ fn main() { | |||
967 | *foo* | 990 | *foo* |
968 | 991 | ||
969 | ```rust | 992 | ```rust |
970 | i32 | 993 | foo: i32 |
971 | ``` | 994 | ``` |
972 | "#]], | 995 | "#]], |
973 | ) | 996 | ) |
@@ -981,7 +1004,7 @@ fn main() { | |||
981 | *foo* | 1004 | *foo* |
982 | 1005 | ||
983 | ```rust | 1006 | ```rust |
984 | i32 | 1007 | foo: i32 |
985 | ``` | 1008 | ``` |
986 | "#]], | 1009 | "#]], |
987 | ) | 1010 | ) |
@@ -1001,7 +1024,7 @@ fn main() { | |||
1001 | *_x* | 1024 | *_x* |
1002 | 1025 | ||
1003 | ```rust | 1026 | ```rust |
1004 | impl Deref<Target = u8> + DerefMut<Target = u8> | 1027 | _x: impl Deref<Target = u8> + DerefMut<Target = u8> |
1005 | ``` | 1028 | ``` |
1006 | "#]], | 1029 | "#]], |
1007 | ) | 1030 | ) |
@@ -1023,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); } | |||
1023 | *foo_test* | 1046 | *foo_test* |
1024 | 1047 | ||
1025 | ```rust | 1048 | ```rust |
1026 | Thing | 1049 | let foo_test: Thing |
1027 | ``` | 1050 | ``` |
1028 | "#]], | 1051 | "#]], |
1029 | ) | 1052 | ) |
@@ -1082,7 +1105,7 @@ fn main() { | |||
1082 | ``` | 1105 | ``` |
1083 | 1106 | ||
1084 | ```rust | 1107 | ```rust |
1085 | const C: u32 = 1 | 1108 | const C: u32 |
1086 | ``` | 1109 | ``` |
1087 | "#]], | 1110 | "#]], |
1088 | ) | 1111 | ) |
@@ -1183,7 +1206,7 @@ fn y() { | |||
1183 | *x* | 1206 | *x* |
1184 | 1207 | ||
1185 | ```rust | 1208 | ```rust |
1186 | i32 | 1209 | let x: i32 |
1187 | ``` | 1210 | ``` |
1188 | "#]], | 1211 | "#]], |
1189 | ) | 1212 | ) |
@@ -1260,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } | |||
1260 | *bar* | 1283 | *bar* |
1261 | 1284 | ||
1262 | ```rust | 1285 | ```rust |
1263 | u32 | 1286 | bar: u32 |
1264 | ``` | 1287 | ``` |
1265 | "#]], | 1288 | "#]], |
1266 | ); | 1289 | ); |
@@ -1278,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); } | |||
1278 | *bar* | 1301 | *bar* |
1279 | 1302 | ||
1280 | ```rust | 1303 | ```rust |
1281 | u32 | 1304 | bar: u32 |
1282 | ``` | 1305 | ``` |
1283 | "#]], | 1306 | "#]], |
1284 | ); | 1307 | ); |
@@ -2101,7 +2124,7 @@ pub fn fo$0o() {} | |||
2101 | 2124 | ||
2102 | #[test] | 2125 | #[test] |
2103 | fn test_hover_macro_generated_struct_fn_doc_comment() { | 2126 | fn test_hover_macro_generated_struct_fn_doc_comment() { |
2104 | mark::check!(hover_macro_generated_struct_fn_doc_comment); | 2127 | cov_mark::check!(hover_macro_generated_struct_fn_doc_comment); |
2105 | 2128 | ||
2106 | check( | 2129 | check( |
2107 | r#" | 2130 | r#" |
@@ -2139,7 +2162,7 @@ fn foo() { let bar = Bar; bar.fo$0o(); } | |||
2139 | 2162 | ||
2140 | #[test] | 2163 | #[test] |
2141 | fn test_hover_macro_generated_struct_fn_doc_attr() { | 2164 | fn test_hover_macro_generated_struct_fn_doc_attr() { |
2142 | mark::check!(hover_macro_generated_struct_fn_doc_attr); | 2165 | cov_mark::check!(hover_macro_generated_struct_fn_doc_attr); |
2143 | 2166 | ||
2144 | check( | 2167 | check( |
2145 | r#" | 2168 | r#" |
@@ -3303,7 +3326,7 @@ fn main() { | |||
3303 | *f* | 3326 | *f* |
3304 | 3327 | ||
3305 | ```rust | 3328 | ```rust |
3306 | &i32 | 3329 | f: &i32 |
3307 | ``` | 3330 | ``` |
3308 | "#]], | 3331 | "#]], |
3309 | ); | 3332 | ); |
@@ -3322,7 +3345,7 @@ impl Foo { | |||
3322 | *self* | 3345 | *self* |
3323 | 3346 | ||
3324 | ```rust | 3347 | ```rust |
3325 | &Foo | 3348 | self: &Foo |
3326 | ``` | 3349 | ``` |
3327 | "#]], | 3350 | "#]], |
3328 | ); | 3351 | ); |
@@ -3342,7 +3365,7 @@ impl Foo { | |||
3342 | *self* | 3365 | *self* |
3343 | 3366 | ||
3344 | ```rust | 3367 | ```rust |
3345 | Arc<Foo> | 3368 | self: Arc<Foo> |
3346 | ``` | 3369 | ``` |
3347 | "#]], | 3370 | "#]], |
3348 | ); | 3371 | ); |
@@ -3538,7 +3561,7 @@ fn foo() { | |||
3538 | ``` | 3561 | ``` |
3539 | 3562 | ||
3540 | ```rust | 3563 | ```rust |
3541 | const FOO: usize = 3 | 3564 | const FOO: usize |
3542 | ``` | 3565 | ``` |
3543 | 3566 | ||
3544 | --- | 3567 | --- |
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 7fcae13e0..20a920ddb 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs | |||
@@ -7,7 +7,7 @@ use syntax::{ | |||
7 | SyntaxKind::{self, USE_TREE, WHITESPACE}, | 7 | SyntaxKind::{self, USE_TREE, WHITESPACE}, |
8 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, | 8 | SyntaxNode, SyntaxToken, TextRange, TextSize, T, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | 10 | |
11 | use text_edit::{TextEdit, TextEditBuilder}; | 11 | use text_edit::{TextEdit, TextEditBuilder}; |
12 | 12 | ||
13 | // Feature: Join Lines | 13 | // Feature: Join Lines |
@@ -60,7 +60,7 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS | |||
60 | let mut string_open_quote = false; | 60 | let mut string_open_quote = false; |
61 | if let Some(string) = ast::String::cast(token.clone()) { | 61 | if let Some(string) = ast::String::cast(token.clone()) { |
62 | if let Some(range) = string.open_quote_text_range() { | 62 | if let Some(range) = string.open_quote_text_range() { |
63 | mark::hit!(join_string_literal); | 63 | cov_mark::hit!(join_string_literal); |
64 | string_open_quote = range.end() == offset; | 64 | string_open_quote = range.end() == offset; |
65 | } | 65 | } |
66 | } | 66 | } |
@@ -206,7 +206,7 @@ fn compute_ws(left: SyntaxKind, right: SyntaxKind) -> &'static str { | |||
206 | #[cfg(test)] | 206 | #[cfg(test)] |
207 | mod tests { | 207 | mod tests { |
208 | use syntax::SourceFile; | 208 | use syntax::SourceFile; |
209 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range, mark}; | 209 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; |
210 | 210 | ||
211 | use super::*; | 211 | use super::*; |
212 | 212 | ||
@@ -786,7 +786,7 @@ fn foo() { | |||
786 | 786 | ||
787 | #[test] | 787 | #[test] |
788 | fn join_string_literal() { | 788 | fn join_string_literal() { |
789 | mark::check!(join_string_literal); | 789 | cov_mark::check!(join_string_literal); |
790 | check_join_lines( | 790 | check_join_lines( |
791 | r#" | 791 | r#" |
792 | fn main() { | 792 | fn main() { |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b600178ee..f83ed65d5 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -478,7 +478,6 @@ impl Analysis { | |||
478 | position: FilePosition, | 478 | position: FilePosition, |
479 | full_import_path: &str, | 479 | full_import_path: &str, |
480 | imported_name: String, | 480 | imported_name: String, |
481 | import_for_trait_assoc_item: bool, | ||
482 | ) -> Cancelable<Vec<TextEdit>> { | 481 | ) -> Cancelable<Vec<TextEdit>> { |
483 | Ok(self | 482 | Ok(self |
484 | .with_db(|db| { | 483 | .with_db(|db| { |
@@ -488,7 +487,6 @@ impl Analysis { | |||
488 | position, | 487 | position, |
489 | full_import_path, | 488 | full_import_path, |
490 | imported_name, | 489 | imported_name, |
491 | import_for_trait_assoc_item, | ||
492 | ) | 490 | ) |
493 | })? | 491 | })? |
494 | .unwrap_or_default()) | 492 | .unwrap_or_default()) |
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs index 1bfa1439d..000c412d9 100644 --- a/crates/ide/src/matching_brace.rs +++ b/crates/ide/src/matching_brace.rs | |||
@@ -2,7 +2,6 @@ use syntax::{ | |||
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SourceFile, SyntaxKind, TextSize, T, | 3 | SourceFile, SyntaxKind, TextSize, T, |
4 | }; | 4 | }; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | // Feature: Matching Brace | 6 | // Feature: Matching Brace |
8 | // | 7 | // |
@@ -28,7 +27,7 @@ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<Text | |||
28 | .next()?; | 27 | .next()?; |
29 | let parent = brace_token.parent(); | 28 | let parent = brace_token.parent(); |
30 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { | 29 | if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { |
31 | mark::hit!(pipes_not_braces); | 30 | cov_mark::hit!(pipes_not_braces); |
32 | return None; | 31 | return None; |
33 | } | 32 | } |
34 | let matching_kind = BRACES[brace_idx ^ 1]; | 33 | let matching_kind = BRACES[brace_idx ^ 1]; |
@@ -63,7 +62,7 @@ mod tests { | |||
63 | do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); | 62 | do_check("fn main() { $0|x: i32| x * 2;}", "fn main() { |x: i32$0| x * 2;}"); |
64 | 63 | ||
65 | { | 64 | { |
66 | mark::check!(pipes_not_braces); | 65 | cov_mark::check!(pipes_not_braces); |
67 | do_check( | 66 | do_check( |
68 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", | 67 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", |
69 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", | 68 | "fn main() { match 92 { 1 | 2 |$0 3 => 92 } }", |
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index ddbaf22b7..03d71b380 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -5,7 +5,6 @@ use syntax::{ | |||
5 | algo::find_node_at_offset, | 5 | algo::find_node_at_offset, |
6 | ast::{self, AstNode}, | 6 | ast::{self, AstNode}, |
7 | }; | 7 | }; |
8 | use test_utils::mark; | ||
9 | 8 | ||
10 | use crate::NavigationTarget; | 9 | use crate::NavigationTarget; |
11 | 10 | ||
@@ -33,7 +32,7 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na | |||
33 | .item_list() | 32 | .item_list() |
34 | .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) | 33 | .map_or(false, |it| it.syntax().text_range().contains_inclusive(position.offset)) |
35 | { | 34 | { |
36 | mark::hit!(test_resolve_parent_module_on_module_decl); | 35 | cov_mark::hit!(test_resolve_parent_module_on_module_decl); |
37 | module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); | 36 | module = m.syntax().ancestors().skip(1).find_map(ast::Module::cast); |
38 | } | 37 | } |
39 | } | 38 | } |
@@ -64,7 +63,6 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
64 | #[cfg(test)] | 63 | #[cfg(test)] |
65 | mod tests { | 64 | mod tests { |
66 | use ide_db::base_db::FileRange; | 65 | use ide_db::base_db::FileRange; |
67 | use test_utils::mark; | ||
68 | 66 | ||
69 | use crate::fixture; | 67 | use crate::fixture; |
70 | 68 | ||
@@ -92,7 +90,7 @@ $0// empty | |||
92 | 90 | ||
93 | #[test] | 91 | #[test] |
94 | fn test_resolve_parent_module_on_module_decl() { | 92 | fn test_resolve_parent_module_on_module_decl() { |
95 | mark::check!(test_resolve_parent_module_on_module_decl); | 93 | cov_mark::check!(test_resolve_parent_module_on_module_decl); |
96 | check( | 94 | check( |
97 | r#" | 95 | r#" |
98 | //- /lib.rs | 96 | //- /lib.rs |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 1919639a3..05c73de88 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -14,7 +14,7 @@ use syntax::{ | |||
14 | ast::{self, NameOwner}, | 14 | ast::{self, NameOwner}, |
15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, | 15 | lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, |
16 | }; | 16 | }; |
17 | use test_utils::mark; | 17 | |
18 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
19 | 19 | ||
20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; | 20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; |
@@ -226,34 +226,36 @@ fn rename_reference( | |||
226 | | (IdentifierKind::Ident, _) | 226 | | (IdentifierKind::Ident, _) |
227 | if def_is_lbl_or_lt => | 227 | if def_is_lbl_or_lt => |
228 | { | 228 | { |
229 | mark::hit!(rename_not_a_lifetime_ident_ref); | 229 | cov_mark::hit!(rename_not_a_lifetime_ident_ref); |
230 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) | 230 | bail!("Invalid name `{}`: not a lifetime identifier", new_name) |
231 | } | 231 | } |
232 | (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), | 232 | (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime), |
233 | (IdentifierKind::Lifetime, _) => { | 233 | (IdentifierKind::Lifetime, _) => { |
234 | mark::hit!(rename_not_an_ident_ref); | 234 | cov_mark::hit!(rename_not_an_ident_ref); |
235 | bail!("Invalid name `{}`: not an identifier", new_name) | 235 | bail!("Invalid name `{}`: not an identifier", new_name) |
236 | } | 236 | } |
237 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { | 237 | (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { |
238 | // no-op | 238 | // no-op |
239 | mark::hit!(rename_self_to_self); | 239 | cov_mark::hit!(rename_self_to_self); |
240 | return Ok(SourceChange::default()); | 240 | return Ok(SourceChange::default()); |
241 | } | 241 | } |
242 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { | 242 | (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { |
243 | mark::hit!(rename_self_to_param); | 243 | cov_mark::hit!(rename_self_to_param); |
244 | return rename_self_to_param(sema, local, new_name, ident_kind); | 244 | return rename_self_to_param(sema, local, new_name, ident_kind); |
245 | } | 245 | } |
246 | (IdentifierKind::ToSelf, Definition::Local(local)) => { | 246 | (IdentifierKind::ToSelf, Definition::Local(local)) => { |
247 | mark::hit!(rename_to_self); | 247 | cov_mark::hit!(rename_to_self); |
248 | return rename_to_self(sema, local); | 248 | return rename_to_self(sema, local); |
249 | } | 249 | } |
250 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), | 250 | (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), |
251 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), | 251 | (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => { |
252 | cov_mark::hit!(rename_ident) | ||
253 | } | ||
252 | } | 254 | } |
253 | 255 | ||
254 | let usages = def.usages(sema).all(); | 256 | let usages = def.usages(sema).all(); |
255 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { | 257 | if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { |
256 | mark::hit!(rename_underscore_multiple); | 258 | cov_mark::hit!(rename_underscore_multiple); |
257 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | 259 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); |
258 | } | 260 | } |
259 | let mut source_change = SourceChange::default(); | 261 | let mut source_change = SourceChange::default(); |
@@ -444,7 +446,7 @@ fn source_edit_from_name_ref( | |||
444 | (Some(field_name), Some(init)) => { | 446 | (Some(field_name), Some(init)) => { |
445 | if field_name == *name_ref { | 447 | if field_name == *name_ref { |
446 | if init.text() == new_name { | 448 | if init.text() == new_name { |
447 | mark::hit!(test_rename_field_put_init_shorthand); | 449 | cov_mark::hit!(test_rename_field_put_init_shorthand); |
448 | // same names, we can use a shorthand here instead. | 450 | // same names, we can use a shorthand here instead. |
449 | // we do not want to erase attributes hence this range start | 451 | // we do not want to erase attributes hence this range start |
450 | let s = field_name.syntax().text_range().start(); | 452 | let s = field_name.syntax().text_range().start(); |
@@ -453,7 +455,7 @@ fn source_edit_from_name_ref( | |||
453 | } | 455 | } |
454 | } else if init == *name_ref { | 456 | } else if init == *name_ref { |
455 | if field_name.text() == new_name { | 457 | if field_name.text() == new_name { |
456 | mark::hit!(test_rename_local_put_init_shorthand); | 458 | cov_mark::hit!(test_rename_local_put_init_shorthand); |
457 | // same names, we can use a shorthand here instead. | 459 | // same names, we can use a shorthand here instead. |
458 | // we do not want to erase attributes hence this range start | 460 | // we do not want to erase attributes hence this range start |
459 | let s = field_name.syntax().text_range().start(); | 461 | let s = field_name.syntax().text_range().start(); |
@@ -467,12 +469,12 @@ fn source_edit_from_name_ref( | |||
467 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the | 469 | // FIXME: instead of splitting the shorthand, recursively trigger a rename of the |
468 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 | 470 | // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 |
469 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { | 471 | (None, Some(_)) if matches!(def, Definition::Field(_)) => { |
470 | mark::hit!(test_rename_field_in_field_shorthand); | 472 | cov_mark::hit!(test_rename_field_in_field_shorthand); |
471 | let s = name_ref.syntax().text_range().start(); | 473 | let s = name_ref.syntax().text_range().start(); |
472 | Some((TextRange::empty(s), format!("{}: ", new_name))) | 474 | Some((TextRange::empty(s), format!("{}: ", new_name))) |
473 | } | 475 | } |
474 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { | 476 | (None, Some(_)) if matches!(def, Definition::Local(_)) => { |
475 | mark::hit!(test_rename_local_in_field_shorthand); | 477 | cov_mark::hit!(test_rename_local_in_field_shorthand); |
476 | let s = name_ref.syntax().text_range().end(); | 478 | let s = name_ref.syntax().text_range().end(); |
477 | Some((TextRange::empty(s), format!(": {}", new_name))) | 479 | Some((TextRange::empty(s), format!(": {}", new_name))) |
478 | } | 480 | } |
@@ -486,7 +488,7 @@ fn source_edit_from_name_ref( | |||
486 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { | 488 | (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { |
487 | // field name is being renamed | 489 | // field name is being renamed |
488 | if pat.name().map_or(false, |it| it.text() == new_name) { | 490 | if pat.name().map_or(false, |it| it.text() == new_name) { |
489 | mark::hit!(test_rename_field_put_init_shorthand_pat); | 491 | cov_mark::hit!(test_rename_field_put_init_shorthand_pat); |
490 | // same names, we can use a shorthand here instead/ | 492 | // same names, we can use a shorthand here instead/ |
491 | // we do not want to erase attributes hence this range start | 493 | // we do not want to erase attributes hence this range start |
492 | let s = field_name.syntax().text_range().start(); | 494 | let s = field_name.syntax().text_range().start(); |
@@ -538,7 +540,7 @@ fn source_edit_from_def( | |||
538 | mod tests { | 540 | mod tests { |
539 | use expect_test::{expect, Expect}; | 541 | use expect_test::{expect, Expect}; |
540 | use stdx::trim_indent; | 542 | use stdx::trim_indent; |
541 | use test_utils::{assert_eq_text, mark}; | 543 | use test_utils::assert_eq_text; |
542 | use text_edit::TextEdit; | 544 | use text_edit::TextEdit; |
543 | 545 | ||
544 | use crate::{fixture, FileId}; | 546 | use crate::{fixture, FileId}; |
@@ -627,7 +629,7 @@ mod tests { | |||
627 | 629 | ||
628 | #[test] | 630 | #[test] |
629 | fn test_rename_to_invalid_identifier_lifetime() { | 631 | fn test_rename_to_invalid_identifier_lifetime() { |
630 | mark::check!(rename_not_an_ident_ref); | 632 | cov_mark::check!(rename_not_an_ident_ref); |
631 | check( | 633 | check( |
632 | "'foo", | 634 | "'foo", |
633 | r#"fn main() { let i$0 = 1; }"#, | 635 | r#"fn main() { let i$0 = 1; }"#, |
@@ -637,7 +639,7 @@ mod tests { | |||
637 | 639 | ||
638 | #[test] | 640 | #[test] |
639 | fn test_rename_to_invalid_identifier_lifetime2() { | 641 | fn test_rename_to_invalid_identifier_lifetime2() { |
640 | mark::check!(rename_not_a_lifetime_ident_ref); | 642 | cov_mark::check!(rename_not_a_lifetime_ident_ref); |
641 | check( | 643 | check( |
642 | "foo", | 644 | "foo", |
643 | r#"fn main<'a>(_: &'a$0 ()) {}"#, | 645 | r#"fn main<'a>(_: &'a$0 ()) {}"#, |
@@ -647,7 +649,7 @@ mod tests { | |||
647 | 649 | ||
648 | #[test] | 650 | #[test] |
649 | fn test_rename_to_underscore_invalid() { | 651 | fn test_rename_to_underscore_invalid() { |
650 | mark::check!(rename_underscore_multiple); | 652 | cov_mark::check!(rename_underscore_multiple); |
651 | check( | 653 | check( |
652 | "_", | 654 | "_", |
653 | r#"fn main(foo$0: ()) {foo;}"#, | 655 | r#"fn main(foo$0: ()) {foo;}"#, |
@@ -666,7 +668,7 @@ mod tests { | |||
666 | 668 | ||
667 | #[test] | 669 | #[test] |
668 | fn test_rename_for_local() { | 670 | fn test_rename_for_local() { |
669 | mark::check!(rename_ident); | 671 | cov_mark::check!(rename_ident); |
670 | check( | 672 | check( |
671 | "k", | 673 | "k", |
672 | r#" | 674 | r#" |
@@ -829,7 +831,7 @@ impl Foo { | |||
829 | 831 | ||
830 | #[test] | 832 | #[test] |
831 | fn test_rename_field_in_field_shorthand() { | 833 | fn test_rename_field_in_field_shorthand() { |
832 | mark::check!(test_rename_field_in_field_shorthand); | 834 | cov_mark::check!(test_rename_field_in_field_shorthand); |
833 | check( | 835 | check( |
834 | "j", | 836 | "j", |
835 | r#" | 837 | r#" |
@@ -855,7 +857,7 @@ impl Foo { | |||
855 | 857 | ||
856 | #[test] | 858 | #[test] |
857 | fn test_rename_local_in_field_shorthand() { | 859 | fn test_rename_local_in_field_shorthand() { |
858 | mark::check!(test_rename_local_in_field_shorthand); | 860 | cov_mark::check!(test_rename_local_in_field_shorthand); |
859 | check( | 861 | check( |
860 | "j", | 862 | "j", |
861 | r#" | 863 | r#" |
@@ -1261,7 +1263,7 @@ fn foo(f: foo::Foo) { | |||
1261 | 1263 | ||
1262 | #[test] | 1264 | #[test] |
1263 | fn test_parameter_to_self() { | 1265 | fn test_parameter_to_self() { |
1264 | mark::check!(rename_to_self); | 1266 | cov_mark::check!(rename_to_self); |
1265 | check( | 1267 | check( |
1266 | "self", | 1268 | "self", |
1267 | r#" | 1269 | r#" |
@@ -1401,7 +1403,7 @@ impl Foo { | |||
1401 | 1403 | ||
1402 | #[test] | 1404 | #[test] |
1403 | fn test_owned_self_to_parameter() { | 1405 | fn test_owned_self_to_parameter() { |
1404 | mark::check!(rename_self_to_param); | 1406 | cov_mark::check!(rename_self_to_param); |
1405 | check( | 1407 | check( |
1406 | "foo", | 1408 | "foo", |
1407 | r#" | 1409 | r#" |
@@ -1454,7 +1456,7 @@ impl Foo { | |||
1454 | 1456 | ||
1455 | #[test] | 1457 | #[test] |
1456 | fn test_rename_field_put_init_shorthand() { | 1458 | fn test_rename_field_put_init_shorthand() { |
1457 | mark::check!(test_rename_field_put_init_shorthand); | 1459 | cov_mark::check!(test_rename_field_put_init_shorthand); |
1458 | check( | 1460 | check( |
1459 | "bar", | 1461 | "bar", |
1460 | r#" | 1462 | r#" |
@@ -1476,7 +1478,7 @@ fn foo(bar: i32) -> Foo { | |||
1476 | 1478 | ||
1477 | #[test] | 1479 | #[test] |
1478 | fn test_rename_local_put_init_shorthand() { | 1480 | fn test_rename_local_put_init_shorthand() { |
1479 | mark::check!(test_rename_local_put_init_shorthand); | 1481 | cov_mark::check!(test_rename_local_put_init_shorthand); |
1480 | check( | 1482 | check( |
1481 | "i", | 1483 | "i", |
1482 | r#" | 1484 | r#" |
@@ -1498,7 +1500,7 @@ fn foo(i: i32) -> Foo { | |||
1498 | 1500 | ||
1499 | #[test] | 1501 | #[test] |
1500 | fn test_struct_field_pat_into_shorthand() { | 1502 | fn test_struct_field_pat_into_shorthand() { |
1501 | mark::check!(test_rename_field_put_init_shorthand_pat); | 1503 | cov_mark::check!(test_rename_field_put_init_shorthand_pat); |
1502 | check( | 1504 | check( |
1503 | "baz", | 1505 | "baz", |
1504 | r#" | 1506 | r#" |
@@ -1610,7 +1612,7 @@ fn foo(foo: Foo) { | |||
1610 | 1612 | ||
1611 | #[test] | 1613 | #[test] |
1612 | fn test_rename_lifetimes() { | 1614 | fn test_rename_lifetimes() { |
1613 | mark::check!(rename_lifetime); | 1615 | cov_mark::check!(rename_lifetime); |
1614 | check( | 1616 | check( |
1615 | "'yeeee", | 1617 | "'yeeee", |
1616 | r#" | 1618 | r#" |
@@ -1698,7 +1700,7 @@ fn foo<'a>() -> &'a () { | |||
1698 | 1700 | ||
1699 | #[test] | 1701 | #[test] |
1700 | fn test_self_to_self() { | 1702 | fn test_self_to_self() { |
1701 | mark::check!(rename_self_to_self); | 1703 | cov_mark::check!(rename_self_to_self); |
1702 | check( | 1704 | check( |
1703 | "self", | 1705 | "self", |
1704 | r#" | 1706 | r#" |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 1e7baed20..280565563 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -9,7 +9,6 @@ use syntax::{ | |||
9 | ast::{self, AstNode, AttrsOwner}, | 9 | ast::{self, AstNode, AttrsOwner}, |
10 | match_ast, SyntaxNode, | 10 | match_ast, SyntaxNode, |
11 | }; | 11 | }; |
12 | use test_utils::mark; | ||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | display::{ToNav, TryToNav}, | 14 | display::{ToNav, TryToNav}, |
@@ -130,7 +129,9 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module | |||
130 | if let hir::ModuleDef::Module(submodule) = def { | 129 | if let hir::ModuleDef::Module(submodule) = def { |
131 | match submodule.definition_source(sema.db).value { | 130 | match submodule.definition_source(sema.db).value { |
132 | hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), | 131 | hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), |
133 | hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules), | 132 | hir::ModuleSource::SourceFile(_) => { |
133 | cov_mark::hit!(dont_recurse_in_outline_submodules) | ||
134 | } | ||
134 | hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable | 135 | hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable |
135 | } | 136 | } |
136 | } | 137 | } |
@@ -189,7 +190,7 @@ pub(crate) fn doc_owner_to_def( | |||
189 | ) -> Option<Definition> { | 190 | ) -> Option<Definition> { |
190 | let res: hir::ModuleDef = match_ast! { | 191 | let res: hir::ModuleDef = match_ast! { |
191 | match item { | 192 | match item { |
192 | ast::SourceFile(it) => sema.scope(&item).module()?.into(), | 193 | ast::SourceFile(_it) => sema.scope(&item).module()?.into(), |
193 | ast::Fn(it) => sema.to_def(&it)?.into(), | 194 | ast::Fn(it) => sema.to_def(&it)?.into(), |
194 | ast::Struct(it) => sema.to_def(&it)?.into(), | 195 | ast::Struct(it) => sema.to_def(&it)?.into(), |
195 | ast::Enum(it) => sema.to_def(&it)?.into(), | 196 | ast::Enum(it) => sema.to_def(&it)?.into(), |
@@ -328,7 +329,6 @@ fn has_test_function_or_multiple_test_submodules( | |||
328 | #[cfg(test)] | 329 | #[cfg(test)] |
329 | mod tests { | 330 | mod tests { |
330 | use expect_test::{expect, Expect}; | 331 | use expect_test::{expect, Expect}; |
331 | use test_utils::mark; | ||
332 | 332 | ||
333 | use crate::fixture; | 333 | use crate::fixture; |
334 | 334 | ||
@@ -1056,7 +1056,7 @@ mod tests { | |||
1056 | 1056 | ||
1057 | #[test] | 1057 | #[test] |
1058 | fn dont_recurse_in_outline_submodules() { | 1058 | fn dont_recurse_in_outline_submodules() { |
1059 | mark::check!(dont_recurse_in_outline_submodules); | 1059 | cov_mark::check!(dont_recurse_in_outline_submodules); |
1060 | check( | 1060 | check( |
1061 | r#" | 1061 | r#" |
1062 | //- /lib.rs | 1062 | //- /lib.rs |
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index 24fcbb584..b0cfdd8b7 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs | |||
@@ -330,10 +330,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight { | |||
330 | HlTag::Symbol(SymbolKind::Local) | 330 | HlTag::Symbol(SymbolKind::Local) |
331 | }; | 331 | }; |
332 | let mut h = Highlight::new(tag); | 332 | let mut h = Highlight::new(tag); |
333 | if local.is_mut(db) || local.ty(db).is_mutable_reference() { | 333 | let ty = local.ty(db); |
334 | if local.is_mut(db) || ty.is_mutable_reference() { | ||
334 | h |= HlMod::Mutable; | 335 | h |= HlMod::Mutable; |
335 | } | 336 | } |
336 | if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { | 337 | if ty.as_callable(db).is_some() || ty.impls_fnonce(db) { |
337 | h |= HlMod::Callable; | 338 | h |= HlMod::Callable; |
338 | } | 339 | } |
339 | return h; | 340 | return h; |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index 63cd51b69..978c479de 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -9,7 +9,7 @@ use syntax::{ | |||
9 | SyntaxKind::*, | 9 | SyntaxKind::*, |
10 | SyntaxToken, TextRange, TextSize, TokenAtOffset, | 10 | SyntaxToken, TextRange, TextSize, TokenAtOffset, |
11 | }; | 11 | }; |
12 | use test_utils::mark; | 12 | |
13 | use text_edit::TextEdit; | 13 | use text_edit::TextEdit; |
14 | 14 | ||
15 | // Feature: On Enter | 15 | // Feature: On Enter |
@@ -55,7 +55,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text | |||
55 | // Continuing single-line non-doc comments (like this one :) ) is annoying | 55 | // Continuing single-line non-doc comments (like this one :) ) is annoying |
56 | if prefix == "//" && comment_range.end() == position.offset { | 56 | if prefix == "//" && comment_range.end() == position.offset { |
57 | if comment.text().ends_with(' ') { | 57 | if comment.text().ends_with(' ') { |
58 | mark::hit!(continues_end_of_line_comment_with_space); | 58 | cov_mark::hit!(continues_end_of_line_comment_with_space); |
59 | remove_trailing_whitespace = true; | 59 | remove_trailing_whitespace = true; |
60 | } else if !followed_by_comment(&comment) { | 60 | } else if !followed_by_comment(&comment) { |
61 | return None; | 61 | return None; |
@@ -109,7 +109,7 @@ fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option<SmolStr> { | |||
109 | #[cfg(test)] | 109 | #[cfg(test)] |
110 | mod tests { | 110 | mod tests { |
111 | use stdx::trim_indent; | 111 | use stdx::trim_indent; |
112 | use test_utils::{assert_eq_text, mark}; | 112 | use test_utils::assert_eq_text; |
113 | 113 | ||
114 | use crate::fixture; | 114 | use crate::fixture; |
115 | 115 | ||
@@ -238,7 +238,7 @@ fn main() { | |||
238 | 238 | ||
239 | #[test] | 239 | #[test] |
240 | fn continues_end_of_line_comment_with_space() { | 240 | fn continues_end_of_line_comment_with_space() { |
241 | mark::check!(continues_end_of_line_comment_with_space); | 241 | cov_mark::check!(continues_end_of_line_comment_with_space); |
242 | do_check( | 242 | do_check( |
243 | r#" | 243 | r#" |
244 | fn main() { | 244 | fn main() { |
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml index a34bdd6c3..dd9aa27c6 100644 --- a/crates/ide_assists/Cargo.toml +++ b/crates/ide_assists/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
14 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
15 | either = "1.6.1" | 16 | either = "1.6.1" |
@@ -20,7 +21,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
20 | profile = { path = "../profile", version = "0.0.0" } | 21 | profile = { path = "../profile", version = "0.0.0" } |
21 | ide_db = { path = "../ide_db", version = "0.0.0" } | 22 | ide_db = { path = "../ide_db", version = "0.0.0" } |
22 | hir = { path = "../hir", version = "0.0.0" } | 23 | hir = { path = "../hir", version = "0.0.0" } |
23 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
24 | 24 | ||
25 | [dev-dependencies] | 25 | [dev-dependencies] |
26 | test_utils = { path = "../test_utils" } | ||
26 | expect-test = "1.1" | 27 | expect-test = "1.1" |
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs index a08b55ebb..3b6efbab4 100644 --- a/crates/ide_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use ide_db::defs::{Definition, NameRefClass}; | 1 | use ide_db::defs::{Definition, NameRefClass}; |
2 | use syntax::{ast, AstNode, SyntaxKind, T}; | 2 | use syntax::{ast, AstNode, SyntaxKind, T}; |
3 | use test_utils::mark; | ||
4 | 3 | ||
5 | use crate::{ | 4 | use crate::{ |
6 | assist_context::{AssistContext, Assists}, | 5 | assist_context::{AssistContext, Assists}, |
@@ -30,13 +29,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
30 | if arg_list.args().count() > 0 { | 29 | if arg_list.args().count() > 0 { |
31 | return None; | 30 | return None; |
32 | } | 31 | } |
33 | mark::hit!(add_turbo_fish_after_call); | 32 | cov_mark::hit!(add_turbo_fish_after_call); |
34 | mark::hit!(add_type_ascription_after_call); | 33 | cov_mark::hit!(add_type_ascription_after_call); |
35 | arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) | 34 | arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT) |
36 | })?; | 35 | })?; |
37 | let next_token = ident.next_token()?; | 36 | let next_token = ident.next_token()?; |
38 | if next_token.kind() == T![::] { | 37 | if next_token.kind() == T![::] { |
39 | mark::hit!(add_turbo_fish_one_fish_is_enough); | 38 | cov_mark::hit!(add_turbo_fish_one_fish_is_enough); |
40 | return None; | 39 | return None; |
41 | } | 40 | } |
42 | let name_ref = ast::NameRef::cast(ident.parent())?; | 41 | let name_ref = ast::NameRef::cast(ident.parent())?; |
@@ -50,7 +49,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
50 | }; | 49 | }; |
51 | let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); | 50 | let generics = hir::GenericDef::Function(fun).params(ctx.sema.db); |
52 | if generics.is_empty() { | 51 | if generics.is_empty() { |
53 | mark::hit!(add_turbo_fish_non_generic); | 52 | cov_mark::hit!(add_turbo_fish_non_generic); |
54 | return None; | 53 | return None; |
55 | } | 54 | } |
56 | 55 | ||
@@ -67,7 +66,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
67 | }, | 66 | }, |
68 | )? | 67 | )? |
69 | } else { | 68 | } else { |
70 | mark::hit!(add_type_ascription_already_typed); | 69 | cov_mark::hit!(add_type_ascription_already_typed); |
71 | } | 70 | } |
72 | } | 71 | } |
73 | 72 | ||
@@ -87,7 +86,6 @@ mod tests { | |||
87 | use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; | 86 | use crate::tests::{check_assist, check_assist_by_label, check_assist_not_applicable}; |
88 | 87 | ||
89 | use super::*; | 88 | use super::*; |
90 | use test_utils::mark; | ||
91 | 89 | ||
92 | #[test] | 90 | #[test] |
93 | fn add_turbo_fish_function() { | 91 | fn add_turbo_fish_function() { |
@@ -110,7 +108,7 @@ fn main() { | |||
110 | 108 | ||
111 | #[test] | 109 | #[test] |
112 | fn add_turbo_fish_after_call() { | 110 | fn add_turbo_fish_after_call() { |
113 | mark::check!(add_turbo_fish_after_call); | 111 | cov_mark::check!(add_turbo_fish_after_call); |
114 | check_assist( | 112 | check_assist( |
115 | add_turbo_fish, | 113 | add_turbo_fish, |
116 | r#" | 114 | r#" |
@@ -155,7 +153,7 @@ fn main() { | |||
155 | 153 | ||
156 | #[test] | 154 | #[test] |
157 | fn add_turbo_fish_one_fish_is_enough() { | 155 | fn add_turbo_fish_one_fish_is_enough() { |
158 | mark::check!(add_turbo_fish_one_fish_is_enough); | 156 | cov_mark::check!(add_turbo_fish_one_fish_is_enough); |
159 | check_assist_not_applicable( | 157 | check_assist_not_applicable( |
160 | add_turbo_fish, | 158 | add_turbo_fish, |
161 | r#" | 159 | r#" |
@@ -169,7 +167,7 @@ fn main() { | |||
169 | 167 | ||
170 | #[test] | 168 | #[test] |
171 | fn add_turbo_fish_non_generic() { | 169 | fn add_turbo_fish_non_generic() { |
172 | mark::check!(add_turbo_fish_non_generic); | 170 | cov_mark::check!(add_turbo_fish_non_generic); |
173 | check_assist_not_applicable( | 171 | check_assist_not_applicable( |
174 | add_turbo_fish, | 172 | add_turbo_fish, |
175 | r#" | 173 | r#" |
@@ -203,7 +201,7 @@ fn main() { | |||
203 | 201 | ||
204 | #[test] | 202 | #[test] |
205 | fn add_type_ascription_after_call() { | 203 | fn add_type_ascription_after_call() { |
206 | mark::check!(add_type_ascription_after_call); | 204 | cov_mark::check!(add_type_ascription_after_call); |
207 | check_assist_by_label( | 205 | check_assist_by_label( |
208 | add_turbo_fish, | 206 | add_turbo_fish, |
209 | r#" | 207 | r#" |
@@ -250,7 +248,7 @@ fn main() { | |||
250 | 248 | ||
251 | #[test] | 249 | #[test] |
252 | fn add_type_ascription_already_typed() { | 250 | fn add_type_ascription_already_typed() { |
253 | mark::check!(add_type_ascription_already_typed); | 251 | cov_mark::check!(add_type_ascription_already_typed); |
254 | check_assist( | 252 | check_assist( |
255 | add_turbo_fish, | 253 | add_turbo_fish, |
256 | r#" | 254 | r#" |
diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs index 128b1eb56..a1c339603 100644 --- a/crates/ide_assists/src/handlers/apply_demorgan.rs +++ b/crates/ide_assists/src/handlers/apply_demorgan.rs | |||
@@ -1,5 +1,4 @@ | |||
1 | use syntax::ast::{self, AstNode}; | 1 | use syntax::ast::{self, AstNode}; |
2 | use test_utils::mark; | ||
3 | 2 | ||
4 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; | 3 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists}; |
5 | 4 | ||
@@ -64,10 +63,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<( | |||
64 | edit.replace(lhs_range, not_lhs.syntax().text()); | 63 | edit.replace(lhs_range, not_lhs.syntax().text()); |
65 | edit.replace(rhs_range, not_rhs.syntax().text()); | 64 | edit.replace(rhs_range, not_rhs.syntax().text()); |
66 | if let Some(neg_expr) = neg_expr { | 65 | if let Some(neg_expr) = neg_expr { |
67 | mark::hit!(demorgan_double_negation); | 66 | cov_mark::hit!(demorgan_double_negation); |
68 | edit.replace(neg_expr.op_token().unwrap().text_range(), ""); | 67 | edit.replace(neg_expr.op_token().unwrap().text_range(), ""); |
69 | } else { | 68 | } else { |
70 | mark::hit!(demorgan_double_parens); | 69 | cov_mark::hit!(demorgan_double_parens); |
71 | edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); | 70 | edit.replace(paren_expr.l_paren_token().unwrap().text_range(), "!("); |
72 | } | 71 | } |
73 | } else { | 72 | } else { |
@@ -90,7 +89,6 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> { | |||
90 | #[cfg(test)] | 89 | #[cfg(test)] |
91 | mod tests { | 90 | mod tests { |
92 | use ide_db::helpers::FamousDefs; | 91 | use ide_db::helpers::FamousDefs; |
93 | use test_utils::mark; | ||
94 | 92 | ||
95 | use super::*; | 93 | use super::*; |
96 | 94 | ||
@@ -188,13 +186,13 @@ fn f() { | |||
188 | 186 | ||
189 | #[test] | 187 | #[test] |
190 | fn demorgan_doesnt_double_negation() { | 188 | fn demorgan_doesnt_double_negation() { |
191 | mark::check!(demorgan_double_negation); | 189 | cov_mark::check!(demorgan_double_negation); |
192 | check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") | 190 | check_assist(apply_demorgan, "fn f() { !(x ||$0 x) }", "fn f() { (!x && !x) }") |
193 | } | 191 | } |
194 | 192 | ||
195 | #[test] | 193 | #[test] |
196 | fn demorgan_doesnt_double_parens() { | 194 | fn demorgan_doesnt_double_parens() { |
197 | mark::check!(demorgan_double_parens); | 195 | cov_mark::check!(demorgan_double_parens); |
198 | check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") | 196 | check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }") |
199 | } | 197 | } |
200 | } | 198 | } |
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs index dc38f90e9..7caee8df0 100644 --- a/crates/ide_assists/src/handlers/auto_import.rs +++ b/crates/ide_assists/src/handlers/auto_import.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use ide_db::helpers::{ | 1 | use ide_db::helpers::{ |
2 | import_assets::{ImportAssets, ImportCandidate}, | 2 | import_assets::{ImportAssets, ImportCandidate}, |
3 | insert_use::{insert_use, ImportScope}, | 3 | insert_use::{insert_use, ImportScope}, |
4 | mod_path_to_ast, | 4 | item_name, mod_path_to_ast, |
5 | }; | 5 | }; |
6 | use syntax::{ast, AstNode, SyntaxNode}; | 6 | use syntax::{ast, AstNode, SyntaxNode}; |
7 | 7 | ||
@@ -92,15 +92,19 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
92 | let range = ctx.sema.original_range(&syntax_under_caret).range; | 92 | let range = ctx.sema.original_range(&syntax_under_caret).range; |
93 | let group = import_group_message(import_assets.import_candidate()); | 93 | let group = import_group_message(import_assets.import_candidate()); |
94 | let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; | 94 | let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; |
95 | for (import, _) in proposed_imports { | 95 | for import in proposed_imports { |
96 | let name = match item_name(ctx.db(), import.original_item) { | ||
97 | Some(name) => name, | ||
98 | None => continue, | ||
99 | }; | ||
96 | acc.add_group( | 100 | acc.add_group( |
97 | &group, | 101 | &group, |
98 | AssistId("auto_import", AssistKind::QuickFix), | 102 | AssistId("auto_import", AssistKind::QuickFix), |
99 | format!("Import `{}`", &import), | 103 | format!("Import `{}`", name), |
100 | range, | 104 | range, |
101 | |builder| { | 105 | |builder| { |
102 | let rewriter = | 106 | let rewriter = |
103 | insert_use(&scope, mod_path_to_ast(&import), ctx.config.insert_use.merge); | 107 | insert_use(&scope, mod_path_to_ast(&import.import_path), ctx.config.insert_use); |
104 | builder.rewrite(rewriter); | 108 | builder.rewrite(rewriter); |
105 | }, | 109 | }, |
106 | ); | 110 | ); |
@@ -126,10 +130,10 @@ fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { | |||
126 | let name = match import_candidate { | 130 | let name = match import_candidate { |
127 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), | 131 | ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), |
128 | ImportCandidate::TraitAssocItem(candidate) => { | 132 | ImportCandidate::TraitAssocItem(candidate) => { |
129 | format!("Import a trait for item {}", candidate.name.text()) | 133 | format!("Import a trait for item {}", candidate.assoc_item_name.text()) |
130 | } | 134 | } |
131 | ImportCandidate::TraitMethod(candidate) => { | 135 | ImportCandidate::TraitMethod(candidate) => { |
132 | format!("Import a trait for method {}", candidate.name.text()) | 136 | format!("Import a trait for method {}", candidate.assoc_item_name.text()) |
133 | } | 137 | } |
134 | }; | 138 | }; |
135 | GroupLabel(name) | 139 | GroupLabel(name) |
@@ -222,41 +226,6 @@ mod tests { | |||
222 | } | 226 | } |
223 | 227 | ||
224 | #[test] | 228 | #[test] |
225 | fn auto_imports_are_merged() { | ||
226 | check_assist( | ||
227 | auto_import, | ||
228 | r" | ||
229 | use PubMod::PubStruct1; | ||
230 | |||
231 | struct Test { | ||
232 | test: Pub$0Struct2<u8>, | ||
233 | } | ||
234 | |||
235 | pub mod PubMod { | ||
236 | pub struct PubStruct1; | ||
237 | pub struct PubStruct2<T> { | ||
238 | _t: T, | ||
239 | } | ||
240 | } | ||
241 | ", | ||
242 | r" | ||
243 | use PubMod::{PubStruct1, PubStruct2}; | ||
244 | |||
245 | struct Test { | ||
246 | test: PubStruct2<u8>, | ||
247 | } | ||
248 | |||
249 | pub mod PubMod { | ||
250 | pub struct PubStruct1; | ||
251 | pub struct PubStruct2<T> { | ||
252 | _t: T, | ||
253 | } | ||
254 | } | ||
255 | ", | ||
256 | ); | ||
257 | } | ||
258 | |||
259 | #[test] | ||
260 | fn applicable_when_found_multiple_imports() { | 229 | fn applicable_when_found_multiple_imports() { |
261 | check_assist( | 230 | check_assist( |
262 | auto_import, | 231 | auto_import, |
diff --git a/crates/ide_assists/src/handlers/change_visibility.rs b/crates/ide_assists/src/handlers/change_visibility.rs index ac8c44124..ec99a5505 100644 --- a/crates/ide_assists/src/handlers/change_visibility.rs +++ b/crates/ide_assists/src/handlers/change_visibility.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, | 4 | SyntaxKind::{CONST, ENUM, FN, MODULE, STATIC, STRUCT, TRAIT, TYPE_ALIAS, VISIBILITY}, |
5 | T, | 5 | T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
@@ -56,7 +55,7 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
56 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { | 55 | } else if let Some(field_name) = ctx.find_node_at_offset::<ast::Name>() { |
57 | let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; | 56 | let field = field_name.syntax().ancestors().find_map(ast::RecordField::cast)?; |
58 | if field.name()? != field_name { | 57 | if field.name()? != field_name { |
59 | mark::hit!(change_visibility_field_false_positive); | 58 | cov_mark::hit!(change_visibility_field_false_positive); |
60 | return None; | 59 | return None; |
61 | } | 60 | } |
62 | if field.visibility().is_some() { | 61 | if field.visibility().is_some() { |
@@ -110,8 +109,6 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { | |||
110 | 109 | ||
111 | #[cfg(test)] | 110 | #[cfg(test)] |
112 | mod tests { | 111 | mod tests { |
113 | use test_utils::mark; | ||
114 | |||
115 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 112 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
116 | 113 | ||
117 | use super::*; | 114 | use super::*; |
@@ -139,7 +136,7 @@ mod tests { | |||
139 | 136 | ||
140 | #[test] | 137 | #[test] |
141 | fn change_visibility_field_false_positive() { | 138 | fn change_visibility_field_false_positive() { |
142 | mark::check!(change_visibility_field_false_positive); | 139 | cov_mark::check!(change_visibility_field_false_positive); |
143 | check_assist_not_applicable( | 140 | check_assist_not_applicable( |
144 | change_visibility, | 141 | change_visibility, |
145 | r"struct S { field: [(); { let $0x = ();}] }", | 142 | r"struct S { field: [(); { let $0x = ();}] }", |
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs index 9f34cc725..dd4501709 100644 --- a/crates/ide_assists/src/handlers/extract_function.rs +++ b/crates/ide_assists/src/handlers/extract_function.rs | |||
@@ -20,7 +20,6 @@ use syntax::{ | |||
20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, | 20 | SyntaxKind::{self, BLOCK_EXPR, BREAK_EXPR, COMMENT, PATH_EXPR, RETURN_EXPR}, |
21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, | 21 | SyntaxNode, SyntaxToken, TextRange, TextSize, TokenAtOffset, WalkEvent, T, |
22 | }; | 22 | }; |
23 | use test_utils::mark; | ||
24 | 23 | ||
25 | use crate::{ | 24 | use crate::{ |
26 | assist_context::{AssistContext, Assists}, | 25 | assist_context::{AssistContext, Assists}, |
@@ -59,7 +58,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
59 | 58 | ||
60 | let node = ctx.covering_element(); | 59 | let node = ctx.covering_element(); |
61 | if node.kind() == COMMENT { | 60 | if node.kind() == COMMENT { |
62 | mark::hit!(extract_function_in_comment_is_not_applicable); | 61 | cov_mark::hit!(extract_function_in_comment_is_not_applicable); |
63 | return None; | 62 | return None; |
64 | } | 63 | } |
65 | 64 | ||
@@ -112,7 +111,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
112 | 111 | ||
113 | let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); | 112 | let fn_def = format_function(ctx, module, &fun, old_indent, new_indent); |
114 | let insert_offset = insert_after.text_range().end(); | 113 | let insert_offset = insert_after.text_range().end(); |
115 | builder.insert(insert_offset, fn_def); | 114 | match ctx.config.snippet_cap { |
115 | Some(cap) => builder.insert_snippet(cap, insert_offset, fn_def), | ||
116 | None => builder.insert(insert_offset, fn_def), | ||
117 | } | ||
116 | }, | 118 | }, |
117 | ) | 119 | ) |
118 | } | 120 | } |
@@ -194,14 +196,14 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con | |||
194 | if let Some(kind) = expr_err_kind(&expr, ctx) { | 196 | if let Some(kind) = expr_err_kind(&expr, ctx) { |
195 | Some(FlowKind::TryReturn { expr, kind }) | 197 | Some(FlowKind::TryReturn { expr, kind }) |
196 | } else { | 198 | } else { |
197 | mark::hit!(external_control_flow_try_and_return_non_err); | 199 | cov_mark::hit!(external_control_flow_try_and_return_non_err); |
198 | return None; | 200 | return None; |
199 | } | 201 | } |
200 | } | 202 | } |
201 | None => return None, | 203 | None => return None, |
202 | }, | 204 | }, |
203 | (Some(_), _, _, _) => { | 205 | (Some(_), _, _, _) => { |
204 | mark::hit!(external_control_flow_try_and_bc); | 206 | cov_mark::hit!(external_control_flow_try_and_bc); |
205 | return None; | 207 | return None; |
206 | } | 208 | } |
207 | (None, Some(r), None, None) => match r.expr() { | 209 | (None, Some(r), None, None) => match r.expr() { |
@@ -209,11 +211,11 @@ fn external_control_flow(ctx: &AssistContext, body: &FunctionBody) -> Option<Con | |||
209 | None => Some(FlowKind::Return), | 211 | None => Some(FlowKind::Return), |
210 | }, | 212 | }, |
211 | (None, Some(_), _, _) => { | 213 | (None, Some(_), _, _) => { |
212 | mark::hit!(external_control_flow_return_and_bc); | 214 | cov_mark::hit!(external_control_flow_return_and_bc); |
213 | return None; | 215 | return None; |
214 | } | 216 | } |
215 | (None, None, Some(_), Some(_)) => { | 217 | (None, None, Some(_), Some(_)) => { |
216 | mark::hit!(external_control_flow_break_and_continue); | 218 | cov_mark::hit!(external_control_flow_break_and_continue); |
217 | return None; | 219 | return None; |
218 | } | 220 | } |
219 | (None, None, Some(b), None) => match b.expr() { | 221 | (None, None, Some(b), None) => match b.expr() { |
@@ -1079,7 +1081,10 @@ fn format_function( | |||
1079 | let params = make_param_list(ctx, module, fun); | 1081 | let params = make_param_list(ctx, module, fun); |
1080 | let ret_ty = make_ret_ty(ctx, module, fun); | 1082 | let ret_ty = make_ret_ty(ctx, module, fun); |
1081 | let body = make_body(ctx, old_indent, new_indent, fun); | 1083 | let body = make_body(ctx, old_indent, new_indent, fun); |
1082 | format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params); | 1084 | match ctx.config.snippet_cap { |
1085 | Some(_) => format_to!(fn_def, "\n\n{}fn $0{}{}", new_indent, fun.name, params), | ||
1086 | None => format_to!(fn_def, "\n\n{}fn {}{}", new_indent, fun.name, params), | ||
1087 | } | ||
1083 | if let Some(ret_ty) = ret_ty { | 1088 | if let Some(ret_ty) = ret_ty { |
1084 | format_to!(fn_def, " {}", ret_ty); | 1089 | format_to!(fn_def, " {}", ret_ty); |
1085 | } | 1090 | } |
@@ -1831,7 +1836,7 @@ fn $0fun_name(n: u32) -> u32 { | |||
1831 | 1836 | ||
1832 | #[test] | 1837 | #[test] |
1833 | fn in_comment_is_not_applicable() { | 1838 | fn in_comment_is_not_applicable() { |
1834 | mark::check!(extract_function_in_comment_is_not_applicable); | 1839 | cov_mark::check!(extract_function_in_comment_is_not_applicable); |
1835 | check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); | 1840 | check_assist_not_applicable(extract_function, r"fn main() { 1 + /* $0comment$0 */ 1; }"); |
1836 | } | 1841 | } |
1837 | 1842 | ||
@@ -2816,7 +2821,7 @@ fn $0fun_name(n: i32) -> Result<i32, i64> { | |||
2816 | 2821 | ||
2817 | #[test] | 2822 | #[test] |
2818 | fn break_and_continue() { | 2823 | fn break_and_continue() { |
2819 | mark::check!(external_control_flow_break_and_continue); | 2824 | cov_mark::check!(external_control_flow_break_and_continue); |
2820 | check_assist_not_applicable( | 2825 | check_assist_not_applicable( |
2821 | extract_function, | 2826 | extract_function, |
2822 | r##" | 2827 | r##" |
@@ -2836,7 +2841,7 @@ fn foo() { | |||
2836 | 2841 | ||
2837 | #[test] | 2842 | #[test] |
2838 | fn return_and_break() { | 2843 | fn return_and_break() { |
2839 | mark::check!(external_control_flow_return_and_bc); | 2844 | cov_mark::check!(external_control_flow_return_and_bc); |
2840 | check_assist_not_applicable( | 2845 | check_assist_not_applicable( |
2841 | extract_function, | 2846 | extract_function, |
2842 | r##" | 2847 | r##" |
@@ -3335,7 +3340,7 @@ fn $0fun_name() -> Result<i32, i64> { | |||
3335 | 3340 | ||
3336 | #[test] | 3341 | #[test] |
3337 | fn try_and_break() { | 3342 | fn try_and_break() { |
3338 | mark::check!(external_control_flow_try_and_bc); | 3343 | cov_mark::check!(external_control_flow_try_and_bc); |
3339 | check_assist_not_applicable( | 3344 | check_assist_not_applicable( |
3340 | extract_function, | 3345 | extract_function, |
3341 | r##" | 3346 | r##" |
@@ -3357,7 +3362,7 @@ fn foo() -> Option<()> { | |||
3357 | 3362 | ||
3358 | #[test] | 3363 | #[test] |
3359 | fn try_and_return_ok() { | 3364 | fn try_and_return_ok() { |
3360 | mark::check!(external_control_flow_try_and_return_non_err); | 3365 | cov_mark::check!(external_control_flow_try_and_return_non_err); |
3361 | check_assist_not_applicable( | 3366 | check_assist_not_applicable( |
3362 | extract_function, | 3367 | extract_function, |
3363 | r##" | 3368 | r##" |
diff --git a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs index 5c7678b53..335e0ed95 100644 --- a/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{AsName, Module, ModuleDef, Name, Variant}; | 4 | use hir::{Module, ModuleDef, Name, Variant}; |
5 | use ide_db::{ | 5 | use ide_db::{ |
6 | defs::Definition, | 6 | defs::Definition, |
7 | helpers::{ | 7 | helpers::{ |
@@ -133,7 +133,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va | |||
133 | ), | 133 | ), |
134 | _ => false, | 134 | _ => false, |
135 | }) | 135 | }) |
136 | .any(|(name, _)| name == variant_name.as_name()) | 136 | .any(|(name, _)| name.to_string() == variant_name.to_string()) |
137 | } | 137 | } |
138 | 138 | ||
139 | fn insert_import( | 139 | fn insert_import( |
@@ -154,7 +154,7 @@ fn insert_import( | |||
154 | mod_path.pop_segment(); | 154 | mod_path.pop_segment(); |
155 | mod_path.push_segment(variant_hir_name.clone()); | 155 | mod_path.push_segment(variant_hir_name.clone()); |
156 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; | 156 | let scope = ImportScope::find_insert_use_container(scope_node, &ctx.sema)?; |
157 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use.merge); | 157 | *rewriter += insert_use(&scope, mod_path_to_ast(&mod_path), ctx.config.insert_use); |
158 | } | 158 | } |
159 | Some(()) | 159 | Some(()) |
160 | } | 160 | } |
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs index 312ac7ac4..7a32483dc 100644 --- a/crates/ide_assists/src/handlers/extract_variable.rs +++ b/crates/ide_assists/src/handlers/extract_variable.rs | |||
@@ -6,7 +6,6 @@ use syntax::{ | |||
6 | }, | 6 | }, |
7 | SyntaxNode, | 7 | SyntaxNode, |
8 | }; | 8 | }; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; | 10 | use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; |
12 | 11 | ||
@@ -32,7 +31,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
32 | } | 31 | } |
33 | let node = ctx.covering_element(); | 32 | let node = ctx.covering_element(); |
34 | if node.kind() == COMMENT { | 33 | if node.kind() == COMMENT { |
35 | mark::hit!(extract_var_in_comment_is_not_applicable); | 34 | cov_mark::hit!(extract_var_in_comment_is_not_applicable); |
36 | return None; | 35 | return None; |
37 | } | 36 | } |
38 | let to_extract = node.ancestors().find_map(valid_target_expr)?; | 37 | let to_extract = node.ancestors().find_map(valid_target_expr)?; |
@@ -69,7 +68,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option | |||
69 | format_to!(buf, "{}", to_extract.syntax()); | 68 | format_to!(buf, "{}", to_extract.syntax()); |
70 | 69 | ||
71 | if let Anchor::Replace(stmt) = anchor { | 70 | if let Anchor::Replace(stmt) = anchor { |
72 | mark::hit!(test_extract_var_expr_stmt); | 71 | cov_mark::hit!(test_extract_var_expr_stmt); |
73 | if stmt.semicolon_token().is_none() { | 72 | if stmt.semicolon_token().is_none() { |
74 | buf.push_str(";"); | 73 | buf.push_str(";"); |
75 | } | 74 | } |
@@ -142,7 +141,7 @@ impl Anchor { | |||
142 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) | 141 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) |
143 | { | 142 | { |
144 | if expr.syntax() == &node { | 143 | if expr.syntax() == &node { |
145 | mark::hit!(test_extract_var_last_expr); | 144 | cov_mark::hit!(test_extract_var_last_expr); |
146 | return Some(Anchor::Before(node)); | 145 | return Some(Anchor::Before(node)); |
147 | } | 146 | } |
148 | } | 147 | } |
@@ -175,8 +174,6 @@ impl Anchor { | |||
175 | 174 | ||
176 | #[cfg(test)] | 175 | #[cfg(test)] |
177 | mod tests { | 176 | mod tests { |
178 | use test_utils::mark; | ||
179 | |||
180 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 177 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
181 | 178 | ||
182 | use super::*; | 179 | use super::*; |
@@ -199,13 +196,13 @@ fn foo() { | |||
199 | 196 | ||
200 | #[test] | 197 | #[test] |
201 | fn extract_var_in_comment_is_not_applicable() { | 198 | fn extract_var_in_comment_is_not_applicable() { |
202 | mark::check!(extract_var_in_comment_is_not_applicable); | 199 | cov_mark::check!(extract_var_in_comment_is_not_applicable); |
203 | check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); | 200 | check_assist_not_applicable(extract_variable, "fn main() { 1 + /* $0comment$0 */ 1; }"); |
204 | } | 201 | } |
205 | 202 | ||
206 | #[test] | 203 | #[test] |
207 | fn test_extract_var_expr_stmt() { | 204 | fn test_extract_var_expr_stmt() { |
208 | mark::check!(test_extract_var_expr_stmt); | 205 | cov_mark::check!(test_extract_var_expr_stmt); |
209 | check_assist( | 206 | check_assist( |
210 | extract_variable, | 207 | extract_variable, |
211 | r#" | 208 | r#" |
@@ -250,7 +247,7 @@ fn foo() { | |||
250 | 247 | ||
251 | #[test] | 248 | #[test] |
252 | fn test_extract_var_last_expr() { | 249 | fn test_extract_var_last_expr() { |
253 | mark::check!(test_extract_var_last_expr); | 250 | cov_mark::check!(test_extract_var_last_expr); |
254 | check_assist( | 251 | check_assist( |
255 | extract_variable, | 252 | extract_variable, |
256 | r#" | 253 | r#" |
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 7086e47d2..878b3a3fa 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -5,7 +5,6 @@ use ide_db::helpers::{mod_path_to_ast, FamousDefs}; | |||
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use itertools::Itertools; | 6 | use itertools::Itertools; |
7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 7 | use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
8 | use test_utils::mark; | ||
9 | 8 | ||
10 | use crate::{ | 9 | use crate::{ |
11 | utils::{does_pat_match_variant, render_snippet, Cursor}, | 10 | utils::{does_pat_match_variant, render_snippet, Cursor}, |
@@ -62,7 +61,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
62 | .collect::<Vec<_>>(); | 61 | .collect::<Vec<_>>(); |
63 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { | 62 | if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { |
64 | // Match `Some` variant first. | 63 | // Match `Some` variant first. |
65 | mark::hit!(option_order); | 64 | cov_mark::hit!(option_order); |
66 | variants.reverse() | 65 | variants.reverse() |
67 | } | 66 | } |
68 | variants | 67 | variants |
@@ -195,7 +194,6 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Optio | |||
195 | #[cfg(test)] | 194 | #[cfg(test)] |
196 | mod tests { | 195 | mod tests { |
197 | use ide_db::helpers::FamousDefs; | 196 | use ide_db::helpers::FamousDefs; |
198 | use test_utils::mark; | ||
199 | 197 | ||
200 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 198 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
201 | 199 | ||
@@ -730,7 +728,7 @@ fn main() { | |||
730 | 728 | ||
731 | #[test] | 729 | #[test] |
732 | fn option_order() { | 730 | fn option_order() { |
733 | mark::check!(option_order); | 731 | cov_mark::check!(option_order); |
734 | let before = r#" | 732 | let before = r#" |
735 | fn foo(opt: Option<i32>) { | 733 | fn foo(opt: Option<i32>) { |
736 | match opt$0 { | 734 | match opt$0 { |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs index 6a2ab9596..588ee1350 100644 --- a/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs +++ b/crates/ide_assists/src/handlers/generate_default_from_enum_variant.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ide_db::helpers::FamousDefs; | 1 | use ide_db::helpers::FamousDefs; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
7 | 6 | ||
@@ -38,12 +37,12 @@ pub(crate) fn generate_default_from_enum_variant( | |||
38 | let variant_name = variant.name()?; | 37 | let variant_name = variant.name()?; |
39 | let enum_name = variant.parent_enum().name()?; | 38 | let enum_name = variant.parent_enum().name()?; |
40 | if !matches!(variant.kind(), ast::StructKind::Unit) { | 39 | if !matches!(variant.kind(), ast::StructKind::Unit) { |
41 | mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); | 40 | cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented); |
42 | return None; | 41 | return None; |
43 | } | 42 | } |
44 | 43 | ||
45 | if existing_default_impl(&ctx.sema, &variant).is_some() { | 44 | if existing_default_impl(&ctx.sema, &variant).is_some() { |
46 | mark::hit!(test_gen_default_impl_already_exists); | 45 | cov_mark::hit!(test_gen_default_impl_already_exists); |
47 | return None; | 46 | return None; |
48 | } | 47 | } |
49 | 48 | ||
@@ -89,8 +88,6 @@ fn existing_default_impl( | |||
89 | 88 | ||
90 | #[cfg(test)] | 89 | #[cfg(test)] |
91 | mod tests { | 90 | mod tests { |
92 | use test_utils::mark; | ||
93 | |||
94 | use crate::tests::{check_assist, check_assist_not_applicable}; | 91 | use crate::tests::{check_assist, check_assist_not_applicable}; |
95 | 92 | ||
96 | use super::*; | 93 | use super::*; |
@@ -127,7 +124,7 @@ impl Default for Variant { | |||
127 | 124 | ||
128 | #[test] | 125 | #[test] |
129 | fn test_generate_default_already_implemented() { | 126 | fn test_generate_default_already_implemented() { |
130 | mark::check!(test_gen_default_impl_already_exists); | 127 | cov_mark::check!(test_gen_default_impl_already_exists); |
131 | check_not_applicable( | 128 | check_not_applicable( |
132 | r#" | 129 | r#" |
133 | enum Variant { | 130 | enum Variant { |
@@ -146,7 +143,7 @@ impl Default for Variant { | |||
146 | 143 | ||
147 | #[test] | 144 | #[test] |
148 | fn test_add_from_impl_no_element() { | 145 | fn test_add_from_impl_no_element() { |
149 | mark::check!(test_gen_default_on_non_unit_variant_not_implemented); | 146 | cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented); |
150 | check_not_applicable( | 147 | check_not_applicable( |
151 | r#" | 148 | r#" |
152 | enum Variant { | 149 | enum Variant { |
diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs new file mode 100644 index 000000000..81c54ba3e --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs | |||
@@ -0,0 +1,373 @@ | |||
1 | use crate::{ | ||
2 | assist_context::{AssistContext, Assists}, | ||
3 | AssistId, | ||
4 | }; | ||
5 | use ide_db::helpers::FamousDefs; | ||
6 | use syntax::{ | ||
7 | ast::{self, Impl, NameOwner}, | ||
8 | AstNode, | ||
9 | }; | ||
10 | |||
11 | // Assist: generate_default_from_new | ||
12 | // | ||
13 | // Generates default implementation from new method. | ||
14 | // | ||
15 | // ``` | ||
16 | // struct Example { _inner: () } | ||
17 | // | ||
18 | // impl Example { | ||
19 | // pub fn n$0ew() -> Self { | ||
20 | // Self { _inner: () } | ||
21 | // } | ||
22 | // } | ||
23 | // ``` | ||
24 | // -> | ||
25 | // ``` | ||
26 | // struct Example { _inner: () } | ||
27 | // | ||
28 | // impl Example { | ||
29 | // pub fn new() -> Self { | ||
30 | // Self { _inner: () } | ||
31 | // } | ||
32 | // } | ||
33 | // | ||
34 | // impl Default for Example { | ||
35 | // fn default() -> Self { | ||
36 | // Self::new() | ||
37 | // } | ||
38 | // } | ||
39 | // ``` | ||
40 | pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
41 | let fn_node = ctx.find_node_at_offset::<ast::Fn>()?; | ||
42 | let fn_name = fn_node.name()?; | ||
43 | |||
44 | if fn_name.text() != "new" { | ||
45 | cov_mark::hit!(other_function_than_new); | ||
46 | return None; | ||
47 | } | ||
48 | |||
49 | if fn_node.param_list()?.params().next().is_some() { | ||
50 | cov_mark::hit!(new_function_with_parameters); | ||
51 | return None; | ||
52 | } | ||
53 | |||
54 | let impl_ = fn_node.syntax().ancestors().into_iter().find_map(ast::Impl::cast)?; | ||
55 | if is_default_implemented(ctx, &impl_) { | ||
56 | cov_mark::hit!(default_block_is_already_present); | ||
57 | cov_mark::hit!(struct_in_module_with_default); | ||
58 | return None; | ||
59 | } | ||
60 | |||
61 | let insert_location = impl_.syntax().text_range(); | ||
62 | |||
63 | acc.add( | ||
64 | AssistId("generate_default_from_new", crate::AssistKind::Generate), | ||
65 | "Generate a Default impl from a new fn", | ||
66 | insert_location, | ||
67 | move |builder| { | ||
68 | let code = default_fn_node_for_new(impl_); | ||
69 | builder.insert(insert_location.end(), code); | ||
70 | }, | ||
71 | ) | ||
72 | } | ||
73 | |||
74 | fn default_fn_node_for_new(impl_: Impl) -> String { | ||
75 | format!( | ||
76 | " | ||
77 | |||
78 | impl Default for {} {{ | ||
79 | fn default() -> Self {{ | ||
80 | Self::new() | ||
81 | }} | ||
82 | }}", | ||
83 | impl_.self_ty().unwrap().syntax().text() | ||
84 | ) | ||
85 | } | ||
86 | |||
87 | fn is_default_implemented(ctx: &AssistContext, impl_: &Impl) -> bool { | ||
88 | let db = ctx.sema.db; | ||
89 | let impl_ = ctx.sema.to_def(impl_); | ||
90 | let impl_def = match impl_ { | ||
91 | Some(value) => value, | ||
92 | None => return false, | ||
93 | }; | ||
94 | |||
95 | let ty = impl_def.target_ty(db); | ||
96 | let krate = impl_def.module(db).krate(); | ||
97 | let default = FamousDefs(&ctx.sema, Some(krate)).core_default_Default(); | ||
98 | let default_trait = match default { | ||
99 | Some(value) => value, | ||
100 | None => return false, | ||
101 | }; | ||
102 | |||
103 | ty.impls_trait(db, default_trait, &[]) | ||
104 | } | ||
105 | |||
106 | #[cfg(test)] | ||
107 | mod tests { | ||
108 | use ide_db::helpers::FamousDefs; | ||
109 | |||
110 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
111 | |||
112 | use super::*; | ||
113 | |||
114 | #[test] | ||
115 | fn generate_default() { | ||
116 | check_pass( | ||
117 | r#" | ||
118 | struct Example { _inner: () } | ||
119 | |||
120 | impl Example { | ||
121 | pub fn ne$0w() -> Self { | ||
122 | Self { _inner: () } | ||
123 | } | ||
124 | } | ||
125 | |||
126 | fn main() {} | ||
127 | "#, | ||
128 | r#" | ||
129 | struct Example { _inner: () } | ||
130 | |||
131 | impl Example { | ||
132 | pub fn new() -> Self { | ||
133 | Self { _inner: () } | ||
134 | } | ||
135 | } | ||
136 | |||
137 | impl Default for Example { | ||
138 | fn default() -> Self { | ||
139 | Self::new() | ||
140 | } | ||
141 | } | ||
142 | |||
143 | fn main() {} | ||
144 | "#, | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn generate_default2() { | ||
150 | check_pass( | ||
151 | r#" | ||
152 | struct Test { value: u32 } | ||
153 | |||
154 | impl Test { | ||
155 | pub fn ne$0w() -> Self { | ||
156 | Self { value: 0 } | ||
157 | } | ||
158 | } | ||
159 | "#, | ||
160 | r#" | ||
161 | struct Test { value: u32 } | ||
162 | |||
163 | impl Test { | ||
164 | pub fn new() -> Self { | ||
165 | Self { value: 0 } | ||
166 | } | ||
167 | } | ||
168 | |||
169 | impl Default for Test { | ||
170 | fn default() -> Self { | ||
171 | Self::new() | ||
172 | } | ||
173 | } | ||
174 | "#, | ||
175 | ); | ||
176 | } | ||
177 | |||
178 | #[test] | ||
179 | fn new_function_with_parameters() { | ||
180 | cov_mark::check!(new_function_with_parameters); | ||
181 | check_not_applicable( | ||
182 | r#" | ||
183 | struct Example { _inner: () } | ||
184 | |||
185 | impl Example { | ||
186 | pub fn $0new(value: ()) -> Self { | ||
187 | Self { _inner: value } | ||
188 | } | ||
189 | } | ||
190 | "#, | ||
191 | ); | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn other_function_than_new() { | ||
196 | cov_mark::check!(other_function_than_new); | ||
197 | check_not_applicable( | ||
198 | r#" | ||
199 | struct Example { _inner: () } | ||
200 | |||
201 | impl Example { | ||
202 | pub fn a$0dd() -> Self { | ||
203 | Self { _inner: () } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | "#, | ||
208 | ); | ||
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn default_block_is_already_present() { | ||
213 | cov_mark::check!(default_block_is_already_present); | ||
214 | check_not_applicable( | ||
215 | r#" | ||
216 | struct Example { _inner: () } | ||
217 | |||
218 | impl Example { | ||
219 | pub fn n$0ew() -> Self { | ||
220 | Self { _inner: () } | ||
221 | } | ||
222 | } | ||
223 | |||
224 | impl Default for Example { | ||
225 | fn default() -> Self { | ||
226 | Self::new() | ||
227 | } | ||
228 | } | ||
229 | "#, | ||
230 | ); | ||
231 | } | ||
232 | |||
233 | #[test] | ||
234 | fn standalone_new_function() { | ||
235 | check_not_applicable( | ||
236 | r#" | ||
237 | fn n$0ew() -> u32 { | ||
238 | 0 | ||
239 | } | ||
240 | "#, | ||
241 | ); | ||
242 | } | ||
243 | |||
244 | #[test] | ||
245 | fn multiple_struct_blocks() { | ||
246 | check_pass( | ||
247 | r#" | ||
248 | struct Example { _inner: () } | ||
249 | struct Test { value: u32 } | ||
250 | |||
251 | impl Example { | ||
252 | pub fn new$0() -> Self { | ||
253 | Self { _inner: () } | ||
254 | } | ||
255 | } | ||
256 | "#, | ||
257 | r#" | ||
258 | struct Example { _inner: () } | ||
259 | struct Test { value: u32 } | ||
260 | |||
261 | impl Example { | ||
262 | pub fn new() -> Self { | ||
263 | Self { _inner: () } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | impl Default for Example { | ||
268 | fn default() -> Self { | ||
269 | Self::new() | ||
270 | } | ||
271 | } | ||
272 | "#, | ||
273 | ); | ||
274 | } | ||
275 | |||
276 | #[test] | ||
277 | fn when_struct_is_after_impl() { | ||
278 | check_pass( | ||
279 | r#" | ||
280 | impl Example { | ||
281 | pub fn $0new() -> Self { | ||
282 | Self { _inner: () } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | struct Example { _inner: () } | ||
287 | "#, | ||
288 | r#" | ||
289 | impl Example { | ||
290 | pub fn new() -> Self { | ||
291 | Self { _inner: () } | ||
292 | } | ||
293 | } | ||
294 | |||
295 | impl Default for Example { | ||
296 | fn default() -> Self { | ||
297 | Self::new() | ||
298 | } | ||
299 | } | ||
300 | |||
301 | struct Example { _inner: () } | ||
302 | "#, | ||
303 | ); | ||
304 | } | ||
305 | |||
306 | #[test] | ||
307 | fn struct_in_module() { | ||
308 | check_pass( | ||
309 | r#" | ||
310 | mod test { | ||
311 | struct Example { _inner: () } | ||
312 | |||
313 | impl Example { | ||
314 | pub fn n$0ew() -> Self { | ||
315 | Self { _inner: () } | ||
316 | } | ||
317 | } | ||
318 | } | ||
319 | "#, | ||
320 | r#" | ||
321 | mod test { | ||
322 | struct Example { _inner: () } | ||
323 | |||
324 | impl Example { | ||
325 | pub fn new() -> Self { | ||
326 | Self { _inner: () } | ||
327 | } | ||
328 | } | ||
329 | |||
330 | impl Default for Example { | ||
331 | fn default() -> Self { | ||
332 | Self::new() | ||
333 | } | ||
334 | } | ||
335 | } | ||
336 | "#, | ||
337 | ); | ||
338 | } | ||
339 | |||
340 | #[test] | ||
341 | fn struct_in_module_with_default() { | ||
342 | cov_mark::check!(struct_in_module_with_default); | ||
343 | check_not_applicable( | ||
344 | r#" | ||
345 | mod test { | ||
346 | struct Example { _inner: () } | ||
347 | |||
348 | impl Example { | ||
349 | pub fn n$0ew() -> Self { | ||
350 | Self { _inner: () } | ||
351 | } | ||
352 | } | ||
353 | |||
354 | impl Default for Example { | ||
355 | fn default() -> Self { | ||
356 | Self::new() | ||
357 | } | ||
358 | } | ||
359 | } | ||
360 | "#, | ||
361 | ); | ||
362 | } | ||
363 | |||
364 | fn check_pass(before: &str, after: &str) { | ||
365 | let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE); | ||
366 | check_assist(generate_default_from_new, before, after); | ||
367 | } | ||
368 | |||
369 | fn check_not_applicable(before: &str) { | ||
370 | let before = &format!("//- /main.rs crate:main deps:core{}{}", before, FamousDefs::FIXTURE); | ||
371 | check_assist_not_applicable(generate_default_from_new, before); | ||
372 | } | ||
373 | } | ||
diff --git a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs index d9388a737..c13c6eebe 100644 --- a/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs +++ b/crates/ide_assists/src/handlers/generate_from_impl_for_enum.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use ide_db::helpers::FamousDefs; | 1 | use ide_db::helpers::FamousDefs; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::RootDatabase; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | 3 | use syntax::ast::{self, AstNode, NameOwner}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists}; |
7 | 6 | ||
@@ -44,7 +43,7 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext | |||
44 | }; | 43 | }; |
45 | 44 | ||
46 | if existing_from_impl(&ctx.sema, &variant).is_some() { | 45 | if existing_from_impl(&ctx.sema, &variant).is_some() { |
47 | mark::hit!(test_add_from_impl_already_exists); | 46 | cov_mark::hit!(test_add_from_impl_already_exists); |
48 | return None; | 47 | return None; |
49 | } | 48 | } |
50 | 49 | ||
@@ -103,8 +102,6 @@ fn existing_from_impl( | |||
103 | 102 | ||
104 | #[cfg(test)] | 103 | #[cfg(test)] |
105 | mod tests { | 104 | mod tests { |
106 | use test_utils::mark; | ||
107 | |||
108 | use crate::tests::{check_assist, check_assist_not_applicable}; | 105 | use crate::tests::{check_assist, check_assist_not_applicable}; |
109 | 106 | ||
110 | use super::*; | 107 | use super::*; |
@@ -172,7 +169,7 @@ impl From<u32> for A { | |||
172 | 169 | ||
173 | #[test] | 170 | #[test] |
174 | fn test_add_from_impl_already_exists() { | 171 | fn test_add_from_impl_already_exists() { |
175 | mark::check!(test_add_from_impl_already_exists); | 172 | cov_mark::check!(test_add_from_impl_already_exists); |
176 | check_not_applicable( | 173 | check_not_applicable( |
177 | r#" | 174 | r#" |
178 | enum A { $0One(u32), } | 175 | enum A { $0One(u32), } |
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs index 959824981..6f95b1a07 100644 --- a/crates/ide_assists/src/handlers/generate_function.rs +++ b/crates/ide_assists/src/handlers/generate_function.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use ide_db::{base_db::FileId, helpers::SnippetCap}; | 2 | use ide_db::{base_db::FileId, helpers::SnippetCap}; |
3 | use rustc_hash::{FxHashMap, FxHashSet}; | 3 | use rustc_hash::{FxHashMap, FxHashSet}; |
4 | use stdx::to_lower_snake_case; | ||
4 | use syntax::{ | 5 | use syntax::{ |
5 | ast::{ | 6 | ast::{ |
6 | self, | 7 | self, |
@@ -82,17 +83,18 @@ struct FunctionTemplate { | |||
82 | leading_ws: String, | 83 | leading_ws: String, |
83 | fn_def: ast::Fn, | 84 | fn_def: ast::Fn, |
84 | ret_type: ast::RetType, | 85 | ret_type: ast::RetType, |
86 | should_render_snippet: bool, | ||
85 | trailing_ws: String, | 87 | trailing_ws: String, |
86 | file: FileId, | 88 | file: FileId, |
87 | } | 89 | } |
88 | 90 | ||
89 | impl FunctionTemplate { | 91 | impl FunctionTemplate { |
90 | fn to_string(&self, cap: Option<SnippetCap>) -> String { | 92 | fn to_string(&self, cap: Option<SnippetCap>) -> String { |
91 | let f = match cap { | 93 | let f = match (cap, self.should_render_snippet) { |
92 | Some(cap) => { | 94 | (Some(cap), true) => { |
93 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) | 95 | render_snippet(cap, self.fn_def.syntax(), Cursor::Replace(self.ret_type.syntax())) |
94 | } | 96 | } |
95 | None => self.fn_def.to_string(), | 97 | _ => self.fn_def.to_string(), |
96 | }; | 98 | }; |
97 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) | 99 | format!("{}{}{}", self.leading_ws, f, self.trailing_ws) |
98 | } | 100 | } |
@@ -103,6 +105,8 @@ struct FunctionBuilder { | |||
103 | fn_name: ast::Name, | 105 | fn_name: ast::Name, |
104 | type_params: Option<ast::GenericParamList>, | 106 | type_params: Option<ast::GenericParamList>, |
105 | params: ast::ParamList, | 107 | params: ast::ParamList, |
108 | ret_type: ast::RetType, | ||
109 | should_render_snippet: bool, | ||
106 | file: FileId, | 110 | file: FileId, |
107 | needs_pub: bool, | 111 | needs_pub: bool, |
108 | } | 112 | } |
@@ -131,7 +135,43 @@ impl FunctionBuilder { | |||
131 | let fn_name = fn_name(&path)?; | 135 | let fn_name = fn_name(&path)?; |
132 | let (type_params, params) = fn_args(ctx, target_module, &call)?; | 136 | let (type_params, params) = fn_args(ctx, target_module, &call)?; |
133 | 137 | ||
134 | Some(Self { target, fn_name, type_params, params, file, needs_pub }) | 138 | // should_render_snippet intends to express a rough level of confidence about |
139 | // the correctness of the return type. | ||
140 | // | ||
141 | // If we are able to infer some return type, and that return type is not unit, we | ||
142 | // don't want to render the snippet. The assumption here is in this situation the | ||
143 | // return type is just as likely to be correct as any other part of the generated | ||
144 | // function. | ||
145 | // | ||
146 | // In the case where the return type is inferred as unit it is likely that the | ||
147 | // user does in fact intend for this generated function to return some non unit | ||
148 | // type, but that the current state of their code doesn't allow that return type | ||
149 | // to be accurately inferred. | ||
150 | let (ret_ty, should_render_snippet) = { | ||
151 | match ctx.sema.type_of_expr(&ast::Expr::CallExpr(call.clone())) { | ||
152 | Some(ty) if ty.is_unknown() || ty.is_unit() => (make::ty_unit(), true), | ||
153 | Some(ty) => { | ||
154 | let rendered = ty.display_source_code(ctx.db(), target_module.into()); | ||
155 | match rendered { | ||
156 | Ok(rendered) => (make::ty(&rendered), false), | ||
157 | Err(_) => (make::ty_unit(), true), | ||
158 | } | ||
159 | } | ||
160 | None => (make::ty_unit(), true), | ||
161 | } | ||
162 | }; | ||
163 | let ret_type = make::ret_type(ret_ty); | ||
164 | |||
165 | Some(Self { | ||
166 | target, | ||
167 | fn_name, | ||
168 | type_params, | ||
169 | params, | ||
170 | ret_type, | ||
171 | should_render_snippet, | ||
172 | file, | ||
173 | needs_pub, | ||
174 | }) | ||
135 | } | 175 | } |
136 | 176 | ||
137 | fn render(self) -> FunctionTemplate { | 177 | fn render(self) -> FunctionTemplate { |
@@ -144,7 +184,7 @@ impl FunctionBuilder { | |||
144 | self.type_params, | 184 | self.type_params, |
145 | self.params, | 185 | self.params, |
146 | fn_body, | 186 | fn_body, |
147 | Some(make::ret_type(make::ty_unit())), | 187 | Some(self.ret_type), |
148 | ); | 188 | ); |
149 | let leading_ws; | 189 | let leading_ws; |
150 | let trailing_ws; | 190 | let trailing_ws; |
@@ -170,6 +210,7 @@ impl FunctionBuilder { | |||
170 | insert_offset, | 210 | insert_offset, |
171 | leading_ws, | 211 | leading_ws, |
172 | ret_type: fn_def.ret_type().unwrap(), | 212 | ret_type: fn_def.ret_type().unwrap(), |
213 | should_render_snippet: self.should_render_snippet, | ||
173 | fn_def, | 214 | fn_def, |
174 | trailing_ws, | 215 | trailing_ws, |
175 | file: self.file, | 216 | file: self.file, |
@@ -257,14 +298,15 @@ fn deduplicate_arg_names(arg_names: &mut Vec<String>) { | |||
257 | fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | 298 | fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { |
258 | match fn_arg { | 299 | match fn_arg { |
259 | ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), | 300 | ast::Expr::CastExpr(cast_expr) => fn_arg_name(&cast_expr.expr()?), |
260 | _ => Some( | 301 | _ => { |
261 | fn_arg | 302 | let s = fn_arg |
262 | .syntax() | 303 | .syntax() |
263 | .descendants() | 304 | .descendants() |
264 | .filter(|d| ast::NameRef::can_cast(d.kind())) | 305 | .filter(|d| ast::NameRef::can_cast(d.kind())) |
265 | .last()? | 306 | .last()? |
266 | .to_string(), | 307 | .to_string(); |
267 | ), | 308 | Some(to_lower_snake_case(&s)) |
309 | } | ||
268 | } | 310 | } |
269 | } | 311 | } |
270 | 312 | ||
@@ -448,6 +490,52 @@ mod baz { | |||
448 | } | 490 | } |
449 | 491 | ||
450 | #[test] | 492 | #[test] |
493 | fn add_function_with_upper_camel_case_arg() { | ||
494 | check_assist( | ||
495 | generate_function, | ||
496 | r" | ||
497 | struct BazBaz; | ||
498 | fn foo() { | ||
499 | bar$0(BazBaz); | ||
500 | } | ||
501 | ", | ||
502 | r" | ||
503 | struct BazBaz; | ||
504 | fn foo() { | ||
505 | bar(BazBaz); | ||
506 | } | ||
507 | |||
508 | fn bar(baz_baz: BazBaz) ${0:-> ()} { | ||
509 | todo!() | ||
510 | } | ||
511 | ", | ||
512 | ); | ||
513 | } | ||
514 | |||
515 | #[test] | ||
516 | fn add_function_with_upper_camel_case_arg_as_cast() { | ||
517 | check_assist( | ||
518 | generate_function, | ||
519 | r" | ||
520 | struct BazBaz; | ||
521 | fn foo() { | ||
522 | bar$0(&BazBaz as *const BazBaz); | ||
523 | } | ||
524 | ", | ||
525 | r" | ||
526 | struct BazBaz; | ||
527 | fn foo() { | ||
528 | bar(&BazBaz as *const BazBaz); | ||
529 | } | ||
530 | |||
531 | fn bar(baz_baz: *const BazBaz) ${0:-> ()} { | ||
532 | todo!() | ||
533 | } | ||
534 | ", | ||
535 | ); | ||
536 | } | ||
537 | |||
538 | #[test] | ||
451 | fn add_function_with_function_call_arg() { | 539 | fn add_function_with_function_call_arg() { |
452 | check_assist( | 540 | check_assist( |
453 | generate_function, | 541 | generate_function, |
@@ -498,7 +586,7 @@ impl Baz { | |||
498 | } | 586 | } |
499 | } | 587 | } |
500 | 588 | ||
501 | fn bar(baz: Baz) ${0:-> ()} { | 589 | fn bar(baz: Baz) -> Baz { |
502 | todo!() | 590 | todo!() |
503 | } | 591 | } |
504 | ", | 592 | ", |
@@ -1012,6 +1100,27 @@ pub(crate) fn bar() ${0:-> ()} { | |||
1012 | } | 1100 | } |
1013 | 1101 | ||
1014 | #[test] | 1102 | #[test] |
1103 | fn add_function_with_return_type() { | ||
1104 | check_assist( | ||
1105 | generate_function, | ||
1106 | r" | ||
1107 | fn main() { | ||
1108 | let x: u32 = foo$0(); | ||
1109 | } | ||
1110 | ", | ||
1111 | r" | ||
1112 | fn main() { | ||
1113 | let x: u32 = foo(); | ||
1114 | } | ||
1115 | |||
1116 | fn foo() -> u32 { | ||
1117 | todo!() | ||
1118 | } | ||
1119 | ", | ||
1120 | ) | ||
1121 | } | ||
1122 | |||
1123 | #[test] | ||
1015 | fn add_function_not_applicable_if_function_already_exists() { | 1124 | fn add_function_not_applicable_if_function_already_exists() { |
1016 | check_assist_not_applicable( | 1125 | check_assist_not_applicable( |
1017 | generate_function, | 1126 | generate_function, |
diff --git a/crates/ide_assists/src/handlers/infer_function_return_type.rs b/crates/ide_assists/src/handlers/infer_function_return_type.rs index 5279af1f3..66113751c 100644 --- a/crates/ide_assists/src/handlers/infer_function_return_type.rs +++ b/crates/ide_assists/src/handlers/infer_function_return_type.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use hir::HirDisplay; | 1 | use hir::HirDisplay; |
2 | use syntax::{ast, AstNode, TextRange, TextSize}; | 2 | use syntax::{ast, AstNode, TextRange, TextSize}; |
3 | use test_utils::mark; | ||
4 | 3 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 4 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 5 | ||
@@ -42,7 +41,7 @@ pub(crate) fn infer_function_return_type(acc: &mut Assists, ctx: &AssistContext) | |||
42 | } | 41 | } |
43 | } | 42 | } |
44 | if let FnType::Closure { wrap_expr: true } = fn_type { | 43 | if let FnType::Closure { wrap_expr: true } = fn_type { |
45 | mark::hit!(wrap_closure_non_block_expr); | 44 | cov_mark::hit!(wrap_closure_non_block_expr); |
46 | // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block | 45 | // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block |
47 | builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); | 46 | builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); |
48 | } | 47 | } |
@@ -61,13 +60,13 @@ fn ret_ty_to_action(ret_ty: Option<ast::RetType>, insert_pos: TextSize) -> Optio | |||
61 | match ret_ty { | 60 | match ret_ty { |
62 | Some(ret_ty) => match ret_ty.ty() { | 61 | Some(ret_ty) => match ret_ty.ty() { |
63 | Some(ast::Type::InferType(_)) | None => { | 62 | Some(ast::Type::InferType(_)) | None => { |
64 | mark::hit!(existing_infer_ret_type); | 63 | cov_mark::hit!(existing_infer_ret_type); |
65 | mark::hit!(existing_infer_ret_type_closure); | 64 | cov_mark::hit!(existing_infer_ret_type_closure); |
66 | Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) | 65 | Some(InsertOrReplace::Replace(ret_ty.syntax().text_range())) |
67 | } | 66 | } |
68 | _ => { | 67 | _ => { |
69 | mark::hit!(existing_ret_type); | 68 | cov_mark::hit!(existing_ret_type); |
70 | mark::hit!(existing_ret_type_closure); | 69 | cov_mark::hit!(existing_ret_type_closure); |
71 | None | 70 | None |
72 | } | 71 | } |
73 | }, | 72 | }, |
@@ -109,11 +108,11 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla | |||
109 | }; | 108 | }; |
110 | let frange = ctx.frange.range; | 109 | let frange = ctx.frange.range; |
111 | if return_type_range.contains_range(frange) { | 110 | if return_type_range.contains_range(frange) { |
112 | mark::hit!(cursor_in_ret_position); | 111 | cov_mark::hit!(cursor_in_ret_position); |
113 | mark::hit!(cursor_in_ret_position_closure); | 112 | cov_mark::hit!(cursor_in_ret_position_closure); |
114 | } else if tail_expr.syntax().text_range().contains_range(frange) { | 113 | } else if tail_expr.syntax().text_range().contains_range(frange) { |
115 | mark::hit!(cursor_on_tail); | 114 | cov_mark::hit!(cursor_on_tail); |
116 | mark::hit!(cursor_on_tail_closure); | 115 | cov_mark::hit!(cursor_on_tail_closure); |
117 | } else { | 116 | } else { |
118 | return None; | 117 | return None; |
119 | } | 118 | } |
@@ -128,7 +127,7 @@ mod tests { | |||
128 | 127 | ||
129 | #[test] | 128 | #[test] |
130 | fn infer_return_type_specified_inferred() { | 129 | fn infer_return_type_specified_inferred() { |
131 | mark::check!(existing_infer_ret_type); | 130 | cov_mark::check!(existing_infer_ret_type); |
132 | check_assist( | 131 | check_assist( |
133 | infer_function_return_type, | 132 | infer_function_return_type, |
134 | r#"fn foo() -> $0_ { | 133 | r#"fn foo() -> $0_ { |
@@ -142,7 +141,7 @@ mod tests { | |||
142 | 141 | ||
143 | #[test] | 142 | #[test] |
144 | fn infer_return_type_specified_inferred_closure() { | 143 | fn infer_return_type_specified_inferred_closure() { |
145 | mark::check!(existing_infer_ret_type_closure); | 144 | cov_mark::check!(existing_infer_ret_type_closure); |
146 | check_assist( | 145 | check_assist( |
147 | infer_function_return_type, | 146 | infer_function_return_type, |
148 | r#"fn foo() { | 147 | r#"fn foo() { |
@@ -156,7 +155,7 @@ mod tests { | |||
156 | 155 | ||
157 | #[test] | 156 | #[test] |
158 | fn infer_return_type_cursor_at_return_type_pos() { | 157 | fn infer_return_type_cursor_at_return_type_pos() { |
159 | mark::check!(cursor_in_ret_position); | 158 | cov_mark::check!(cursor_in_ret_position); |
160 | check_assist( | 159 | check_assist( |
161 | infer_function_return_type, | 160 | infer_function_return_type, |
162 | r#"fn foo() $0{ | 161 | r#"fn foo() $0{ |
@@ -170,7 +169,7 @@ mod tests { | |||
170 | 169 | ||
171 | #[test] | 170 | #[test] |
172 | fn infer_return_type_cursor_at_return_type_pos_closure() { | 171 | fn infer_return_type_cursor_at_return_type_pos_closure() { |
173 | mark::check!(cursor_in_ret_position_closure); | 172 | cov_mark::check!(cursor_in_ret_position_closure); |
174 | check_assist( | 173 | check_assist( |
175 | infer_function_return_type, | 174 | infer_function_return_type, |
176 | r#"fn foo() { | 175 | r#"fn foo() { |
@@ -184,7 +183,7 @@ mod tests { | |||
184 | 183 | ||
185 | #[test] | 184 | #[test] |
186 | fn infer_return_type() { | 185 | fn infer_return_type() { |
187 | mark::check!(cursor_on_tail); | 186 | cov_mark::check!(cursor_on_tail); |
188 | check_assist( | 187 | check_assist( |
189 | infer_function_return_type, | 188 | infer_function_return_type, |
190 | r#"fn foo() { | 189 | r#"fn foo() { |
@@ -219,7 +218,7 @@ mod tests { | |||
219 | 218 | ||
220 | #[test] | 219 | #[test] |
221 | fn not_applicable_ret_type_specified() { | 220 | fn not_applicable_ret_type_specified() { |
222 | mark::check!(existing_ret_type); | 221 | cov_mark::check!(existing_ret_type); |
223 | check_assist_not_applicable( | 222 | check_assist_not_applicable( |
224 | infer_function_return_type, | 223 | infer_function_return_type, |
225 | r#"fn foo() -> i32 { | 224 | r#"fn foo() -> i32 { |
@@ -251,7 +250,7 @@ mod tests { | |||
251 | 250 | ||
252 | #[test] | 251 | #[test] |
253 | fn infer_return_type_closure_block() { | 252 | fn infer_return_type_closure_block() { |
254 | mark::check!(cursor_on_tail_closure); | 253 | cov_mark::check!(cursor_on_tail_closure); |
255 | check_assist( | 254 | check_assist( |
256 | infer_function_return_type, | 255 | infer_function_return_type, |
257 | r#"fn foo() { | 256 | r#"fn foo() { |
@@ -282,7 +281,7 @@ mod tests { | |||
282 | 281 | ||
283 | #[test] | 282 | #[test] |
284 | fn infer_return_type_closure_wrap() { | 283 | fn infer_return_type_closure_wrap() { |
285 | mark::check!(wrap_closure_non_block_expr); | 284 | cov_mark::check!(wrap_closure_non_block_expr); |
286 | check_assist( | 285 | check_assist( |
287 | infer_function_return_type, | 286 | infer_function_return_type, |
288 | r#"fn foo() { | 287 | r#"fn foo() { |
@@ -321,7 +320,7 @@ mod tests { | |||
321 | 320 | ||
322 | #[test] | 321 | #[test] |
323 | fn not_applicable_ret_type_specified_closure() { | 322 | fn not_applicable_ret_type_specified_closure() { |
324 | mark::check!(existing_ret_type_closure); | 323 | cov_mark::check!(existing_ret_type_closure); |
325 | check_assist_not_applicable( | 324 | check_assist_not_applicable( |
326 | infer_function_return_type, | 325 | infer_function_return_type, |
327 | r#"fn foo() { | 326 | r#"fn foo() { |
diff --git a/crates/ide_assists/src/handlers/inline_function.rs b/crates/ide_assists/src/handlers/inline_function.rs index 6ec99b09b..8e56029cb 100644 --- a/crates/ide_assists/src/handlers/inline_function.rs +++ b/crates/ide_assists/src/handlers/inline_function.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | ast::{self, edit::AstNodeEdit, ArgListOwner}, | 4 | ast::{self, edit::AstNodeEdit, ArgListOwner}, |
5 | AstNode, | 5 | AstNode, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | 9 | assist_context::{AssistContext, Assists}, |
@@ -49,7 +48,7 @@ pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
49 | if arguments.len() != parameters.len() { | 48 | if arguments.len() != parameters.len() { |
50 | // Can't inline the function because they've passed the wrong number of | 49 | // Can't inline the function because they've passed the wrong number of |
51 | // arguments to this function | 50 | // arguments to this function |
52 | mark::hit!(inline_function_incorrect_number_of_arguments); | 51 | cov_mark::hit!(inline_function_incorrect_number_of_arguments); |
53 | return None; | 52 | return None; |
54 | } | 53 | } |
55 | 54 | ||
@@ -155,7 +154,7 @@ fn main() { Foo.bar$0(); } | |||
155 | 154 | ||
156 | #[test] | 155 | #[test] |
157 | fn not_applicable_when_incorrect_number_of_parameters_are_provided() { | 156 | fn not_applicable_when_incorrect_number_of_parameters_are_provided() { |
158 | mark::check!(inline_function_incorrect_number_of_arguments); | 157 | cov_mark::check!(inline_function_incorrect_number_of_arguments); |
159 | check_assist_not_applicable( | 158 | check_assist_not_applicable( |
160 | inline_function, | 159 | inline_function, |
161 | r#" | 160 | r#" |
diff --git a/crates/ide_assists/src/handlers/inline_local_variable.rs b/crates/ide_assists/src/handlers/inline_local_variable.rs index da5522670..ea1466dc8 100644 --- a/crates/ide_assists/src/handlers/inline_local_variable.rs +++ b/crates/ide_assists/src/handlers/inline_local_variable.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | ast::{self, AstNode, AstToken}, | 4 | ast::{self, AstNode, AstToken}, |
5 | TextRange, | 5 | TextRange, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{ | 8 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | 9 | assist_context::{AssistContext, Assists}, |
@@ -34,11 +33,11 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
34 | _ => return None, | 33 | _ => return None, |
35 | }; | 34 | }; |
36 | if bind_pat.mut_token().is_some() { | 35 | if bind_pat.mut_token().is_some() { |
37 | mark::hit!(test_not_inline_mut_variable); | 36 | cov_mark::hit!(test_not_inline_mut_variable); |
38 | return None; | 37 | return None; |
39 | } | 38 | } |
40 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { | 39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { |
41 | mark::hit!(not_applicable_outside_of_bind_pat); | 40 | cov_mark::hit!(not_applicable_outside_of_bind_pat); |
42 | return None; | 41 | return None; |
43 | } | 42 | } |
44 | let initializer_expr = let_stmt.initializer()?; | 43 | let initializer_expr = let_stmt.initializer()?; |
@@ -47,7 +46,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
47 | let def = Definition::Local(def); | 46 | let def = Definition::Local(def); |
48 | let usages = def.usages(&ctx.sema).all(); | 47 | let usages = def.usages(&ctx.sema).all(); |
49 | if usages.is_empty() { | 48 | if usages.is_empty() { |
50 | mark::hit!(test_not_applicable_if_variable_unused); | 49 | cov_mark::hit!(test_not_applicable_if_variable_unused); |
51 | return None; | 50 | return None; |
52 | }; | 51 | }; |
53 | 52 | ||
@@ -130,7 +129,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
130 | Some(name_ref) | 129 | Some(name_ref) |
131 | if ast::RecordExprField::for_field_name(name_ref).is_some() => | 130 | if ast::RecordExprField::for_field_name(name_ref).is_some() => |
132 | { | 131 | { |
133 | mark::hit!(inline_field_shorthand); | 132 | cov_mark::hit!(inline_field_shorthand); |
134 | builder.insert(reference.range.end(), format!(": {}", replacement)); | 133 | builder.insert(reference.range.end(), format!(": {}", replacement)); |
135 | } | 134 | } |
136 | _ => builder.replace(reference.range, replacement), | 135 | _ => builder.replace(reference.range, replacement), |
@@ -143,8 +142,6 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O | |||
143 | 142 | ||
144 | #[cfg(test)] | 143 | #[cfg(test)] |
145 | mod tests { | 144 | mod tests { |
146 | use test_utils::mark; | ||
147 | |||
148 | use crate::tests::{check_assist, check_assist_not_applicable}; | 145 | use crate::tests::{check_assist, check_assist_not_applicable}; |
149 | 146 | ||
150 | use super::*; | 147 | use super::*; |
@@ -351,7 +348,7 @@ fn foo() { | |||
351 | 348 | ||
352 | #[test] | 349 | #[test] |
353 | fn test_not_inline_mut_variable() { | 350 | fn test_not_inline_mut_variable() { |
354 | mark::check!(test_not_inline_mut_variable); | 351 | cov_mark::check!(test_not_inline_mut_variable); |
355 | check_assist_not_applicable( | 352 | check_assist_not_applicable( |
356 | inline_local_variable, | 353 | inline_local_variable, |
357 | r" | 354 | r" |
@@ -684,7 +681,7 @@ fn foo() { | |||
684 | 681 | ||
685 | #[test] | 682 | #[test] |
686 | fn inline_field_shorthand() { | 683 | fn inline_field_shorthand() { |
687 | mark::check!(inline_field_shorthand); | 684 | cov_mark::check!(inline_field_shorthand); |
688 | check_assist( | 685 | check_assist( |
689 | inline_local_variable, | 686 | inline_local_variable, |
690 | r" | 687 | r" |
@@ -705,7 +702,7 @@ fn main() { | |||
705 | 702 | ||
706 | #[test] | 703 | #[test] |
707 | fn test_not_applicable_if_variable_unused() { | 704 | fn test_not_applicable_if_variable_unused() { |
708 | mark::check!(test_not_applicable_if_variable_unused); | 705 | cov_mark::check!(test_not_applicable_if_variable_unused); |
709 | check_assist_not_applicable( | 706 | check_assist_not_applicable( |
710 | inline_local_variable, | 707 | inline_local_variable, |
711 | r" | 708 | r" |
@@ -718,7 +715,7 @@ fn foo() { | |||
718 | 715 | ||
719 | #[test] | 716 | #[test] |
720 | fn not_applicable_outside_of_bind_pat() { | 717 | fn not_applicable_outside_of_bind_pat() { |
721 | mark::check!(not_applicable_outside_of_bind_pat); | 718 | cov_mark::check!(not_applicable_outside_of_bind_pat); |
722 | check_assist_not_applicable( | 719 | check_assist_not_applicable( |
723 | inline_local_variable, | 720 | inline_local_variable, |
724 | r" | 721 | r" |
diff --git a/crates/ide_assists/src/handlers/move_module_to_file.rs b/crates/ide_assists/src/handlers/move_module_to_file.rs index 91c395c1b..6e685b4b2 100644 --- a/crates/ide_assists/src/handlers/move_module_to_file.rs +++ b/crates/ide_assists/src/handlers/move_module_to_file.rs | |||
@@ -5,7 +5,6 @@ use syntax::{ | |||
5 | ast::{self, edit::AstNodeEdit, NameOwner}, | 5 | ast::{self, edit::AstNodeEdit, NameOwner}, |
6 | AstNode, TextRange, | 6 | AstNode, TextRange, |
7 | }; | 7 | }; |
8 | use test_utils::mark; | ||
9 | 8 | ||
10 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
11 | 10 | ||
@@ -28,7 +27,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
28 | 27 | ||
29 | let l_curly_offset = module_items.syntax().text_range().start(); | 28 | let l_curly_offset = module_items.syntax().text_range().start(); |
30 | if l_curly_offset <= ctx.offset() { | 29 | if l_curly_offset <= ctx.offset() { |
31 | mark::hit!(available_before_curly); | 30 | cov_mark::hit!(available_before_curly); |
32 | return None; | 31 | return None; |
33 | } | 32 | } |
34 | let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); | 33 | let target = TextRange::new(module_ast.syntax().text_range().start(), l_curly_offset); |
@@ -182,7 +181,7 @@ pub(crate) mod tests; | |||
182 | 181 | ||
183 | #[test] | 182 | #[test] |
184 | fn available_before_curly() { | 183 | fn available_before_curly() { |
185 | mark::check!(available_before_curly); | 184 | cov_mark::check!(available_before_curly); |
186 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); | 185 | check_assist_not_applicable(move_module_to_file, r#"mod m { $0 }"#); |
187 | } | 186 | } |
188 | } | 187 | } |
diff --git a/crates/ide_assists/src/handlers/pull_assignment_up.rs b/crates/ide_assists/src/handlers/pull_assignment_up.rs index 13e1cb754..04bae4e58 100644 --- a/crates/ide_assists/src/handlers/pull_assignment_up.rs +++ b/crates/ide_assists/src/handlers/pull_assignment_up.rs | |||
@@ -2,7 +2,6 @@ use syntax::{ | |||
2 | ast::{self, edit::AstNodeEdit, make}, | 2 | ast::{self, edit::AstNodeEdit, make}, |
3 | AstNode, | 3 | AstNode, |
4 | }; | 4 | }; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{ | 6 | use crate::{ |
8 | assist_context::{AssistContext, Assists}, | 7 | assist_context::{AssistContext, Assists}, |
@@ -104,7 +103,7 @@ fn exprify_if( | |||
104 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) | 103 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) |
105 | } | 104 | } |
106 | ast::ElseBranch::IfExpr(expr) => { | 105 | ast::ElseBranch::IfExpr(expr) => { |
107 | mark::hit!(test_pull_assignment_up_chained_if); | 106 | cov_mark::hit!(test_pull_assignment_up_chained_if); |
108 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( | 107 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( |
109 | exprify_if(&expr, sema, name)?.syntax().to_owned(), | 108 | exprify_if(&expr, sema, name)?.syntax().to_owned(), |
110 | )?) | 109 | )?) |
@@ -144,7 +143,7 @@ fn is_equivalent( | |||
144 | ) -> bool { | 143 | ) -> bool { |
145 | match (expr0, expr1) { | 144 | match (expr0, expr1) { |
146 | (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { | 145 | (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { |
147 | mark::hit!(test_pull_assignment_up_field_assignment); | 146 | cov_mark::hit!(test_pull_assignment_up_field_assignment); |
148 | sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) | 147 | sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) |
149 | } | 148 | } |
150 | (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { | 149 | (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { |
@@ -156,6 +155,17 @@ fn is_equivalent( | |||
156 | false | 155 | false |
157 | } | 156 | } |
158 | } | 157 | } |
158 | (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1)) | ||
159 | if prefix0.op_kind() == Some(ast::PrefixOp::Deref) | ||
160 | && prefix1.op_kind() == Some(ast::PrefixOp::Deref) => | ||
161 | { | ||
162 | cov_mark::hit!(test_pull_assignment_up_deref); | ||
163 | if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) { | ||
164 | is_equivalent(sema, &prefix0, &prefix1) | ||
165 | } else { | ||
166 | false | ||
167 | } | ||
168 | } | ||
159 | _ => false, | 169 | _ => false, |
160 | } | 170 | } |
161 | } | 171 | } |
@@ -252,7 +262,7 @@ fn foo() { | |||
252 | 262 | ||
253 | #[test] | 263 | #[test] |
254 | fn test_pull_assignment_up_chained_if() { | 264 | fn test_pull_assignment_up_chained_if() { |
255 | mark::check!(test_pull_assignment_up_chained_if); | 265 | cov_mark::check!(test_pull_assignment_up_chained_if); |
256 | check_assist( | 266 | check_assist( |
257 | pull_assignment_up, | 267 | pull_assignment_up, |
258 | r#" | 268 | r#" |
@@ -368,7 +378,7 @@ fn foo() { | |||
368 | 378 | ||
369 | #[test] | 379 | #[test] |
370 | fn test_pull_assignment_up_field_assignment() { | 380 | fn test_pull_assignment_up_field_assignment() { |
371 | mark::check!(test_pull_assignment_up_field_assignment); | 381 | cov_mark::check!(test_pull_assignment_up_field_assignment); |
372 | check_assist( | 382 | check_assist( |
373 | pull_assignment_up, | 383 | pull_assignment_up, |
374 | r#" | 384 | r#" |
@@ -397,4 +407,36 @@ fn foo() { | |||
397 | }"#, | 407 | }"#, |
398 | ) | 408 | ) |
399 | } | 409 | } |
410 | |||
411 | #[test] | ||
412 | fn test_pull_assignment_up_deref() { | ||
413 | cov_mark::check!(test_pull_assignment_up_deref); | ||
414 | check_assist( | ||
415 | pull_assignment_up, | ||
416 | r#" | ||
417 | fn foo() { | ||
418 | let mut a = 1; | ||
419 | let b = &mut a; | ||
420 | |||
421 | if true { | ||
422 | $0*b = 2; | ||
423 | } else { | ||
424 | *b = 3; | ||
425 | } | ||
426 | } | ||
427 | "#, | ||
428 | r#" | ||
429 | fn foo() { | ||
430 | let mut a = 1; | ||
431 | let b = &mut a; | ||
432 | |||
433 | *b = if true { | ||
434 | 2 | ||
435 | } else { | ||
436 | 3 | ||
437 | }; | ||
438 | } | ||
439 | "#, | ||
440 | ) | ||
441 | } | ||
400 | } | 442 | } |
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs index b0b0d31b4..272874ae3 100644 --- a/crates/ide_assists/src/handlers/qualify_path.rs +++ b/crates/ide_assists/src/handlers/qualify_path.rs | |||
@@ -1,14 +1,16 @@ | |||
1 | use std::iter; | 1 | use std::iter; |
2 | 2 | ||
3 | use hir::{AsAssocItem, AsName}; | 3 | use hir::AsAssocItem; |
4 | use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; | 4 | use ide_db::helpers::{ |
5 | import_assets::{ImportCandidate, LocatedImport}, | ||
6 | item_name, mod_path_to_ast, | ||
7 | }; | ||
5 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
6 | use syntax::{ | 9 | use syntax::{ |
7 | ast, | 10 | ast, |
8 | ast::{make, ArgListOwner}, | 11 | ast::{make, ArgListOwner}, |
9 | AstNode, | 12 | AstNode, |
10 | }; | 13 | }; |
11 | use test_utils::mark; | ||
12 | 14 | ||
13 | use crate::{ | 15 | use crate::{ |
14 | assist_context::{AssistContext, Assists}, | 16 | assist_context::{AssistContext, Assists}, |
@@ -47,42 +49,42 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
47 | let qualify_candidate = match candidate { | 49 | let qualify_candidate = match candidate { |
48 | ImportCandidate::Path(candidate) => { | 50 | ImportCandidate::Path(candidate) => { |
49 | if candidate.qualifier.is_some() { | 51 | if candidate.qualifier.is_some() { |
50 | mark::hit!(qualify_path_qualifier_start); | 52 | cov_mark::hit!(qualify_path_qualifier_start); |
51 | let path = ast::Path::cast(syntax_under_caret)?; | 53 | let path = ast::Path::cast(syntax_under_caret)?; |
52 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); | 54 | let (prev_segment, segment) = (path.qualifier()?.segment()?, path.segment()?); |
53 | QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) | 55 | QualifyCandidate::QualifierStart(segment, prev_segment.generic_arg_list()) |
54 | } else { | 56 | } else { |
55 | mark::hit!(qualify_path_unqualified_name); | 57 | cov_mark::hit!(qualify_path_unqualified_name); |
56 | let path = ast::Path::cast(syntax_under_caret)?; | 58 | let path = ast::Path::cast(syntax_under_caret)?; |
57 | let generics = path.segment()?.generic_arg_list(); | 59 | let generics = path.segment()?.generic_arg_list(); |
58 | QualifyCandidate::UnqualifiedName(generics) | 60 | QualifyCandidate::UnqualifiedName(generics) |
59 | } | 61 | } |
60 | } | 62 | } |
61 | ImportCandidate::TraitAssocItem(_) => { | 63 | ImportCandidate::TraitAssocItem(_) => { |
62 | mark::hit!(qualify_path_trait_assoc_item); | 64 | cov_mark::hit!(qualify_path_trait_assoc_item); |
63 | let path = ast::Path::cast(syntax_under_caret)?; | 65 | let path = ast::Path::cast(syntax_under_caret)?; |
64 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); | 66 | let (qualifier, segment) = (path.qualifier()?, path.segment()?); |
65 | QualifyCandidate::TraitAssocItem(qualifier, segment) | 67 | QualifyCandidate::TraitAssocItem(qualifier, segment) |
66 | } | 68 | } |
67 | ImportCandidate::TraitMethod(_) => { | 69 | ImportCandidate::TraitMethod(_) => { |
68 | mark::hit!(qualify_path_trait_method); | 70 | cov_mark::hit!(qualify_path_trait_method); |
69 | let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; | 71 | let mcall_expr = ast::MethodCallExpr::cast(syntax_under_caret)?; |
70 | QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) | 72 | QualifyCandidate::TraitMethod(ctx.sema.db, mcall_expr) |
71 | } | 73 | } |
72 | }; | 74 | }; |
73 | 75 | ||
74 | let group_label = group_label(candidate); | 76 | let group_label = group_label(candidate); |
75 | for (import, item) in proposed_imports { | 77 | for import in proposed_imports { |
76 | acc.add_group( | 78 | acc.add_group( |
77 | &group_label, | 79 | &group_label, |
78 | AssistId("qualify_path", AssistKind::QuickFix), | 80 | AssistId("qualify_path", AssistKind::QuickFix), |
79 | label(candidate, &import), | 81 | label(ctx.db(), candidate, &import), |
80 | range, | 82 | range, |
81 | |builder| { | 83 | |builder| { |
82 | qualify_candidate.qualify( | 84 | qualify_candidate.qualify( |
83 | |replace_with: String| builder.replace(range, replace_with), | 85 | |replace_with: String| builder.replace(range, replace_with), |
84 | import, | 86 | &import.import_path, |
85 | item, | 87 | import.item_to_import, |
86 | ) | 88 | ) |
87 | }, | 89 | }, |
88 | ); | 90 | ); |
@@ -98,8 +100,13 @@ enum QualifyCandidate<'db> { | |||
98 | } | 100 | } |
99 | 101 | ||
100 | impl QualifyCandidate<'_> { | 102 | impl QualifyCandidate<'_> { |
101 | fn qualify(&self, mut replacer: impl FnMut(String), import: hir::ModPath, item: hir::ItemInNs) { | 103 | fn qualify( |
102 | let import = mod_path_to_ast(&import); | 104 | &self, |
105 | mut replacer: impl FnMut(String), | ||
106 | import: &hir::ModPath, | ||
107 | item: hir::ItemInNs, | ||
108 | ) { | ||
109 | let import = mod_path_to_ast(import); | ||
103 | match self { | 110 | match self { |
104 | QualifyCandidate::QualifierStart(segment, generics) => { | 111 | QualifyCandidate::QualifierStart(segment, generics) => { |
105 | let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); | 112 | let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); |
@@ -160,7 +167,9 @@ fn find_trait_method( | |||
160 | ) -> Option<hir::Function> { | 167 | ) -> Option<hir::Function> { |
161 | if let Some(hir::AssocItem::Function(method)) = | 168 | if let Some(hir::AssocItem::Function(method)) = |
162 | trait_.items(db).into_iter().find(|item: &hir::AssocItem| { | 169 | trait_.items(db).into_iter().find(|item: &hir::AssocItem| { |
163 | item.name(db).map(|name| name == trait_method_name.as_name()).unwrap_or(false) | 170 | item.name(db) |
171 | .map(|name| name.to_string() == trait_method_name.to_string()) | ||
172 | .unwrap_or(false) | ||
164 | }) | 173 | }) |
165 | { | 174 | { |
166 | Some(method) | 175 | Some(method) |
@@ -182,23 +191,29 @@ fn item_as_trait(db: &RootDatabase, item: hir::ItemInNs) -> Option<hir::Trait> { | |||
182 | fn group_label(candidate: &ImportCandidate) -> GroupLabel { | 191 | fn group_label(candidate: &ImportCandidate) -> GroupLabel { |
183 | let name = match candidate { | 192 | let name = match candidate { |
184 | ImportCandidate::Path(it) => &it.name, | 193 | ImportCandidate::Path(it) => &it.name, |
185 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => &it.name, | 194 | ImportCandidate::TraitAssocItem(it) | ImportCandidate::TraitMethod(it) => { |
195 | &it.assoc_item_name | ||
196 | } | ||
186 | } | 197 | } |
187 | .text(); | 198 | .text(); |
188 | GroupLabel(format!("Qualify {}", name)) | 199 | GroupLabel(format!("Qualify {}", name)) |
189 | } | 200 | } |
190 | 201 | ||
191 | fn label(candidate: &ImportCandidate, import: &hir::ModPath) -> String { | 202 | fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String { |
203 | let display_path = match item_name(db, import.original_item) { | ||
204 | Some(display_path) => display_path.to_string(), | ||
205 | None => "{unknown}".to_string(), | ||
206 | }; | ||
192 | match candidate { | 207 | match candidate { |
193 | ImportCandidate::Path(candidate) => { | 208 | ImportCandidate::Path(candidate) => { |
194 | if candidate.qualifier.is_some() { | 209 | if candidate.qualifier.is_some() { |
195 | format!("Qualify with `{}`", &import) | 210 | format!("Qualify with `{}`", display_path) |
196 | } else { | 211 | } else { |
197 | format!("Qualify as `{}`", &import) | 212 | format!("Qualify as `{}`", display_path) |
198 | } | 213 | } |
199 | } | 214 | } |
200 | ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", &import), | 215 | ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path), |
201 | ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", &import), | 216 | ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path), |
202 | } | 217 | } |
203 | } | 218 | } |
204 | 219 | ||
@@ -210,7 +225,7 @@ mod tests { | |||
210 | 225 | ||
211 | #[test] | 226 | #[test] |
212 | fn applicable_when_found_an_import_partial() { | 227 | fn applicable_when_found_an_import_partial() { |
213 | mark::check!(qualify_path_unqualified_name); | 228 | cov_mark::check!(qualify_path_unqualified_name); |
214 | check_assist( | 229 | check_assist( |
215 | qualify_path, | 230 | qualify_path, |
216 | r" | 231 | r" |
@@ -502,7 +517,7 @@ fn main() { | |||
502 | 517 | ||
503 | #[test] | 518 | #[test] |
504 | fn associated_struct_const() { | 519 | fn associated_struct_const() { |
505 | mark::check!(qualify_path_qualifier_start); | 520 | cov_mark::check!(qualify_path_qualifier_start); |
506 | check_assist( | 521 | check_assist( |
507 | qualify_path, | 522 | qualify_path, |
508 | r" | 523 | r" |
@@ -603,7 +618,7 @@ fn main() { | |||
603 | 618 | ||
604 | #[test] | 619 | #[test] |
605 | fn associated_trait_const() { | 620 | fn associated_trait_const() { |
606 | mark::check!(qualify_path_trait_assoc_item); | 621 | cov_mark::check!(qualify_path_trait_assoc_item); |
607 | check_assist( | 622 | check_assist( |
608 | qualify_path, | 623 | qualify_path, |
609 | r" | 624 | r" |
@@ -673,7 +688,7 @@ fn main() { | |||
673 | 688 | ||
674 | #[test] | 689 | #[test] |
675 | fn trait_method() { | 690 | fn trait_method() { |
676 | mark::check!(qualify_path_trait_method); | 691 | cov_mark::check!(qualify_path_trait_method); |
677 | check_assist( | 692 | check_assist( |
678 | qualify_path, | 693 | qualify_path, |
679 | r" | 694 | r" |
diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d95267607..d0f1613f3 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | use std::borrow::Cow; | 1 | use std::borrow::Cow; |
2 | 2 | ||
3 | use syntax::{ast, AstToken, TextRange, TextSize}; | 3 | use syntax::{ast, AstToken, TextRange, TextSize}; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
7 | 6 | ||
@@ -149,7 +148,7 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
149 | let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; | 148 | let internal_text = &text[token.text_range_between_quotes()? - text_range.start()]; |
150 | 149 | ||
151 | if existing_hashes == required_hashes(internal_text) { | 150 | if existing_hashes == required_hashes(internal_text) { |
152 | mark::hit!(cant_remove_required_hash); | 151 | cov_mark::hit!(cant_remove_required_hash); |
153 | return None; | 152 | return None; |
154 | } | 153 | } |
155 | 154 | ||
@@ -182,8 +181,6 @@ fn test_required_hashes() { | |||
182 | 181 | ||
183 | #[cfg(test)] | 182 | #[cfg(test)] |
184 | mod tests { | 183 | mod tests { |
185 | use test_utils::mark; | ||
186 | |||
187 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; | 184 | use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; |
188 | 185 | ||
189 | use super::*; | 186 | use super::*; |
@@ -396,7 +393,7 @@ string"###; | |||
396 | 393 | ||
397 | #[test] | 394 | #[test] |
398 | fn cant_remove_required_hash() { | 395 | fn cant_remove_required_hash() { |
399 | mark::check!(cant_remove_required_hash); | 396 | cov_mark::check!(cant_remove_required_hash); |
400 | check_assist_not_applicable( | 397 | check_assist_not_applicable( |
401 | remove_hash, | 398 | remove_hash, |
402 | r##" | 399 | r##" |
diff --git a/crates/ide_assists/src/handlers/remove_unused_param.rs b/crates/ide_assists/src/handlers/remove_unused_param.rs index c961680e2..2699d2861 100644 --- a/crates/ide_assists/src/handlers/remove_unused_param.rs +++ b/crates/ide_assists/src/handlers/remove_unused_param.rs | |||
@@ -4,7 +4,7 @@ use syntax::{ | |||
4 | ast::{self, ArgListOwner}, | 4 | ast::{self, ArgListOwner}, |
5 | AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, | 5 | AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, T, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | |
8 | use SyntaxKind::WHITESPACE; | 8 | use SyntaxKind::WHITESPACE; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
@@ -49,7 +49,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Opt | |||
49 | Definition::Local(local) | 49 | Definition::Local(local) |
50 | }; | 50 | }; |
51 | if param_def.usages(&ctx.sema).at_least_one() { | 51 | if param_def.usages(&ctx.sema).at_least_one() { |
52 | mark::hit!(keep_used); | 52 | cov_mark::hit!(keep_used); |
53 | return None; | 53 | return None; |
54 | } | 54 | } |
55 | acc.add( | 55 | acc.add( |
@@ -243,7 +243,7 @@ fn b2() { foo(9) } | |||
243 | 243 | ||
244 | #[test] | 244 | #[test] |
245 | fn keep_used() { | 245 | fn keep_used() { |
246 | mark::check!(keep_used); | 246 | cov_mark::check!(keep_used); |
247 | check_assist_not_applicable( | 247 | check_assist_not_applicable( |
248 | remove_unused_param, | 248 | remove_unused_param, |
249 | r#" | 249 | r#" |
diff --git a/crates/ide_assists/src/handlers/reorder_fields.rs b/crates/ide_assists/src/handlers/reorder_fields.rs index fba7d6ddb..794c89323 100644 --- a/crates/ide_assists/src/handlers/reorder_fields.rs +++ b/crates/ide_assists/src/handlers/reorder_fields.rs | |||
@@ -4,7 +4,6 @@ use rustc_hash::FxHashMap; | |||
4 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; | 4 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; |
5 | use ide_db::RootDatabase; | 5 | use ide_db::RootDatabase; |
6 | use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; | 6 | use syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
@@ -39,7 +38,7 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | |||
39 | }); | 38 | }); |
40 | 39 | ||
41 | if sorted_fields == fields { | 40 | if sorted_fields == fields { |
42 | mark::hit!(reorder_sorted_fields); | 41 | cov_mark::hit!(reorder_sorted_fields); |
43 | return None; | 42 | return None; |
44 | } | 43 | } |
45 | 44 | ||
@@ -109,15 +108,13 @@ fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<FxHashM | |||
109 | 108 | ||
110 | #[cfg(test)] | 109 | #[cfg(test)] |
111 | mod tests { | 110 | mod tests { |
112 | use test_utils::mark; | ||
113 | |||
114 | use crate::tests::{check_assist, check_assist_not_applicable}; | 111 | use crate::tests::{check_assist, check_assist_not_applicable}; |
115 | 112 | ||
116 | use super::*; | 113 | use super::*; |
117 | 114 | ||
118 | #[test] | 115 | #[test] |
119 | fn reorder_sorted_fields() { | 116 | fn reorder_sorted_fields() { |
120 | mark::check!(reorder_sorted_fields); | 117 | cov_mark::check!(reorder_sorted_fields); |
121 | check_assist_not_applicable( | 118 | check_assist_not_applicable( |
122 | reorder_fields, | 119 | reorder_fields, |
123 | r#" | 120 | r#" |
diff --git a/crates/ide_assists/src/handlers/reorder_impl.rs b/crates/ide_assists/src/handlers/reorder_impl.rs index 309f291c8..edf4b0bfe 100644 --- a/crates/ide_assists/src/handlers/reorder_impl.rs +++ b/crates/ide_assists/src/handlers/reorder_impl.rs | |||
@@ -8,7 +8,6 @@ use syntax::{ | |||
8 | ast::{self, NameOwner}, | 8 | ast::{self, NameOwner}, |
9 | AstNode, | 9 | AstNode, |
10 | }; | 10 | }; |
11 | use test_utils::mark; | ||
12 | 11 | ||
13 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 12 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
14 | 13 | ||
@@ -71,7 +70,7 @@ pub(crate) fn reorder_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
71 | 70 | ||
72 | // Don't edit already sorted methods: | 71 | // Don't edit already sorted methods: |
73 | if methods == sorted { | 72 | if methods == sorted { |
74 | mark::hit!(not_applicable_if_sorted); | 73 | cov_mark::hit!(not_applicable_if_sorted); |
75 | return None; | 74 | return None; |
76 | } | 75 | } |
77 | 76 | ||
@@ -121,15 +120,13 @@ fn get_methods(items: &ast::AssocItemList) -> Vec<ast::Fn> { | |||
121 | 120 | ||
122 | #[cfg(test)] | 121 | #[cfg(test)] |
123 | mod tests { | 122 | mod tests { |
124 | use test_utils::mark; | ||
125 | |||
126 | use crate::tests::{check_assist, check_assist_not_applicable}; | 123 | use crate::tests::{check_assist, check_assist_not_applicable}; |
127 | 124 | ||
128 | use super::*; | 125 | use super::*; |
129 | 126 | ||
130 | #[test] | 127 | #[test] |
131 | fn not_applicable_if_sorted() { | 128 | fn not_applicable_if_sorted() { |
132 | mark::check!(not_applicable_if_sorted); | 129 | cov_mark::check!(not_applicable_if_sorted); |
133 | check_assist_not_applicable( | 130 | check_assist_not_applicable( |
134 | reorder_impl, | 131 | reorder_impl, |
135 | r#" | 132 | r#" |
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs index c69bc5cac..88fe2fe90 100644 --- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use hir::ModuleDef; | ||
1 | use ide_db::helpers::mod_path_to_ast; | 2 | use ide_db::helpers::mod_path_to_ast; |
2 | use ide_db::imports_locator; | 3 | use ide_db::items_locator; |
3 | use itertools::Itertools; | 4 | use itertools::Itertools; |
4 | use syntax::{ | 5 | use syntax::{ |
5 | ast::{self, make, AstNode, NameOwner}, | 6 | ast::{self, make, AstNode, NameOwner}, |
@@ -64,22 +65,20 @@ pub(crate) fn replace_derive_with_manual_impl( | |||
64 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; | 65 | let current_module = ctx.sema.scope(annotated_name.syntax()).module()?; |
65 | let current_crate = current_module.krate(); | 66 | let current_crate = current_module.krate(); |
66 | 67 | ||
67 | let found_traits = imports_locator::find_exact_imports( | 68 | let found_traits = |
68 | &ctx.sema, | 69 | items_locator::with_exact_name(&ctx.sema, current_crate, trait_token.text().to_string()) |
69 | current_crate, | 70 | .into_iter() |
70 | trait_token.text().to_string(), | 71 | .filter_map(|item| match ModuleDef::from(item.as_module_def_id()?) { |
71 | ) | 72 | ModuleDef::Trait(trait_) => Some(trait_), |
72 | .filter_map(|candidate: either::Either<hir::ModuleDef, hir::MacroDef>| match candidate { | 73 | _ => None, |
73 | either::Either::Left(hir::ModuleDef::Trait(trait_)) => Some(trait_), | 74 | }) |
74 | _ => None, | 75 | .flat_map(|trait_| { |
75 | }) | 76 | current_module |
76 | .flat_map(|trait_| { | 77 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) |
77 | current_module | 78 | .as_ref() |
78 | .find_use_path(ctx.sema.db, hir::ModuleDef::Trait(trait_)) | 79 | .map(mod_path_to_ast) |
79 | .as_ref() | 80 | .zip(Some(trait_)) |
80 | .map(mod_path_to_ast) | 81 | }); |
81 | .zip(Some(trait_)) | ||
82 | }); | ||
83 | 82 | ||
84 | let mut no_traits_found = true; | 83 | let mut no_traits_found = true; |
85 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { | 84 | for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { |
diff --git a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs index 27da28bc0..50b05ab0b 100644 --- a/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs +++ b/crates/ide_assists/src/handlers/replace_for_loop_with_for_each.rs | |||
@@ -3,7 +3,6 @@ use hir::known; | |||
3 | use ide_db::helpers::FamousDefs; | 3 | use ide_db::helpers::FamousDefs; |
4 | use stdx::format_to; | 4 | use stdx::format_to; |
5 | use syntax::{ast, AstNode}; | 5 | use syntax::{ast, AstNode}; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 7 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
9 | 8 | ||
@@ -34,7 +33,7 @@ pub(crate) fn replace_for_loop_with_for_each(acc: &mut Assists, ctx: &AssistCont | |||
34 | let pat = for_loop.pat()?; | 33 | let pat = for_loop.pat()?; |
35 | let body = for_loop.loop_body()?; | 34 | let body = for_loop.loop_body()?; |
36 | if body.syntax().text_range().start() < ctx.offset() { | 35 | if body.syntax().text_range().start() < ctx.offset() { |
37 | mark::hit!(not_available_in_body); | 36 | cov_mark::hit!(not_available_in_body); |
38 | return None; | 37 | return None; |
39 | } | 38 | } |
40 | 39 | ||
@@ -187,7 +186,7 @@ fn main() { | |||
187 | 186 | ||
188 | #[test] | 187 | #[test] |
189 | fn not_available_in_body() { | 188 | fn not_available_in_body() { |
190 | mark::check!(not_available_in_body); | 189 | cov_mark::check!(not_available_in_body); |
191 | check_assist_not_applicable( | 190 | check_assist_not_applicable( |
192 | replace_for_loop_with_for_each, | 191 | replace_for_loop_with_for_each, |
193 | r" | 192 | r" |
diff --git a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs index f3bc6cf39..36d2e0331 100644 --- a/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs +++ b/crates/ide_assists/src/handlers/replace_qualified_name_with_use.rs | |||
@@ -1,6 +1,5 @@ | |||
1 | use ide_db::helpers::insert_use::{insert_use, ImportScope}; | 1 | use ide_db::helpers::insert_use::{insert_use, ImportScope}; |
2 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; | 2 | use syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SyntaxNode}; |
3 | use test_utils::mark; | ||
4 | 3 | ||
5 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 4 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
6 | 5 | ||
@@ -27,7 +26,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
27 | return None; | 26 | return None; |
28 | } | 27 | } |
29 | if path.qualifier().is_none() { | 28 | if path.qualifier().is_none() { |
30 | mark::hit!(dont_import_trivial_paths); | 29 | cov_mark::hit!(dont_import_trivial_paths); |
31 | return None; | 30 | return None; |
32 | } | 31 | } |
33 | 32 | ||
@@ -44,7 +43,7 @@ pub(crate) fn replace_qualified_name_with_use( | |||
44 | let mut rewriter = SyntaxRewriter::default(); | 43 | let mut rewriter = SyntaxRewriter::default(); |
45 | shorten_paths(&mut rewriter, syntax.clone(), &path); | 44 | shorten_paths(&mut rewriter, syntax.clone(), &path); |
46 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { | 45 | if let Some(ref import_scope) = ImportScope::from(syntax.clone()) { |
47 | rewriter += insert_use(import_scope, path, ctx.config.insert_use.merge); | 46 | rewriter += insert_use(import_scope, path, ctx.config.insert_use); |
48 | builder.rewrite(rewriter); | 47 | builder.rewrite(rewriter); |
49 | } | 48 | } |
50 | }, | 49 | }, |
@@ -458,7 +457,7 @@ impl Debug for Foo { | |||
458 | 457 | ||
459 | #[test] | 458 | #[test] |
460 | fn dont_import_trivial_paths() { | 459 | fn dont_import_trivial_paths() { |
461 | mark::check!(dont_import_trivial_paths); | 460 | cov_mark::check!(dont_import_trivial_paths); |
462 | check_assist_not_applicable( | 461 | check_assist_not_applicable( |
463 | replace_qualified_name_with_use, | 462 | replace_qualified_name_with_use, |
464 | r" | 463 | r" |
diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 317318c24..634b9c0b7 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs | |||
@@ -25,13 +25,16 @@ pub(crate) fn replace_string_with_char(acc: &mut Assists, ctx: &AssistContext) - | |||
25 | if value.chars().take(2).count() != 1 { | 25 | if value.chars().take(2).count() != 1 { |
26 | return None; | 26 | return None; |
27 | } | 27 | } |
28 | let quote_offets = token.quote_offsets()?; | ||
28 | 29 | ||
29 | acc.add( | 30 | acc.add( |
30 | AssistId("replace_string_with_char", AssistKind::RefactorRewrite), | 31 | AssistId("replace_string_with_char", AssistKind::RefactorRewrite), |
31 | "Replace string with char", | 32 | "Replace string with char", |
32 | target, | 33 | target, |
33 | |edit| { | 34 | |edit| { |
34 | edit.replace(token.syntax().text_range(), format!("'{}'", value)); | 35 | let (left, right) = quote_offets.quotes; |
36 | edit.replace(left, String::from('\'')); | ||
37 | edit.replace(right, String::from('\'')); | ||
35 | }, | 38 | }, |
36 | ) | 39 | ) |
37 | } | 40 | } |
@@ -47,10 +50,10 @@ mod tests { | |||
47 | check_assist_target( | 50 | check_assist_target( |
48 | replace_string_with_char, | 51 | replace_string_with_char, |
49 | r#" | 52 | r#" |
50 | fn f() { | 53 | fn f() { |
51 | let s = "$0c"; | 54 | let s = "$0c"; |
52 | } | 55 | } |
53 | "#, | 56 | "#, |
54 | r#""c""#, | 57 | r#""c""#, |
55 | ); | 58 | ); |
56 | } | 59 | } |
@@ -60,15 +63,15 @@ mod tests { | |||
60 | check_assist( | 63 | check_assist( |
61 | replace_string_with_char, | 64 | replace_string_with_char, |
62 | r#" | 65 | r#" |
63 | fn f() { | 66 | fn f() { |
64 | let s = "$0c"; | 67 | let s = "$0c"; |
65 | } | 68 | } |
66 | "#, | 69 | "#, |
67 | r##" | 70 | r##" |
68 | fn f() { | 71 | fn f() { |
69 | let s = 'c'; | 72 | let s = 'c'; |
70 | } | 73 | } |
71 | "##, | 74 | "##, |
72 | ) | 75 | ) |
73 | } | 76 | } |
74 | 77 | ||
@@ -77,15 +80,15 @@ mod tests { | |||
77 | check_assist( | 80 | check_assist( |
78 | replace_string_with_char, | 81 | replace_string_with_char, |
79 | r#" | 82 | r#" |
80 | fn f() { | 83 | fn f() { |
81 | let s = "$0😀"; | 84 | let s = "$0😀"; |
82 | } | 85 | } |
83 | "#, | 86 | "#, |
84 | r##" | 87 | r##" |
85 | fn f() { | 88 | fn f() { |
86 | let s = '😀'; | 89 | let s = '😀'; |
87 | } | 90 | } |
88 | "##, | 91 | "##, |
89 | ) | 92 | ) |
90 | } | 93 | } |
91 | 94 | ||
@@ -94,10 +97,10 @@ mod tests { | |||
94 | check_assist_not_applicable( | 97 | check_assist_not_applicable( |
95 | replace_string_with_char, | 98 | replace_string_with_char, |
96 | r#" | 99 | r#" |
97 | fn f() { | 100 | fn f() { |
98 | let s = "$0test"; | 101 | let s = "$0test"; |
99 | } | 102 | } |
100 | "#, | 103 | "#, |
101 | ) | 104 | ) |
102 | } | 105 | } |
103 | 106 | ||
@@ -106,15 +109,15 @@ mod tests { | |||
106 | check_assist( | 109 | check_assist( |
107 | replace_string_with_char, | 110 | replace_string_with_char, |
108 | r#" | 111 | r#" |
109 | fn f() { | 112 | fn f() { |
110 | format!($0"x", 92) | 113 | format!($0"x", 92) |
111 | } | 114 | } |
112 | "#, | 115 | "#, |
113 | r##" | 116 | r##" |
114 | fn f() { | 117 | fn f() { |
115 | format!('x', 92) | 118 | format!('x', 92) |
116 | } | 119 | } |
117 | "##, | 120 | "##, |
118 | ) | 121 | ) |
119 | } | 122 | } |
120 | 123 | ||
@@ -123,15 +126,66 @@ mod tests { | |||
123 | check_assist( | 126 | check_assist( |
124 | replace_string_with_char, | 127 | replace_string_with_char, |
125 | r#" | 128 | r#" |
126 | fn f() { | 129 | fn f() { |
127 | find($0"x"); | 130 | find($0"x"); |
128 | } | 131 | } |
129 | "#, | 132 | "#, |
130 | r##" | 133 | r##" |
131 | fn f() { | 134 | fn f() { |
132 | find('x'); | 135 | find('x'); |
133 | } | 136 | } |
134 | "##, | 137 | "##, |
138 | ) | ||
139 | } | ||
140 | |||
141 | #[test] | ||
142 | fn replace_string_with_char_newline() { | ||
143 | check_assist( | ||
144 | replace_string_with_char, | ||
145 | r#" | ||
146 | fn f() { | ||
147 | find($0"\n"); | ||
148 | } | ||
149 | "#, | ||
150 | r##" | ||
151 | fn f() { | ||
152 | find('\n'); | ||
153 | } | ||
154 | "##, | ||
155 | ) | ||
156 | } | ||
157 | |||
158 | #[test] | ||
159 | fn replace_string_with_char_unicode_escape() { | ||
160 | check_assist( | ||
161 | replace_string_with_char, | ||
162 | r#" | ||
163 | fn f() { | ||
164 | find($0"\u{7FFF}"); | ||
165 | } | ||
166 | "#, | ||
167 | r##" | ||
168 | fn f() { | ||
169 | find('\u{7FFF}'); | ||
170 | } | ||
171 | "##, | ||
172 | ) | ||
173 | } | ||
174 | |||
175 | #[test] | ||
176 | fn replace_raw_string_with_char() { | ||
177 | check_assist( | ||
178 | replace_string_with_char, | ||
179 | r##" | ||
180 | fn f() { | ||
181 | $0r#"X"# | ||
182 | } | ||
183 | "##, | ||
184 | r##" | ||
185 | fn f() { | ||
186 | 'X' | ||
187 | } | ||
188 | "##, | ||
135 | ) | 189 | ) |
136 | } | 190 | } |
137 | } | 191 | } |
diff --git a/crates/ide_assists/src/handlers/unmerge_use.rs b/crates/ide_assists/src/handlers/unmerge_use.rs index 3dbef8e51..616af7c2e 100644 --- a/crates/ide_assists/src/handlers/unmerge_use.rs +++ b/crates/ide_assists/src/handlers/unmerge_use.rs | |||
@@ -3,7 +3,6 @@ use syntax::{ | |||
3 | ast::{self, edit::AstNodeEdit, VisibilityOwner}, | 3 | ast::{self, edit::AstNodeEdit, VisibilityOwner}, |
4 | AstNode, SyntaxKind, | 4 | AstNode, SyntaxKind, |
5 | }; | 5 | }; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | assist_context::{AssistContext, Assists}, | 8 | assist_context::{AssistContext, Assists}, |
@@ -27,7 +26,7 @@ pub(crate) fn unmerge_use(acc: &mut Assists, ctx: &AssistContext) -> Option<()> | |||
27 | 26 | ||
28 | let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; | 27 | let tree_list = tree.syntax().parent().and_then(ast::UseTreeList::cast)?; |
29 | if tree_list.use_trees().count() < 2 { | 28 | if tree_list.use_trees().count() < 2 { |
30 | mark::hit!(skip_single_use_item); | 29 | cov_mark::hit!(skip_single_use_item); |
31 | return None; | 30 | return None; |
32 | } | 31 | } |
33 | 32 | ||
@@ -89,7 +88,7 @@ mod tests { | |||
89 | 88 | ||
90 | #[test] | 89 | #[test] |
91 | fn skip_single_use_item() { | 90 | fn skip_single_use_item() { |
92 | mark::check!(skip_single_use_item); | 91 | cov_mark::check!(skip_single_use_item); |
93 | check_assist_not_applicable( | 92 | check_assist_not_applicable( |
94 | unmerge_use, | 93 | unmerge_use, |
95 | r" | 94 | r" |
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs index fec16fc49..e838630ea 100644 --- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -4,7 +4,6 @@ use syntax::{ | |||
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | 4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, |
5 | match_ast, AstNode, SyntaxNode, | 5 | match_ast, AstNode, SyntaxNode, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | ||
8 | 7 | ||
9 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 8 | use crate::{AssistContext, AssistId, AssistKind, Assists}; |
10 | 9 | ||
@@ -39,7 +38,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext) | |||
39 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); | 38 | let first_part_ret_type = ret_type_str.splitn(2, '<').next(); |
40 | if let Some(ret_type_first_part) = first_part_ret_type { | 39 | if let Some(ret_type_first_part) = first_part_ret_type { |
41 | if ret_type_first_part.ends_with("Result") { | 40 | if ret_type_first_part.ends_with("Result") { |
42 | mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); | 41 | cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); |
43 | return None; | 42 | return None; |
44 | } | 43 | } |
45 | } | 44 | } |
@@ -367,7 +366,7 @@ fn foo() -> std::result::Result<i32$0, String> { | |||
367 | 366 | ||
368 | #[test] | 367 | #[test] |
369 | fn wrap_return_type_in_result_simple_return_type_already_result() { | 368 | fn wrap_return_type_in_result_simple_return_type_already_result() { |
370 | mark::check!(wrap_return_type_in_result_simple_return_type_already_result); | 369 | cov_mark::check!(wrap_return_type_in_result_simple_return_type_already_result); |
371 | check_assist_not_applicable( | 370 | check_assist_not_applicable( |
372 | wrap_return_type_in_result, | 371 | wrap_return_type_in_result, |
373 | r#" | 372 | r#" |
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 9c8148462..ea62d5f5d 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -127,6 +127,7 @@ mod handlers { | |||
127 | mod flip_comma; | 127 | mod flip_comma; |
128 | mod flip_trait_bound; | 128 | mod flip_trait_bound; |
129 | mod generate_default_from_enum_variant; | 129 | mod generate_default_from_enum_variant; |
130 | mod generate_default_from_new; | ||
130 | mod generate_derive; | 131 | mod generate_derive; |
131 | mod generate_enum_is_method; | 132 | mod generate_enum_is_method; |
132 | mod generate_enum_projection_method; | 133 | mod generate_enum_projection_method; |
@@ -189,6 +190,7 @@ mod handlers { | |||
189 | flip_comma::flip_comma, | 190 | flip_comma::flip_comma, |
190 | flip_trait_bound::flip_trait_bound, | 191 | flip_trait_bound::flip_trait_bound, |
191 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 192 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
193 | generate_default_from_new::generate_default_from_new, | ||
192 | generate_derive::generate_derive, | 194 | generate_derive::generate_derive, |
193 | generate_enum_is_method::generate_enum_is_method, | 195 | generate_enum_is_method::generate_enum_is_method, |
194 | generate_enum_projection_method::generate_enum_as_method, | 196 | generate_enum_projection_method::generate_enum_as_method, |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index b7f616760..a7a923beb 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -23,6 +23,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { | |||
23 | insert_use: InsertUseConfig { | 23 | insert_use: InsertUseConfig { |
24 | merge: Some(MergeBehavior::Full), | 24 | merge: Some(MergeBehavior::Full), |
25 | prefix_kind: hir::PrefixKind::Plain, | 25 | prefix_kind: hir::PrefixKind::Plain, |
26 | group: true, | ||
26 | }, | 27 | }, |
27 | }; | 28 | }; |
28 | 29 | ||
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 4f007aa48..304b5798f 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -440,6 +440,37 @@ impl Default for Version { | |||
440 | } | 440 | } |
441 | 441 | ||
442 | #[test] | 442 | #[test] |
443 | fn doctest_generate_default_from_new() { | ||
444 | check_doc_test( | ||
445 | "generate_default_from_new", | ||
446 | r#####" | ||
447 | struct Example { _inner: () } | ||
448 | |||
449 | impl Example { | ||
450 | pub fn n$0ew() -> Self { | ||
451 | Self { _inner: () } | ||
452 | } | ||
453 | } | ||
454 | "#####, | ||
455 | r#####" | ||
456 | struct Example { _inner: () } | ||
457 | |||
458 | impl Example { | ||
459 | pub fn new() -> Self { | ||
460 | Self { _inner: () } | ||
461 | } | ||
462 | } | ||
463 | |||
464 | impl Default for Example { | ||
465 | fn default() -> Self { | ||
466 | Self::new() | ||
467 | } | ||
468 | } | ||
469 | "#####, | ||
470 | ) | ||
471 | } | ||
472 | |||
473 | #[test] | ||
443 | fn doctest_generate_derive() { | 474 | fn doctest_generate_derive() { |
444 | check_doc_test( | 475 | check_doc_test( |
445 | "generate_derive", | 476 | "generate_derive", |
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml index c09101ccb..585ecca50 100644 --- a/crates/ide_completion/Cargo.toml +++ b/crates/ide_completion/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
14 | log = "0.4.8" | 15 | log = "0.4.8" |
15 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
@@ -21,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
21 | base_db = { path = "../base_db", version = "0.0.0" } | 22 | base_db = { path = "../base_db", version = "0.0.0" } |
22 | ide_db = { path = "../ide_db", version = "0.0.0" } | 23 | ide_db = { path = "../ide_db", version = "0.0.0" } |
23 | profile = { path = "../profile", version = "0.0.0" } | 24 | profile = { path = "../profile", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
25 | 25 | ||
26 | # completions crate should depend only on the top-level `hir` package. if you need | 26 | # completions crate should depend only on the top-level `hir` package. if you need |
27 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 27 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
28 | hir = { path = "../hir", version = "0.0.0" } | 28 | hir = { path = "../hir", version = "0.0.0" } |
29 | 29 | ||
30 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | test_utils = { path = "../test_utils" } | ||
31 | expect-test = "1.1" | 32 | expect-test = "1.1" |
diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 084d7721d..5ee9a9f07 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use hir::{HasVisibility, Type}; | 3 | use hir::{HasVisibility, Type}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{context::CompletionContext, Completions}; | 6 | use crate::{context::CompletionContext, Completions}; |
8 | 7 | ||
@@ -19,7 +18,7 @@ pub(crate) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { | |||
19 | }; | 18 | }; |
20 | 19 | ||
21 | if ctx.is_call { | 20 | if ctx.is_call { |
22 | mark::hit!(test_no_struct_field_completion_for_method_call); | 21 | cov_mark::hit!(test_no_struct_field_completion_for_method_call); |
23 | } else { | 22 | } else { |
24 | complete_fields(acc, ctx, &receiver_ty); | 23 | complete_fields(acc, ctx, &receiver_ty); |
25 | } | 24 | } |
@@ -62,7 +61,6 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T | |||
62 | #[cfg(test)] | 61 | #[cfg(test)] |
63 | mod tests { | 62 | mod tests { |
64 | use expect_test::{expect, Expect}; | 63 | use expect_test::{expect, Expect}; |
65 | use test_utils::mark; | ||
66 | 64 | ||
67 | use crate::{test_utils::completion_list, CompletionKind}; | 65 | use crate::{test_utils::completion_list, CompletionKind}; |
68 | 66 | ||
@@ -122,7 +120,7 @@ impl A { | |||
122 | 120 | ||
123 | #[test] | 121 | #[test] |
124 | fn test_no_struct_field_completion_for_method_call() { | 122 | fn test_no_struct_field_completion_for_method_call() { |
125 | mark::check!(test_no_struct_field_completion_for_method_call); | 123 | cov_mark::check!(test_no_struct_field_completion_for_method_call); |
126 | check( | 124 | check( |
127 | r#" | 125 | r#" |
128 | struct A { the_field: u32 } | 126 | struct A { the_field: u32 } |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index da8375af9..391a11c91 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -21,6 +21,46 @@ | |||
21 | //! ``` | 21 | //! ``` |
22 | //! | 22 | //! |
23 | //! Also completes associated items, that require trait imports. | 23 | //! Also completes associated items, that require trait imports. |
24 | //! If any unresolved and/or partially-qualified path predeces the input, it will be taken into account. | ||
25 | //! Currently, only the imports with their import path ending with the whole qialifier will be proposed | ||
26 | //! (no fuzzy matching for qualifier). | ||
27 | //! | ||
28 | //! ``` | ||
29 | //! mod foo { | ||
30 | //! pub mod bar { | ||
31 | //! pub struct Item; | ||
32 | //! | ||
33 | //! impl Item { | ||
34 | //! pub const TEST_ASSOC: usize = 3; | ||
35 | //! } | ||
36 | //! } | ||
37 | //! } | ||
38 | //! | ||
39 | //! fn main() { | ||
40 | //! bar::Item::TEST_A$0 | ||
41 | //! } | ||
42 | //! ``` | ||
43 | //! -> | ||
44 | //! ``` | ||
45 | //! use foo::bar; | ||
46 | //! | ||
47 | //! mod foo { | ||
48 | //! pub mod bar { | ||
49 | //! pub struct Item; | ||
50 | //! | ||
51 | //! impl Item { | ||
52 | //! pub const TEST_ASSOC: usize = 3; | ||
53 | //! } | ||
54 | //! } | ||
55 | //! } | ||
56 | //! | ||
57 | //! fn main() { | ||
58 | //! bar::Item::TEST_ASSOC | ||
59 | //! } | ||
60 | //! ``` | ||
61 | //! | ||
62 | //! NOTE: currently, if an assoc item comes from a trait that's not currently imported and it also has an unresolved and/or partially-qualified path, | ||
63 | //! no imports will be proposed. | ||
24 | //! | 64 | //! |
25 | //! .Fuzzy search details | 65 | //! .Fuzzy search details |
26 | //! | 66 | //! |
@@ -48,14 +88,13 @@ | |||
48 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 88 | //! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding |
49 | //! capability enabled. | 89 | //! capability enabled. |
50 | 90 | ||
51 | use hir::{AsAssocItem, ModPath, ScopeDef}; | 91 | use hir::ModPath; |
52 | use ide_db::helpers::{ | 92 | use ide_db::helpers::{ |
53 | import_assets::{ImportAssets, ImportCandidate}, | 93 | import_assets::{ImportAssets, ImportCandidate}, |
54 | insert_use::ImportScope, | 94 | insert_use::ImportScope, |
55 | }; | 95 | }; |
56 | use rustc_hash::FxHashSet; | 96 | use itertools::Itertools; |
57 | use syntax::{AstNode, SyntaxNode, T}; | 97 | use syntax::{AstNode, SyntaxNode, T}; |
58 | use test_utils::mark; | ||
59 | 98 | ||
60 | use crate::{ | 99 | use crate::{ |
61 | context::CompletionContext, | 100 | context::CompletionContext, |
@@ -93,50 +132,26 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext) | |||
93 | &ctx.sema, | 132 | &ctx.sema, |
94 | )?; | 133 | )?; |
95 | 134 | ||
96 | let scope_definitions = scope_definitions(ctx); | 135 | acc.add_all( |
97 | let mut all_mod_paths = import_assets | 136 | import_assets |
98 | .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) | 137 | .search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind) |
99 | .into_iter() | 138 | .into_iter() |
100 | .map(|(mod_path, item_in_ns)| { | 139 | .sorted_by_key(|located_import| { |
101 | let scope_item = match item_in_ns { | 140 | compute_fuzzy_completion_order_key( |
102 | hir::ItemInNs::Types(id) => ScopeDef::ModuleDef(id.into()), | 141 | &located_import.import_path, |
103 | hir::ItemInNs::Values(id) => ScopeDef::ModuleDef(id.into()), | 142 | &user_input_lowercased, |
104 | hir::ItemInNs::Macros(id) => ScopeDef::MacroDef(id.into()), | 143 | ) |
105 | }; | 144 | }) |
106 | (mod_path, scope_item) | 145 | .filter_map(|import| { |
107 | }) | 146 | render_resolution_with_import( |
108 | .filter(|(_, proposed_def)| !scope_definitions.contains(proposed_def)) | 147 | RenderContext::new(ctx), |
109 | .collect::<Vec<_>>(); | 148 | ImportEdit { import, scope: import_scope.clone() }, |
110 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { | 149 | ) |
111 | compute_fuzzy_completion_order_key(mod_path, &user_input_lowercased) | 150 | }), |
112 | }); | 151 | ); |
113 | |||
114 | acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { | ||
115 | let import_for_trait_assoc_item = match definition { | ||
116 | ScopeDef::ModuleDef(module_def) => module_def | ||
117 | .as_assoc_item(ctx.db) | ||
118 | .and_then(|assoc| assoc.containing_trait(ctx.db)) | ||
119 | .is_some(), | ||
120 | _ => false, | ||
121 | }; | ||
122 | let import_edit = ImportEdit { | ||
123 | import_path, | ||
124 | import_scope: import_scope.clone(), | ||
125 | import_for_trait_assoc_item, | ||
126 | }; | ||
127 | render_resolution_with_import(RenderContext::new(ctx), import_edit, &definition) | ||
128 | })); | ||
129 | Some(()) | 152 | Some(()) |
130 | } | 153 | } |
131 | 154 | ||
132 | fn scope_definitions(ctx: &CompletionContext) -> FxHashSet<ScopeDef> { | ||
133 | let mut scope_definitions = FxHashSet::default(); | ||
134 | ctx.scope.process_all_names(&mut |_, scope_def| { | ||
135 | scope_definitions.insert(scope_def); | ||
136 | }); | ||
137 | scope_definitions | ||
138 | } | ||
139 | |||
140 | pub(crate) fn position_for_import<'a>( | 155 | pub(crate) fn position_for_import<'a>( |
141 | ctx: &'a CompletionContext, | 156 | ctx: &'a CompletionContext, |
142 | import_candidate: Option<&ImportCandidate>, | 157 | import_candidate: Option<&ImportCandidate>, |
@@ -161,23 +176,30 @@ fn import_assets(ctx: &CompletionContext, fuzzy_name: String) -> Option<ImportAs | |||
161 | current_module, | 176 | current_module, |
162 | ctx.sema.type_of_expr(dot_receiver)?, | 177 | ctx.sema.type_of_expr(dot_receiver)?, |
163 | fuzzy_name, | 178 | fuzzy_name, |
179 | dot_receiver.syntax().clone(), | ||
164 | ) | 180 | ) |
165 | } else { | 181 | } else { |
166 | let fuzzy_name_length = fuzzy_name.len(); | 182 | let fuzzy_name_length = fuzzy_name.len(); |
183 | let approximate_node = match current_module.definition_source(ctx.db).value { | ||
184 | hir::ModuleSource::SourceFile(s) => s.syntax().clone(), | ||
185 | hir::ModuleSource::Module(m) => m.syntax().clone(), | ||
186 | hir::ModuleSource::BlockExpr(b) => b.syntax().clone(), | ||
187 | }; | ||
167 | let assets_for_path = ImportAssets::for_fuzzy_path( | 188 | let assets_for_path = ImportAssets::for_fuzzy_path( |
168 | current_module, | 189 | current_module, |
169 | ctx.path_qual.clone(), | 190 | ctx.path_qual.clone(), |
170 | fuzzy_name, | 191 | fuzzy_name, |
171 | &ctx.sema, | 192 | &ctx.sema, |
172 | ); | 193 | approximate_node, |
194 | )?; | ||
173 | 195 | ||
174 | if matches!(assets_for_path.as_ref()?.import_candidate(), ImportCandidate::Path(_)) | 196 | if matches!(assets_for_path.import_candidate(), ImportCandidate::Path(_)) |
175 | && fuzzy_name_length < 2 | 197 | && fuzzy_name_length < 2 |
176 | { | 198 | { |
177 | mark::hit!(ignore_short_input_for_path); | 199 | cov_mark::hit!(ignore_short_input_for_path); |
178 | None | 200 | None |
179 | } else { | 201 | } else { |
180 | assets_for_path | 202 | Some(assets_for_path) |
181 | } | 203 | } |
182 | } | 204 | } |
183 | } | 205 | } |
@@ -186,12 +208,12 @@ fn compute_fuzzy_completion_order_key( | |||
186 | proposed_mod_path: &ModPath, | 208 | proposed_mod_path: &ModPath, |
187 | user_input_lowercased: &str, | 209 | user_input_lowercased: &str, |
188 | ) -> usize { | 210 | ) -> usize { |
189 | mark::hit!(certain_fuzzy_order_test); | 211 | cov_mark::hit!(certain_fuzzy_order_test); |
190 | let proposed_import_name = match proposed_mod_path.segments().last() { | 212 | let import_name = match proposed_mod_path.segments().last() { |
191 | Some(name) => name.to_string().to_lowercase(), | 213 | Some(name) => name.to_string().to_lowercase(), |
192 | None => return usize::MAX, | 214 | None => return usize::MAX, |
193 | }; | 215 | }; |
194 | match proposed_import_name.match_indices(user_input_lowercased).next() { | 216 | match import_name.match_indices(user_input_lowercased).next() { |
195 | Some((first_matching_index, _)) => first_matching_index, | 217 | Some((first_matching_index, _)) => first_matching_index, |
196 | None => usize::MAX, | 218 | None => usize::MAX, |
197 | } | 219 | } |
@@ -200,7 +222,6 @@ fn compute_fuzzy_completion_order_key( | |||
200 | #[cfg(test)] | 222 | #[cfg(test)] |
201 | mod tests { | 223 | mod tests { |
202 | use expect_test::{expect, Expect}; | 224 | use expect_test::{expect, Expect}; |
203 | use test_utils::mark; | ||
204 | 225 | ||
205 | use crate::{ | 226 | use crate::{ |
206 | item::CompletionKind, | 227 | item::CompletionKind, |
@@ -295,7 +316,7 @@ fn main() { | |||
295 | 316 | ||
296 | #[test] | 317 | #[test] |
297 | fn short_paths_are_ignored() { | 318 | fn short_paths_are_ignored() { |
298 | mark::check!(ignore_short_input_for_path); | 319 | cov_mark::check!(ignore_short_input_for_path); |
299 | 320 | ||
300 | check( | 321 | check( |
301 | r#" | 322 | r#" |
@@ -319,7 +340,7 @@ fn main() { | |||
319 | 340 | ||
320 | #[test] | 341 | #[test] |
321 | fn fuzzy_completions_come_in_specific_order() { | 342 | fn fuzzy_completions_come_in_specific_order() { |
322 | mark::check!(certain_fuzzy_order_test); | 343 | cov_mark::check!(certain_fuzzy_order_test); |
323 | check( | 344 | check( |
324 | r#" | 345 | r#" |
325 | //- /lib.rs crate:dep | 346 | //- /lib.rs crate:dep |
@@ -775,4 +796,155 @@ fn main() { | |||
775 | }"#, | 796 | }"#, |
776 | ); | 797 | ); |
777 | } | 798 | } |
799 | |||
800 | #[test] | ||
801 | fn unresolved_qualifier() { | ||
802 | let fixture = r#" | ||
803 | mod foo { | ||
804 | pub mod bar { | ||
805 | pub mod baz { | ||
806 | pub struct Item; | ||
807 | } | ||
808 | } | ||
809 | } | ||
810 | |||
811 | fn main() { | ||
812 | bar::baz::Ite$0 | ||
813 | }"#; | ||
814 | |||
815 | check( | ||
816 | fixture, | ||
817 | expect![[r#" | ||
818 | st foo::bar::baz::Item | ||
819 | "#]], | ||
820 | ); | ||
821 | |||
822 | check_edit( | ||
823 | "Item", | ||
824 | fixture, | ||
825 | r#" | ||
826 | use foo::bar; | ||
827 | |||
828 | mod foo { | ||
829 | pub mod bar { | ||
830 | pub mod baz { | ||
831 | pub struct Item; | ||
832 | } | ||
833 | } | ||
834 | } | ||
835 | |||
836 | fn main() { | ||
837 | bar::baz::Item | ||
838 | }"#, | ||
839 | ); | ||
840 | } | ||
841 | |||
842 | #[test] | ||
843 | fn unresolved_assoc_item_container() { | ||
844 | let fixture = r#" | ||
845 | mod foo { | ||
846 | pub struct Item; | ||
847 | |||
848 | impl Item { | ||
849 | pub const TEST_ASSOC: usize = 3; | ||
850 | } | ||
851 | } | ||
852 | |||
853 | fn main() { | ||
854 | Item::TEST_A$0 | ||
855 | }"#; | ||
856 | |||
857 | check( | ||
858 | fixture, | ||
859 | expect![[r#" | ||
860 | ct TEST_ASSOC (foo::Item) | ||
861 | "#]], | ||
862 | ); | ||
863 | |||
864 | check_edit( | ||
865 | "TEST_ASSOC", | ||
866 | fixture, | ||
867 | r#" | ||
868 | use foo::Item; | ||
869 | |||
870 | mod foo { | ||
871 | pub struct Item; | ||
872 | |||
873 | impl Item { | ||
874 | pub const TEST_ASSOC: usize = 3; | ||
875 | } | ||
876 | } | ||
877 | |||
878 | fn main() { | ||
879 | Item::TEST_ASSOC | ||
880 | }"#, | ||
881 | ); | ||
882 | } | ||
883 | |||
884 | #[test] | ||
885 | fn unresolved_assoc_item_container_with_path() { | ||
886 | let fixture = r#" | ||
887 | mod foo { | ||
888 | pub mod bar { | ||
889 | pub struct Item; | ||
890 | |||
891 | impl Item { | ||
892 | pub const TEST_ASSOC: usize = 3; | ||
893 | } | ||
894 | } | ||
895 | } | ||
896 | |||
897 | fn main() { | ||
898 | bar::Item::TEST_A$0 | ||
899 | }"#; | ||
900 | |||
901 | check( | ||
902 | fixture, | ||
903 | expect![[r#" | ||
904 | ct TEST_ASSOC (foo::bar::Item) | ||
905 | "#]], | ||
906 | ); | ||
907 | |||
908 | check_edit( | ||
909 | "TEST_ASSOC", | ||
910 | fixture, | ||
911 | r#" | ||
912 | use foo::bar; | ||
913 | |||
914 | mod foo { | ||
915 | pub mod bar { | ||
916 | pub struct Item; | ||
917 | |||
918 | impl Item { | ||
919 | pub const TEST_ASSOC: usize = 3; | ||
920 | } | ||
921 | } | ||
922 | } | ||
923 | |||
924 | fn main() { | ||
925 | bar::Item::TEST_ASSOC | ||
926 | }"#, | ||
927 | ); | ||
928 | } | ||
929 | |||
930 | #[test] | ||
931 | fn fuzzy_unresolved_path() { | ||
932 | check( | ||
933 | r#" | ||
934 | mod foo { | ||
935 | pub mod bar { | ||
936 | pub struct Item; | ||
937 | |||
938 | impl Item { | ||
939 | pub const TEST_ASSOC: usize = 3; | ||
940 | } | ||
941 | } | ||
942 | } | ||
943 | |||
944 | fn main() { | ||
945 | bar::Ass$0 | ||
946 | }"#, | ||
947 | expect![[]], | ||
948 | ) | ||
949 | } | ||
778 | } | 950 | } |
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs index 03c6dd454..80aa9fb06 100644 --- a/crates/ide_completion/src/completions/keyword.rs +++ b/crates/ide_completion/src/completions/keyword.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use syntax::SyntaxKind; | 5 | use syntax::SyntaxKind; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; | 7 | use crate::{CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions}; |
9 | 8 | ||
@@ -47,11 +46,11 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC | |||
47 | 46 | ||
48 | pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { | 47 | pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { |
49 | if ctx.token.kind() == SyntaxKind::COMMENT { | 48 | if ctx.token.kind() == SyntaxKind::COMMENT { |
50 | mark::hit!(no_keyword_completion_in_comments); | 49 | cov_mark::hit!(no_keyword_completion_in_comments); |
51 | return; | 50 | return; |
52 | } | 51 | } |
53 | if ctx.record_lit_syntax.is_some() { | 52 | if ctx.record_lit_syntax.is_some() { |
54 | mark::hit!(no_keyword_completion_in_record_lit); | 53 | cov_mark::hit!(no_keyword_completion_in_record_lit); |
55 | return; | 54 | return; |
56 | } | 55 | } |
57 | 56 | ||
@@ -172,7 +171,7 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet | |||
172 | Some(cap) => { | 171 | Some(cap) => { |
173 | let tmp; | 172 | let tmp; |
174 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { | 173 | let snippet = if snippet.ends_with('}') && ctx.incomplete_let { |
175 | mark::hit!(let_semi); | 174 | cov_mark::hit!(let_semi); |
176 | tmp = format!("{};", snippet); | 175 | tmp = format!("{};", snippet); |
177 | &tmp | 176 | &tmp |
178 | } else { | 177 | } else { |
@@ -188,7 +187,6 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet | |||
188 | #[cfg(test)] | 187 | #[cfg(test)] |
189 | mod tests { | 188 | mod tests { |
190 | use expect_test::{expect, Expect}; | 189 | use expect_test::{expect, Expect}; |
191 | use test_utils::mark; | ||
192 | 190 | ||
193 | use crate::{ | 191 | use crate::{ |
194 | test_utils::{check_edit, completion_list}, | 192 | test_utils::{check_edit, completion_list}, |
@@ -494,7 +492,7 @@ fn quux() -> i32 { | |||
494 | 492 | ||
495 | #[test] | 493 | #[test] |
496 | fn no_keyword_completion_in_comments() { | 494 | fn no_keyword_completion_in_comments() { |
497 | mark::check!(no_keyword_completion_in_comments); | 495 | cov_mark::check!(no_keyword_completion_in_comments); |
498 | check( | 496 | check( |
499 | r#" | 497 | r#" |
500 | fn test() { | 498 | fn test() { |
@@ -599,7 +597,7 @@ struct Foo { | |||
599 | 597 | ||
600 | #[test] | 598 | #[test] |
601 | fn skip_struct_initializer() { | 599 | fn skip_struct_initializer() { |
602 | mark::check!(no_keyword_completion_in_record_lit); | 600 | cov_mark::check!(no_keyword_completion_in_record_lit); |
603 | check( | 601 | check( |
604 | r#" | 602 | r#" |
605 | struct Foo { | 603 | struct Foo { |
@@ -643,7 +641,7 @@ fn foo() { | |||
643 | 641 | ||
644 | #[test] | 642 | #[test] |
645 | fn let_semi() { | 643 | fn let_semi() { |
646 | mark::check!(let_semi); | 644 | cov_mark::check!(let_semi); |
647 | check_edit( | 645 | check_edit( |
648 | "match", | 646 | "match", |
649 | r#" | 647 | r#" |
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs index 9c34ed0b6..d45ad7944 100644 --- a/crates/ide_completion/src/completions/postfix.rs +++ b/crates/ide_completion/src/completions/postfix.rs | |||
@@ -187,6 +187,16 @@ pub(crate) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
187 | ctx, | 187 | ctx, |
188 | cap, | 188 | cap, |
189 | &dot_receiver, | 189 | &dot_receiver, |
190 | "err", | ||
191 | "Err(expr)", | ||
192 | &format!("Err({})", receiver_text), | ||
193 | ) | ||
194 | .add_to(acc); | ||
195 | |||
196 | postfix_snippet( | ||
197 | ctx, | ||
198 | cap, | ||
199 | &dot_receiver, | ||
190 | "some", | 200 | "some", |
191 | "Some(expr)", | 201 | "Some(expr)", |
192 | &format!("Some({})", receiver_text), | 202 | &format!("Some({})", receiver_text), |
@@ -325,6 +335,7 @@ fn main() { | |||
325 | sn match match expr {} | 335 | sn match match expr {} |
326 | sn box Box::new(expr) | 336 | sn box Box::new(expr) |
327 | sn ok Ok(expr) | 337 | sn ok Ok(expr) |
338 | sn err Err(expr) | ||
328 | sn some Some(expr) | 339 | sn some Some(expr) |
329 | sn dbg dbg!(expr) | 340 | sn dbg dbg!(expr) |
330 | sn dbgr dbg!(&expr) | 341 | sn dbgr dbg!(&expr) |
@@ -357,6 +368,7 @@ fn main() { | |||
357 | sn match match expr {} | 368 | sn match match expr {} |
358 | sn box Box::new(expr) | 369 | sn box Box::new(expr) |
359 | sn ok Ok(expr) | 370 | sn ok Ok(expr) |
371 | sn err Err(expr) | ||
360 | sn some Some(expr) | 372 | sn some Some(expr) |
361 | sn dbg dbg!(expr) | 373 | sn dbg dbg!(expr) |
362 | sn dbgr dbg!(&expr) | 374 | sn dbgr dbg!(&expr) |
@@ -380,6 +392,7 @@ fn main() { | |||
380 | sn match match expr {} | 392 | sn match match expr {} |
381 | sn box Box::new(expr) | 393 | sn box Box::new(expr) |
382 | sn ok Ok(expr) | 394 | sn ok Ok(expr) |
395 | sn err Err(expr) | ||
383 | sn some Some(expr) | 396 | sn some Some(expr) |
384 | sn dbg dbg!(expr) | 397 | sn dbg dbg!(expr) |
385 | sn dbgr dbg!(&expr) | 398 | sn dbgr dbg!(&expr) |
@@ -408,6 +421,7 @@ fn main() { | |||
408 | sn match match expr {} | 421 | sn match match expr {} |
409 | sn box Box::new(expr) | 422 | sn box Box::new(expr) |
410 | sn ok Ok(expr) | 423 | sn ok Ok(expr) |
424 | sn err Err(expr) | ||
411 | sn some Some(expr) | 425 | sn some Some(expr) |
412 | sn dbg dbg!(expr) | 426 | sn dbg dbg!(expr) |
413 | sn dbgr dbg!(&expr) | 427 | sn dbgr dbg!(&expr) |
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index 72fb757b1..df74b739e 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; | 3 | use hir::{Adt, HasVisibility, PathResolution, ScopeDef}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use syntax::AstNode; | 5 | use syntax::AstNode; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{CompletionContext, Completions}; | 7 | use crate::{CompletionContext, Completions}; |
9 | 8 | ||
@@ -39,7 +38,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
39 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { | 38 | if let Some(name_ref) = ctx.name_ref_syntax.as_ref() { |
40 | if name_ref.syntax().text() == name.to_string().as_str() { | 39 | if name_ref.syntax().text() == name.to_string().as_str() { |
41 | // for `use self::foo$0`, don't suggest `foo` as a completion | 40 | // for `use self::foo$0`, don't suggest `foo` as a completion |
42 | mark::hit!(dont_complete_current_use); | 41 | cov_mark::hit!(dont_complete_current_use); |
43 | continue; | 42 | continue; |
44 | } | 43 | } |
45 | } | 44 | } |
@@ -155,7 +154,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon | |||
155 | #[cfg(test)] | 154 | #[cfg(test)] |
156 | mod tests { | 155 | mod tests { |
157 | use expect_test::{expect, Expect}; | 156 | use expect_test::{expect, Expect}; |
158 | use test_utils::mark; | ||
159 | 157 | ||
160 | use crate::{ | 158 | use crate::{ |
161 | test_utils::{check_edit, completion_list}, | 159 | test_utils::{check_edit, completion_list}, |
@@ -174,7 +172,7 @@ mod tests { | |||
174 | 172 | ||
175 | #[test] | 173 | #[test] |
176 | fn dont_complete_current_use() { | 174 | fn dont_complete_current_use() { |
177 | mark::check!(dont_complete_current_use); | 175 | cov_mark::check!(dont_complete_current_use); |
178 | check(r#"use self::foo$0;"#, expect![[""]]); | 176 | check(r#"use self::foo$0;"#, expect![[""]]); |
179 | } | 177 | } |
180 | 178 | ||
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs index e9d0ff665..044dfd160 100644 --- a/crates/ide_completion/src/completions/unqualified_path.rs +++ b/crates/ide_completion/src/completions/unqualified_path.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use hir::ScopeDef; | 3 | use hir::ScopeDef; |
4 | use syntax::AstNode; | 4 | use syntax::AstNode; |
5 | use test_utils::mark; | ||
6 | 5 | ||
7 | use crate::{CompletionContext, Completions}; | 6 | use crate::{CompletionContext, Completions}; |
8 | 7 | ||
@@ -30,13 +29,13 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
30 | 29 | ||
31 | ctx.scope.process_all_names(&mut |name, res| { | 30 | ctx.scope.process_all_names(&mut |name, res| { |
32 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { | 31 | if let ScopeDef::GenericParam(hir::GenericParam::LifetimeParam(_)) = res { |
33 | mark::hit!(skip_lifetime_completion); | 32 | cov_mark::hit!(skip_lifetime_completion); |
34 | return; | 33 | return; |
35 | } | 34 | } |
36 | if ctx.use_item_syntax.is_some() { | 35 | if ctx.use_item_syntax.is_some() { |
37 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { | 36 | if let (ScopeDef::Unknown, Some(name_ref)) = (&res, &ctx.name_ref_syntax) { |
38 | if name_ref.syntax().text() == name.to_string().as_str() { | 37 | if name_ref.syntax().text() == name.to_string().as_str() { |
39 | mark::hit!(self_fulfilling_completion); | 38 | cov_mark::hit!(self_fulfilling_completion); |
40 | return; | 39 | return; |
41 | } | 40 | } |
42 | } | 41 | } |
@@ -48,7 +47,6 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC | |||
48 | #[cfg(test)] | 47 | #[cfg(test)] |
49 | mod tests { | 48 | mod tests { |
50 | use expect_test::{expect, Expect}; | 49 | use expect_test::{expect, Expect}; |
51 | use test_utils::mark; | ||
52 | 50 | ||
53 | use crate::{ | 51 | use crate::{ |
54 | test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, | 52 | test_utils::{check_edit, completion_list_with_config, TEST_CONFIG}, |
@@ -66,7 +64,7 @@ mod tests { | |||
66 | 64 | ||
67 | #[test] | 65 | #[test] |
68 | fn self_fulfilling_completion() { | 66 | fn self_fulfilling_completion() { |
69 | mark::check!(self_fulfilling_completion); | 67 | cov_mark::check!(self_fulfilling_completion); |
70 | check( | 68 | check( |
71 | r#" | 69 | r#" |
72 | use foo$0 | 70 | use foo$0 |
@@ -185,7 +183,7 @@ fn quux() { | |||
185 | 183 | ||
186 | #[test] | 184 | #[test] |
187 | fn completes_if_prefix_is_keyword() { | 185 | fn completes_if_prefix_is_keyword() { |
188 | mark::check!(completes_if_prefix_is_keyword); | 186 | cov_mark::check!(completes_if_prefix_is_keyword); |
189 | check_edit( | 187 | check_edit( |
190 | "wherewolf", | 188 | "wherewolf", |
191 | r#" | 189 | r#" |
@@ -223,7 +221,7 @@ fn main() { | |||
223 | 221 | ||
224 | #[test] | 222 | #[test] |
225 | fn does_not_complete_lifetimes() { | 223 | fn does_not_complete_lifetimes() { |
226 | mark::check!(skip_lifetime_completion); | 224 | cov_mark::check!(skip_lifetime_completion); |
227 | check( | 225 | check( |
228 | r#"fn quux<'a>() { $0 }"#, | 226 | r#"fn quux<'a>() { $0 }"#, |
229 | expect![[r#" | 227 | expect![[r#" |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 3db357855..17d9a3adf 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -7,7 +7,7 @@ use syntax::{ | |||
7 | algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, | 7 | algo::find_node_at_offset, ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, |
8 | SyntaxToken, TextRange, TextSize, | 8 | SyntaxToken, TextRange, TextSize, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | 10 | |
11 | use text_edit::Indel; | 11 | use text_edit::Indel; |
12 | 12 | ||
13 | use crate::{ | 13 | use crate::{ |
@@ -240,7 +240,7 @@ impl<'a> CompletionContext<'a> { | |||
240 | // check kind of macro-expanded token, but use range of original token | 240 | // check kind of macro-expanded token, but use range of original token |
241 | let kind = self.token.kind(); | 241 | let kind = self.token.kind(); |
242 | if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { | 242 | if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { |
243 | mark::hit!(completes_if_prefix_is_keyword); | 243 | cov_mark::hit!(completes_if_prefix_is_keyword); |
244 | self.original_token.text_range() | 244 | self.original_token.text_range() |
245 | } else { | 245 | } else { |
246 | TextRange::empty(self.position.offset) | 246 | TextRange::empty(self.position.offset) |
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs index 884711f11..14afec603 100644 --- a/crates/ide_completion/src/item.rs +++ b/crates/ide_completion/src/item.rs | |||
@@ -2,15 +2,16 @@ | |||
2 | 2 | ||
3 | use std::fmt; | 3 | use std::fmt; |
4 | 4 | ||
5 | use hir::{Documentation, ModPath, Mutability}; | 5 | use hir::{Documentation, Mutability}; |
6 | use ide_db::{ | 6 | use ide_db::{ |
7 | helpers::{ | 7 | helpers::{ |
8 | insert_use::{self, ImportScope, MergeBehavior}, | 8 | import_assets::LocatedImport, |
9 | insert_use::{self, ImportScope, InsertUseConfig}, | ||
9 | mod_path_to_ast, SnippetCap, | 10 | mod_path_to_ast, SnippetCap, |
10 | }, | 11 | }, |
11 | SymbolKind, | 12 | SymbolKind, |
12 | }; | 13 | }; |
13 | use stdx::{impl_from, never}; | 14 | use stdx::{format_to, impl_from, never}; |
14 | use syntax::{algo, TextRange}; | 15 | use syntax::{algo, TextRange}; |
15 | use text_edit::TextEdit; | 16 | use text_edit::TextEdit; |
16 | 17 | ||
@@ -62,12 +63,18 @@ pub struct CompletionItem { | |||
62 | /// after completion. | 63 | /// after completion. |
63 | trigger_call_info: bool, | 64 | trigger_call_info: bool, |
64 | 65 | ||
65 | /// Score is useful to pre select or display in better order completion items | 66 | /// We use this to sort completion. Relevance records facts like "do the |
66 | score: Option<CompletionScore>, | 67 | /// types align precisely?". We can't sort by relevances directly, they are |
68 | /// only partially ordered. | ||
69 | /// | ||
70 | /// Note that Relevance ignores fuzzy match score. We compute Relevance for | ||
71 | /// all possible items, and then separately build an ordered completion list | ||
72 | /// based on relevance and fuzzy matching with the already typed identifier. | ||
73 | relevance: Relevance, | ||
67 | 74 | ||
68 | /// Indicates that a reference or mutable reference to this variable is a | 75 | /// Indicates that a reference or mutable reference to this variable is a |
69 | /// possible match. | 76 | /// possible match. |
70 | ref_match: Option<(Mutability, CompletionScore)>, | 77 | ref_match: Option<Mutability>, |
71 | 78 | ||
72 | /// The import data to add to completion's edits. | 79 | /// The import data to add to completion's edits. |
73 | import_to_add: Option<ImportEdit>, | 80 | import_to_add: Option<ImportEdit>, |
@@ -100,8 +107,11 @@ impl fmt::Debug for CompletionItem { | |||
100 | if self.deprecated { | 107 | if self.deprecated { |
101 | s.field("deprecated", &true); | 108 | s.field("deprecated", &true); |
102 | } | 109 | } |
103 | if let Some(score) = &self.score { | 110 | if self.relevance.is_relevant() { |
104 | s.field("score", score); | 111 | s.field("relevance", &self.relevance); |
112 | } | ||
113 | if let Some(mutability) = &self.ref_match { | ||
114 | s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref())); | ||
105 | } | 115 | } |
106 | if self.trigger_call_info { | 116 | if self.trigger_call_info { |
107 | s.field("trigger_call_info", &true); | 117 | s.field("trigger_call_info", &true); |
@@ -118,6 +128,36 @@ pub enum CompletionScore { | |||
118 | TypeAndNameMatch, | 128 | TypeAndNameMatch, |
119 | } | 129 | } |
120 | 130 | ||
131 | #[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)] | ||
132 | pub struct Relevance { | ||
133 | /// This is set in cases like these: | ||
134 | /// | ||
135 | /// ``` | ||
136 | /// fn f(spam: String) {} | ||
137 | /// fn main { | ||
138 | /// let spam = 92; | ||
139 | /// f($0) // name of local matches the name of param | ||
140 | /// } | ||
141 | /// ``` | ||
142 | pub exact_name_match: bool, | ||
143 | /// This is set in cases like these: | ||
144 | /// | ||
145 | /// ``` | ||
146 | /// fn f(spam: String) {} | ||
147 | /// fn main { | ||
148 | /// let foo = String::new(); | ||
149 | /// f($0) // type of local matches the type of param | ||
150 | /// } | ||
151 | /// ``` | ||
152 | pub exact_type_match: bool, | ||
153 | } | ||
154 | |||
155 | impl Relevance { | ||
156 | pub fn is_relevant(&self) -> bool { | ||
157 | self != &Relevance::default() | ||
158 | } | ||
159 | } | ||
160 | |||
121 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] | 161 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
122 | pub enum CompletionItemKind { | 162 | pub enum CompletionItemKind { |
123 | SymbolKind(SymbolKind), | 163 | SymbolKind(SymbolKind), |
@@ -207,9 +247,9 @@ impl CompletionItem { | |||
207 | lookup: None, | 247 | lookup: None, |
208 | kind: None, | 248 | kind: None, |
209 | text_edit: None, | 249 | text_edit: None, |
210 | deprecated: None, | 250 | deprecated: false, |
211 | trigger_call_info: None, | 251 | trigger_call_info: None, |
212 | score: None, | 252 | relevance: Relevance::default(), |
213 | ref_match: None, | 253 | ref_match: None, |
214 | import_to_add: None, | 254 | import_to_add: None, |
215 | } | 255 | } |
@@ -252,15 +292,15 @@ impl CompletionItem { | |||
252 | self.deprecated | 292 | self.deprecated |
253 | } | 293 | } |
254 | 294 | ||
255 | pub fn score(&self) -> Option<CompletionScore> { | 295 | pub fn relevance(&self) -> Relevance { |
256 | self.score | 296 | self.relevance |
257 | } | 297 | } |
258 | 298 | ||
259 | pub fn trigger_call_info(&self) -> bool { | 299 | pub fn trigger_call_info(&self) -> bool { |
260 | self.trigger_call_info | 300 | self.trigger_call_info |
261 | } | 301 | } |
262 | 302 | ||
263 | pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { | 303 | pub fn ref_match(&self) -> Option<Mutability> { |
264 | self.ref_match | 304 | self.ref_match |
265 | } | 305 | } |
266 | 306 | ||
@@ -272,22 +312,18 @@ impl CompletionItem { | |||
272 | /// An extra import to add after the completion is applied. | 312 | /// An extra import to add after the completion is applied. |
273 | #[derive(Debug, Clone)] | 313 | #[derive(Debug, Clone)] |
274 | pub struct ImportEdit { | 314 | pub struct ImportEdit { |
275 | pub import_path: ModPath, | 315 | pub import: LocatedImport, |
276 | pub import_scope: ImportScope, | 316 | pub scope: ImportScope, |
277 | pub import_for_trait_assoc_item: bool, | ||
278 | } | 317 | } |
279 | 318 | ||
280 | impl ImportEdit { | 319 | impl ImportEdit { |
281 | /// Attempts to insert the import to the given scope, producing a text edit. | 320 | /// Attempts to insert the import to the given scope, producing a text edit. |
282 | /// May return no edit in edge cases, such as scope already containing the import. | 321 | /// May return no edit in edge cases, such as scope already containing the import. |
283 | pub fn to_text_edit(&self, merge_behavior: Option<MergeBehavior>) -> Option<TextEdit> { | 322 | pub fn to_text_edit(&self, cfg: InsertUseConfig) -> Option<TextEdit> { |
284 | let _p = profile::span("ImportEdit::to_text_edit"); | 323 | let _p = profile::span("ImportEdit::to_text_edit"); |
285 | 324 | ||
286 | let rewriter = insert_use::insert_use( | 325 | let rewriter = |
287 | &self.import_scope, | 326 | insert_use::insert_use(&self.scope, mod_path_to_ast(&self.import.import_path), cfg); |
288 | mod_path_to_ast(&self.import_path), | ||
289 | merge_behavior, | ||
290 | ); | ||
291 | let old_ast = rewriter.rewrite_root()?; | 327 | let old_ast = rewriter.rewrite_root()?; |
292 | let mut import_insert = TextEdit::builder(); | 328 | let mut import_insert = TextEdit::builder(); |
293 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); | 329 | algo::diff(&old_ast, &rewriter.rewrite(&old_ast)).into_text_edit(&mut import_insert); |
@@ -311,10 +347,10 @@ pub(crate) struct Builder { | |||
311 | lookup: Option<String>, | 347 | lookup: Option<String>, |
312 | kind: Option<CompletionItemKind>, | 348 | kind: Option<CompletionItemKind>, |
313 | text_edit: Option<TextEdit>, | 349 | text_edit: Option<TextEdit>, |
314 | deprecated: Option<bool>, | 350 | deprecated: bool, |
315 | trigger_call_info: Option<bool>, | 351 | trigger_call_info: Option<bool>, |
316 | score: Option<CompletionScore>, | 352 | relevance: Relevance, |
317 | ref_match: Option<(Mutability, CompletionScore)>, | 353 | ref_match: Option<Mutability>, |
318 | } | 354 | } |
319 | 355 | ||
320 | impl Builder { | 356 | impl Builder { |
@@ -325,20 +361,19 @@ impl Builder { | |||
325 | let mut lookup = self.lookup; | 361 | let mut lookup = self.lookup; |
326 | let mut insert_text = self.insert_text; | 362 | let mut insert_text = self.insert_text; |
327 | 363 | ||
328 | if let Some(import_to_add) = self.import_to_add.as_ref() { | 364 | if let Some(original_path) = self |
329 | if import_to_add.import_for_trait_assoc_item { | 365 | .import_to_add |
330 | lookup = lookup.or_else(|| Some(label.clone())); | 366 | .as_ref() |
331 | insert_text = insert_text.or_else(|| Some(label.clone())); | 367 | .and_then(|import_edit| import_edit.import.original_path.as_ref()) |
332 | label = format!("{} ({})", label, import_to_add.import_path); | 368 | { |
369 | lookup = lookup.or_else(|| Some(label.clone())); | ||
370 | insert_text = insert_text.or_else(|| Some(label.clone())); | ||
371 | |||
372 | let original_path_label = original_path.to_string(); | ||
373 | if original_path_label.ends_with(&label) { | ||
374 | label = original_path_label; | ||
333 | } else { | 375 | } else { |
334 | let mut import_path_without_last_segment = import_to_add.import_path.to_owned(); | 376 | format_to!(label, " ({})", original_path) |
335 | let _ = import_path_without_last_segment.pop_segment(); | ||
336 | |||
337 | if !import_path_without_last_segment.segments().is_empty() { | ||
338 | lookup = lookup.or_else(|| Some(label.clone())); | ||
339 | insert_text = insert_text.or_else(|| Some(label.clone())); | ||
340 | label = format!("{}::{}", import_path_without_last_segment, label); | ||
341 | } | ||
342 | } | 377 | } |
343 | } | 378 | } |
344 | 379 | ||
@@ -359,9 +394,9 @@ impl Builder { | |||
359 | lookup, | 394 | lookup, |
360 | kind: self.kind, | 395 | kind: self.kind, |
361 | completion_kind: self.completion_kind, | 396 | completion_kind: self.completion_kind, |
362 | deprecated: self.deprecated.unwrap_or(false), | 397 | deprecated: self.deprecated, |
363 | trigger_call_info: self.trigger_call_info.unwrap_or(false), | 398 | trigger_call_info: self.trigger_call_info.unwrap_or(false), |
364 | score: self.score, | 399 | relevance: self.relevance, |
365 | ref_match: self.ref_match, | 400 | ref_match: self.ref_match, |
366 | import_to_add: self.import_to_add, | 401 | import_to_add: self.import_to_add, |
367 | } | 402 | } |
@@ -419,11 +454,11 @@ impl Builder { | |||
419 | self | 454 | self |
420 | } | 455 | } |
421 | pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { | 456 | pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { |
422 | self.deprecated = Some(deprecated); | 457 | self.deprecated = deprecated; |
423 | self | 458 | self |
424 | } | 459 | } |
425 | pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { | 460 | pub(crate) fn set_relevance(mut self, relevance: Relevance) -> Builder { |
426 | self.score = Some(score); | 461 | self.relevance = relevance; |
427 | self | 462 | self |
428 | } | 463 | } |
429 | pub(crate) fn trigger_call_info(mut self) -> Builder { | 464 | pub(crate) fn trigger_call_info(mut self) -> Builder { |
@@ -434,17 +469,8 @@ impl Builder { | |||
434 | self.import_to_add = import_to_add; | 469 | self.import_to_add = import_to_add; |
435 | self | 470 | self |
436 | } | 471 | } |
437 | pub(crate) fn set_ref_match( | 472 | pub(crate) fn ref_match(mut self, mutability: Mutability) -> Builder { |
438 | mut self, | 473 | self.ref_match = Some(mutability); |
439 | ref_match: Option<(Mutability, CompletionScore)>, | ||
440 | ) -> Builder { | ||
441 | self.ref_match = ref_match; | ||
442 | self | 474 | self |
443 | } | 475 | } |
444 | } | 476 | } |
445 | |||
446 | impl<'a> Into<CompletionItem> for Builder { | ||
447 | fn into(self) -> CompletionItem { | ||
448 | self.build() | ||
449 | } | ||
450 | } | ||
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 76f31de9e..d46f521a0 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -13,7 +13,9 @@ mod completions; | |||
13 | 13 | ||
14 | use completions::flyimport::position_for_import; | 14 | use completions::flyimport::position_for_import; |
15 | use ide_db::{ | 15 | use ide_db::{ |
16 | base_db::FilePosition, helpers::insert_use::ImportScope, imports_locator, RootDatabase, | 16 | base_db::FilePosition, |
17 | helpers::{import_assets::LocatedImport, insert_use::ImportScope}, | ||
18 | items_locator, RootDatabase, | ||
17 | }; | 19 | }; |
18 | use text_edit::TextEdit; | 20 | use text_edit::TextEdit; |
19 | 21 | ||
@@ -21,7 +23,10 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi | |||
21 | 23 | ||
22 | pub use crate::{ | 24 | pub use crate::{ |
23 | config::CompletionConfig, | 25 | config::CompletionConfig, |
24 | item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, | 26 | item::{ |
27 | CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat, | ||
28 | Relevance, | ||
29 | }, | ||
25 | }; | 30 | }; |
26 | 31 | ||
27 | //FIXME: split the following feature into fine-grained features. | 32 | //FIXME: split the following feature into fine-grained features. |
@@ -139,25 +144,27 @@ pub fn resolve_completion_edits( | |||
139 | position: FilePosition, | 144 | position: FilePosition, |
140 | full_import_path: &str, | 145 | full_import_path: &str, |
141 | imported_name: String, | 146 | imported_name: String, |
142 | import_for_trait_assoc_item: bool, | ||
143 | ) -> Option<Vec<TextEdit>> { | 147 | ) -> Option<Vec<TextEdit>> { |
144 | let ctx = CompletionContext::new(db, position, config)?; | 148 | let ctx = CompletionContext::new(db, position, config)?; |
145 | let position_for_import = position_for_import(&ctx, None)?; | 149 | let position_for_import = position_for_import(&ctx, None)?; |
146 | let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; | 150 | let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?; |
147 | 151 | ||
148 | let current_module = ctx.sema.scope(position_for_import).module()?; | 152 | let current_module = ctx.sema.scope(position_for_import).module()?; |
149 | let current_crate = current_module.krate(); | 153 | let current_crate = current_module.krate(); |
150 | 154 | ||
151 | let import_path = imports_locator::find_exact_imports(&ctx.sema, current_crate, imported_name) | 155 | let (import_path, item_to_import) = |
152 | .filter_map(|candidate| { | 156 | items_locator::with_exact_name(&ctx.sema, current_crate, imported_name) |
153 | let item: hir::ItemInNs = candidate.either(Into::into, Into::into); | 157 | .into_iter() |
154 | current_module.find_use_path_prefixed(db, item, config.insert_use.prefix_kind) | 158 | .filter_map(|candidate| { |
155 | }) | 159 | current_module |
156 | .find(|mod_path| mod_path.to_string() == full_import_path)?; | 160 | .find_use_path_prefixed(db, candidate, config.insert_use.prefix_kind) |
161 | .zip(Some(candidate)) | ||
162 | }) | ||
163 | .find(|(mod_path, _)| mod_path.to_string() == full_import_path)?; | ||
164 | let import = | ||
165 | LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path)); | ||
157 | 166 | ||
158 | ImportEdit { import_path, import_scope, import_for_trait_assoc_item } | 167 | ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) |
159 | .to_text_edit(config.insert_use.merge) | ||
160 | .map(|edit| vec![edit]) | ||
161 | } | 168 | } |
162 | 169 | ||
163 | #[cfg(test)] | 170 | #[cfg(test)] |
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index eddaaa6f3..8c8b149a1 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -13,13 +13,15 @@ mod builder_ext; | |||
13 | use hir::{ | 13 | use hir::{ |
14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, | 14 | AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type, |
15 | }; | 15 | }; |
16 | use ide_db::{helpers::SnippetCap, RootDatabase, SymbolKind}; | 16 | use ide_db::{ |
17 | helpers::{item_name, SnippetCap}, | ||
18 | RootDatabase, SymbolKind, | ||
19 | }; | ||
17 | use syntax::TextRange; | 20 | use syntax::TextRange; |
18 | use test_utils::mark; | ||
19 | 21 | ||
20 | use crate::{ | 22 | use crate::{ |
21 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 23 | item::{ImportEdit, Relevance}, |
22 | CompletionScore, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
23 | }; | 25 | }; |
24 | 26 | ||
25 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; |
@@ -51,18 +53,20 @@ pub(crate) fn render_resolution<'a>( | |||
51 | pub(crate) fn render_resolution_with_import<'a>( | 53 | pub(crate) fn render_resolution_with_import<'a>( |
52 | ctx: RenderContext<'a>, | 54 | ctx: RenderContext<'a>, |
53 | import_edit: ImportEdit, | 55 | import_edit: ImportEdit, |
54 | resolution: &ScopeDef, | ||
55 | ) -> Option<CompletionItem> { | 56 | ) -> Option<CompletionItem> { |
57 | let resolution = ScopeDef::from(import_edit.import.original_item); | ||
56 | let local_name = match resolution { | 58 | let local_name = match resolution { |
57 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), | 59 | ScopeDef::ModuleDef(ModuleDef::Function(f)) => f.name(ctx.completion.db).to_string(), |
58 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), | 60 | ScopeDef::ModuleDef(ModuleDef::Const(c)) => c.name(ctx.completion.db)?.to_string(), |
59 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), | 61 | ScopeDef::ModuleDef(ModuleDef::TypeAlias(t)) => t.name(ctx.completion.db).to_string(), |
60 | _ => import_edit.import_path.segments().last()?.to_string(), | 62 | _ => item_name(ctx.db(), import_edit.import.original_item)?.to_string(), |
61 | }; | 63 | }; |
62 | Render::new(ctx).render_resolution(local_name, Some(import_edit), resolution).map(|mut item| { | 64 | Render::new(ctx).render_resolution(local_name, Some(import_edit), &resolution).map( |
63 | item.completion_kind = CompletionKind::Magic; | 65 | |mut item| { |
64 | item | 66 | item.completion_kind = CompletionKind::Magic; |
65 | }) | 67 | item |
68 | }, | ||
69 | ) | ||
66 | } | 70 | } |
67 | 71 | ||
68 | /// Interface for data and methods required for items rendering. | 72 | /// Interface for data and methods required for items rendering. |
@@ -113,13 +117,13 @@ impl<'a> RenderContext<'a> { | |||
113 | node.docs(self.db()) | 117 | node.docs(self.db()) |
114 | } | 118 | } |
115 | 119 | ||
116 | fn active_name_and_type(&self) -> Option<(String, Type)> { | 120 | fn expected_name_and_type(&self) -> Option<(String, Type)> { |
117 | if let Some(record_field) = &self.completion.record_field_syntax { | 121 | if let Some(record_field) = &self.completion.record_field_syntax { |
118 | mark::hit!(record_field_type_match); | 122 | cov_mark::hit!(record_field_type_match); |
119 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | 123 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; |
120 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | 124 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) |
121 | } else if let Some(active_parameter) = &self.completion.active_parameter { | 125 | } else if let Some(active_parameter) = &self.completion.active_parameter { |
122 | mark::hit!(active_param_type_match); | 126 | cov_mark::hit!(active_param_type_match); |
123 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | 127 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) |
124 | } else { | 128 | } else { |
125 | None | 129 | None |
@@ -151,8 +155,8 @@ impl<'a> Render<'a> { | |||
151 | .set_documentation(field.docs(self.ctx.db())) | 155 | .set_documentation(field.docs(self.ctx.db())) |
152 | .set_deprecated(is_deprecated); | 156 | .set_deprecated(is_deprecated); |
153 | 157 | ||
154 | if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { | 158 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) { |
155 | item = item.set_score(score); | 159 | item = item.set_relevance(relevance); |
156 | } | 160 | } |
157 | 161 | ||
158 | item.build() | 162 | item.build() |
@@ -242,16 +246,23 @@ impl<'a> Render<'a> { | |||
242 | } | 246 | } |
243 | }; | 247 | }; |
244 | 248 | ||
245 | let mut ref_match = None; | ||
246 | if let ScopeDef::Local(local) = resolution { | 249 | if let ScopeDef::Local(local) = resolution { |
247 | if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { | 250 | let ty = local.ty(self.ctx.db()); |
248 | let ty = local.ty(self.ctx.db()); | 251 | if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) { |
249 | if let Some(score) = | 252 | item = item.set_relevance(relevance) |
250 | compute_score_from_active(&active_type, &active_name, &ty, &local_name) | 253 | } |
251 | { | 254 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { |
252 | item = item.set_score(score); | 255 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
256 | if ty_without_ref == ty { | ||
257 | cov_mark::hit!(suggest_ref); | ||
258 | let mutability = if expected_type.is_mutable_reference() { | ||
259 | Mutability::Mut | ||
260 | } else { | ||
261 | Mutability::Shared | ||
262 | }; | ||
263 | item = item.ref_match(mutability) | ||
264 | } | ||
253 | } | 265 | } |
254 | ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name); | ||
255 | } | 266 | } |
256 | } | 267 | } |
257 | 268 | ||
@@ -269,7 +280,7 @@ impl<'a> Render<'a> { | |||
269 | _ => false, | 280 | _ => false, |
270 | }; | 281 | }; |
271 | if has_non_default_type_params { | 282 | if has_non_default_type_params { |
272 | mark::hit!(inserts_angle_brackets_for_generics); | 283 | cov_mark::hit!(inserts_angle_brackets_for_generics); |
273 | item = item | 284 | item = item |
274 | .lookup_by(local_name.clone()) | 285 | .lookup_by(local_name.clone()) |
275 | .label(format!("{}<…>", local_name)) | 286 | .label(format!("{}<…>", local_name)) |
@@ -281,7 +292,6 @@ impl<'a> Render<'a> { | |||
281 | Some( | 292 | Some( |
282 | item.kind(kind) | 293 | item.kind(kind) |
283 | .add_import(import_to_add) | 294 | .add_import(import_to_add) |
284 | .set_ref_match(ref_match) | ||
285 | .set_documentation(self.docs(resolution)) | 295 | .set_documentation(self.docs(resolution)) |
286 | .set_deprecated(self.is_deprecated(resolution)) | 296 | .set_deprecated(self.is_deprecated(resolution)) |
287 | .build(), | 297 | .build(), |
@@ -313,56 +323,23 @@ impl<'a> Render<'a> { | |||
313 | } | 323 | } |
314 | } | 324 | } |
315 | 325 | ||
316 | fn compute_score_from_active( | 326 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<Relevance> { |
317 | active_type: &Type, | 327 | let (expected_name, expected_type) = ctx.expected_name_and_type()?; |
318 | active_name: &str, | 328 | let mut res = Relevance::default(); |
319 | ty: &Type, | 329 | res.exact_type_match = ty == &expected_type; |
320 | name: &str, | 330 | res.exact_name_match = name == &expected_name; |
321 | ) -> Option<CompletionScore> { | ||
322 | // Compute score | ||
323 | // For the same type | ||
324 | if active_type != ty { | ||
325 | return None; | ||
326 | } | ||
327 | |||
328 | let mut res = CompletionScore::TypeMatch; | ||
329 | |||
330 | // If same type + same name then go top position | ||
331 | if active_name == name { | ||
332 | res = CompletionScore::TypeAndNameMatch | ||
333 | } | ||
334 | |||
335 | Some(res) | 331 | Some(res) |
336 | } | 332 | } |
337 | fn refed_type_matches( | ||
338 | active_type: &Type, | ||
339 | active_name: &str, | ||
340 | ty: &Type, | ||
341 | name: &str, | ||
342 | ) -> Option<(Mutability, CompletionScore)> { | ||
343 | let derefed_active = active_type.remove_ref()?; | ||
344 | let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?; | ||
345 | Some(( | ||
346 | if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared }, | ||
347 | score, | ||
348 | )) | ||
349 | } | ||
350 | |||
351 | fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> { | ||
352 | let (active_name, active_type) = ctx.active_name_and_type()?; | ||
353 | compute_score_from_active(&active_type, &active_name, ty, name) | ||
354 | } | ||
355 | 333 | ||
356 | #[cfg(test)] | 334 | #[cfg(test)] |
357 | mod tests { | 335 | mod tests { |
358 | use std::cmp::Reverse; | 336 | use std::cmp::Reverse; |
359 | 337 | ||
360 | use expect_test::{expect, Expect}; | 338 | use expect_test::{expect, Expect}; |
361 | use test_utils::mark; | ||
362 | 339 | ||
363 | use crate::{ | 340 | use crate::{ |
364 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, | 341 | test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, |
365 | CompletionKind, CompletionScore, | 342 | CompletionKind, Relevance, |
366 | }; | 343 | }; |
367 | 344 | ||
368 | fn check(ra_fixture: &str, expect: Expect) { | 345 | fn check(ra_fixture: &str, expect: Expect) { |
@@ -370,24 +347,25 @@ mod tests { | |||
370 | expect.assert_debug_eq(&actual); | 347 | expect.assert_debug_eq(&actual); |
371 | } | 348 | } |
372 | 349 | ||
373 | fn check_scores(ra_fixture: &str, expect: Expect) { | 350 | fn check_relevance(ra_fixture: &str, expect: Expect) { |
374 | fn display_score(score: Option<CompletionScore>) -> &'static str { | 351 | fn display_relevance(relevance: Relevance) -> &'static str { |
375 | match score { | 352 | match relevance { |
376 | Some(CompletionScore::TypeMatch) => "[type]", | 353 | Relevance { exact_type_match: true, exact_name_match: true } => "[type+name]", |
377 | Some(CompletionScore::TypeAndNameMatch) => "[type+name]", | 354 | Relevance { exact_type_match: true, exact_name_match: false } => "[type]", |
378 | None => "[]".into(), | 355 | Relevance { exact_type_match: false, exact_name_match: true } => "[name]", |
356 | Relevance { exact_type_match: false, exact_name_match: false } => "[]", | ||
379 | } | 357 | } |
380 | } | 358 | } |
381 | 359 | ||
382 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); | 360 | let mut completions = get_all_items(TEST_CONFIG, ra_fixture); |
383 | completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); | 361 | completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string())); |
384 | let actual = completions | 362 | let actual = completions |
385 | .into_iter() | 363 | .into_iter() |
386 | .filter(|it| it.completion_kind == CompletionKind::Reference) | 364 | .filter(|it| it.completion_kind == CompletionKind::Reference) |
387 | .map(|it| { | 365 | .map(|it| { |
388 | let tag = it.kind().unwrap().tag(); | 366 | let tag = it.kind().unwrap().tag(); |
389 | let score = display_score(it.score()); | 367 | let relevance = display_relevance(it.relevance()); |
390 | format!("{} {} {}\n", tag, it.label(), score) | 368 | format!("{} {} {}\n", tag, it.label(), relevance) |
391 | }) | 369 | }) |
392 | .collect::<String>(); | 370 | .collect::<String>(); |
393 | expect.assert_eq(&actual); | 371 | expect.assert_eq(&actual); |
@@ -734,7 +712,7 @@ fn foo(s: S) { s.$0 } | |||
734 | 712 | ||
735 | #[test] | 713 | #[test] |
736 | fn no_call_parens_if_fn_ptr_needed() { | 714 | fn no_call_parens_if_fn_ptr_needed() { |
737 | mark::check!(no_call_parens_if_fn_ptr_needed); | 715 | cov_mark::check!(no_call_parens_if_fn_ptr_needed); |
738 | check_edit( | 716 | check_edit( |
739 | "foo", | 717 | "foo", |
740 | r#" | 718 | r#" |
@@ -758,7 +736,7 @@ fn main() -> ManualVtable { | |||
758 | 736 | ||
759 | #[test] | 737 | #[test] |
760 | fn no_parens_in_use_item() { | 738 | fn no_parens_in_use_item() { |
761 | mark::check!(no_parens_in_use_item); | 739 | cov_mark::check!(no_parens_in_use_item); |
762 | check_edit( | 740 | check_edit( |
763 | "foo", | 741 | "foo", |
764 | r#" | 742 | r#" |
@@ -802,7 +780,7 @@ fn f(foo: &Foo) { foo.foo(); } | |||
802 | 780 | ||
803 | #[test] | 781 | #[test] |
804 | fn inserts_angle_brackets_for_generics() { | 782 | fn inserts_angle_brackets_for_generics() { |
805 | mark::check!(inserts_angle_brackets_for_generics); | 783 | cov_mark::check!(inserts_angle_brackets_for_generics); |
806 | check_edit( | 784 | check_edit( |
807 | "Vec", | 785 | "Vec", |
808 | r#" | 786 | r#" |
@@ -850,9 +828,9 @@ fn foo(xs: Vec<i128>) | |||
850 | } | 828 | } |
851 | 829 | ||
852 | #[test] | 830 | #[test] |
853 | fn active_param_score() { | 831 | fn active_param_relevance() { |
854 | mark::check!(active_param_type_match); | 832 | cov_mark::check!(active_param_type_match); |
855 | check_scores( | 833 | check_relevance( |
856 | r#" | 834 | r#" |
857 | struct S { foo: i64, bar: u32, baz: u32 } | 835 | struct S { foo: i64, bar: u32, baz: u32 } |
858 | fn test(bar: u32) { } | 836 | fn test(bar: u32) { } |
@@ -867,9 +845,9 @@ fn foo(s: S) { test(s.$0) } | |||
867 | } | 845 | } |
868 | 846 | ||
869 | #[test] | 847 | #[test] |
870 | fn record_field_scores() { | 848 | fn record_field_relevances() { |
871 | mark::check!(record_field_type_match); | 849 | cov_mark::check!(record_field_type_match); |
872 | check_scores( | 850 | check_relevance( |
873 | r#" | 851 | r#" |
874 | struct A { foo: i64, bar: u32, baz: u32 } | 852 | struct A { foo: i64, bar: u32, baz: u32 } |
875 | struct B { x: (), y: f32, bar: u32 } | 853 | struct B { x: (), y: f32, bar: u32 } |
@@ -884,8 +862,8 @@ fn foo(a: A) { B { bar: a.$0 }; } | |||
884 | } | 862 | } |
885 | 863 | ||
886 | #[test] | 864 | #[test] |
887 | fn record_field_and_call_scores() { | 865 | fn record_field_and_call_relevances() { |
888 | check_scores( | 866 | check_relevance( |
889 | r#" | 867 | r#" |
890 | struct A { foo: i64, bar: u32, baz: u32 } | 868 | struct A { foo: i64, bar: u32, baz: u32 } |
891 | struct B { x: (), y: f32, bar: u32 } | 869 | struct B { x: (), y: f32, bar: u32 } |
@@ -898,7 +876,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; } | |||
898 | fd baz [] | 876 | fd baz [] |
899 | "#]], | 877 | "#]], |
900 | ); | 878 | ); |
901 | check_scores( | 879 | check_relevance( |
902 | r#" | 880 | r#" |
903 | struct A { foo: i64, bar: u32, baz: u32 } | 881 | struct A { foo: i64, bar: u32, baz: u32 } |
904 | struct B { x: (), y: f32, bar: u32 } | 882 | struct B { x: (), y: f32, bar: u32 } |
@@ -915,7 +893,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); } | |||
915 | 893 | ||
916 | #[test] | 894 | #[test] |
917 | fn prioritize_exact_ref_match() { | 895 | fn prioritize_exact_ref_match() { |
918 | check_scores( | 896 | check_relevance( |
919 | r#" | 897 | r#" |
920 | struct WorldSnapshot { _f: () }; | 898 | struct WorldSnapshot { _f: () }; |
921 | fn go(world: &WorldSnapshot) { go(w$0) } | 899 | fn go(world: &WorldSnapshot) { go(w$0) } |
@@ -930,7 +908,7 @@ fn go(world: &WorldSnapshot) { go(w$0) } | |||
930 | 908 | ||
931 | #[test] | 909 | #[test] |
932 | fn too_many_arguments() { | 910 | fn too_many_arguments() { |
933 | check_scores( | 911 | check_relevance( |
934 | r#" | 912 | r#" |
935 | struct Foo; | 913 | struct Foo; |
936 | fn f(foo: &Foo) { f(foo, w$0) } | 914 | fn f(foo: &Foo) { f(foo, w$0) } |
@@ -942,4 +920,70 @@ fn f(foo: &Foo) { f(foo, w$0) } | |||
942 | "#]], | 920 | "#]], |
943 | ); | 921 | ); |
944 | } | 922 | } |
923 | |||
924 | #[test] | ||
925 | fn suggest_ref_mut() { | ||
926 | cov_mark::check!(suggest_ref); | ||
927 | check( | ||
928 | r#" | ||
929 | struct S; | ||
930 | fn foo(s: &mut S) {} | ||
931 | fn main() { | ||
932 | let mut s = S; | ||
933 | foo($0); | ||
934 | } | ||
935 | "#, | ||
936 | expect![[r#" | ||
937 | [ | ||
938 | CompletionItem { | ||
939 | label: "S", | ||
940 | source_range: 70..70, | ||
941 | delete: 70..70, | ||
942 | insert: "S", | ||
943 | kind: SymbolKind( | ||
944 | Struct, | ||
945 | ), | ||
946 | }, | ||
947 | CompletionItem { | ||
948 | label: "foo(…)", | ||
949 | source_range: 70..70, | ||
950 | delete: 70..70, | ||
951 | insert: "foo(${1:&mut s})$0", | ||
952 | kind: SymbolKind( | ||
953 | Function, | ||
954 | ), | ||
955 | lookup: "foo", | ||
956 | detail: "-> ()", | ||
957 | trigger_call_info: true, | ||
958 | }, | ||
959 | CompletionItem { | ||
960 | label: "main()", | ||
961 | source_range: 70..70, | ||
962 | delete: 70..70, | ||
963 | insert: "main()$0", | ||
964 | kind: SymbolKind( | ||
965 | Function, | ||
966 | ), | ||
967 | lookup: "main", | ||
968 | detail: "-> ()", | ||
969 | }, | ||
970 | CompletionItem { | ||
971 | label: "s", | ||
972 | source_range: 70..70, | ||
973 | delete: 70..70, | ||
974 | insert: "s", | ||
975 | kind: SymbolKind( | ||
976 | Local, | ||
977 | ), | ||
978 | detail: "S", | ||
979 | relevance: Relevance { | ||
980 | exact_name_match: true, | ||
981 | exact_type_match: false, | ||
982 | }, | ||
983 | ref_match: "&mut ", | ||
984 | }, | ||
985 | ] | ||
986 | "#]], | ||
987 | ) | ||
988 | } | ||
945 | } | 989 | } |
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs index d053a988b..95a7596c1 100644 --- a/crates/ide_completion/src/render/builder_ext.rs +++ b/crates/ide_completion/src/render/builder_ext.rs | |||
@@ -1,7 +1,6 @@ | |||
1 | //! Extensions for `Builder` structure required for item rendering. | 1 | //! Extensions for `Builder` structure required for item rendering. |
2 | 2 | ||
3 | use itertools::Itertools; | 3 | use itertools::Itertools; |
4 | use test_utils::mark; | ||
5 | 4 | ||
6 | use crate::{item::Builder, CompletionContext}; | 5 | use crate::{item::Builder, CompletionContext}; |
7 | 6 | ||
@@ -30,7 +29,7 @@ impl Builder { | |||
30 | return false; | 29 | return false; |
31 | } | 30 | } |
32 | if ctx.use_item_syntax.is_some() { | 31 | if ctx.use_item_syntax.is_some() { |
33 | mark::hit!(no_parens_in_use_item); | 32 | cov_mark::hit!(no_parens_in_use_item); |
34 | return false; | 33 | return false; |
35 | } | 34 | } |
36 | if ctx.is_pattern_call { | 35 | if ctx.is_pattern_call { |
@@ -43,7 +42,7 @@ impl Builder { | |||
43 | // Don't add parentheses if the expected type is some function reference. | 42 | // Don't add parentheses if the expected type is some function reference. |
44 | if let Some(ty) = &ctx.expected_type { | 43 | if let Some(ty) = &ctx.expected_type { |
45 | if ty.is_fn() { | 44 | if ty.is_fn() { |
46 | mark::hit!(no_call_parens_if_fn_ptr_needed); | 45 | cov_mark::hit!(no_call_parens_if_fn_ptr_needed); |
47 | return false; | 46 | return false; |
48 | } | 47 | } |
49 | } | 48 | } |
@@ -67,7 +66,7 @@ impl Builder { | |||
67 | None => return self, | 66 | None => return self, |
68 | }; | 67 | }; |
69 | // If not an import, add parenthesis automatically. | 68 | // If not an import, add parenthesis automatically. |
70 | mark::hit!(inserts_parens_for_function_calls); | 69 | cov_mark::hit!(inserts_parens_for_function_calls); |
71 | 70 | ||
72 | let (snippet, label) = if params.is_empty() { | 71 | let (snippet, label) = if params.is_empty() { |
73 | (format!("{}()$0", name), format!("{}()", name)) | 72 | (format!("{}()$0", name), format!("{}()", name)) |
@@ -82,7 +81,7 @@ impl Builder { | |||
82 | format!("{}({})$0", name, function_params_snippet) | 81 | format!("{}({})$0", name, function_params_snippet) |
83 | } | 82 | } |
84 | _ => { | 83 | _ => { |
85 | mark::hit!(suppress_arg_snippets); | 84 | cov_mark::hit!(suppress_arg_snippets); |
86 | format!("{}($0)", name) | 85 | format!("{}($0)", name) |
87 | } | 86 | } |
88 | }; | 87 | }; |
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs index 9214193b4..ed055c1fb 100644 --- a/crates/ide_completion/src/render/enum_variant.rs +++ b/crates/ide_completion/src/render/enum_variant.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; | 3 | use hir::{HasAttrs, HirDisplay, ModPath, StructKind}; |
4 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use itertools::Itertools; | 5 | use itertools::Itertools; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
@@ -68,7 +67,7 @@ impl<'a> EnumRender<'a> { | |||
68 | .detail(self.detail()); | 67 | .detail(self.detail()); |
69 | 68 | ||
70 | if self.variant_kind == StructKind::Tuple { | 69 | if self.variant_kind == StructKind::Tuple { |
71 | mark::hit!(inserts_parens_for_tuple_enums); | 70 | cov_mark::hit!(inserts_parens_for_tuple_enums); |
72 | let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); | 71 | let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); |
73 | builder = | 72 | builder = |
74 | builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params); | 73 | builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params); |
@@ -103,13 +102,11 @@ impl<'a> EnumRender<'a> { | |||
103 | 102 | ||
104 | #[cfg(test)] | 103 | #[cfg(test)] |
105 | mod tests { | 104 | mod tests { |
106 | use test_utils::mark; | ||
107 | |||
108 | use crate::test_utils::check_edit; | 105 | use crate::test_utils::check_edit; |
109 | 106 | ||
110 | #[test] | 107 | #[test] |
111 | fn inserts_parens_for_tuple_enums() { | 108 | fn inserts_parens_for_tuple_enums() { |
112 | mark::check!(inserts_parens_for_tuple_enums); | 109 | cov_mark::check!(inserts_parens_for_tuple_enums); |
113 | check_edit( | 110 | check_edit( |
114 | "Some", | 111 | "Some", |
115 | r#" | 112 | r#" |
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs index e46e21d24..5931945a8 100644 --- a/crates/ide_completion/src/render/function.rs +++ b/crates/ide_completion/src/render/function.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{HasSource, HirDisplay, Type}; | 3 | use hir::{HasSource, HirDisplay, Type}; |
4 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use syntax::ast::Fn; | 5 | use syntax::ast::Fn; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionItemKind, CompletionKind, ImportEdit}, |
@@ -82,7 +81,7 @@ impl<'a> FunctionRender<'a> { | |||
82 | self.func.method_params(self.ctx.db()).unwrap_or_default() | 81 | self.func.method_params(self.ctx.db()).unwrap_or_default() |
83 | } else { | 82 | } else { |
84 | if let Some(s) = ast_params.self_param() { | 83 | if let Some(s) = ast_params.self_param() { |
85 | mark::hit!(parens_for_method_call_as_assoc_fn); | 84 | cov_mark::hit!(parens_for_method_call_as_assoc_fn); |
86 | params_pats.push(Some(s.to_string())); | 85 | params_pats.push(Some(s.to_string())); |
87 | } | 86 | } |
88 | self.func.assoc_fn_params(self.ctx.db()) | 87 | self.func.assoc_fn_params(self.ctx.db()) |
@@ -114,8 +113,6 @@ impl<'a> FunctionRender<'a> { | |||
114 | 113 | ||
115 | #[cfg(test)] | 114 | #[cfg(test)] |
116 | mod tests { | 115 | mod tests { |
117 | use test_utils::mark; | ||
118 | |||
119 | use crate::{ | 116 | use crate::{ |
120 | test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, | 117 | test_utils::{check_edit, check_edit_with_config, TEST_CONFIG}, |
121 | CompletionConfig, | 118 | CompletionConfig, |
@@ -123,7 +120,7 @@ mod tests { | |||
123 | 120 | ||
124 | #[test] | 121 | #[test] |
125 | fn inserts_parens_for_function_calls() { | 122 | fn inserts_parens_for_function_calls() { |
126 | mark::check!(inserts_parens_for_function_calls); | 123 | cov_mark::check!(inserts_parens_for_function_calls); |
127 | check_edit( | 124 | check_edit( |
128 | "no_args", | 125 | "no_args", |
129 | r#" | 126 | r#" |
@@ -191,7 +188,7 @@ fn bar(s: &S) { | |||
191 | 188 | ||
192 | #[test] | 189 | #[test] |
193 | fn parens_for_method_call_as_assoc_fn() { | 190 | fn parens_for_method_call_as_assoc_fn() { |
194 | mark::check!(parens_for_method_call_as_assoc_fn); | 191 | cov_mark::check!(parens_for_method_call_as_assoc_fn); |
195 | check_edit( | 192 | check_edit( |
196 | "foo", | 193 | "foo", |
197 | r#" | 194 | r#" |
@@ -213,7 +210,7 @@ fn main() { S::foo(${1:&self})$0 } | |||
213 | 210 | ||
214 | #[test] | 211 | #[test] |
215 | fn suppress_arg_snippets() { | 212 | fn suppress_arg_snippets() { |
216 | mark::check!(suppress_arg_snippets); | 213 | cov_mark::check!(suppress_arg_snippets); |
217 | check_edit_with_config( | 214 | check_edit_with_config( |
218 | CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, | 215 | CompletionConfig { add_call_argument_snippets: false, ..TEST_CONFIG }, |
219 | "with_args", | 216 | "with_args", |
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs index a4535786f..a6cf3e479 100644 --- a/crates/ide_completion/src/render/macro_.rs +++ b/crates/ide_completion/src/render/macro_.rs | |||
@@ -3,7 +3,6 @@ | |||
3 | use hir::{Documentation, HasSource}; | 3 | use hir::{Documentation, HasSource}; |
4 | use ide_db::SymbolKind; | 4 | use ide_db::SymbolKind; |
5 | use syntax::display::macro_label; | 5 | use syntax::display::macro_label; |
6 | use test_utils::mark; | ||
7 | 6 | ||
8 | use crate::{ | 7 | use crate::{ |
9 | item::{CompletionItem, CompletionKind, ImportEdit}, | 8 | item::{CompletionItem, CompletionKind, ImportEdit}, |
@@ -57,7 +56,7 @@ impl<'a> MacroRender<'a> { | |||
57 | } | 56 | } |
58 | None if needs_bang => builder.insert_text(self.banged_name()), | 57 | None if needs_bang => builder.insert_text(self.banged_name()), |
59 | _ => { | 58 | _ => { |
60 | mark::hit!(dont_insert_macro_call_parens_unncessary); | 59 | cov_mark::hit!(dont_insert_macro_call_parens_unncessary); |
61 | builder.insert_text(&self.name) | 60 | builder.insert_text(&self.name) |
62 | } | 61 | } |
63 | }; | 62 | }; |
@@ -125,13 +124,11 @@ fn guess_macro_braces(macro_name: &str, docs: &str) -> (&'static str, &'static s | |||
125 | 124 | ||
126 | #[cfg(test)] | 125 | #[cfg(test)] |
127 | mod tests { | 126 | mod tests { |
128 | use test_utils::mark; | ||
129 | |||
130 | use crate::test_utils::check_edit; | 127 | use crate::test_utils::check_edit; |
131 | 128 | ||
132 | #[test] | 129 | #[test] |
133 | fn dont_insert_macro_call_parens_unncessary() { | 130 | fn dont_insert_macro_call_parens_unncessary() { |
134 | mark::check!(dont_insert_macro_call_parens_unncessary); | 131 | cov_mark::check!(dont_insert_macro_call_parens_unncessary); |
135 | check_edit( | 132 | check_edit( |
136 | "frobnicate!", | 133 | "frobnicate!", |
137 | r#" | 134 | r#" |
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs index baff83305..9da844031 100644 --- a/crates/ide_completion/src/test_utils.rs +++ b/crates/ide_completion/src/test_utils.rs | |||
@@ -25,6 +25,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { | |||
25 | insert_use: InsertUseConfig { | 25 | insert_use: InsertUseConfig { |
26 | merge: Some(MergeBehavior::Full), | 26 | merge: Some(MergeBehavior::Full), |
27 | prefix_kind: PrefixKind::Plain, | 27 | prefix_kind: PrefixKind::Plain, |
28 | group: true, | ||
28 | }, | 29 | }, |
29 | }; | 30 | }; |
30 | 31 | ||
@@ -119,7 +120,7 @@ pub(crate) fn check_edit_with_config( | |||
119 | 120 | ||
120 | let mut combined_edit = completion.text_edit().to_owned(); | 121 | let mut combined_edit = completion.text_edit().to_owned(); |
121 | if let Some(import_text_edit) = | 122 | if let Some(import_text_edit) = |
122 | completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use.merge)) | 123 | completion.import_to_add().and_then(|edit| edit.to_text_edit(config.insert_use)) |
123 | { | 124 | { |
124 | combined_edit.union(import_text_edit).expect( | 125 | combined_edit.union(import_text_edit).expect( |
125 | "Failed to apply completion resolve changes: change ranges overlap, but should not", | 126 | "Failed to apply completion resolve changes: change ranges overlap, but should not", |
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml index d4612a75e..1f7a90d20 100644 --- a/crates/ide_db/Cargo.toml +++ b/crates/ide_db/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | log = "0.4.8" | 14 | log = "0.4.8" |
14 | rayon = "1.5.0" | 15 | rayon = "1.5.0" |
15 | fst = { version = "0.4", default-features = false } | 16 | fst = { version = "0.4", default-features = false } |
@@ -23,10 +24,10 @@ syntax = { path = "../syntax", version = "0.0.0" } | |||
23 | text_edit = { path = "../text_edit", version = "0.0.0" } | 24 | text_edit = { path = "../text_edit", version = "0.0.0" } |
24 | base_db = { path = "../base_db", version = "0.0.0" } | 25 | base_db = { path = "../base_db", version = "0.0.0" } |
25 | profile = { path = "../profile", version = "0.0.0" } | 26 | profile = { path = "../profile", version = "0.0.0" } |
26 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
27 | # ide should depend only on the top-level `hir` package. if you need | 27 | # ide should depend only on the top-level `hir` package. if you need |
28 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. | 28 | # something from some `hir_xxx` subpackage, reexport the API via `hir`. |
29 | hir = { path = "../hir", version = "0.0.0" } | 29 | hir = { path = "../hir", version = "0.0.0" } |
30 | 30 | ||
31 | [dev-dependencies] | 31 | [dev-dependencies] |
32 | test_utils = { path = "../test_utils" } | ||
32 | expect-test = "1.1" | 33 | expect-test = "1.1" |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index 615fa7b0e..d8878aa91 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -7,7 +7,6 @@ use syntax::{ | |||
7 | ast::{self, ArgListOwner}, | 7 | ast::{self, ArgListOwner}, |
8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, | 8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | }; | 9 | }; |
10 | use test_utils::mark; | ||
11 | 10 | ||
12 | use crate::RootDatabase; | 11 | use crate::RootDatabase; |
13 | 12 | ||
@@ -122,7 +121,7 @@ fn call_info_impl( | |||
122 | 121 | ||
123 | let arg_list_range = arg_list.syntax().text_range(); | 122 | let arg_list_range = arg_list.syntax().text_range(); |
124 | if !arg_list_range.contains_inclusive(token.text_range().start()) { | 123 | if !arg_list_range.contains_inclusive(token.text_range().start()) { |
125 | mark::hit!(call_info_bad_offset); | 124 | cov_mark::hit!(call_info_bad_offset); |
126 | return None; | 125 | return None; |
127 | } | 126 | } |
128 | let param = std::cmp::min( | 127 | let param = std::cmp::min( |
@@ -162,7 +161,7 @@ impl ActiveParameter { | |||
162 | let idx = active_parameter?; | 161 | let idx = active_parameter?; |
163 | let mut params = signature.params(sema.db); | 162 | let mut params = signature.params(sema.db); |
164 | if !(idx < params.len()) { | 163 | if !(idx < params.len()) { |
165 | mark::hit!(too_many_arguments); | 164 | cov_mark::hit!(too_many_arguments); |
166 | return None; | 165 | return None; |
167 | } | 166 | } |
168 | let (pat, ty) = params.swap_remove(idx); | 167 | let (pat, ty) = params.swap_remove(idx); |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index c714cf280..9f84c253c 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use crate::RootDatabase; | 1 | use crate::RootDatabase; |
2 | use base_db::{fixture::ChangeFixture, FilePosition}; | 2 | use base_db::{fixture::ChangeFixture, FilePosition}; |
3 | use expect_test::{expect, Expect}; | 3 | use expect_test::{expect, Expect}; |
4 | use test_utils::{mark, RangeOrOffset}; | 4 | use test_utils::RangeOrOffset; |
5 | 5 | ||
6 | /// Creates analysis from a multi-file fixture, returns positions marked with $0. | 6 | /// Creates analysis from a multi-file fixture, returns positions marked with $0. |
7 | pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { | 7 | pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) { |
@@ -347,7 +347,7 @@ pub fn foo(mut r: WriteHandler<()>) { | |||
347 | 347 | ||
348 | #[test] | 348 | #[test] |
349 | fn call_info_bad_offset() { | 349 | fn call_info_bad_offset() { |
350 | mark::check!(call_info_bad_offset); | 350 | cov_mark::check!(call_info_bad_offset); |
351 | check( | 351 | check( |
352 | r#" | 352 | r#" |
353 | fn foo(x: u32, y: u32) -> u32 {x + y} | 353 | fn foo(x: u32, y: u32) -> u32 {x + y} |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 3ff77400b..3c95d3cff 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -2,11 +2,19 @@ | |||
2 | pub mod insert_use; | 2 | pub mod insert_use; |
3 | pub mod import_assets; | 3 | pub mod import_assets; |
4 | 4 | ||
5 | use hir::{Crate, Enum, Module, ScopeDef, Semantics, Trait}; | 5 | use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; |
6 | use syntax::ast::{self, make}; | 6 | use syntax::ast::{self, make}; |
7 | 7 | ||
8 | use crate::RootDatabase; | 8 | use crate::RootDatabase; |
9 | 9 | ||
10 | pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> { | ||
11 | match item { | ||
12 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).name(db), | ||
13 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).name(db), | ||
14 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).name(db), | ||
15 | } | ||
16 | } | ||
17 | |||
10 | /// Converts the mod path struct into its ast representation. | 18 | /// Converts the mod path struct into its ast representation. |
11 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | 19 | pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { |
12 | let _p = profile::span("mod_path_to_ast"); | 20 | let _p = profile::span("mod_path_to_ast"); |
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 517abbb4b..e03ccd351 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -1,19 +1,29 @@ | |||
1 | //! Look up accessible paths for items. | 1 | //! Look up accessible paths for items. |
2 | use either::Either; | 2 | use hir::{ |
3 | use hir::{AsAssocItem, AssocItem, Crate, MacroDef, Module, ModuleDef, PrefixKind, Semantics}; | 3 | AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, |
4 | ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type, | ||
5 | }; | ||
6 | use itertools::Itertools; | ||
4 | use rustc_hash::FxHashSet; | 7 | use rustc_hash::FxHashSet; |
5 | use syntax::{ast, AstNode}; | 8 | use syntax::{ast, utils::path_to_string_stripping_turbo_fish, AstNode, SyntaxNode}; |
6 | 9 | ||
7 | use crate::{ | 10 | use crate::{ |
8 | imports_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, | 11 | items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT}, |
9 | RootDatabase, | 12 | RootDatabase, |
10 | }; | 13 | }; |
11 | 14 | ||
15 | use super::item_name; | ||
16 | |||
17 | /// A candidate for import, derived during various IDE activities: | ||
18 | /// * completion with imports on the fly proposals | ||
19 | /// * completion edit resolve requests | ||
20 | /// * assists | ||
21 | /// * etc. | ||
12 | #[derive(Debug)] | 22 | #[derive(Debug)] |
13 | pub enum ImportCandidate { | 23 | pub enum ImportCandidate { |
14 | // A path, qualified (`std::collections::HashMap`) or not (`HashMap`). | 24 | /// A path, qualified (`std::collections::HashMap`) or not (`HashMap`). |
15 | Path(PathImportCandidate), | 25 | Path(PathImportCandidate), |
16 | /// A trait associated function (with no self parameter) or associated constant. | 26 | /// A trait associated function (with no self parameter) or an associated constant. |
17 | /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type | 27 | /// For 'test_mod::TestEnum::test_function', `ty` is the `test_mod::TestEnum` expression type |
18 | /// and `name` is the `test_function` | 28 | /// and `name` is the `test_function` |
19 | TraitAssocItem(TraitImportCandidate), | 29 | TraitAssocItem(TraitImportCandidate), |
@@ -23,21 +33,40 @@ pub enum ImportCandidate { | |||
23 | TraitMethod(TraitImportCandidate), | 33 | TraitMethod(TraitImportCandidate), |
24 | } | 34 | } |
25 | 35 | ||
36 | /// A trait import needed for a given associated item access. | ||
37 | /// For `some::path::SomeStruct::ASSOC_`, contains the | ||
38 | /// type of `some::path::SomeStruct` and `ASSOC_` as the item name. | ||
26 | #[derive(Debug)] | 39 | #[derive(Debug)] |
27 | pub struct TraitImportCandidate { | 40 | pub struct TraitImportCandidate { |
28 | pub receiver_ty: hir::Type, | 41 | /// A type of the item that has the associated item accessed at. |
29 | pub name: NameToImport, | 42 | pub receiver_ty: Type, |
43 | /// The associated item name that the trait to import should contain. | ||
44 | pub assoc_item_name: NameToImport, | ||
30 | } | 45 | } |
31 | 46 | ||
47 | /// Path import for a given name, qualified or not. | ||
32 | #[derive(Debug)] | 48 | #[derive(Debug)] |
33 | pub struct PathImportCandidate { | 49 | pub struct PathImportCandidate { |
34 | pub qualifier: Option<ast::Path>, | 50 | /// Optional qualifier before name. |
51 | pub qualifier: Option<FirstSegmentUnresolved>, | ||
52 | /// The name the item (struct, trait, enum, etc.) should have. | ||
35 | pub name: NameToImport, | 53 | pub name: NameToImport, |
36 | } | 54 | } |
37 | 55 | ||
56 | /// A qualifier that has a first segment and it's unresolved. | ||
57 | #[derive(Debug)] | ||
58 | pub struct FirstSegmentUnresolved { | ||
59 | fist_segment: ast::NameRef, | ||
60 | full_qualifier: ast::Path, | ||
61 | } | ||
62 | |||
63 | /// A name that will be used during item lookups. | ||
38 | #[derive(Debug)] | 64 | #[derive(Debug)] |
39 | pub enum NameToImport { | 65 | pub enum NameToImport { |
66 | /// Requires items with names that exactly match the given string, case-sensitive. | ||
40 | Exact(String), | 67 | Exact(String), |
68 | /// Requires items with names that case-insensitively contain all letters from the string, | ||
69 | /// in the same order, but not necessary adjacent. | ||
41 | Fuzzy(String), | 70 | Fuzzy(String), |
42 | } | 71 | } |
43 | 72 | ||
@@ -50,10 +79,12 @@ impl NameToImport { | |||
50 | } | 79 | } |
51 | } | 80 | } |
52 | 81 | ||
82 | /// A struct to find imports in the project, given a certain name (or its part) and the context. | ||
53 | #[derive(Debug)] | 83 | #[derive(Debug)] |
54 | pub struct ImportAssets { | 84 | pub struct ImportAssets { |
55 | import_candidate: ImportCandidate, | 85 | import_candidate: ImportCandidate, |
56 | module_with_candidate: hir::Module, | 86 | candidate_node: SyntaxNode, |
87 | module_with_candidate: Module, | ||
57 | } | 88 | } |
58 | 89 | ||
59 | impl ImportAssets { | 90 | impl ImportAssets { |
@@ -61,9 +92,11 @@ impl ImportAssets { | |||
61 | method_call: &ast::MethodCallExpr, | 92 | method_call: &ast::MethodCallExpr, |
62 | sema: &Semantics<RootDatabase>, | 93 | sema: &Semantics<RootDatabase>, |
63 | ) -> Option<Self> { | 94 | ) -> Option<Self> { |
95 | let candidate_node = method_call.syntax().clone(); | ||
64 | Some(Self { | 96 | Some(Self { |
65 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, | 97 | import_candidate: ImportCandidate::for_method_call(sema, method_call)?, |
66 | module_with_candidate: sema.scope(method_call.syntax()).module()?, | 98 | module_with_candidate: sema.scope(&candidate_node).module()?, |
99 | candidate_node, | ||
67 | }) | 100 | }) |
68 | } | 101 | } |
69 | 102 | ||
@@ -71,94 +104,94 @@ impl ImportAssets { | |||
71 | fully_qualified_path: &ast::Path, | 104 | fully_qualified_path: &ast::Path, |
72 | sema: &Semantics<RootDatabase>, | 105 | sema: &Semantics<RootDatabase>, |
73 | ) -> Option<Self> { | 106 | ) -> Option<Self> { |
74 | let syntax_under_caret = fully_qualified_path.syntax(); | 107 | let candidate_node = fully_qualified_path.syntax().clone(); |
75 | if syntax_under_caret.ancestors().find_map(ast::Use::cast).is_some() { | 108 | if candidate_node.ancestors().find_map(ast::Use::cast).is_some() { |
76 | return None; | 109 | return None; |
77 | } | 110 | } |
78 | Some(Self { | 111 | Some(Self { |
79 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, | 112 | import_candidate: ImportCandidate::for_regular_path(sema, fully_qualified_path)?, |
80 | module_with_candidate: sema.scope(syntax_under_caret).module()?, | 113 | module_with_candidate: sema.scope(&candidate_node).module()?, |
114 | candidate_node, | ||
81 | }) | 115 | }) |
82 | } | 116 | } |
83 | 117 | ||
84 | pub fn for_fuzzy_path( | 118 | pub fn for_fuzzy_path( |
85 | module_with_path: Module, | 119 | module_with_candidate: Module, |
86 | qualifier: Option<ast::Path>, | 120 | qualifier: Option<ast::Path>, |
87 | fuzzy_name: String, | 121 | fuzzy_name: String, |
88 | sema: &Semantics<RootDatabase>, | 122 | sema: &Semantics<RootDatabase>, |
123 | candidate_node: SyntaxNode, | ||
89 | ) -> Option<Self> { | 124 | ) -> Option<Self> { |
90 | Some(match qualifier { | 125 | Some(Self { |
91 | Some(qualifier) => { | 126 | import_candidate: ImportCandidate::for_fuzzy_path(qualifier, fuzzy_name, sema)?, |
92 | let qualifier_resolution = sema.resolve_path(&qualifier)?; | 127 | module_with_candidate, |
93 | match qualifier_resolution { | 128 | candidate_node, |
94 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => Self { | ||
95 | import_candidate: ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
96 | receiver_ty: assoc_item_path.ty(sema.db), | ||
97 | name: NameToImport::Fuzzy(fuzzy_name), | ||
98 | }), | ||
99 | module_with_candidate: module_with_path, | ||
100 | }, | ||
101 | _ => Self { | ||
102 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
103 | qualifier: Some(qualifier), | ||
104 | name: NameToImport::Fuzzy(fuzzy_name), | ||
105 | }), | ||
106 | module_with_candidate: module_with_path, | ||
107 | }, | ||
108 | } | ||
109 | } | ||
110 | None => Self { | ||
111 | import_candidate: ImportCandidate::Path(PathImportCandidate { | ||
112 | qualifier: None, | ||
113 | name: NameToImport::Fuzzy(fuzzy_name), | ||
114 | }), | ||
115 | module_with_candidate: module_with_path, | ||
116 | }, | ||
117 | }) | 129 | }) |
118 | } | 130 | } |
119 | 131 | ||
120 | pub fn for_fuzzy_method_call( | 132 | pub fn for_fuzzy_method_call( |
121 | module_with_method_call: Module, | 133 | module_with_method_call: Module, |
122 | receiver_ty: hir::Type, | 134 | receiver_ty: Type, |
123 | fuzzy_method_name: String, | 135 | fuzzy_method_name: String, |
136 | candidate_node: SyntaxNode, | ||
124 | ) -> Option<Self> { | 137 | ) -> Option<Self> { |
125 | Some(Self { | 138 | Some(Self { |
126 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { | 139 | import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate { |
127 | receiver_ty, | 140 | receiver_ty, |
128 | name: NameToImport::Fuzzy(fuzzy_method_name), | 141 | assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name), |
129 | }), | 142 | }), |
130 | module_with_candidate: module_with_method_call, | 143 | module_with_candidate: module_with_method_call, |
144 | candidate_node, | ||
131 | }) | 145 | }) |
132 | } | 146 | } |
133 | } | 147 | } |
134 | 148 | ||
149 | /// An import (not necessary the only one) that corresponds a certain given [`PathImportCandidate`]. | ||
150 | /// (the structure is not entirely correct, since there can be situations requiring two imports, see FIXME below for the details) | ||
151 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
152 | pub struct LocatedImport { | ||
153 | /// The path to use in the `use` statement for a given candidate to be imported. | ||
154 | pub import_path: ModPath, | ||
155 | /// An item that will be imported with the import path given. | ||
156 | pub item_to_import: ItemInNs, | ||
157 | /// The path import candidate, resolved. | ||
158 | /// | ||
159 | /// Not necessary matches the import: | ||
160 | /// For any associated constant from the trait, we try to access as `some::path::SomeStruct::ASSOC_` | ||
161 | /// the original item is the associated constant, but the import has to be a trait that | ||
162 | /// defines this constant. | ||
163 | pub original_item: ItemInNs, | ||
164 | /// A path of the original item. | ||
165 | pub original_path: Option<ModPath>, | ||
166 | } | ||
167 | |||
168 | impl LocatedImport { | ||
169 | pub fn new( | ||
170 | import_path: ModPath, | ||
171 | item_to_import: ItemInNs, | ||
172 | original_item: ItemInNs, | ||
173 | original_path: Option<ModPath>, | ||
174 | ) -> Self { | ||
175 | Self { import_path, item_to_import, original_item, original_path } | ||
176 | } | ||
177 | } | ||
178 | |||
135 | impl ImportAssets { | 179 | impl ImportAssets { |
136 | pub fn import_candidate(&self) -> &ImportCandidate { | 180 | pub fn import_candidate(&self) -> &ImportCandidate { |
137 | &self.import_candidate | 181 | &self.import_candidate |
138 | } | 182 | } |
139 | 183 | ||
140 | fn name_to_import(&self) -> &NameToImport { | ||
141 | match &self.import_candidate { | ||
142 | ImportCandidate::Path(candidate) => &candidate.name, | ||
143 | ImportCandidate::TraitAssocItem(candidate) | ||
144 | | ImportCandidate::TraitMethod(candidate) => &candidate.name, | ||
145 | } | ||
146 | } | ||
147 | |||
148 | pub fn search_for_imports( | 184 | pub fn search_for_imports( |
149 | &self, | 185 | &self, |
150 | sema: &Semantics<RootDatabase>, | 186 | sema: &Semantics<RootDatabase>, |
151 | prefix_kind: PrefixKind, | 187 | prefix_kind: PrefixKind, |
152 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 188 | ) -> Vec<LocatedImport> { |
153 | let _p = profile::span("import_assets::search_for_imports"); | 189 | let _p = profile::span("import_assets::search_for_imports"); |
154 | self.search_for(sema, Some(prefix_kind)) | 190 | self.search_for(sema, Some(prefix_kind)) |
155 | } | 191 | } |
156 | 192 | ||
157 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. | 193 | /// This may return non-absolute paths if a part of the returned path is already imported into scope. |
158 | pub fn search_for_relative_paths( | 194 | pub fn search_for_relative_paths(&self, sema: &Semantics<RootDatabase>) -> Vec<LocatedImport> { |
159 | &self, | ||
160 | sema: &Semantics<RootDatabase>, | ||
161 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | ||
162 | let _p = profile::span("import_assets::search_for_relative_paths"); | 195 | let _p = profile::span("import_assets::search_for_relative_paths"); |
163 | self.search_for(sema, None) | 196 | self.search_for(sema, None) |
164 | } | 197 | } |
@@ -166,29 +199,29 @@ impl ImportAssets { | |||
166 | fn search_for( | 199 | fn search_for( |
167 | &self, | 200 | &self, |
168 | sema: &Semantics<RootDatabase>, | 201 | sema: &Semantics<RootDatabase>, |
169 | prefixed: Option<hir::PrefixKind>, | 202 | prefixed: Option<PrefixKind>, |
170 | ) -> Vec<(hir::ModPath, hir::ItemInNs)> { | 203 | ) -> Vec<LocatedImport> { |
171 | let current_crate = self.module_with_candidate.krate(); | 204 | let items_with_candidate_name = match self.name_to_import() { |
172 | 205 | NameToImport::Exact(exact_name) => items_locator::with_exact_name( | |
173 | let unfiltered_imports = match self.name_to_import() { | 206 | sema, |
174 | NameToImport::Exact(exact_name) => { | 207 | self.module_with_candidate.krate(), |
175 | imports_locator::find_exact_imports(sema, current_crate, exact_name.clone()) | 208 | exact_name.clone(), |
176 | } | 209 | ), |
177 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: | 210 | // FIXME: ideally, we should avoid using `fst` for seacrhing trait imports for assoc items: |
178 | // instead, we need to look up all trait impls for a certain struct and search through them only | 211 | // instead, we need to look up all trait impls for a certain struct and search through them only |
179 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 | 212 | // see https://github.com/rust-analyzer/rust-analyzer/pull/7293#issuecomment-761585032 |
180 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup | 213 | // and https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Fwg-rls-2.2E0/topic/Blanket.20trait.20impls.20lookup |
181 | // for the details | 214 | // for the details |
182 | NameToImport::Fuzzy(fuzzy_name) => { | 215 | NameToImport::Fuzzy(fuzzy_name) => { |
183 | let (assoc_item_search, limit) = match self.import_candidate { | 216 | let (assoc_item_search, limit) = if self.import_candidate.is_trait_candidate() { |
184 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 217 | (AssocItemSearch::AssocItemsOnly, None) |
185 | (AssocItemSearch::AssocItemsOnly, None) | 218 | } else { |
186 | } | 219 | (AssocItemSearch::Include, Some(DEFAULT_QUERY_SEARCH_LIMIT)) |
187 | _ => (AssocItemSearch::Exclude, Some(DEFAULT_QUERY_SEARCH_LIMIT)), | ||
188 | }; | 220 | }; |
189 | imports_locator::find_similar_imports( | 221 | |
222 | items_locator::with_similar_name( | ||
190 | sema, | 223 | sema, |
191 | current_crate, | 224 | self.module_with_candidate.krate(), |
192 | fuzzy_name.clone(), | 225 | fuzzy_name.clone(), |
193 | assoc_item_search, | 226 | assoc_item_search, |
194 | limit, | 227 | limit, |
@@ -196,63 +229,224 @@ impl ImportAssets { | |||
196 | } | 229 | } |
197 | }; | 230 | }; |
198 | 231 | ||
199 | let db = sema.db; | 232 | let scope_definitions = self.scope_definitions(sema); |
200 | let mut res = | 233 | self.applicable_defs(sema.db, prefixed, items_with_candidate_name) |
201 | applicable_defs(self.import_candidate(), current_crate, db, unfiltered_imports) | 234 | .into_iter() |
202 | .filter_map(|candidate| { | 235 | .filter(|import| import.import_path.len() > 1) |
203 | let item: hir::ItemInNs = candidate.clone().either(Into::into, Into::into); | 236 | .filter(|import| !scope_definitions.contains(&ScopeDef::from(import.item_to_import))) |
204 | 237 | .sorted_by_key(|import| import.import_path.clone()) | |
205 | let item_to_search = match self.import_candidate { | 238 | .collect() |
206 | ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_) => { | 239 | } |
207 | let canidate_trait = match candidate { | 240 | |
208 | Either::Left(module_def) => { | 241 | fn scope_definitions(&self, sema: &Semantics<RootDatabase>) -> FxHashSet<ScopeDef> { |
209 | module_def.as_assoc_item(db)?.containing_trait(db) | 242 | let mut scope_definitions = FxHashSet::default(); |
210 | } | 243 | sema.scope(&self.candidate_node).process_all_names(&mut |_, scope_def| { |
211 | _ => None, | 244 | scope_definitions.insert(scope_def); |
212 | }?; | 245 | }); |
213 | ModuleDef::from(canidate_trait).into() | 246 | scope_definitions |
214 | } | 247 | } |
215 | _ => item, | 248 | |
216 | }; | 249 | fn name_to_import(&self) -> &NameToImport { |
217 | 250 | match &self.import_candidate { | |
218 | if let Some(prefix_kind) = prefixed { | 251 | ImportCandidate::Path(candidate) => &candidate.name, |
219 | self.module_with_candidate.find_use_path_prefixed( | 252 | ImportCandidate::TraitAssocItem(candidate) |
220 | db, | 253 | | ImportCandidate::TraitMethod(candidate) => &candidate.assoc_item_name, |
221 | item_to_search, | 254 | } |
222 | prefix_kind, | 255 | } |
223 | ) | 256 | |
224 | } else { | 257 | fn applicable_defs( |
225 | self.module_with_candidate.find_use_path(db, item_to_search) | 258 | &self, |
226 | } | 259 | db: &RootDatabase, |
227 | .map(|path| (path, item)) | 260 | prefixed: Option<PrefixKind>, |
228 | }) | 261 | items_with_candidate_name: FxHashSet<ItemInNs>, |
229 | .filter(|(use_path, _)| use_path.len() > 1) | 262 | ) -> FxHashSet<LocatedImport> { |
230 | .collect::<Vec<_>>(); | 263 | let _p = profile::span("import_assets::applicable_defs"); |
231 | res.sort_by_cached_key(|(path, _)| path.clone()); | 264 | let current_crate = self.module_with_candidate.krate(); |
232 | res | 265 | |
266 | let mod_path = |item| { | ||
267 | get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed) | ||
268 | }; | ||
269 | |||
270 | match &self.import_candidate { | ||
271 | ImportCandidate::Path(path_candidate) => { | ||
272 | path_applicable_imports(db, path_candidate, mod_path, items_with_candidate_name) | ||
273 | } | ||
274 | ImportCandidate::TraitAssocItem(trait_candidate) => trait_applicable_items( | ||
275 | db, | ||
276 | current_crate, | ||
277 | trait_candidate, | ||
278 | true, | ||
279 | mod_path, | ||
280 | items_with_candidate_name, | ||
281 | ), | ||
282 | ImportCandidate::TraitMethod(trait_candidate) => trait_applicable_items( | ||
283 | db, | ||
284 | current_crate, | ||
285 | trait_candidate, | ||
286 | false, | ||
287 | mod_path, | ||
288 | items_with_candidate_name, | ||
289 | ), | ||
290 | } | ||
233 | } | 291 | } |
234 | } | 292 | } |
235 | 293 | ||
236 | fn applicable_defs<'a>( | 294 | fn path_applicable_imports( |
237 | import_candidate: &ImportCandidate, | ||
238 | current_crate: Crate, | ||
239 | db: &RootDatabase, | 295 | db: &RootDatabase, |
240 | unfiltered_imports: Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a>, | 296 | path_candidate: &PathImportCandidate, |
241 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 297 | mod_path: impl Fn(ItemInNs) -> Option<ModPath> + Copy, |
242 | let receiver_ty = match import_candidate { | 298 | items_with_candidate_name: FxHashSet<ItemInNs>, |
243 | ImportCandidate::Path(_) => return unfiltered_imports, | 299 | ) -> FxHashSet<LocatedImport> { |
244 | ImportCandidate::TraitAssocItem(candidate) | ImportCandidate::TraitMethod(candidate) => { | 300 | let _p = profile::span("import_assets::path_applicable_imports"); |
245 | &candidate.receiver_ty | 301 | |
302 | let (unresolved_first_segment, unresolved_qualifier) = match &path_candidate.qualifier { | ||
303 | None => { | ||
304 | return items_with_candidate_name | ||
305 | .into_iter() | ||
306 | .filter_map(|item| { | ||
307 | Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item))) | ||
308 | }) | ||
309 | .collect(); | ||
246 | } | 310 | } |
311 | Some(first_segment_unresolved) => ( | ||
312 | first_segment_unresolved.fist_segment.to_string(), | ||
313 | path_to_string_stripping_turbo_fish(&first_segment_unresolved.full_qualifier), | ||
314 | ), | ||
247 | }; | 315 | }; |
248 | 316 | ||
317 | items_with_candidate_name | ||
318 | .into_iter() | ||
319 | .filter_map(|item| { | ||
320 | import_for_item(db, mod_path, &unresolved_first_segment, &unresolved_qualifier, item) | ||
321 | }) | ||
322 | .collect() | ||
323 | } | ||
324 | |||
325 | fn import_for_item( | ||
326 | db: &RootDatabase, | ||
327 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | ||
328 | unresolved_first_segment: &str, | ||
329 | unresolved_qualifier: &str, | ||
330 | original_item: ItemInNs, | ||
331 | ) -> Option<LocatedImport> { | ||
332 | let _p = profile::span("import_assets::import_for_item"); | ||
333 | |||
334 | let original_item_candidate = item_for_path_search(db, original_item)?; | ||
335 | let import_path_candidate = mod_path(original_item_candidate)?; | ||
336 | let import_path_string = import_path_candidate.to_string(); | ||
337 | |||
338 | let expected_import_end = if item_as_assoc(db, original_item).is_some() { | ||
339 | unresolved_qualifier.to_string() | ||
340 | } else { | ||
341 | format!("{}::{}", unresolved_qualifier, item_name(db, original_item)?) | ||
342 | }; | ||
343 | if !import_path_string.contains(unresolved_first_segment) | ||
344 | || !import_path_string.ends_with(&expected_import_end) | ||
345 | { | ||
346 | return None; | ||
347 | } | ||
348 | |||
349 | let segment_import = | ||
350 | find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?; | ||
351 | let trait_item_to_import = item_as_assoc(db, original_item) | ||
352 | .and_then(|assoc| assoc.containing_trait(db)) | ||
353 | .map(|trait_| ItemInNs::from(ModuleDef::from(trait_))); | ||
354 | Some(match (segment_import == original_item_candidate, trait_item_to_import) { | ||
355 | (true, Some(_)) => { | ||
356 | // FIXME we should be able to import both the trait and the segment, | ||
357 | // but it's unclear what to do with overlapping edits (merge imports?) | ||
358 | // especially in case of lazy completion edit resolutions. | ||
359 | return None; | ||
360 | } | ||
361 | (false, Some(trait_to_import)) => LocatedImport::new( | ||
362 | mod_path(trait_to_import)?, | ||
363 | trait_to_import, | ||
364 | original_item, | ||
365 | mod_path(original_item), | ||
366 | ), | ||
367 | (true, None) => LocatedImport::new( | ||
368 | import_path_candidate, | ||
369 | original_item_candidate, | ||
370 | original_item, | ||
371 | mod_path(original_item), | ||
372 | ), | ||
373 | (false, None) => LocatedImport::new( | ||
374 | mod_path(segment_import)?, | ||
375 | segment_import, | ||
376 | original_item, | ||
377 | mod_path(original_item), | ||
378 | ), | ||
379 | }) | ||
380 | } | ||
381 | |||
382 | fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> { | ||
383 | Some(match item { | ||
384 | ItemInNs::Types(_) | ItemInNs::Values(_) => match item_as_assoc(db, item) { | ||
385 | Some(assoc_item) => match assoc_item.container(db) { | ||
386 | AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)), | ||
387 | AssocItemContainer::Impl(impl_) => { | ||
388 | ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)) | ||
389 | } | ||
390 | }, | ||
391 | None => item, | ||
392 | }, | ||
393 | ItemInNs::Macros(_) => item, | ||
394 | }) | ||
395 | } | ||
396 | |||
397 | fn find_import_for_segment( | ||
398 | db: &RootDatabase, | ||
399 | original_item: ItemInNs, | ||
400 | unresolved_first_segment: &str, | ||
401 | ) -> Option<ItemInNs> { | ||
402 | let segment_is_name = item_name(db, original_item) | ||
403 | .map(|name| name.to_string() == unresolved_first_segment) | ||
404 | .unwrap_or(false); | ||
405 | |||
406 | Some(if segment_is_name { | ||
407 | original_item | ||
408 | } else { | ||
409 | let matching_module = | ||
410 | module_with_segment_name(db, &unresolved_first_segment, original_item)?; | ||
411 | ItemInNs::from(ModuleDef::from(matching_module)) | ||
412 | }) | ||
413 | } | ||
414 | |||
415 | fn module_with_segment_name( | ||
416 | db: &RootDatabase, | ||
417 | segment_name: &str, | ||
418 | candidate: ItemInNs, | ||
419 | ) -> Option<Module> { | ||
420 | let mut current_module = match candidate { | ||
421 | ItemInNs::Types(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
422 | ItemInNs::Values(module_def_id) => ModuleDef::from(module_def_id).module(db), | ||
423 | ItemInNs::Macros(macro_def_id) => MacroDef::from(macro_def_id).module(db), | ||
424 | }; | ||
425 | while let Some(module) = current_module { | ||
426 | if let Some(module_name) = module.name(db) { | ||
427 | if module_name.to_string() == segment_name { | ||
428 | return Some(module); | ||
429 | } | ||
430 | } | ||
431 | current_module = module.parent(db); | ||
432 | } | ||
433 | None | ||
434 | } | ||
435 | |||
436 | fn trait_applicable_items( | ||
437 | db: &RootDatabase, | ||
438 | current_crate: Crate, | ||
439 | trait_candidate: &TraitImportCandidate, | ||
440 | trait_assoc_item: bool, | ||
441 | mod_path: impl Fn(ItemInNs) -> Option<ModPath>, | ||
442 | items_with_candidate_name: FxHashSet<ItemInNs>, | ||
443 | ) -> FxHashSet<LocatedImport> { | ||
444 | let _p = profile::span("import_assets::trait_applicable_items"); | ||
249 | let mut required_assoc_items = FxHashSet::default(); | 445 | let mut required_assoc_items = FxHashSet::default(); |
250 | 446 | ||
251 | let trait_candidates = unfiltered_imports | 447 | let trait_candidates = items_with_candidate_name |
252 | .filter_map(|input| match input { | 448 | .into_iter() |
253 | Either::Left(module_def) => module_def.as_assoc_item(db), | 449 | .filter_map(|input| item_as_assoc(db, input)) |
254 | _ => None, | ||
255 | }) | ||
256 | .filter_map(|assoc| { | 450 | .filter_map(|assoc| { |
257 | let assoc_item_trait = assoc.containing_trait(db)?; | 451 | let assoc_item_trait = assoc.containing_trait(db)?; |
258 | required_assoc_items.insert(assoc); | 452 | required_assoc_items.insert(assoc); |
@@ -260,11 +454,10 @@ fn applicable_defs<'a>( | |||
260 | }) | 454 | }) |
261 | .collect(); | 455 | .collect(); |
262 | 456 | ||
263 | let mut applicable_defs = FxHashSet::default(); | 457 | let mut located_imports = FxHashSet::default(); |
264 | 458 | ||
265 | match import_candidate { | 459 | if trait_assoc_item { |
266 | ImportCandidate::Path(_) => unreachable!(), | 460 | trait_candidate.receiver_ty.iterate_path_candidates( |
267 | ImportCandidate::TraitAssocItem(_) => receiver_ty.iterate_path_candidates( | ||
268 | db, | 461 | db, |
269 | current_crate, | 462 | current_crate, |
270 | &trait_candidates, | 463 | &trait_candidates, |
@@ -276,12 +469,21 @@ fn applicable_defs<'a>( | |||
276 | return None; | 469 | return None; |
277 | } | 470 | } |
278 | } | 471 | } |
279 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 472 | |
473 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | ||
474 | let original_item = assoc_to_item(assoc); | ||
475 | located_imports.insert(LocatedImport::new( | ||
476 | mod_path(item)?, | ||
477 | item, | ||
478 | original_item, | ||
479 | mod_path(original_item), | ||
480 | )); | ||
280 | } | 481 | } |
281 | None::<()> | 482 | None::<()> |
282 | }, | 483 | }, |
283 | ), | 484 | ) |
284 | ImportCandidate::TraitMethod(_) => receiver_ty.iterate_method_candidates( | 485 | } else { |
486 | trait_candidate.receiver_ty.iterate_method_candidates( | ||
285 | db, | 487 | db, |
286 | current_crate, | 488 | current_crate, |
287 | &trait_candidates, | 489 | &trait_candidates, |
@@ -289,21 +491,41 @@ fn applicable_defs<'a>( | |||
289 | |_, function| { | 491 | |_, function| { |
290 | let assoc = function.as_assoc_item(db)?; | 492 | let assoc = function.as_assoc_item(db)?; |
291 | if required_assoc_items.contains(&assoc) { | 493 | if required_assoc_items.contains(&assoc) { |
292 | applicable_defs.insert(Either::Left(assoc_to_module_def(assoc))); | 494 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); |
495 | let original_item = assoc_to_item(assoc); | ||
496 | located_imports.insert(LocatedImport::new( | ||
497 | mod_path(item)?, | ||
498 | item, | ||
499 | original_item, | ||
500 | mod_path(original_item), | ||
501 | )); | ||
293 | } | 502 | } |
294 | None::<()> | 503 | None::<()> |
295 | }, | 504 | }, |
296 | ), | 505 | ) |
297 | }; | 506 | }; |
298 | 507 | ||
299 | Box::new(applicable_defs.into_iter()) | 508 | located_imports |
300 | } | 509 | } |
301 | 510 | ||
302 | fn assoc_to_module_def(assoc: AssocItem) -> ModuleDef { | 511 | fn assoc_to_item(assoc: AssocItem) -> ItemInNs { |
303 | match assoc { | 512 | match assoc { |
304 | AssocItem::Function(f) => f.into(), | 513 | AssocItem::Function(f) => ItemInNs::from(ModuleDef::from(f)), |
305 | AssocItem::Const(c) => c.into(), | 514 | AssocItem::Const(c) => ItemInNs::from(ModuleDef::from(c)), |
306 | AssocItem::TypeAlias(t) => t.into(), | 515 | AssocItem::TypeAlias(t) => ItemInNs::from(ModuleDef::from(t)), |
516 | } | ||
517 | } | ||
518 | |||
519 | fn get_mod_path( | ||
520 | db: &RootDatabase, | ||
521 | item_to_search: ItemInNs, | ||
522 | module_with_candidate: &Module, | ||
523 | prefixed: Option<PrefixKind>, | ||
524 | ) -> Option<ModPath> { | ||
525 | if let Some(prefix_kind) = prefixed { | ||
526 | module_with_candidate.find_use_path_prefixed(db, item_to_search, prefix_kind) | ||
527 | } else { | ||
528 | module_with_candidate.find_use_path(db, item_to_search) | ||
307 | } | 529 | } |
308 | } | 530 | } |
309 | 531 | ||
@@ -316,7 +538,7 @@ impl ImportCandidate { | |||
316 | Some(_) => None, | 538 | Some(_) => None, |
317 | None => Some(Self::TraitMethod(TraitImportCandidate { | 539 | None => Some(Self::TraitMethod(TraitImportCandidate { |
318 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, | 540 | receiver_ty: sema.type_of_expr(&method_call.receiver()?)?, |
319 | name: NameToImport::Exact(method_call.name_ref()?.to_string()), | 541 | assoc_item_name: NameToImport::Exact(method_call.name_ref()?.to_string()), |
320 | })), | 542 | })), |
321 | } | 543 | } |
322 | } | 544 | } |
@@ -325,41 +547,63 @@ impl ImportCandidate { | |||
325 | if sema.resolve_path(path).is_some() { | 547 | if sema.resolve_path(path).is_some() { |
326 | return None; | 548 | return None; |
327 | } | 549 | } |
550 | path_import_candidate( | ||
551 | sema, | ||
552 | path.qualifier(), | ||
553 | NameToImport::Exact(path.segment()?.name_ref()?.to_string()), | ||
554 | ) | ||
555 | } | ||
328 | 556 | ||
329 | let segment = path.segment()?; | 557 | fn for_fuzzy_path( |
330 | let candidate = if let Some(qualifier) = path.qualifier() { | 558 | qualifier: Option<ast::Path>, |
331 | let qualifier_start = qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | 559 | fuzzy_name: String, |
332 | let qualifier_start_path = | 560 | sema: &Semantics<RootDatabase>, |
333 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | 561 | ) -> Option<Self> { |
334 | if let Some(qualifier_start_resolution) = sema.resolve_path(&qualifier_start_path) { | 562 | path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name)) |
335 | let qualifier_resolution = if qualifier_start_path == qualifier { | 563 | } |
336 | qualifier_start_resolution | 564 | |
565 | fn is_trait_candidate(&self) -> bool { | ||
566 | matches!(self, ImportCandidate::TraitAssocItem(_) | ImportCandidate::TraitMethod(_)) | ||
567 | } | ||
568 | } | ||
569 | |||
570 | fn path_import_candidate( | ||
571 | sema: &Semantics<RootDatabase>, | ||
572 | qualifier: Option<ast::Path>, | ||
573 | name: NameToImport, | ||
574 | ) -> Option<ImportCandidate> { | ||
575 | Some(match qualifier { | ||
576 | Some(qualifier) => match sema.resolve_path(&qualifier) { | ||
577 | None => { | ||
578 | let qualifier_start = | ||
579 | qualifier.syntax().descendants().find_map(ast::NameRef::cast)?; | ||
580 | let qualifier_start_path = | ||
581 | qualifier_start.syntax().ancestors().find_map(ast::Path::cast)?; | ||
582 | if sema.resolve_path(&qualifier_start_path).is_none() { | ||
583 | ImportCandidate::Path(PathImportCandidate { | ||
584 | qualifier: Some(FirstSegmentUnresolved { | ||
585 | fist_segment: qualifier_start, | ||
586 | full_qualifier: qualifier, | ||
587 | }), | ||
588 | name, | ||
589 | }) | ||
337 | } else { | 590 | } else { |
338 | sema.resolve_path(&qualifier)? | 591 | return None; |
339 | }; | ||
340 | match qualifier_resolution { | ||
341 | hir::PathResolution::Def(hir::ModuleDef::Adt(assoc_item_path)) => { | ||
342 | ImportCandidate::TraitAssocItem(TraitImportCandidate { | ||
343 | receiver_ty: assoc_item_path.ty(sema.db), | ||
344 | name: NameToImport::Exact(segment.name_ref()?.to_string()), | ||
345 | }) | ||
346 | } | ||
347 | _ => return None, | ||
348 | } | 592 | } |
349 | } else { | 593 | } |
350 | ImportCandidate::Path(PathImportCandidate { | 594 | Some(PathResolution::Def(ModuleDef::Adt(assoc_item_path))) => { |
351 | qualifier: Some(qualifier), | 595 | ImportCandidate::TraitAssocItem(TraitImportCandidate { |
352 | name: NameToImport::Exact(qualifier_start.to_string()), | 596 | receiver_ty: assoc_item_path.ty(sema.db), |
597 | assoc_item_name: name, | ||
353 | }) | 598 | }) |
354 | } | 599 | } |
355 | } else { | 600 | Some(_) => return None, |
356 | ImportCandidate::Path(PathImportCandidate { | 601 | }, |
357 | qualifier: None, | 602 | None => ImportCandidate::Path(PathImportCandidate { qualifier: None, name }), |
358 | name: NameToImport::Exact( | 603 | }) |
359 | segment.syntax().descendants().find_map(ast::NameRef::cast)?.to_string(), | 604 | } |
360 | ), | 605 | |
361 | }) | 606 | fn item_as_assoc(db: &RootDatabase, item: ItemInNs) -> Option<AssocItem> { |
362 | }; | 607 | item.as_module_def_id() |
363 | Some(candidate) | 608 | .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db)) |
364 | } | ||
365 | } | 609 | } |
diff --git a/crates/ide_db/src/helpers/insert_use.rs b/crates/ide_db/src/helpers/insert_use.rs index fd4035198..df66d8ea0 100644 --- a/crates/ide_db/src/helpers/insert_use.rs +++ b/crates/ide_db/src/helpers/insert_use.rs | |||
@@ -13,12 +13,12 @@ use syntax::{ | |||
13 | }, | 13 | }, |
14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, | 14 | AstToken, InsertPosition, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, |
15 | }; | 15 | }; |
16 | use test_utils::mark; | ||
17 | 16 | ||
18 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 17 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
19 | pub struct InsertUseConfig { | 18 | pub struct InsertUseConfig { |
20 | pub merge: Option<MergeBehavior>, | 19 | pub merge: Option<MergeBehavior>, |
21 | pub prefix_kind: hir::PrefixKind, | 20 | pub prefix_kind: hir::PrefixKind, |
21 | pub group: bool, | ||
22 | } | 22 | } |
23 | 23 | ||
24 | #[derive(Debug, Clone)] | 24 | #[derive(Debug, Clone)] |
@@ -99,13 +99,13 @@ fn is_inner_comment(token: SyntaxToken) -> bool { | |||
99 | pub fn insert_use<'a>( | 99 | pub fn insert_use<'a>( |
100 | scope: &ImportScope, | 100 | scope: &ImportScope, |
101 | path: ast::Path, | 101 | path: ast::Path, |
102 | merge: Option<MergeBehavior>, | 102 | cfg: InsertUseConfig, |
103 | ) -> SyntaxRewriter<'a> { | 103 | ) -> SyntaxRewriter<'a> { |
104 | let _p = profile::span("insert_use"); | 104 | let _p = profile::span("insert_use"); |
105 | let mut rewriter = SyntaxRewriter::default(); | 105 | let mut rewriter = SyntaxRewriter::default(); |
106 | let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); | 106 | let use_item = make::use_(None, make::use_tree(path.clone(), None, None, false)); |
107 | // merge into existing imports if possible | 107 | // merge into existing imports if possible |
108 | if let Some(mb) = merge { | 108 | if let Some(mb) = cfg.merge { |
109 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { | 109 | for existing_use in scope.as_syntax_node().children().filter_map(ast::Use::cast) { |
110 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { | 110 | if let Some(merged) = try_merge_imports(&existing_use, &use_item, mb) { |
111 | rewriter.replace(existing_use.syntax(), merged.syntax()); | 111 | rewriter.replace(existing_use.syntax(), merged.syntax()); |
@@ -116,7 +116,7 @@ pub fn insert_use<'a>( | |||
116 | 116 | ||
117 | // either we weren't allowed to merge or there is no import that fits the merge conditions | 117 | // either we weren't allowed to merge or there is no import that fits the merge conditions |
118 | // so look for the place we have to insert to | 118 | // so look for the place we have to insert to |
119 | let (insert_position, add_blank) = find_insert_position(scope, path); | 119 | let (insert_position, add_blank) = find_insert_position(scope, path, cfg.group); |
120 | 120 | ||
121 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { | 121 | let indent = if let ident_level @ 1..=usize::MAX = scope.indent_level().0 as usize { |
122 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) | 122 | Some(make::tokens::whitespace(&" ".repeat(4 * ident_level)).into()) |
@@ -137,7 +137,7 @@ pub fn insert_use<'a>( | |||
137 | 137 | ||
138 | if add_blank.has_before() { | 138 | if add_blank.has_before() { |
139 | if let Some(indent) = indent.clone() { | 139 | if let Some(indent) = indent.clone() { |
140 | mark::hit!(insert_use_indent_before); | 140 | cov_mark::hit!(insert_use_indent_before); |
141 | buf.push(indent); | 141 | buf.push(indent); |
142 | } | 142 | } |
143 | } | 143 | } |
@@ -155,11 +155,11 @@ pub fn insert_use<'a>( | |||
155 | // only add indentation *after* our stuff if there's another node directly after it | 155 | // only add indentation *after* our stuff if there's another node directly after it |
156 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { | 156 | if add_blank.has_after() && matches!(insert_position, InsertPosition::Before(_)) { |
157 | if let Some(indent) = indent { | 157 | if let Some(indent) = indent { |
158 | mark::hit!(insert_use_indent_after); | 158 | cov_mark::hit!(insert_use_indent_after); |
159 | buf.push(indent); | 159 | buf.push(indent); |
160 | } | 160 | } |
161 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { | 161 | } else if add_blank.has_after() && matches!(insert_position, InsertPosition::After(_)) { |
162 | mark::hit!(insert_use_no_indent_after); | 162 | cov_mark::hit!(insert_use_no_indent_after); |
163 | } | 163 | } |
164 | 164 | ||
165 | buf | 165 | buf |
@@ -538,6 +538,7 @@ impl AddBlankLine { | |||
538 | fn find_insert_position( | 538 | fn find_insert_position( |
539 | scope: &ImportScope, | 539 | scope: &ImportScope, |
540 | insert_path: ast::Path, | 540 | insert_path: ast::Path, |
541 | group_imports: bool, | ||
541 | ) -> (InsertPosition<SyntaxElement>, AddBlankLine) { | 542 | ) -> (InsertPosition<SyntaxElement>, AddBlankLine) { |
542 | let group = ImportGroup::new(&insert_path); | 543 | let group = ImportGroup::new(&insert_path); |
543 | let path_node_iter = scope | 544 | let path_node_iter = scope |
@@ -550,6 +551,14 @@ fn find_insert_position( | |||
550 | let has_tl = tree.use_tree_list().is_some(); | 551 | let has_tl = tree.use_tree_list().is_some(); |
551 | Some((path, has_tl, node)) | 552 | Some((path, has_tl, node)) |
552 | }); | 553 | }); |
554 | |||
555 | if !group_imports { | ||
556 | if let Some((_, _, node)) = path_node_iter.last() { | ||
557 | return (InsertPosition::After(node.into()), AddBlankLine::Before); | ||
558 | } | ||
559 | return (InsertPosition::First, AddBlankLine::AfterTwice); | ||
560 | } | ||
561 | |||
553 | // Iterator that discards anything thats not in the required grouping | 562 | // Iterator that discards anything thats not in the required grouping |
554 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits | 563 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits |
555 | let group_iter = path_node_iter | 564 | let group_iter = path_node_iter |
@@ -565,6 +574,7 @@ fn find_insert_position( | |||
565 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater | 574 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater |
566 | }, | 575 | }, |
567 | ); | 576 | ); |
577 | |||
568 | match post_insert { | 578 | match post_insert { |
569 | // insert our import before that element | 579 | // insert our import before that element |
570 | Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), | 580 | Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), |
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs index 4bbe66f1f..3d151e629 100644 --- a/crates/ide_db/src/helpers/insert_use/tests.rs +++ b/crates/ide_db/src/helpers/insert_use/tests.rs | |||
@@ -1,8 +1,32 @@ | |||
1 | use super::*; | 1 | use super::*; |
2 | 2 | ||
3 | use hir::PrefixKind; | ||
3 | use test_utils::assert_eq_text; | 4 | use test_utils::assert_eq_text; |
4 | 5 | ||
5 | #[test] | 6 | #[test] |
7 | fn insert_not_group() { | ||
8 | check( | ||
9 | "use external_crate2::bar::A", | ||
10 | r" | ||
11 | use std::bar::B; | ||
12 | use external_crate::bar::A; | ||
13 | use crate::bar::A; | ||
14 | use self::bar::A; | ||
15 | use super::bar::A;", | ||
16 | r" | ||
17 | use std::bar::B; | ||
18 | use external_crate::bar::A; | ||
19 | use crate::bar::A; | ||
20 | use self::bar::A; | ||
21 | use super::bar::A; | ||
22 | use external_crate2::bar::A;", | ||
23 | None, | ||
24 | false, | ||
25 | false, | ||
26 | ); | ||
27 | } | ||
28 | |||
29 | #[test] | ||
6 | fn insert_existing() { | 30 | fn insert_existing() { |
7 | check_full("std::fs", "use std::fs;", "use std::fs;") | 31 | check_full("std::fs", "use std::fs;", "use std::fs;") |
8 | } | 32 | } |
@@ -27,7 +51,7 @@ use std::bar::G;", | |||
27 | 51 | ||
28 | #[test] | 52 | #[test] |
29 | fn insert_start_indent() { | 53 | fn insert_start_indent() { |
30 | mark::check!(insert_use_indent_after); | 54 | cov_mark::check!(insert_use_indent_after); |
31 | check_none( | 55 | check_none( |
32 | "std::bar::AA", | 56 | "std::bar::AA", |
33 | r" | 57 | r" |
@@ -96,7 +120,7 @@ use std::bar::ZZ;", | |||
96 | 120 | ||
97 | #[test] | 121 | #[test] |
98 | fn insert_end_indent() { | 122 | fn insert_end_indent() { |
99 | mark::check!(insert_use_indent_before); | 123 | cov_mark::check!(insert_use_indent_before); |
100 | check_none( | 124 | check_none( |
101 | "std::bar::ZZ", | 125 | "std::bar::ZZ", |
102 | r" | 126 | r" |
@@ -231,7 +255,7 @@ fn insert_empty_file() { | |||
231 | 255 | ||
232 | #[test] | 256 | #[test] |
233 | fn insert_empty_module() { | 257 | fn insert_empty_module() { |
234 | mark::check!(insert_use_no_indent_after); | 258 | cov_mark::check!(insert_use_no_indent_after); |
235 | check( | 259 | check( |
236 | "foo::bar", | 260 | "foo::bar", |
237 | "mod x {}", | 261 | "mod x {}", |
@@ -240,6 +264,7 @@ fn insert_empty_module() { | |||
240 | }", | 264 | }", |
241 | None, | 265 | None, |
242 | true, | 266 | true, |
267 | true, | ||
243 | ) | 268 | ) |
244 | } | 269 | } |
245 | 270 | ||
@@ -584,6 +609,7 @@ fn check( | |||
584 | ra_fixture_after: &str, | 609 | ra_fixture_after: &str, |
585 | mb: Option<MergeBehavior>, | 610 | mb: Option<MergeBehavior>, |
586 | module: bool, | 611 | module: bool, |
612 | group: bool, | ||
587 | ) { | 613 | ) { |
588 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); | 614 | let mut syntax = ast::SourceFile::parse(ra_fixture_before).tree().syntax().clone(); |
589 | if module { | 615 | if module { |
@@ -597,21 +623,25 @@ fn check( | |||
597 | .find_map(ast::Path::cast) | 623 | .find_map(ast::Path::cast) |
598 | .unwrap(); | 624 | .unwrap(); |
599 | 625 | ||
600 | let rewriter = insert_use(&file, path, mb); | 626 | let rewriter = insert_use( |
627 | &file, | ||
628 | path, | ||
629 | InsertUseConfig { merge: mb, prefix_kind: PrefixKind::Plain, group }, | ||
630 | ); | ||
601 | let result = rewriter.rewrite(file.as_syntax_node()).to_string(); | 631 | let result = rewriter.rewrite(file.as_syntax_node()).to_string(); |
602 | assert_eq_text!(ra_fixture_after, &result); | 632 | assert_eq_text!(ra_fixture_after, &result); |
603 | } | 633 | } |
604 | 634 | ||
605 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 635 | fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
606 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false) | 636 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true) |
607 | } | 637 | } |
608 | 638 | ||
609 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 639 | fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
610 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false) | 640 | check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true) |
611 | } | 641 | } |
612 | 642 | ||
613 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 643 | fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
614 | check(path, ra_fixture_before, ra_fixture_after, None, false) | 644 | check(path, ra_fixture_before, ra_fixture_after, None, false, true) |
615 | } | 645 | } |
616 | 646 | ||
617 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { | 647 | fn check_merge_only_fail(ra_fixture0: &str, ra_fixture1: &str, mb: MergeBehavior) { |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/items_locator.rs index 502e8281a..8a7f02935 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/items_locator.rs | |||
@@ -1,9 +1,10 @@ | |||
1 | //! This module contains an import search functionality that is provided to the assists module. | 1 | //! This module contains an import search functionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use either::Either; | ||
4 | use hir::{ | 5 | use hir::{ |
5 | import_map::{self, ImportKind}, | 6 | import_map::{self, ImportKind}, |
6 | AsAssocItem, Crate, MacroDef, ModuleDef, Semantics, | 7 | AsAssocItem, Crate, ItemInNs, ModuleDef, Semantics, |
7 | }; | 8 | }; |
8 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 9 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
9 | 10 | ||
@@ -12,47 +13,47 @@ use crate::{ | |||
12 | symbol_index::{self, FileSymbol}, | 13 | symbol_index::{self, FileSymbol}, |
13 | RootDatabase, | 14 | RootDatabase, |
14 | }; | 15 | }; |
15 | use either::Either; | ||
16 | use rustc_hash::FxHashSet; | 16 | use rustc_hash::FxHashSet; |
17 | 17 | ||
18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; | 18 | pub(crate) const DEFAULT_QUERY_SEARCH_LIMIT: usize = 40; |
19 | 19 | ||
20 | pub fn find_exact_imports<'a>( | 20 | pub fn with_exact_name( |
21 | sema: &Semantics<'a, RootDatabase>, | 21 | sema: &Semantics<'_, RootDatabase>, |
22 | krate: Crate, | 22 | krate: Crate, |
23 | name_to_import: String, | 23 | exact_name: String, |
24 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>>> { | 24 | ) -> FxHashSet<ItemInNs> { |
25 | let _p = profile::span("find_exact_imports"); | 25 | let _p = profile::span("find_exact_imports"); |
26 | Box::new(find_imports( | 26 | find_items( |
27 | sema, | 27 | sema, |
28 | krate, | 28 | krate, |
29 | { | 29 | { |
30 | let mut local_query = symbol_index::Query::new(name_to_import.clone()); | 30 | let mut local_query = symbol_index::Query::new(exact_name.clone()); |
31 | local_query.exact(); | 31 | local_query.exact(); |
32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); | 32 | local_query.limit(DEFAULT_QUERY_SEARCH_LIMIT); |
33 | local_query | 33 | local_query |
34 | }, | 34 | }, |
35 | import_map::Query::new(name_to_import) | 35 | import_map::Query::new(exact_name) |
36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) | 36 | .limit(DEFAULT_QUERY_SEARCH_LIMIT) |
37 | .name_only() | 37 | .name_only() |
38 | .search_mode(import_map::SearchMode::Equals) | 38 | .search_mode(import_map::SearchMode::Equals) |
39 | .case_sensitive(), | 39 | .case_sensitive(), |
40 | )) | 40 | ) |
41 | } | 41 | } |
42 | 42 | ||
43 | #[derive(Debug)] | ||
43 | pub enum AssocItemSearch { | 44 | pub enum AssocItemSearch { |
44 | Include, | 45 | Include, |
45 | Exclude, | 46 | Exclude, |
46 | AssocItemsOnly, | 47 | AssocItemsOnly, |
47 | } | 48 | } |
48 | 49 | ||
49 | pub fn find_similar_imports<'a>( | 50 | pub fn with_similar_name( |
50 | sema: &Semantics<'a, RootDatabase>, | 51 | sema: &Semantics<'_, RootDatabase>, |
51 | krate: Crate, | 52 | krate: Crate, |
52 | fuzzy_search_string: String, | 53 | fuzzy_search_string: String, |
53 | assoc_item_search: AssocItemSearch, | 54 | assoc_item_search: AssocItemSearch, |
54 | limit: Option<usize>, | 55 | limit: Option<usize>, |
55 | ) -> Box<dyn Iterator<Item = Either<ModuleDef, MacroDef>> + 'a> { | 56 | ) -> FxHashSet<ItemInNs> { |
56 | let _p = profile::span("find_similar_imports"); | 57 | let _p = profile::span("find_similar_imports"); |
57 | 58 | ||
58 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) | 59 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
@@ -76,37 +77,39 @@ pub fn find_similar_imports<'a>( | |||
76 | local_query.limit(limit); | 77 | local_query.limit(limit); |
77 | } | 78 | } |
78 | 79 | ||
79 | let db = sema.db; | 80 | find_items(sema, krate, local_query, external_query) |
80 | Box::new(find_imports(sema, krate, local_query, external_query).filter( | 81 | .into_iter() |
81 | move |import_candidate| match assoc_item_search { | 82 | .filter(move |&item| match assoc_item_search { |
82 | AssocItemSearch::Include => true, | 83 | AssocItemSearch::Include => true, |
83 | AssocItemSearch::Exclude => !is_assoc_item(import_candidate, db), | 84 | AssocItemSearch::Exclude => !is_assoc_item(item, sema.db), |
84 | AssocItemSearch::AssocItemsOnly => is_assoc_item(import_candidate, db), | 85 | AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db), |
85 | }, | 86 | }) |
86 | )) | 87 | .collect() |
87 | } | 88 | } |
88 | 89 | ||
89 | fn is_assoc_item(import_candidate: &Either<ModuleDef, MacroDef>, db: &RootDatabase) -> bool { | 90 | fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool { |
90 | match import_candidate { | 91 | item.as_module_def_id() |
91 | Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_some(), | 92 | .and_then(|module_def_id| ModuleDef::from(module_def_id).as_assoc_item(db)) |
92 | Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_some(), | 93 | .is_some() |
93 | Either::Left(ModuleDef::TypeAlias(type_alias)) => type_alias.as_assoc_item(db).is_some(), | ||
94 | _ => false, | ||
95 | } | ||
96 | } | 94 | } |
97 | 95 | ||
98 | fn find_imports<'a>( | 96 | fn find_items( |
99 | sema: &Semantics<'a, RootDatabase>, | 97 | sema: &Semantics<'_, RootDatabase>, |
100 | krate: Crate, | 98 | krate: Crate, |
101 | local_query: symbol_index::Query, | 99 | local_query: symbol_index::Query, |
102 | external_query: import_map::Query, | 100 | external_query: import_map::Query, |
103 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 101 | ) -> FxHashSet<ItemInNs> { |
104 | let _p = profile::span("find_similar_imports"); | 102 | let _p = profile::span("find_similar_imports"); |
105 | let db = sema.db; | 103 | let db = sema.db; |
106 | 104 | ||
107 | // Query dependencies first. | 105 | // Query dependencies first. |
108 | let mut candidates: FxHashSet<_> = | 106 | let mut candidates = krate |
109 | krate.query_external_importables(db, external_query).collect(); | 107 | .query_external_importables(db, external_query) |
108 | .map(|external_importable| match external_importable { | ||
109 | Either::Left(module_def) => ItemInNs::from(module_def), | ||
110 | Either::Right(macro_def) => ItemInNs::from(macro_def), | ||
111 | }) | ||
112 | .collect::<FxHashSet<_>>(); | ||
110 | 113 | ||
111 | // Query the local crate using the symbol index. | 114 | // Query the local crate using the symbol index. |
112 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); | 115 | let local_results = symbol_index::crate_symbols(db, krate.into(), local_query); |
@@ -114,19 +117,19 @@ fn find_imports<'a>( | |||
114 | candidates.extend( | 117 | candidates.extend( |
115 | local_results | 118 | local_results |
116 | .into_iter() | 119 | .into_iter() |
117 | .filter_map(|import_candidate| get_name_definition(sema, &import_candidate)) | 120 | .filter_map(|local_candidate| get_name_definition(sema, &local_candidate)) |
118 | .filter_map(|name_definition_to_import| match name_definition_to_import { | 121 | .filter_map(|name_definition_to_import| match name_definition_to_import { |
119 | Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), | 122 | Definition::ModuleDef(module_def) => Some(ItemInNs::from(module_def)), |
120 | Definition::Macro(macro_def) => Some(Either::Right(macro_def)), | 123 | Definition::Macro(macro_def) => Some(ItemInNs::from(macro_def)), |
121 | _ => None, | 124 | _ => None, |
122 | }), | 125 | }), |
123 | ); | 126 | ); |
124 | 127 | ||
125 | candidates.into_iter() | 128 | candidates |
126 | } | 129 | } |
127 | 130 | ||
128 | fn get_name_definition<'a>( | 131 | fn get_name_definition( |
129 | sema: &Semantics<'a, RootDatabase>, | 132 | sema: &Semantics<'_, RootDatabase>, |
130 | import_candidate: &FileSymbol, | 133 | import_candidate: &FileSymbol, |
131 | ) -> Option<Definition> { | 134 | ) -> Option<Definition> { |
132 | let _p = profile::span("get_name_definition"); | 135 | let _p = profile::span("get_name_definition"); |
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 6eb34b06b..88ee4a87d 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -8,7 +8,7 @@ pub mod line_index; | |||
8 | pub mod symbol_index; | 8 | pub mod symbol_index; |
9 | pub mod defs; | 9 | pub mod defs; |
10 | pub mod search; | 10 | pub mod search; |
11 | pub mod imports_locator; | 11 | pub mod items_locator; |
12 | pub mod source_change; | 12 | pub mod source_change; |
13 | pub mod ty_filter; | 13 | pub mod ty_filter; |
14 | pub mod traits; | 14 | pub mod traits; |
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml index edbc1846b..8c31df13a 100644 --- a/crates/ide_ssr/Cargo.toml +++ b/crates/ide_ssr/Cargo.toml | |||
@@ -11,6 +11,7 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = "1.1" | ||
14 | rustc-hash = "1.1.0" | 15 | rustc-hash = "1.1.0" |
15 | itertools = "0.10.0" | 16 | itertools = "0.10.0" |
16 | 17 | ||
@@ -18,7 +19,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" } | |||
18 | syntax = { path = "../syntax", version = "0.0.0" } | 19 | syntax = { path = "../syntax", version = "0.0.0" } |
19 | ide_db = { path = "../ide_db", version = "0.0.0" } | 20 | ide_db = { path = "../ide_db", version = "0.0.0" } |
20 | hir = { path = "../hir", version = "0.0.0" } | 21 | hir = { path = "../hir", version = "0.0.0" } |
21 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
22 | 22 | ||
23 | [dev-dependencies] | 23 | [dev-dependencies] |
24 | test_utils = { path = "../test_utils" } | ||
24 | expect-test = "1.1" | 25 | expect-test = "1.1" |
diff --git a/crates/ide_ssr/src/matching.rs b/crates/ide_ssr/src/matching.rs index df013bae9..e1adb381e 100644 --- a/crates/ide_ssr/src/matching.rs +++ b/crates/ide_ssr/src/matching.rs | |||
@@ -15,7 +15,6 @@ use syntax::{ | |||
15 | ast::{AstNode, AstToken}, | 15 | ast::{AstNode, AstToken}, |
16 | SmolStr, | 16 | SmolStr, |
17 | }; | 17 | }; |
18 | use test_utils::mark; | ||
19 | 18 | ||
20 | // Creates a match error. If we're currently attempting to match some code that we thought we were | 19 | // Creates a match error. If we're currently attempting to match some code that we thought we were |
21 | // going to match, as indicated by the --debug-snippet flag, then populate the reason field. | 20 | // going to match, as indicated by the --debug-snippet flag, then populate the reason field. |
@@ -731,7 +730,7 @@ impl NodeKind { | |||
731 | fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { | 730 | fn matches(&self, node: &SyntaxNode) -> Result<(), MatchFailed> { |
732 | let ok = match self { | 731 | let ok = match self { |
733 | Self::Literal => { | 732 | Self::Literal => { |
734 | mark::hit!(literal_constraint); | 733 | cov_mark::hit!(literal_constraint); |
735 | ast::Literal::can_cast(node.kind()) | 734 | ast::Literal::can_cast(node.kind()) |
736 | } | 735 | } |
737 | }; | 736 | }; |
diff --git a/crates/ide_ssr/src/parsing.rs b/crates/ide_ssr/src/parsing.rs index 3d5e4feb7..5ff25cb6d 100644 --- a/crates/ide_ssr/src/parsing.rs +++ b/crates/ide_ssr/src/parsing.rs | |||
@@ -10,7 +10,6 @@ use crate::{SsrError, SsrPattern, SsrRule}; | |||
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use std::{fmt::Display, str::FromStr}; | 11 | use std::{fmt::Display, str::FromStr}; |
12 | use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; | 12 | use syntax::{ast, AstNode, SmolStr, SyntaxKind, SyntaxNode, T}; |
13 | use test_utils::mark; | ||
14 | 13 | ||
15 | #[derive(Debug)] | 14 | #[derive(Debug)] |
16 | pub(crate) struct ParsedRule { | 15 | pub(crate) struct ParsedRule { |
@@ -131,7 +130,7 @@ impl RuleBuilder { | |||
131 | let old_len = self.rules.len(); | 130 | let old_len = self.rules.len(); |
132 | self.rules.retain(|rule| contains_path(&rule.pattern)); | 131 | self.rules.retain(|rule| contains_path(&rule.pattern)); |
133 | if self.rules.len() < old_len { | 132 | if self.rules.len() < old_len { |
134 | mark::hit!(pattern_is_a_single_segment_path); | 133 | cov_mark::hit!(pattern_is_a_single_segment_path); |
135 | } | 134 | } |
136 | } | 135 | } |
137 | Ok(self.rules) | 136 | Ok(self.rules) |
diff --git a/crates/ide_ssr/src/replacing.rs b/crates/ide_ssr/src/replacing.rs index 06a94a46c..c9ccc1961 100644 --- a/crates/ide_ssr/src/replacing.rs +++ b/crates/ide_ssr/src/replacing.rs | |||
@@ -5,7 +5,7 @@ use itertools::Itertools; | |||
5 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
6 | use syntax::ast::{self, AstNode, AstToken}; | 6 | use syntax::ast::{self, AstNode, AstToken}; |
7 | use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; | 7 | use syntax::{SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize}; |
8 | use test_utils::mark; | 8 | |
9 | use text_edit::TextEdit; | 9 | use text_edit::TextEdit; |
10 | 10 | ||
11 | /// Returns a text edit that will replace each match in `matches` with its corresponding replacement | 11 | /// Returns a text edit that will replace each match in `matches` with its corresponding replacement |
@@ -128,7 +128,7 @@ impl ReplacementRenderer<'_> { | |||
128 | && (placeholder_value.autoderef_count > 0 | 128 | && (placeholder_value.autoderef_count > 0 |
129 | || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) | 129 | || placeholder_value.autoref_kind != ast::SelfParamKind::Owned) |
130 | { | 130 | { |
131 | mark::hit!(replace_autoref_autoderef_capture); | 131 | cov_mark::hit!(replace_autoref_autoderef_capture); |
132 | let ref_kind = match placeholder_value.autoref_kind { | 132 | let ref_kind = match placeholder_value.autoref_kind { |
133 | ast::SelfParamKind::Owned => "", | 133 | ast::SelfParamKind::Owned => "", |
134 | ast::SelfParamKind::Ref => "&", | 134 | ast::SelfParamKind::Ref => "&", |
diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index 14e5a3b69..af94c7bb1 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs | |||
@@ -6,7 +6,6 @@ use ide_db::base_db::FilePosition; | |||
6 | use parsing::Placeholder; | 6 | use parsing::Placeholder; |
7 | use rustc_hash::FxHashMap; | 7 | use rustc_hash::FxHashMap; |
8 | use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; | 8 | use syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; |
9 | use test_utils::mark; | ||
10 | 9 | ||
11 | pub(crate) struct ResolutionScope<'db> { | 10 | pub(crate) struct ResolutionScope<'db> { |
12 | scope: hir::SemanticsScope<'db>, | 11 | scope: hir::SemanticsScope<'db>, |
@@ -170,13 +169,13 @@ impl Resolver<'_, '_> { | |||
170 | // calls. e.g. `Foo::bar($s)` should match `x.bar()`. | 169 | // calls. e.g. `Foo::bar($s)` should match `x.bar()`. |
171 | true | 170 | true |
172 | } else { | 171 | } else { |
173 | mark::hit!(replace_associated_trait_default_function_call); | 172 | cov_mark::hit!(replace_associated_trait_default_function_call); |
174 | false | 173 | false |
175 | } | 174 | } |
176 | } | 175 | } |
177 | hir::PathResolution::AssocItem(_) => { | 176 | hir::PathResolution::AssocItem(_) => { |
178 | // Not a function. Could be a constant or an associated type. | 177 | // Not a function. Could be a constant or an associated type. |
179 | mark::hit!(replace_associated_trait_constant); | 178 | cov_mark::hit!(replace_associated_trait_constant); |
180 | false | 179 | false |
181 | } | 180 | } |
182 | _ => true, | 181 | _ => true, |
@@ -267,7 +266,7 @@ fn pick_node_for_resolution(node: SyntaxNode) -> SyntaxNode { | |||
267 | match node.kind() { | 266 | match node.kind() { |
268 | SyntaxKind::EXPR_STMT => { | 267 | SyntaxKind::EXPR_STMT => { |
269 | if let Some(n) = node.first_child() { | 268 | if let Some(n) = node.first_child() { |
270 | mark::hit!(cursor_after_semicolon); | 269 | cov_mark::hit!(cursor_after_semicolon); |
271 | return n; | 270 | return n; |
272 | } | 271 | } |
273 | } | 272 | } |
@@ -291,7 +290,7 @@ fn path_contains_type_arguments(path: Option<ast::Path>) -> bool { | |||
291 | if let Some(path) = path { | 290 | if let Some(path) = path { |
292 | if let Some(segment) = path.segment() { | 291 | if let Some(segment) = path.segment() { |
293 | if segment.generic_arg_list().is_some() { | 292 | if segment.generic_arg_list().is_some() { |
294 | mark::hit!(type_arguments_within_path); | 293 | cov_mark::hit!(type_arguments_within_path); |
295 | return true; | 294 | return true; |
296 | } | 295 | } |
297 | } | 296 | } |
diff --git a/crates/ide_ssr/src/search.rs b/crates/ide_ssr/src/search.rs index 836eb94b2..28cef742c 100644 --- a/crates/ide_ssr/src/search.rs +++ b/crates/ide_ssr/src/search.rs | |||
@@ -12,7 +12,6 @@ use ide_db::{ | |||
12 | }; | 12 | }; |
13 | use rustc_hash::FxHashSet; | 13 | use rustc_hash::FxHashSet; |
14 | use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; | 14 | use syntax::{ast, AstNode, SyntaxKind, SyntaxNode}; |
15 | use test_utils::mark; | ||
16 | 15 | ||
17 | /// A cache for the results of find_usages. This is for when we have multiple patterns that have the | 16 | /// A cache for the results of find_usages. This is for when we have multiple patterns that have the |
18 | /// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type | 17 | /// same path. e.g. if the pattern was `foo::Bar` that can parse as a path, an expression, a type |
@@ -61,7 +60,7 @@ impl<'db> MatchFinder<'db> { | |||
61 | for file_range in self.find_usages(usage_cache, definition).file_ranges() { | 60 | for file_range in self.find_usages(usage_cache, definition).file_ranges() { |
62 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { | 61 | if let Some(node_to_match) = self.find_node_to_match(resolved_path, file_range) { |
63 | if !is_search_permitted_ancestors(&node_to_match) { | 62 | if !is_search_permitted_ancestors(&node_to_match) { |
64 | mark::hit!(use_declaration_with_braces); | 63 | cov_mark::hit!(use_declaration_with_braces); |
65 | continue; | 64 | continue; |
66 | } | 65 | } |
67 | self.try_add_match(rule, &node_to_match, &None, matches_out); | 66 | self.try_add_match(rule, &node_to_match, &None, matches_out); |
@@ -205,7 +204,7 @@ impl<'db> MatchFinder<'db> { | |||
205 | matches_out: &mut Vec<Match>, | 204 | matches_out: &mut Vec<Match>, |
206 | ) { | 205 | ) { |
207 | if !self.within_range_restrictions(code) { | 206 | if !self.within_range_restrictions(code) { |
208 | mark::hit!(replace_nonpath_within_selection); | 207 | cov_mark::hit!(replace_nonpath_within_selection); |
209 | return; | 208 | return; |
210 | } | 209 | } |
211 | if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { | 210 | if let Ok(m) = matching::get_match(false, rule, code, restrict_range, &self.sema) { |
diff --git a/crates/ide_ssr/src/tests.rs b/crates/ide_ssr/src/tests.rs index a3ea44f23..1d8565dc0 100644 --- a/crates/ide_ssr/src/tests.rs +++ b/crates/ide_ssr/src/tests.rs | |||
@@ -3,7 +3,7 @@ use expect_test::{expect, Expect}; | |||
3 | use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; | 3 | use ide_db::base_db::{salsa::Durability, FileId, FilePosition, FileRange, SourceDatabaseExt}; |
4 | use rustc_hash::FxHashSet; | 4 | use rustc_hash::FxHashSet; |
5 | use std::sync::Arc; | 5 | use std::sync::Arc; |
6 | use test_utils::{mark, RangeOrOffset}; | 6 | use test_utils::RangeOrOffset; |
7 | 7 | ||
8 | fn parse_error_text(query: &str) -> String { | 8 | fn parse_error_text(query: &str) -> String { |
9 | format!("{}", query.parse::<SsrRule>().unwrap_err()) | 9 | format!("{}", query.parse::<SsrRule>().unwrap_err()) |
@@ -492,7 +492,7 @@ fn match_resolved_type_name() { | |||
492 | 492 | ||
493 | #[test] | 493 | #[test] |
494 | fn type_arguments_within_path() { | 494 | fn type_arguments_within_path() { |
495 | mark::check!(type_arguments_within_path); | 495 | cov_mark::check!(type_arguments_within_path); |
496 | let code = r#" | 496 | let code = r#" |
497 | mod foo { | 497 | mod foo { |
498 | pub struct Bar<T> {t: T} | 498 | pub struct Bar<T> {t: T} |
@@ -508,7 +508,7 @@ fn type_arguments_within_path() { | |||
508 | 508 | ||
509 | #[test] | 509 | #[test] |
510 | fn literal_constraint() { | 510 | fn literal_constraint() { |
511 | mark::check!(literal_constraint); | 511 | cov_mark::check!(literal_constraint); |
512 | let code = r#" | 512 | let code = r#" |
513 | enum Option<T> { Some(T), None } | 513 | enum Option<T> { Some(T), None } |
514 | use Option::Some; | 514 | use Option::Some; |
@@ -641,7 +641,7 @@ fn replace_associated_function_call() { | |||
641 | 641 | ||
642 | #[test] | 642 | #[test] |
643 | fn replace_associated_trait_default_function_call() { | 643 | fn replace_associated_trait_default_function_call() { |
644 | mark::check!(replace_associated_trait_default_function_call); | 644 | cov_mark::check!(replace_associated_trait_default_function_call); |
645 | assert_ssr_transform( | 645 | assert_ssr_transform( |
646 | "Bar2::foo() ==>> Bar2::foo2()", | 646 | "Bar2::foo() ==>> Bar2::foo2()", |
647 | r#" | 647 | r#" |
@@ -673,7 +673,7 @@ fn replace_associated_trait_default_function_call() { | |||
673 | 673 | ||
674 | #[test] | 674 | #[test] |
675 | fn replace_associated_trait_constant() { | 675 | fn replace_associated_trait_constant() { |
676 | mark::check!(replace_associated_trait_constant); | 676 | cov_mark::check!(replace_associated_trait_constant); |
677 | assert_ssr_transform( | 677 | assert_ssr_transform( |
678 | "Bar2::VALUE ==>> Bar2::VALUE_2222", | 678 | "Bar2::VALUE ==>> Bar2::VALUE_2222", |
679 | r#" | 679 | r#" |
@@ -998,7 +998,7 @@ fn use_declaration_with_braces() { | |||
998 | // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up | 998 | // It would be OK for a path rule to match and alter a use declaration. We shouldn't mess it up |
999 | // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, | 999 | // though. In particular, we must not change `use foo::{baz, bar}` to `use foo::{baz, |
1000 | // foo2::bar2}`. | 1000 | // foo2::bar2}`. |
1001 | mark::check!(use_declaration_with_braces); | 1001 | cov_mark::check!(use_declaration_with_braces); |
1002 | assert_ssr_transform( | 1002 | assert_ssr_transform( |
1003 | "foo::bar ==>> foo2::bar2", | 1003 | "foo::bar ==>> foo2::bar2", |
1004 | r#" | 1004 | r#" |
@@ -1076,7 +1076,7 @@ fn ufcs_matches_method_call() { | |||
1076 | 1076 | ||
1077 | #[test] | 1077 | #[test] |
1078 | fn pattern_is_a_single_segment_path() { | 1078 | fn pattern_is_a_single_segment_path() { |
1079 | mark::check!(pattern_is_a_single_segment_path); | 1079 | cov_mark::check!(pattern_is_a_single_segment_path); |
1080 | // The first function should not be altered because the `foo` in scope at the cursor position is | 1080 | // The first function should not be altered because the `foo` in scope at the cursor position is |
1081 | // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT -> | 1081 | // a different `foo`. This case is special because "foo" can be parsed as a pattern (IDENT_PAT -> |
1082 | // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo` | 1082 | // NAME -> IDENT), which contains no path. If we're not careful we'll end up matching the `foo` |
@@ -1118,7 +1118,7 @@ fn replace_local_variable_reference() { | |||
1118 | // The pattern references a local variable `foo` in the block containing the cursor. We should | 1118 | // The pattern references a local variable `foo` in the block containing the cursor. We should |
1119 | // only replace references to this variable `foo`, not other variables that just happen to have | 1119 | // only replace references to this variable `foo`, not other variables that just happen to have |
1120 | // the same name. | 1120 | // the same name. |
1121 | mark::check!(cursor_after_semicolon); | 1121 | cov_mark::check!(cursor_after_semicolon); |
1122 | assert_ssr_transform( | 1122 | assert_ssr_transform( |
1123 | "foo + $a ==>> $a - foo", | 1123 | "foo + $a ==>> $a - foo", |
1124 | r#" | 1124 | r#" |
@@ -1179,7 +1179,7 @@ fn replace_path_within_selection() { | |||
1179 | 1179 | ||
1180 | #[test] | 1180 | #[test] |
1181 | fn replace_nonpath_within_selection() { | 1181 | fn replace_nonpath_within_selection() { |
1182 | mark::check!(replace_nonpath_within_selection); | 1182 | cov_mark::check!(replace_nonpath_within_selection); |
1183 | assert_ssr_transform( | 1183 | assert_ssr_transform( |
1184 | "$a + $b ==>> $b * $a", | 1184 | "$a + $b ==>> $b * $a", |
1185 | r#" | 1185 | r#" |
@@ -1269,7 +1269,7 @@ fn replace_autoref_autoderef_capture() { | |||
1269 | // second, we already have a reference, so it isn't. When $a is used in a context where autoref | 1269 | // second, we already have a reference, so it isn't. When $a is used in a context where autoref |
1270 | // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef | 1270 | // doesn't apply, we need to prefix it with `&`. Finally, we have some cases where autoderef |
1271 | // needs to be applied. | 1271 | // needs to be applied. |
1272 | mark::check!(replace_autoref_autoderef_capture); | 1272 | cov_mark::check!(replace_autoref_autoderef_capture); |
1273 | let code = r#" | 1273 | let code = r#" |
1274 | struct Foo {} | 1274 | struct Foo {} |
1275 | impl Foo { | 1275 | impl Foo { |
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index bb2656a80..139214207 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml | |||
@@ -10,6 +10,7 @@ edition = "2018" | |||
10 | doctest = false | 10 | doctest = false |
11 | 11 | ||
12 | [dependencies] | 12 | [dependencies] |
13 | cov-mark = "1.1" | ||
13 | rustc-hash = "1.1.0" | 14 | rustc-hash = "1.1.0" |
14 | smallvec = "1.2.0" | 15 | smallvec = "1.2.0" |
15 | log = "0.4.8" | 16 | log = "0.4.8" |
@@ -17,8 +18,8 @@ log = "0.4.8" | |||
17 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
18 | parser = { path = "../parser", version = "0.0.0" } | 19 | parser = { path = "../parser", version = "0.0.0" } |
19 | tt = { path = "../tt", version = "0.0.0" } | 20 | tt = { path = "../tt", version = "0.0.0" } |
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
21 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | 22 | ||
23 | [dev-dependencies] | 23 | [dev-dependencies] |
24 | profile = { path = "../profile" } | 24 | profile = { path = "../profile" } |
25 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index f3d2da55a..33b85e23d 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -17,7 +17,6 @@ mod benchmark; | |||
17 | 17 | ||
18 | use std::fmt; | 18 | use std::fmt; |
19 | 19 | ||
20 | use test_utils::mark; | ||
21 | pub use tt::{Delimiter, DelimiterKind, Punct}; | 20 | pub use tt::{Delimiter, DelimiterKind, Punct}; |
22 | 21 | ||
23 | use crate::{ | 22 | use crate::{ |
@@ -217,7 +216,7 @@ impl MacroDef { | |||
217 | let mut rules = Vec::new(); | 216 | let mut rules = Vec::new(); |
218 | 217 | ||
219 | if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { | 218 | if Some(tt::DelimiterKind::Brace) == tt.delimiter_kind() { |
220 | mark::hit!(parse_macro_def_rules); | 219 | cov_mark::hit!(parse_macro_def_rules); |
221 | while src.len() > 0 { | 220 | while src.len() > 0 { |
222 | let rule = Rule::parse(&mut src, true)?; | 221 | let rule = Rule::parse(&mut src, true)?; |
223 | rules.push(rule); | 222 | rules.push(rule); |
@@ -229,7 +228,7 @@ impl MacroDef { | |||
229 | } | 228 | } |
230 | } | 229 | } |
231 | } else { | 230 | } else { |
232 | mark::hit!(parse_macro_def_simple); | 231 | cov_mark::hit!(parse_macro_def_simple); |
233 | let rule = Rule::parse(&mut src, false)?; | 232 | let rule = Rule::parse(&mut src, false)?; |
234 | if src.len() != 0 { | 233 | if src.len() != 0 { |
235 | return Err(ParseError::Expected("remain tokens in macro def".to_string())); | 234 | return Err(ParseError::Expected("remain tokens in macro def".to_string())); |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 5c641ebf2..3a168bb4b 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -6,7 +6,7 @@ use syntax::{ | |||
6 | SyntaxKind::{ERROR, IDENT}, | 6 | SyntaxKind::{ERROR, IDENT}, |
7 | SyntaxNode, WalkEvent, T, | 7 | SyntaxNode, WalkEvent, T, |
8 | }; | 8 | }; |
9 | use test_utils::{assert_eq_text, mark}; | 9 | use test_utils::assert_eq_text; |
10 | 10 | ||
11 | use super::*; | 11 | use super::*; |
12 | 12 | ||
@@ -687,7 +687,7 @@ fn test_match_literal() { | |||
687 | 687 | ||
688 | #[test] | 688 | #[test] |
689 | fn test_parse_macro_def_simple() { | 689 | fn test_parse_macro_def_simple() { |
690 | mark::check!(parse_macro_def_simple); | 690 | cov_mark::check!(parse_macro_def_simple); |
691 | 691 | ||
692 | parse_macro2( | 692 | parse_macro2( |
693 | r#" | 693 | r#" |
@@ -701,7 +701,7 @@ macro foo($id:ident) { | |||
701 | 701 | ||
702 | #[test] | 702 | #[test] |
703 | fn test_parse_macro_def_rules() { | 703 | fn test_parse_macro_def_rules() { |
704 | mark::check!(parse_macro_def_rules); | 704 | cov_mark::check!(parse_macro_def_rules); |
705 | 705 | ||
706 | parse_macro2( | 706 | parse_macro2( |
707 | r#" | 707 | r#" |
@@ -954,7 +954,8 @@ fn test_meta() { | |||
954 | .assert_expand_items( | 954 | .assert_expand_items( |
955 | r#"foo! { cfg(target_os = "windows") }"#, | 955 | r#"foo! { cfg(target_os = "windows") }"#, |
956 | r#"# [cfg (target_os = "windows")] fn bar () {}"#, | 956 | r#"# [cfg (target_os = "windows")] fn bar () {}"#, |
957 | ); | 957 | ) |
958 | .assert_expand_items(r#"foo! { hello::world }"#, r#"# [hello :: world] fn bar () {}"#); | ||
958 | } | 959 | } |
959 | 960 | ||
960 | #[test] | 961 | #[test] |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 6913e9ec2..6159d064c 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -95,7 +95,7 @@ pub(crate) mod fragments { | |||
95 | // https://doc.rust-lang.org/reference/paths.html#simple-paths | 95 | // https://doc.rust-lang.org/reference/paths.html#simple-paths |
96 | // The start of an meta must be a simple path | 96 | // The start of an meta must be a simple path |
97 | match p.current() { | 97 | match p.current() { |
98 | IDENT | T![::] | T![super] | T![self] | T![crate] => p.bump_any(), | 98 | IDENT | T![super] | T![self] | T![crate] => p.bump_any(), |
99 | T![=] => { | 99 | T![=] => { |
100 | p.bump_any(); | 100 | p.bump_any(); |
101 | match p.current() { | 101 | match p.current() { |
@@ -105,6 +105,7 @@ pub(crate) mod fragments { | |||
105 | } | 105 | } |
106 | break; | 106 | break; |
107 | } | 107 | } |
108 | _ if p.at(T![::]) => p.bump(T![::]), | ||
108 | _ => break, | 109 | _ => break, |
109 | } | 110 | } |
110 | } | 111 | } |
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml index 4c1b3036a..63b3f1532 100644 --- a/crates/proc_macro_srv/Cargo.toml +++ b/crates/proc_macro_srv/Cargo.toml | |||
@@ -17,9 +17,9 @@ memmap2 = "0.2.0" | |||
17 | tt = { path = "../tt", version = "0.0.0" } | 17 | tt = { path = "../tt", version = "0.0.0" } |
18 | mbe = { path = "../mbe", version = "0.0.0" } | 18 | mbe = { path = "../mbe", version = "0.0.0" } |
19 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } | 19 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } |
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
21 | 20 | ||
22 | [dev-dependencies] | 21 | [dev-dependencies] |
22 | test_utils = { path = "../test_utils" } | ||
23 | cargo_metadata = "0.13" | 23 | cargo_metadata = "0.13" |
24 | 24 | ||
25 | # used as proc macro test targets | 25 | # used as proc macro test targets |
diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index 28a6ee547..baf10fea9 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs | |||
@@ -138,7 +138,7 @@ impl Expander { | |||
138 | parsed_body, | 138 | parsed_body, |
139 | false, | 139 | false, |
140 | ); | 140 | ); |
141 | return res.map(|it| it.subtree); | 141 | return res.map(|it| it.into_subtree()); |
142 | } | 142 | } |
143 | bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { | 143 | bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { |
144 | let res = client.run( | 144 | let res = client.run( |
@@ -147,7 +147,7 @@ impl Expander { | |||
147 | parsed_body, | 147 | parsed_body, |
148 | false, | 148 | false, |
149 | ); | 149 | ); |
150 | return res.map(|it| it.subtree); | 150 | return res.map(|it| it.into_subtree()); |
151 | } | 151 | } |
152 | bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { | 152 | bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { |
153 | let res = client.run( | 153 | let res = client.run( |
@@ -157,7 +157,7 @@ impl Expander { | |||
157 | parsed_body, | 157 | parsed_body, |
158 | false, | 158 | false, |
159 | ); | 159 | ); |
160 | return res.map(|it| it.subtree); | 160 | return res.map(|it| it.into_subtree()); |
161 | } | 161 | } |
162 | _ => continue, | 162 | _ => continue, |
163 | } | 163 | } |
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs index ca6749b9b..b036d4e20 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/client.rs | |||
@@ -238,7 +238,7 @@ macro_rules! define_client_side { | |||
238 | $(impl $name { | 238 | $(impl $name { |
239 | #[allow(unused)] | 239 | #[allow(unused)] |
240 | $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { | 240 | $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { |
241 | panic!("hello"); | 241 | panic!("crates should be linked against the sysroot version of proc_macro, not this one from rust-analyzer"); |
242 | // Bridge::with(|bridge| { | 242 | // Bridge::with(|bridge| { |
243 | // let mut b = bridge.cached_buffer.take(); | 243 | // let mut b = bridge.cached_buffer.take(); |
244 | 244 | ||
diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs index e4006a5ab..e67902682 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs +++ b/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs | |||
@@ -268,7 +268,7 @@ trait Mark { | |||
268 | fn mark(unmarked: Self::Unmarked) -> Self; | 268 | fn mark(unmarked: Self::Unmarked) -> Self; |
269 | } | 269 | } |
270 | 270 | ||
271 | /// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). | 271 | /// Unwrap types wrapped by `cov_mark::mark` (see `Mark` for details). |
272 | trait Unmark { | 272 | trait Unmark { |
273 | type Unmarked; | 273 | type Unmarked; |
274 | fn unmark(self) -> Self::Unmarked; | 274 | fn unmark(self) -> Self::Unmarked; |
diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/rustc_server.rs index 952b4a97f..ceefd187d 100644 --- a/crates/proc_macro_srv/src/rustc_server.rs +++ b/crates/proc_macro_srv/src/rustc_server.rs | |||
@@ -25,27 +25,35 @@ type Span = tt::TokenId; | |||
25 | 25 | ||
26 | #[derive(Debug, Clone)] | 26 | #[derive(Debug, Clone)] |
27 | pub struct TokenStream { | 27 | pub struct TokenStream { |
28 | pub subtree: tt::Subtree, | 28 | pub token_trees: Vec<TokenTree>, |
29 | } | 29 | } |
30 | 30 | ||
31 | impl TokenStream { | 31 | impl TokenStream { |
32 | pub fn new() -> Self { | 32 | pub fn new() -> Self { |
33 | TokenStream { subtree: Default::default() } | 33 | TokenStream { token_trees: Default::default() } |
34 | } | 34 | } |
35 | 35 | ||
36 | pub fn with_subtree(subtree: tt::Subtree) -> Self { | 36 | pub fn with_subtree(subtree: tt::Subtree) -> Self { |
37 | TokenStream { subtree } | 37 | if subtree.delimiter.is_some() { |
38 | TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } | ||
39 | } else { | ||
40 | TokenStream { token_trees: subtree.token_trees } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | pub fn into_subtree(self) -> tt::Subtree { | ||
45 | tt::Subtree { delimiter: None, token_trees: self.token_trees } | ||
38 | } | 46 | } |
39 | 47 | ||
40 | pub fn is_empty(&self) -> bool { | 48 | pub fn is_empty(&self) -> bool { |
41 | self.subtree.token_trees.is_empty() | 49 | self.token_trees.is_empty() |
42 | } | 50 | } |
43 | } | 51 | } |
44 | 52 | ||
45 | /// Creates a token stream containing a single token tree. | 53 | /// Creates a token stream containing a single token tree. |
46 | impl From<TokenTree> for TokenStream { | 54 | impl From<TokenTree> for TokenStream { |
47 | fn from(tree: TokenTree) -> TokenStream { | 55 | fn from(tree: TokenTree) -> TokenStream { |
48 | TokenStream { subtree: tt::Subtree { delimiter: None, token_trees: vec![tree] } } | 56 | TokenStream { token_trees: vec![tree] } |
49 | } | 57 | } |
50 | } | 58 | } |
51 | 59 | ||
@@ -78,10 +86,10 @@ impl Extend<TokenStream> for TokenStream { | |||
78 | for tkn in item { | 86 | for tkn in item { |
79 | match tkn { | 87 | match tkn { |
80 | tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => { | 88 | tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => { |
81 | self.subtree.token_trees.extend(subtree.token_trees); | 89 | self.token_trees.extend(subtree.token_trees); |
82 | } | 90 | } |
83 | _ => { | 91 | _ => { |
84 | self.subtree.token_trees.push(tkn); | 92 | self.token_trees.push(tkn); |
85 | } | 93 | } |
86 | } | 94 | } |
87 | } | 95 | } |
@@ -164,7 +172,7 @@ pub mod token_stream { | |||
164 | type IntoIter = super::IntoIter<TokenTree>; | 172 | type IntoIter = super::IntoIter<TokenTree>; |
165 | 173 | ||
166 | fn into_iter(self) -> Self::IntoIter { | 174 | fn into_iter(self) -> Self::IntoIter { |
167 | self.subtree.token_trees.into_iter() | 175 | self.token_trees.into_iter() |
168 | } | 176 | } |
169 | } | 177 | } |
170 | 178 | ||
@@ -184,28 +192,23 @@ pub mod token_stream { | |||
184 | let (subtree, _token_map) = | 192 | let (subtree, _token_map) = |
185 | mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?; | 193 | mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?; |
186 | 194 | ||
187 | Ok(TokenStream { subtree }) | 195 | let subtree = subtree_replace_token_ids_with_unspecified(subtree); |
196 | Ok(TokenStream::with_subtree(subtree)) | ||
188 | } | 197 | } |
189 | } | 198 | } |
190 | 199 | ||
191 | impl ToString for TokenStream { | 200 | impl ToString for TokenStream { |
192 | fn to_string(&self) -> String { | 201 | fn to_string(&self) -> String { |
193 | let tt = self.subtree.clone().into(); | 202 | return tokentrees_to_text(&self.token_trees[..]); |
194 | to_text(&tt) | ||
195 | } | ||
196 | } | ||
197 | 203 | ||
198 | fn to_text(tkn: &tt::TokenTree) -> String { | 204 | fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String { |
199 | match tkn { | 205 | tkns.iter() |
200 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(), | ||
201 | tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(), | ||
202 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char), | ||
203 | tt::TokenTree::Subtree(subtree) => { | ||
204 | let content = subtree | ||
205 | .token_trees | ||
206 | .iter() | ||
207 | .fold((String::new(), true), |(last, last_to_joint), tkn| { | 206 | .fold((String::new(), true), |(last, last_to_joint), tkn| { |
208 | let s = [last, to_text(tkn)].join(if last_to_joint { "" } else { " " }); | 207 | let s = [last, tokentree_to_text(tkn)].join(if last_to_joint { |
208 | "" | ||
209 | } else { | ||
210 | " " | ||
211 | }); | ||
209 | let mut is_joint = false; | 212 | let mut is_joint = false; |
210 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { | 213 | if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { |
211 | if punct.spacing == tt::Spacing::Joint { | 214 | if punct.spacing == tt::Spacing::Joint { |
@@ -214,15 +217,63 @@ pub mod token_stream { | |||
214 | } | 217 | } |
215 | (s, is_joint) | 218 | (s, is_joint) |
216 | }) | 219 | }) |
217 | .0; | 220 | .0 |
218 | 221 | } | |
219 | let (open, close) = match subtree.delimiter.map(|it| it.kind) { | 222 | |
220 | None => ("", ""), | 223 | fn tokentree_to_text(tkn: &tt::TokenTree) -> String { |
221 | Some(tt::DelimiterKind::Brace) => ("{", "}"), | 224 | match tkn { |
222 | Some(tt::DelimiterKind::Parenthesis) => ("(", ")"), | 225 | tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(), |
223 | Some(tt::DelimiterKind::Bracket) => ("[", "]"), | 226 | tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(), |
224 | }; | 227 | tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char), |
225 | format!("{}{}{}", open, content, close) | 228 | tt::TokenTree::Subtree(subtree) => { |
229 | let content = tokentrees_to_text(&subtree.token_trees); | ||
230 | let (open, close) = match subtree.delimiter.map(|it| it.kind) { | ||
231 | None => ("", ""), | ||
232 | Some(tt::DelimiterKind::Brace) => ("{", "}"), | ||
233 | Some(tt::DelimiterKind::Parenthesis) => ("(", ")"), | ||
234 | Some(tt::DelimiterKind::Bracket) => ("[", "]"), | ||
235 | }; | ||
236 | format!("{}{}{}", open, content, close) | ||
237 | } | ||
238 | } | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | |||
243 | fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree { | ||
244 | tt::Subtree { | ||
245 | delimiter: subtree | ||
246 | .delimiter | ||
247 | .map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }), | ||
248 | token_trees: subtree | ||
249 | .token_trees | ||
250 | .into_iter() | ||
251 | .map(|t| token_tree_replace_token_ids_with_unspecified(t)) | ||
252 | .collect(), | ||
253 | } | ||
254 | } | ||
255 | |||
256 | fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree { | ||
257 | match tt { | ||
258 | tt::TokenTree::Leaf(leaf) => { | ||
259 | tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf)) | ||
260 | } | ||
261 | tt::TokenTree::Subtree(subtree) => { | ||
262 | tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree)) | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | |||
267 | fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf { | ||
268 | match leaf { | ||
269 | tt::Leaf::Literal(lit) => { | ||
270 | tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit }) | ||
271 | } | ||
272 | tt::Leaf::Punct(punct) => { | ||
273 | tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct }) | ||
274 | } | ||
275 | tt::Leaf::Ident(ident) => { | ||
276 | tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident }) | ||
226 | } | 277 | } |
227 | } | 278 | } |
228 | } | 279 | } |
@@ -277,42 +328,6 @@ impl server::FreeFunctions for Rustc { | |||
277 | } | 328 | } |
278 | } | 329 | } |
279 | 330 | ||
280 | fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree { | ||
281 | tt::Subtree { | ||
282 | delimiter: subtree.delimiter.map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }), | ||
283 | token_trees: subtree | ||
284 | .token_trees | ||
285 | .into_iter() | ||
286 | .map(|t| token_tree_replace_token_ids_with_unspecified(t)) | ||
287 | .collect(), | ||
288 | } | ||
289 | } | ||
290 | |||
291 | fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree { | ||
292 | match tt { | ||
293 | tt::TokenTree::Leaf(leaf) => { | ||
294 | tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf)) | ||
295 | } | ||
296 | tt::TokenTree::Subtree(subtree) => { | ||
297 | tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree)) | ||
298 | } | ||
299 | } | ||
300 | } | ||
301 | |||
302 | fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf { | ||
303 | match leaf { | ||
304 | tt::Leaf::Literal(lit) => { | ||
305 | tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit }) | ||
306 | } | ||
307 | tt::Leaf::Punct(punct) => { | ||
308 | tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct }) | ||
309 | } | ||
310 | tt::Leaf::Ident(ident) => { | ||
311 | tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident }) | ||
312 | } | ||
313 | } | ||
314 | } | ||
315 | |||
316 | impl server::TokenStream for Rustc { | 331 | impl server::TokenStream for Rustc { |
317 | fn new(&mut self) -> Self::TokenStream { | 332 | fn new(&mut self) -> Self::TokenStream { |
318 | Self::TokenStream::new() | 333 | Self::TokenStream::new() |
@@ -322,8 +337,9 @@ impl server::TokenStream for Rustc { | |||
322 | stream.is_empty() | 337 | stream.is_empty() |
323 | } | 338 | } |
324 | fn from_str(&mut self, src: &str) -> Self::TokenStream { | 339 | fn from_str(&mut self, src: &str) -> Self::TokenStream { |
325 | let (subtree, _) = mbe::parse_to_token_tree(src).expect("cannot parse string"); | 340 | use std::str::FromStr; |
326 | TokenStream::with_subtree(subtree_replace_token_ids_with_unspecified(subtree)) | 341 | |
342 | Self::TokenStream::from_str(src).expect("cannot parse string") | ||
327 | } | 343 | } |
328 | fn to_string(&mut self, stream: &Self::TokenStream) -> String { | 344 | fn to_string(&mut self, stream: &Self::TokenStream) -> String { |
329 | stream.to_string() | 345 | stream.to_string() |
@@ -429,10 +445,7 @@ fn spacing_to_external(spacing: Spacing) -> bridge::Spacing { | |||
429 | 445 | ||
430 | impl server::Group for Rustc { | 446 | impl server::Group for Rustc { |
431 | fn new(&mut self, delimiter: bridge::Delimiter, stream: Self::TokenStream) -> Self::Group { | 447 | fn new(&mut self, delimiter: bridge::Delimiter, stream: Self::TokenStream) -> Self::Group { |
432 | Self::Group { | 448 | Self::Group { delimiter: delim_to_internal(delimiter), token_trees: stream.token_trees } |
433 | delimiter: delim_to_internal(delimiter), | ||
434 | token_trees: stream.subtree.token_trees, | ||
435 | } | ||
436 | } | 449 | } |
437 | fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter { | 450 | fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter { |
438 | delim_to_external(group.delimiter) | 451 | delim_to_external(group.delimiter) |
@@ -440,9 +453,7 @@ impl server::Group for Rustc { | |||
440 | 453 | ||
441 | // NOTE: Return value of do not include delimiter | 454 | // NOTE: Return value of do not include delimiter |
442 | fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { | 455 | fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { |
443 | TokenStream { | 456 | TokenStream { token_trees: group.token_trees.clone() } |
444 | subtree: tt::Subtree { delimiter: None, token_trees: group.token_trees.clone() }, | ||
445 | } | ||
446 | } | 457 | } |
447 | 458 | ||
448 | fn span(&mut self, group: &Self::Group) -> Self::Span { | 459 | fn span(&mut self, group: &Self::Group) -> Self::Span { |
@@ -751,28 +762,48 @@ mod tests { | |||
751 | #[test] | 762 | #[test] |
752 | fn test_rustc_server_to_string() { | 763 | fn test_rustc_server_to_string() { |
753 | let s = TokenStream { | 764 | let s = TokenStream { |
754 | subtree: tt::Subtree { | 765 | token_trees: vec![ |
755 | delimiter: None, | 766 | tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { |
756 | token_trees: vec![ | 767 | text: "struct".into(), |
757 | tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { | 768 | id: tt::TokenId::unspecified(), |
758 | text: "struct".into(), | 769 | })), |
759 | id: tt::TokenId::unspecified(), | 770 | tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { |
760 | })), | 771 | text: "T".into(), |
761 | tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { | 772 | id: tt::TokenId::unspecified(), |
762 | text: "T".into(), | 773 | })), |
774 | tt::TokenTree::Subtree(tt::Subtree { | ||
775 | delimiter: Some(tt::Delimiter { | ||
763 | id: tt::TokenId::unspecified(), | 776 | id: tt::TokenId::unspecified(), |
764 | })), | 777 | kind: tt::DelimiterKind::Brace, |
765 | tt::TokenTree::Subtree(tt::Subtree { | ||
766 | delimiter: Some(tt::Delimiter { | ||
767 | id: tt::TokenId::unspecified(), | ||
768 | kind: tt::DelimiterKind::Brace, | ||
769 | }), | ||
770 | token_trees: vec![], | ||
771 | }), | 778 | }), |
772 | ], | 779 | token_trees: vec![], |
773 | }, | 780 | }), |
781 | ], | ||
774 | }; | 782 | }; |
775 | 783 | ||
776 | assert_eq!(s.to_string(), "struct T {}"); | 784 | assert_eq!(s.to_string(), "struct T {}"); |
777 | } | 785 | } |
786 | |||
787 | #[test] | ||
788 | fn test_rustc_server_from_str() { | ||
789 | use std::str::FromStr; | ||
790 | let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { | ||
791 | delimiter: Some(tt::Delimiter { | ||
792 | id: tt::TokenId::unspecified(), | ||
793 | kind: tt::DelimiterKind::Parenthesis, | ||
794 | }), | ||
795 | token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { | ||
796 | text: "a".into(), | ||
797 | id: tt::TokenId::unspecified(), | ||
798 | }))], | ||
799 | }); | ||
800 | |||
801 | let t1 = TokenStream::from_str("(a)").unwrap(); | ||
802 | assert_eq!(t1.token_trees.len(), 1); | ||
803 | assert_eq!(t1.token_trees[0], subtree_paren_a); | ||
804 | |||
805 | let t2 = TokenStream::from_str("(a);").unwrap(); | ||
806 | assert_eq!(t2.token_trees.len(), 2); | ||
807 | assert_eq!(t2.token_trees[0], subtree_paren_a); | ||
808 | } | ||
778 | } | 809 | } |
diff --git a/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt index ea34e688f..fa581f110 100644 --- a/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt +++ b/crates/proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt | |||
@@ -101,7 +101,7 @@ SUBTREE $ | |||
101 | PUNCH : [alone] 4294967295 | 101 | PUNCH : [alone] 4294967295 |
102 | IDENT Serialize 4294967295 | 102 | IDENT Serialize 4294967295 |
103 | IDENT for 4294967295 | 103 | IDENT for 4294967295 |
104 | IDENT Foo 1 | 104 | IDENT Foo 4294967295 |
105 | SUBTREE {} 4294967295 | 105 | SUBTREE {} 4294967295 |
106 | IDENT fn 4294967295 | 106 | IDENT fn 4294967295 |
107 | IDENT serialize 4294967295 | 107 | IDENT serialize 4294967295 |
diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs index 22813052d..0484c3af4 100644 --- a/crates/proc_macro_srv/src/tests/utils.rs +++ b/crates/proc_macro_srv/src/tests/utils.rs | |||
@@ -52,7 +52,7 @@ pub fn assert_expand( | |||
52 | let expander = dylib::Expander::new(&path).unwrap(); | 52 | let expander = dylib::Expander::new(&path).unwrap(); |
53 | let fixture = parse_string(ra_fixture).unwrap(); | 53 | let fixture = parse_string(ra_fixture).unwrap(); |
54 | 54 | ||
55 | let res = expander.expand(macro_name, &fixture.subtree, None).unwrap(); | 55 | let res = expander.expand(macro_name, &fixture.into_subtree(), None).unwrap(); |
56 | assert_eq_text!(&expect.trim(), &format!("{:?}", res)); | 56 | assert_eq_text!(&expect.trim(), &format!("{:?}", res)); |
57 | } | 57 | } |
58 | 58 | ||
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs index f7241b711..bc6e20341 100644 --- a/crates/project_model/src/cargo_workspace.rs +++ b/crates/project_model/src/cargo_workspace.rs | |||
@@ -9,6 +9,8 @@ use cargo_metadata::{CargoOpt, MetadataCommand}; | |||
9 | use la_arena::{Arena, Idx}; | 9 | use la_arena::{Arena, Idx}; |
10 | use paths::{AbsPath, AbsPathBuf}; | 10 | use paths::{AbsPath, AbsPathBuf}; |
11 | use rustc_hash::FxHashMap; | 11 | use rustc_hash::FxHashMap; |
12 | use serde::Deserialize; | ||
13 | use serde_json::from_value; | ||
12 | 14 | ||
13 | use crate::build_data::BuildDataConfig; | 15 | use crate::build_data::BuildDataConfig; |
14 | use crate::utf8_stdout; | 16 | use crate::utf8_stdout; |
@@ -104,6 +106,13 @@ pub struct PackageData { | |||
104 | pub active_features: Vec<String>, | 106 | pub active_features: Vec<String>, |
105 | // String representation of package id | 107 | // String representation of package id |
106 | pub id: String, | 108 | pub id: String, |
109 | // The contents of [package.metadata.rust-analyzer] | ||
110 | pub metadata: RustAnalyzerPackageMetaData, | ||
111 | } | ||
112 | |||
113 | #[derive(Deserialize, Default, Debug, Clone, Eq, PartialEq)] | ||
114 | pub struct RustAnalyzerPackageMetaData { | ||
115 | pub rustc_private: bool, | ||
107 | } | 116 | } |
108 | 117 | ||
109 | #[derive(Debug, Clone, Eq, PartialEq)] | 118 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -161,6 +170,13 @@ impl PackageData { | |||
161 | } | 170 | } |
162 | } | 171 | } |
163 | 172 | ||
173 | #[derive(Deserialize, Default)] | ||
174 | // Deserialise helper for the cargo metadata | ||
175 | struct PackageMetadata { | ||
176 | #[serde(rename = "rust-analyzer")] | ||
177 | rust_analyzer: Option<RustAnalyzerPackageMetaData>, | ||
178 | } | ||
179 | |||
164 | impl CargoWorkspace { | 180 | impl CargoWorkspace { |
165 | pub fn from_cargo_metadata( | 181 | pub fn from_cargo_metadata( |
166 | cargo_toml: &AbsPath, | 182 | cargo_toml: &AbsPath, |
@@ -244,8 +260,10 @@ impl CargoWorkspace { | |||
244 | 260 | ||
245 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); | 261 | meta.packages.sort_by(|a, b| a.id.cmp(&b.id)); |
246 | for meta_pkg in &meta.packages { | 262 | for meta_pkg in &meta.packages { |
247 | let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } = | 263 | let cargo_metadata::Package { |
248 | meta_pkg; | 264 | id, edition, name, manifest_path, version, metadata, .. |
265 | } = meta_pkg; | ||
266 | let meta = from_value::<PackageMetadata>(metadata.clone()).unwrap_or_default(); | ||
249 | let is_member = ws_members.contains(&id); | 267 | let is_member = ws_members.contains(&id); |
250 | let edition = edition | 268 | let edition = edition |
251 | .parse::<Edition>() | 269 | .parse::<Edition>() |
@@ -262,6 +280,7 @@ impl CargoWorkspace { | |||
262 | dependencies: Vec::new(), | 280 | dependencies: Vec::new(), |
263 | features: meta_pkg.features.clone().into_iter().collect(), | 281 | features: meta_pkg.features.clone().into_iter().collect(), |
264 | active_features: Vec::new(), | 282 | active_features: Vec::new(), |
283 | metadata: meta.rust_analyzer.unwrap_or_default(), | ||
265 | }); | 284 | }); |
266 | let pkg_data = &mut packages[pkg]; | 285 | let pkg_data = &mut packages[pkg]; |
267 | pkg_by_id.insert(id, pkg); | 286 | pkg_by_id.insert(id, pkg); |
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs index 0220efdb4..1b53fcc30 100644 --- a/crates/project_model/src/workspace.rs +++ b/crates/project_model/src/workspace.rs | |||
@@ -2,11 +2,7 @@ | |||
2 | //! metadata` or `rust-project.json`) into representation stored in the salsa | 2 | //! metadata` or `rust-project.json`) into representation stored in the salsa |
3 | //! database -- `CrateGraph`. | 3 | //! database -- `CrateGraph`. |
4 | 4 | ||
5 | use std::{ | 5 | use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; |
6 | fmt, fs, | ||
7 | path::{Component, Path}, | ||
8 | process::Command, | ||
9 | }; | ||
10 | 6 | ||
11 | use anyhow::{Context, Result}; | 7 | use anyhow::{Context, Result}; |
12 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; | 8 | use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; |
@@ -60,6 +56,7 @@ impl fmt::Debug for ProjectWorkspace { | |||
60 | match self { | 56 | match self { |
61 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f | 57 | ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } => f |
62 | .debug_struct("Cargo") | 58 | .debug_struct("Cargo") |
59 | .field("root", &cargo.workspace_root().file_name()) | ||
63 | .field("n_packages", &cargo.packages().len()) | 60 | .field("n_packages", &cargo.packages().len()) |
64 | .field("n_sysroot_crates", &sysroot.crates().len()) | 61 | .field("n_sysroot_crates", &sysroot.crates().len()) |
65 | .field( | 62 | .field( |
@@ -279,11 +276,8 @@ impl ProjectWorkspace { | |||
279 | 276 | ||
280 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { | 277 | pub fn collect_build_data_configs(&self, collector: &mut BuildDataCollector) { |
281 | match self { | 278 | match self { |
282 | ProjectWorkspace::Cargo { cargo, rustc, .. } => { | 279 | ProjectWorkspace::Cargo { cargo, .. } => { |
283 | collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); | 280 | collector.add_config(&cargo.workspace_root(), cargo.build_data_config().clone()); |
284 | if let Some(rustc) = rustc { | ||
285 | collector.add_config(rustc.workspace_root(), rustc.build_data_config().clone()); | ||
286 | } | ||
287 | } | 281 | } |
288 | _ => {} | 282 | _ => {} |
289 | } | 283 | } |
@@ -380,9 +374,11 @@ fn cargo_to_crate_graph( | |||
380 | cfg_options.insert_atom("debug_assertions".into()); | 374 | cfg_options.insert_atom("debug_assertions".into()); |
381 | 375 | ||
382 | let mut pkg_crates = FxHashMap::default(); | 376 | let mut pkg_crates = FxHashMap::default(); |
383 | 377 | // Does any crate signal to rust-analyzer that they need the rustc_private crates? | |
378 | let mut has_private = false; | ||
384 | // Next, create crates for each package, target pair | 379 | // Next, create crates for each package, target pair |
385 | for pkg in cargo.packages() { | 380 | for pkg in cargo.packages() { |
381 | has_private |= cargo[pkg].metadata.rustc_private; | ||
386 | let mut lib_tgt = None; | 382 | let mut lib_tgt = None; |
387 | for &tgt in cargo[pkg].targets.iter() { | 383 | for &tgt in cargo[pkg].targets.iter() { |
388 | if let Some(file_id) = load(&cargo[tgt].root) { | 384 | if let Some(file_id) = load(&cargo[tgt].root) { |
@@ -443,28 +439,66 @@ fn cargo_to_crate_graph( | |||
443 | } | 439 | } |
444 | } | 440 | } |
445 | 441 | ||
446 | let mut rustc_pkg_crates = FxHashMap::default(); | 442 | if has_private { |
443 | // If the user provided a path to rustc sources, we add all the rustc_private crates | ||
444 | // and create dependencies on them for the crates which opt-in to that | ||
445 | if let Some(rustc_workspace) = rustc { | ||
446 | handle_rustc_crates( | ||
447 | rustc_workspace, | ||
448 | load, | ||
449 | &mut crate_graph, | ||
450 | rustc_build_data_map, | ||
451 | &cfg_options, | ||
452 | proc_macro_loader, | ||
453 | &mut pkg_to_lib_crate, | ||
454 | &public_deps, | ||
455 | cargo, | ||
456 | &pkg_crates, | ||
457 | ); | ||
458 | } | ||
459 | } | ||
460 | crate_graph | ||
461 | } | ||
447 | 462 | ||
448 | // If the user provided a path to rustc sources, we add all the rustc_private crates | 463 | fn handle_rustc_crates( |
449 | // and create dependencies on them for the crates in the current workspace | 464 | rustc_workspace: &CargoWorkspace, |
450 | if let Some(rustc_workspace) = rustc { | 465 | load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, |
451 | for pkg in rustc_workspace.packages() { | 466 | crate_graph: &mut CrateGraph, |
467 | rustc_build_data_map: Option<&FxHashMap<String, BuildData>>, | ||
468 | cfg_options: &CfgOptions, | ||
469 | proc_macro_loader: &dyn Fn(&Path) -> Vec<ProcMacro>, | ||
470 | pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>, | ||
471 | public_deps: &[(CrateName, CrateId)], | ||
472 | cargo: &CargoWorkspace, | ||
473 | pkg_crates: &FxHashMap<la_arena::Idx<crate::PackageData>, Vec<CrateId>>, | ||
474 | ) { | ||
475 | let mut rustc_pkg_crates = FxHashMap::default(); | ||
476 | // The root package of the rustc-dev component is rustc_driver, so we match that | ||
477 | let root_pkg = | ||
478 | rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver"); | ||
479 | // The rustc workspace might be incomplete (such as if rustc-dev is not | ||
480 | // installed for the current toolchain) and `rustcSource` is set to discover. | ||
481 | if let Some(root_pkg) = root_pkg { | ||
482 | // Iterate through every crate in the dependency subtree of rustc_driver using BFS | ||
483 | let mut queue = VecDeque::new(); | ||
484 | queue.push_back(root_pkg); | ||
485 | while let Some(pkg) = queue.pop_front() { | ||
486 | // Don't duplicate packages if they are dependended on a diamond pattern | ||
487 | // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates | ||
488 | // which is not ideal | ||
489 | if rustc_pkg_crates.contains_key(&pkg) { | ||
490 | continue; | ||
491 | } | ||
492 | for dep in &rustc_workspace[pkg].dependencies { | ||
493 | queue.push_back(dep.pkg); | ||
494 | } | ||
452 | for &tgt in rustc_workspace[pkg].targets.iter() { | 495 | for &tgt in rustc_workspace[pkg].targets.iter() { |
453 | if rustc_workspace[tgt].kind != TargetKind::Lib { | 496 | if rustc_workspace[tgt].kind != TargetKind::Lib { |
454 | continue; | 497 | continue; |
455 | } | 498 | } |
456 | // Exclude alloc / core / std | ||
457 | if rustc_workspace[tgt] | ||
458 | .root | ||
459 | .components() | ||
460 | .any(|c| c == Component::Normal("library".as_ref())) | ||
461 | { | ||
462 | continue; | ||
463 | } | ||
464 | |||
465 | if let Some(file_id) = load(&rustc_workspace[tgt].root) { | 499 | if let Some(file_id) = load(&rustc_workspace[tgt].root) { |
466 | let crate_id = add_target_crate_root( | 500 | let crate_id = add_target_crate_root( |
467 | &mut crate_graph, | 501 | crate_graph, |
468 | &rustc_workspace[pkg], | 502 | &rustc_workspace[pkg], |
469 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), | 503 | rustc_build_data_map.and_then(|it| it.get(&rustc_workspace[pkg].id)), |
470 | &cfg_options, | 504 | &cfg_options, |
@@ -472,44 +506,50 @@ fn cargo_to_crate_graph( | |||
472 | file_id, | 506 | file_id, |
473 | ); | 507 | ); |
474 | pkg_to_lib_crate.insert(pkg, crate_id); | 508 | pkg_to_lib_crate.insert(pkg, crate_id); |
475 | // Add dependencies on the core / std / alloc for rustc | 509 | // Add dependencies on core / std / alloc for this crate |
476 | for (name, krate) in public_deps.iter() { | 510 | for (name, krate) in public_deps.iter() { |
477 | add_dep(&mut crate_graph, crate_id, name.clone(), *krate); | 511 | add_dep(crate_graph, crate_id, name.clone(), *krate); |
478 | } | 512 | } |
479 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); | 513 | rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); |
480 | } | 514 | } |
481 | } | 515 | } |
482 | } | 516 | } |
483 | // Now add a dep edge from all targets of upstream to the lib | 517 | } |
484 | // target of downstream. | 518 | // Now add a dep edge from all targets of upstream to the lib |
485 | for pkg in rustc_workspace.packages() { | 519 | // target of downstream. |
486 | for dep in rustc_workspace[pkg].dependencies.iter() { | 520 | for pkg in rustc_pkg_crates.keys().copied() { |
487 | let name = CrateName::new(&dep.name).unwrap(); | 521 | for dep in rustc_workspace[pkg].dependencies.iter() { |
488 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { | 522 | let name = CrateName::new(&dep.name).unwrap(); |
489 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { | 523 | if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { |
490 | add_dep(&mut crate_graph, from, name.clone(), to); | 524 | for &from in rustc_pkg_crates.get(&pkg).into_iter().flatten() { |
491 | } | 525 | add_dep(crate_graph, from, name.clone(), to); |
492 | } | 526 | } |
493 | } | 527 | } |
494 | } | 528 | } |
495 | 529 | } | |
496 | // Add dependencies for all the crates of the current workspace to rustc_private libraries | 530 | // Add a dependency on the rustc_private crates for all targets of each package |
497 | for dep in rustc_workspace.packages() { | 531 | // which opts in |
498 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); | 532 | for dep in rustc_workspace.packages() { |
499 | 533 | let name = CrateName::normalize_dashes(&rustc_workspace[dep].name); | |
500 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { | 534 | |
501 | for pkg in cargo.packages() { | 535 | if let Some(&to) = pkg_to_lib_crate.get(&dep) { |
502 | if !cargo[pkg].is_member { | 536 | for pkg in cargo.packages() { |
503 | continue; | 537 | let package = &cargo[pkg]; |
504 | } | 538 | if !package.metadata.rustc_private { |
505 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | 539 | continue; |
506 | add_dep(&mut crate_graph, from, name.clone(), to); | 540 | } |
541 | for &from in pkg_crates.get(&pkg).into_iter().flatten() { | ||
542 | // Avoid creating duplicate dependencies | ||
543 | // This avoids the situation where `from` depends on e.g. `arrayvec`, but | ||
544 | // `rust_analyzer` thinks that it should use the one from the `rustcSource` | ||
545 | // instead of the one from `crates.io` | ||
546 | if !crate_graph[from].dependencies.iter().any(|d| d.name == name) { | ||
547 | add_dep(crate_graph, from, name.clone(), to); | ||
507 | } | 548 | } |
508 | } | 549 | } |
509 | } | 550 | } |
510 | } | 551 | } |
511 | } | 552 | } |
512 | crate_graph | ||
513 | } | 553 | } |
514 | 554 | ||
515 | fn add_target_crate_root( | 555 | fn add_target_crate_root( |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 8789f0852..3130785cc 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -24,7 +24,7 @@ jod-thread = "0.1.0" | |||
24 | log = "0.4.8" | 24 | log = "0.4.8" |
25 | lsp-types = { version = "0.88.0", features = ["proposed"] } | 25 | lsp-types = { version = "0.88.0", features = ["proposed"] } |
26 | parking_lot = "0.11.0" | 26 | parking_lot = "0.11.0" |
27 | xflags = "0.1.2" | 27 | xflags = "0.2.1" |
28 | oorandom = "11.1.2" | 28 | oorandom = "11.1.2" |
29 | rustc-hash = "1.1.0" | 29 | rustc-hash = "1.1.0" |
30 | serde = { version = "1.0.106", features = ["derive"] } | 30 | serde = { version = "1.0.106", features = ["derive"] } |
diff --git a/crates/rust-analyzer/src/bin/flags.rs b/crates/rust-analyzer/src/bin/flags.rs index 244912d26..3a7caaf3f 100644 --- a/crates/rust-analyzer/src/bin/flags.rs +++ b/crates/rust-analyzer/src/bin/flags.rs | |||
@@ -6,7 +6,9 @@ use ide_ssr::{SsrPattern, SsrRule}; | |||
6 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; | 6 | use rust_analyzer::cli::{BenchWhat, Position, Verbosity}; |
7 | use vfs::AbsPathBuf; | 7 | use vfs::AbsPathBuf; |
8 | 8 | ||
9 | xflags::args_parser! { | 9 | xflags::xflags! { |
10 | src "./src/bin/flags.rs" | ||
11 | |||
10 | /// LSP server for the Rust programming language. | 12 | /// LSP server for the Rust programming language. |
11 | cmd rust-analyzer { | 13 | cmd rust-analyzer { |
12 | /// Verbosity level, can be repeated multiple times. | 14 | /// Verbosity level, can be repeated multiple times. |
@@ -120,7 +122,7 @@ xflags::args_parser! { | |||
120 | 122 | ||
121 | // generated start | 123 | // generated start |
122 | // The following code is generated by `xflags` macro. | 124 | // The following code is generated by `xflags` macro. |
123 | // Run `env XFLAGS_DUMP= cargo build` to regenerate. | 125 | // Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. |
124 | #[derive(Debug)] | 126 | #[derive(Debug)] |
125 | pub struct RustAnalyzer { | 127 | pub struct RustAnalyzer { |
126 | pub verbose: u32, | 128 | pub verbose: u32, |
@@ -158,7 +160,7 @@ pub struct Parse { | |||
158 | } | 160 | } |
159 | 161 | ||
160 | #[derive(Debug)] | 162 | #[derive(Debug)] |
161 | pub struct Symbols {} | 163 | pub struct Symbols; |
162 | 164 | ||
163 | #[derive(Debug)] | 165 | #[derive(Debug)] |
164 | pub struct Highlight { | 166 | pub struct Highlight { |
@@ -211,14 +213,13 @@ pub struct Search { | |||
211 | } | 213 | } |
212 | 214 | ||
213 | #[derive(Debug)] | 215 | #[derive(Debug)] |
214 | pub struct ProcMacro {} | 216 | pub struct ProcMacro; |
215 | 217 | ||
216 | impl RustAnalyzer { | 218 | impl RustAnalyzer { |
217 | pub const HELP: &'static str = Self::_HELP; | 219 | pub const HELP: &'static str = Self::HELP_; |
218 | 220 | ||
219 | pub fn from_env() -> xflags::Result<Self> { | 221 | pub fn from_env() -> xflags::Result<Self> { |
220 | let mut p = xflags::rt::Parser::new_from_env(); | 222 | Self::from_env_() |
221 | Self::_parse(&mut p) | ||
222 | } | 223 | } |
223 | } | 224 | } |
224 | // generated end | 225 | // generated end |
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index 3bd7e678d..49994824f 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -108,7 +108,11 @@ impl BenchCmd { | |||
108 | add_call_parenthesis: true, | 108 | add_call_parenthesis: true, |
109 | add_call_argument_snippets: true, | 109 | add_call_argument_snippets: true, |
110 | snippet_cap: SnippetCap::new(true), | 110 | snippet_cap: SnippetCap::new(true), |
111 | insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, | 111 | insert_use: InsertUseConfig { |
112 | merge: None, | ||
113 | prefix_kind: PrefixKind::Plain, | ||
114 | group: true, | ||
115 | }, | ||
112 | }; | 116 | }; |
113 | let res = do_work(&mut host, file_id, |analysis| { | 117 | let res = do_work(&mut host, file_id, |analysis| { |
114 | analysis.completions(&options, file_position) | 118 | analysis.completions(&options, file_position) |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 556fc2eeb..25df13554 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -16,7 +16,6 @@ use ide_db::helpers::{ | |||
16 | insert_use::{InsertUseConfig, MergeBehavior}, | 16 | insert_use::{InsertUseConfig, MergeBehavior}, |
17 | SnippetCap, | 17 | SnippetCap, |
18 | }; | 18 | }; |
19 | use itertools::Itertools; | ||
20 | use lsp_types::{ClientCapabilities, MarkupKind}; | 19 | use lsp_types::{ClientCapabilities, MarkupKind}; |
21 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; | 20 | use project_model::{CargoConfig, ProjectJson, ProjectJsonData, ProjectManifest, RustcSource}; |
22 | use rustc_hash::FxHashSet; | 21 | use rustc_hash::FxHashSet; |
@@ -35,7 +34,8 @@ config_data! { | |||
35 | assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", | 34 | assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", |
36 | /// The path structure for newly inserted paths to use. | 35 | /// The path structure for newly inserted paths to use. |
37 | assist_importPrefix: ImportPrefixDef = "\"plain\"", | 36 | assist_importPrefix: ImportPrefixDef = "\"plain\"", |
38 | 37 | /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | |
38 | assist_importGroup: bool = "true", | ||
39 | /// Show function name and docs in parameter hints. | 39 | /// Show function name and docs in parameter hints. |
40 | callInfo_full: bool = "true", | 40 | callInfo_full: bool = "true", |
41 | 41 | ||
@@ -46,9 +46,9 @@ config_data! { | |||
46 | cargo_allFeatures: bool = "false", | 46 | cargo_allFeatures: bool = "false", |
47 | /// List of features to activate. | 47 | /// List of features to activate. |
48 | cargo_features: Vec<String> = "[]", | 48 | cargo_features: Vec<String> = "[]", |
49 | /// Run `cargo check` on startup to get the correct value for package | 49 | /// Run build scripts (`build.rs`) for more precise code analysis. |
50 | /// OUT_DIRs. | 50 | cargo_runBuildScripts | |
51 | cargo_loadOutDirsFromCheck: bool = "false", | 51 | cargo_loadOutDirsFromCheck: bool = "true", |
52 | /// Do not activate the `default` feature. | 52 | /// Do not activate the `default` feature. |
53 | cargo_noDefaultFeatures: bool = "false", | 53 | cargo_noDefaultFeatures: bool = "false", |
54 | /// Compilation target (target triple). | 54 | /// Compilation target (target triple). |
@@ -97,13 +97,15 @@ config_data! { | |||
97 | diagnostics_enableExperimental: bool = "true", | 97 | diagnostics_enableExperimental: bool = "true", |
98 | /// List of rust-analyzer diagnostics to disable. | 98 | /// List of rust-analyzer diagnostics to disable. |
99 | diagnostics_disabled: FxHashSet<String> = "[]", | 99 | diagnostics_disabled: FxHashSet<String> = "[]", |
100 | /// List of warnings that should be displayed with info severity.\n\nThe | 100 | /// List of warnings that should be displayed with info severity. |
101 | /// warnings will be indicated by a blue squiggly underline in code and | 101 | /// |
102 | /// a blue icon in the `Problems Panel`. | 102 | /// The warnings will be indicated by a blue squiggly underline in code |
103 | /// and a blue icon in the `Problems Panel`. | ||
103 | diagnostics_warningsAsHint: Vec<String> = "[]", | 104 | diagnostics_warningsAsHint: Vec<String> = "[]", |
104 | /// List of warnings that should be displayed with hint severity.\n\nThe | 105 | /// List of warnings that should be displayed with hint severity. |
105 | /// warnings will be indicated by faded text or three dots in code and | 106 | /// |
106 | /// will not show up in the `Problems Panel`. | 107 | /// The warnings will be indicated by faded text or three dots in code |
108 | /// and will not show up in the `Problems Panel`. | ||
107 | diagnostics_warningsAsInfo: Vec<String> = "[]", | 109 | diagnostics_warningsAsInfo: Vec<String> = "[]", |
108 | 110 | ||
109 | /// Controls file watching implementation. | 111 | /// Controls file watching implementation. |
@@ -157,7 +159,9 @@ config_data! { | |||
157 | lens_references: bool = "false", | 159 | lens_references: bool = "false", |
158 | 160 | ||
159 | /// Disable project auto-discovery in favor of explicitly specified set | 161 | /// Disable project auto-discovery in favor of explicitly specified set |
160 | /// of projects.\n\nElements must be paths pointing to `Cargo.toml`, | 162 | /// of projects. |
163 | /// | ||
164 | /// Elements must be paths pointing to `Cargo.toml`, | ||
161 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. | 165 | /// `rust-project.json`, or JSON objects in `rust-project.json` format. |
162 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", | 166 | linkedProjects: Vec<ManifestOrProjectJson> = "[]", |
163 | 167 | ||
@@ -167,8 +171,7 @@ config_data! { | |||
167 | /// Whether to show `can't find Cargo.toml` error message. | 171 | /// Whether to show `can't find Cargo.toml` error message. |
168 | notifications_cargoTomlNotFound: bool = "true", | 172 | notifications_cargoTomlNotFound: bool = "true", |
169 | 173 | ||
170 | /// Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be | 174 | /// Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`. |
171 | /// enabled. | ||
172 | procMacro_enable: bool = "false", | 175 | procMacro_enable: bool = "false", |
173 | /// Internal config, path to proc-macro server executable (typically, | 176 | /// Internal config, path to proc-macro server executable (typically, |
174 | /// this is rust-analyzer itself, but we override this in tests). | 177 | /// this is rust-analyzer itself, but we override this in tests). |
@@ -177,12 +180,17 @@ config_data! { | |||
177 | /// Command to be executed instead of 'cargo' for runnables. | 180 | /// Command to be executed instead of 'cargo' for runnables. |
178 | runnables_overrideCargo: Option<String> = "null", | 181 | runnables_overrideCargo: Option<String> = "null", |
179 | /// Additional arguments to be passed to cargo for runnables such as | 182 | /// Additional arguments to be passed to cargo for runnables such as |
180 | /// tests or binaries.\nFor example, it may be `--release`. | 183 | /// tests or binaries. For example, it may be `--release`. |
181 | runnables_cargoExtraArgs: Vec<String> = "[]", | 184 | runnables_cargoExtraArgs: Vec<String> = "[]", |
182 | 185 | ||
183 | /// Path to the rust compiler sources, for usage in rustc_private projects, or "discover" | 186 | /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private |
184 | /// to try to automatically find it. | 187 | /// projects, or "discover" to try to automatically find it. |
185 | rustcSource : Option<String> = "null", | 188 | /// |
189 | /// Any project which uses rust-analyzer with the rustcPrivate | ||
190 | /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. | ||
191 | /// | ||
192 | /// This option is not reloaded automatically; you must restart rust-analyzer for it to take effect. | ||
193 | rustcSource: Option<String> = "null", | ||
186 | 194 | ||
187 | /// Additional arguments to `rustfmt`. | 195 | /// Additional arguments to `rustfmt`. |
188 | rustfmt_extraArgs: Vec<String> = "[]", | 196 | rustfmt_extraArgs: Vec<String> = "[]", |
@@ -480,8 +488,8 @@ impl Config { | |||
480 | pub fn cargo_autoreload(&self) -> bool { | 488 | pub fn cargo_autoreload(&self) -> bool { |
481 | self.data.cargo_autoreload | 489 | self.data.cargo_autoreload |
482 | } | 490 | } |
483 | pub fn load_out_dirs_from_check(&self) -> bool { | 491 | pub fn run_build_scripts(&self) -> bool { |
484 | self.data.cargo_loadOutDirsFromCheck | 492 | self.data.cargo_runBuildScripts || self.data.procMacro_enable |
485 | } | 493 | } |
486 | pub fn cargo(&self) -> CargoConfig { | 494 | pub fn cargo(&self) -> CargoConfig { |
487 | let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { | 495 | let rustc_source = self.data.rustcSource.as_ref().map(|rustc_src| { |
@@ -575,6 +583,7 @@ impl Config { | |||
575 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, | 583 | ImportPrefixDef::ByCrate => PrefixKind::ByCrate, |
576 | ImportPrefixDef::BySelf => PrefixKind::BySelf, | 584 | ImportPrefixDef::BySelf => PrefixKind::BySelf, |
577 | }, | 585 | }, |
586 | group: self.data.assist_importGroup, | ||
578 | } | 587 | } |
579 | } | 588 | } |
580 | pub fn completion(&self) -> CompletionConfig { | 589 | pub fn completion(&self) -> CompletionConfig { |
@@ -759,7 +768,8 @@ fn schema(fields: &[(&'static str, &'static str, &[&str], &str)]) -> serde_json: | |||
759 | } | 768 | } |
760 | 769 | ||
761 | fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { | 770 | fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json::Value { |
762 | let doc = doc.iter().map(|it| it.trim()).join(" "); | 771 | let doc = doc_comment_to_string(doc); |
772 | let doc = doc.trim_end_matches('\n'); | ||
763 | assert!( | 773 | assert!( |
764 | doc.ends_with('.') && doc.starts_with(char::is_uppercase), | 774 | doc.ends_with('.') && doc.starts_with(char::is_uppercase), |
765 | "bad docs for {}: {:?}", | 775 | "bad docs for {}: {:?}", |
@@ -848,21 +858,26 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String { | |||
848 | .iter() | 858 | .iter() |
849 | .map(|(field, _ty, doc, default)| { | 859 | .map(|(field, _ty, doc, default)| { |
850 | let name = format!("rust-analyzer.{}", field.replace("_", ".")); | 860 | let name = format!("rust-analyzer.{}", field.replace("_", ".")); |
851 | format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" ")) | 861 | let doc = doc_comment_to_string(*doc); |
862 | format!("[[{}]]{} (default: `{}`)::\n+\n--\n{}--\n", name, name, default, doc) | ||
852 | }) | 863 | }) |
853 | .collect::<String>() | 864 | .collect::<String>() |
854 | } | 865 | } |
855 | 866 | ||
867 | fn doc_comment_to_string(doc: &[&str]) -> String { | ||
868 | doc.iter().map(|it| it.strip_prefix(' ').unwrap_or(it)).map(|it| format!("{}\n", it)).collect() | ||
869 | } | ||
870 | |||
856 | #[cfg(test)] | 871 | #[cfg(test)] |
857 | mod tests { | 872 | mod tests { |
858 | use std::fs; | 873 | use std::fs; |
859 | 874 | ||
860 | use test_utils::project_dir; | 875 | use test_utils::{ensure_file_contents, project_root}; |
861 | 876 | ||
862 | use super::*; | 877 | use super::*; |
863 | 878 | ||
864 | #[test] | 879 | #[test] |
865 | fn schema_in_sync_with_package_json() { | 880 | fn generate_package_json_config() { |
866 | let s = Config::json_schema(); | 881 | let s = Config::json_schema(); |
867 | let schema = format!("{:#}", s); | 882 | let schema = format!("{:#}", s); |
868 | let mut schema = schema | 883 | let mut schema = schema |
@@ -875,7 +890,7 @@ mod tests { | |||
875 | .to_string(); | 890 | .to_string(); |
876 | schema.push_str(",\n"); | 891 | schema.push_str(",\n"); |
877 | 892 | ||
878 | let package_json_path = project_dir().join("editors/code/package.json"); | 893 | let package_json_path = project_root().join("editors/code/package.json"); |
879 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); | 894 | let mut package_json = fs::read_to_string(&package_json_path).unwrap(); |
880 | 895 | ||
881 | let start_marker = " \"$generated-start\": false,\n"; | 896 | let start_marker = " \"$generated-start\": false,\n"; |
@@ -883,26 +898,20 @@ mod tests { | |||
883 | 898 | ||
884 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); | 899 | let start = package_json.find(start_marker).unwrap() + start_marker.len(); |
885 | let end = package_json.find(end_marker).unwrap(); | 900 | let end = package_json.find(end_marker).unwrap(); |
901 | |||
886 | let p = remove_ws(&package_json[start..end]); | 902 | let p = remove_ws(&package_json[start..end]); |
887 | let s = remove_ws(&schema); | 903 | let s = remove_ws(&schema); |
888 | |||
889 | if !p.contains(&s) { | 904 | if !p.contains(&s) { |
890 | package_json.replace_range(start..end, &schema); | 905 | package_json.replace_range(start..end, &schema); |
891 | fs::write(&package_json_path, &mut package_json).unwrap(); | 906 | ensure_file_contents(&package_json_path, &package_json) |
892 | panic!("new config, updating package.json") | ||
893 | } | 907 | } |
894 | } | 908 | } |
895 | 909 | ||
896 | #[test] | 910 | #[test] |
897 | fn schema_in_sync_with_docs() { | 911 | fn generate_config_documentation() { |
898 | let docs_path = project_dir().join("docs/user/generated_config.adoc"); | 912 | let docs_path = project_root().join("docs/user/generated_config.adoc"); |
899 | let current = fs::read_to_string(&docs_path).unwrap(); | ||
900 | let expected = ConfigData::manual(); | 913 | let expected = ConfigData::manual(); |
901 | 914 | ensure_file_contents(&docs_path, &expected); | |
902 | if remove_ws(¤t) != remove_ws(&expected) { | ||
903 | fs::write(&docs_path, expected).unwrap(); | ||
904 | panic!("updated config manual"); | ||
905 | } | ||
906 | } | 915 | } |
907 | 916 | ||
908 | fn remove_ws(text: &str) -> String { | 917 | fn remove_ws(text: &str) -> String { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 4f6f250d6..b5b2ffe50 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -697,7 +697,6 @@ pub(crate) fn handle_completion_resolve( | |||
697 | FilePosition { file_id, offset }, | 697 | FilePosition { file_id, offset }, |
698 | &resolve_data.full_import_path, | 698 | &resolve_data.full_import_path, |
699 | resolve_data.imported_name, | 699 | resolve_data.imported_name, |
700 | resolve_data.import_for_trait_assoc_item, | ||
701 | )? | 700 | )? |
702 | .into_iter() | 701 | .into_iter() |
703 | .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) | 702 | .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) |
@@ -1135,20 +1134,13 @@ pub(crate) fn handle_document_highlight( | |||
1135 | None | 1134 | None |
1136 | }; | 1135 | }; |
1137 | 1136 | ||
1138 | let res = refs | 1137 | let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice); |
1139 | .references | 1138 | let mut res = Vec::with_capacity(file_refs.len() + 1); |
1140 | .get(&position.file_id) | 1139 | res.extend(decl); |
1141 | .map(|file_refs| { | 1140 | res.extend(file_refs.iter().map(|&(range, access)| DocumentHighlight { |
1142 | file_refs | 1141 | range: to_proto::range(&line_index, range), |
1143 | .into_iter() | 1142 | kind: access.map(to_proto::document_highlight_kind), |
1144 | .map(|&(range, access)| DocumentHighlight { | 1143 | })); |
1145 | range: to_proto::range(&line_index, range), | ||
1146 | kind: access.map(to_proto::document_highlight_kind), | ||
1147 | }) | ||
1148 | .chain(decl) | ||
1149 | .collect() | ||
1150 | }) | ||
1151 | .unwrap_or_default(); | ||
1152 | Ok(Some(res)) | 1144 | Ok(Some(res)) |
1153 | } | 1145 | } |
1154 | 1146 | ||
@@ -1525,7 +1517,6 @@ struct CompletionResolveData { | |||
1525 | position: lsp_types::TextDocumentPositionParams, | 1517 | position: lsp_types::TextDocumentPositionParams, |
1526 | full_import_path: String, | 1518 | full_import_path: String, |
1527 | imported_name: String, | 1519 | imported_name: String, |
1528 | import_for_trait_assoc_item: bool, | ||
1529 | } | 1520 | } |
1530 | 1521 | ||
1531 | fn fill_resolve_data( | 1522 | fn fill_resolve_data( |
@@ -1534,15 +1525,13 @@ fn fill_resolve_data( | |||
1534 | position: &TextDocumentPositionParams, | 1525 | position: &TextDocumentPositionParams, |
1535 | ) -> Option<()> { | 1526 | ) -> Option<()> { |
1536 | let import_edit = item.import_to_add()?; | 1527 | let import_edit = item.import_to_add()?; |
1537 | let full_import_path = import_edit.import_path.to_string(); | 1528 | let import_path = &import_edit.import.import_path; |
1538 | let imported_name = import_edit.import_path.segments().last()?.to_string(); | ||
1539 | 1529 | ||
1540 | *resolve_data = Some( | 1530 | *resolve_data = Some( |
1541 | to_value(CompletionResolveData { | 1531 | to_value(CompletionResolveData { |
1542 | position: position.to_owned(), | 1532 | position: position.to_owned(), |
1543 | full_import_path, | 1533 | full_import_path: import_path.to_string(), |
1544 | imported_name, | 1534 | imported_name: import_path.segments().last()?.to_string(), |
1545 | import_for_trait_assoc_item: import_edit.import_for_trait_assoc_item, | ||
1546 | }) | 1535 | }) |
1547 | .unwrap(), | 1536 | .unwrap(), |
1548 | ); | 1537 | ); |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 2829d5970..f0cb309e4 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -312,7 +312,7 @@ impl GlobalState { | |||
312 | } else { | 312 | } else { |
313 | assert_eq!(n_done, n_total); | 313 | assert_eq!(n_done, n_total); |
314 | new_status = Status::Ready { | 314 | new_status = Status::Ready { |
315 | partial: self.config.load_out_dirs_from_check() | 315 | partial: self.config.run_build_scripts() |
316 | && self.workspace_build_data.is_none() | 316 | && self.workspace_build_data.is_none() |
317 | || config_version < self.vfs_config_version, | 317 | || config_version < self.vfs_config_version, |
318 | }; | 318 | }; |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index c07efa330..aa8504c3d 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -337,7 +337,7 @@ impl GlobalState { | |||
337 | }; | 337 | }; |
338 | change.set_crate_graph(crate_graph); | 338 | change.set_crate_graph(crate_graph); |
339 | 339 | ||
340 | if self.config.load_out_dirs_from_check() && workspace_build_data.is_none() { | 340 | if self.config.run_build_scripts() && workspace_build_data.is_none() { |
341 | let mut collector = BuildDataCollector::default(); | 341 | let mut collector = BuildDataCollector::default(); |
342 | for ws in &workspaces { | 342 | for ws in &workspaces { |
343 | ws.collect_build_data_configs(&mut collector); | 343 | ws.collect_build_data_configs(&mut collector); |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index c1ca88df6..a9846fa70 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -173,20 +173,14 @@ pub(crate) fn snippet_text_edit_vec( | |||
173 | 173 | ||
174 | pub(crate) fn completion_item( | 174 | pub(crate) fn completion_item( |
175 | line_index: &LineIndex, | 175 | line_index: &LineIndex, |
176 | completion_item: CompletionItem, | 176 | item: CompletionItem, |
177 | ) -> Vec<lsp_types::CompletionItem> { | 177 | ) -> Vec<lsp_types::CompletionItem> { |
178 | fn set_score(res: &mut lsp_types::CompletionItem, label: &str) { | ||
179 | res.preselect = Some(true); | ||
180 | // HACK: sort preselect items first | ||
181 | res.sort_text = Some(format!(" {}", label)); | ||
182 | } | ||
183 | |||
184 | let mut additional_text_edits = Vec::new(); | 178 | let mut additional_text_edits = Vec::new(); |
185 | let mut text_edit = None; | 179 | let mut text_edit = None; |
186 | // LSP does not allow arbitrary edits in completion, so we have to do a | 180 | // LSP does not allow arbitrary edits in completion, so we have to do a |
187 | // non-trivial mapping here. | 181 | // non-trivial mapping here. |
188 | let source_range = completion_item.source_range(); | 182 | let source_range = item.source_range(); |
189 | for indel in completion_item.text_edit().iter() { | 183 | for indel in item.text_edit().iter() { |
190 | if indel.delete.contains_range(source_range) { | 184 | if indel.delete.contains_range(source_range) { |
191 | text_edit = Some(if indel.delete == source_range { | 185 | text_edit = Some(if indel.delete == source_range { |
192 | self::text_edit(line_index, indel.clone()) | 186 | self::text_edit(line_index, indel.clone()) |
@@ -207,46 +201,52 @@ pub(crate) fn completion_item( | |||
207 | } | 201 | } |
208 | let text_edit = text_edit.unwrap(); | 202 | let text_edit = text_edit.unwrap(); |
209 | 203 | ||
210 | let mut res = lsp_types::CompletionItem { | 204 | let mut lsp_item = lsp_types::CompletionItem { |
211 | label: completion_item.label().to_string(), | 205 | label: item.label().to_string(), |
212 | detail: completion_item.detail().map(|it| it.to_string()), | 206 | detail: item.detail().map(|it| it.to_string()), |
213 | filter_text: Some(completion_item.lookup().to_string()), | 207 | filter_text: Some(item.lookup().to_string()), |
214 | kind: completion_item.kind().map(completion_item_kind), | 208 | kind: item.kind().map(completion_item_kind), |
215 | text_edit: Some(text_edit.into()), | 209 | text_edit: Some(text_edit.into()), |
216 | additional_text_edits: Some(additional_text_edits), | 210 | additional_text_edits: Some(additional_text_edits), |
217 | documentation: completion_item.documentation().map(documentation), | 211 | documentation: item.documentation().map(documentation), |
218 | deprecated: Some(completion_item.deprecated()), | 212 | deprecated: Some(item.deprecated()), |
219 | ..Default::default() | 213 | ..Default::default() |
220 | }; | 214 | }; |
221 | 215 | ||
222 | if completion_item.score().is_some() { | 216 | if item.relevance().is_relevant() { |
223 | set_score(&mut res, completion_item.label()); | 217 | lsp_item.preselect = Some(true); |
218 | // HACK: sort preselect items first | ||
219 | lsp_item.sort_text = Some(format!(" {}", item.label())); | ||
224 | } | 220 | } |
225 | 221 | ||
226 | if completion_item.deprecated() { | 222 | if item.deprecated() { |
227 | res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) | 223 | lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) |
228 | } | 224 | } |
229 | 225 | ||
230 | if completion_item.trigger_call_info() { | 226 | if item.trigger_call_info() { |
231 | res.command = Some(command::trigger_parameter_hints()); | 227 | lsp_item.command = Some(command::trigger_parameter_hints()); |
232 | } | 228 | } |
233 | 229 | ||
234 | let mut all_results = match completion_item.ref_match() { | 230 | let mut res = match item.ref_match() { |
235 | Some(ref_match) => { | 231 | Some(mutability) => { |
236 | let mut refed = res.clone(); | 232 | let mut lsp_item_with_ref = lsp_item.clone(); |
237 | let (mutability, _score) = ref_match; | 233 | lsp_item.preselect = Some(true); |
238 | let label = format!("&{}{}", mutability.as_keyword_for_ref(), refed.label); | 234 | lsp_item.sort_text = Some(format!(" {}", item.label())); |
239 | set_score(&mut refed, &label); | 235 | lsp_item_with_ref.label = |
240 | refed.label = label; | 236 | format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label); |
241 | vec![res, refed] | 237 | if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit |
238 | { | ||
239 | it.new_text = format!("&{}{}", mutability.as_keyword_for_ref(), it.new_text); | ||
240 | } | ||
241 | vec![lsp_item_with_ref, lsp_item] | ||
242 | } | 242 | } |
243 | None => vec![res], | 243 | None => vec![lsp_item], |
244 | }; | 244 | }; |
245 | 245 | ||
246 | for mut r in all_results.iter_mut() { | 246 | for lsp_item in res.iter_mut() { |
247 | r.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); | 247 | lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format())); |
248 | } | 248 | } |
249 | all_results | 249 | res |
250 | } | 250 | } |
251 | 251 | ||
252 | pub(crate) fn signature_help( | 252 | pub(crate) fn signature_help( |
@@ -1087,7 +1087,11 @@ mod tests { | |||
1087 | add_call_parenthesis: true, | 1087 | add_call_parenthesis: true, |
1088 | add_call_argument_snippets: true, | 1088 | add_call_argument_snippets: true, |
1089 | snippet_cap: SnippetCap::new(true), | 1089 | snippet_cap: SnippetCap::new(true), |
1090 | insert_use: InsertUseConfig { merge: None, prefix_kind: PrefixKind::Plain }, | 1090 | insert_use: InsertUseConfig { |
1091 | merge: None, | ||
1092 | prefix_kind: PrefixKind::Plain, | ||
1093 | group: true, | ||
1094 | }, | ||
1091 | }, | 1095 | }, |
1092 | ide_db::base_db::FilePosition { file_id, offset }, | 1096 | ide_db::base_db::FilePosition { file_id, offset }, |
1093 | ) | 1097 | ) |
@@ -1101,13 +1105,15 @@ mod tests { | |||
1101 | expect_test::expect![[r#" | 1105 | expect_test::expect![[r#" |
1102 | [ | 1106 | [ |
1103 | ( | 1107 | ( |
1104 | "arg", | 1108 | "&arg", |
1105 | None, | 1109 | Some( |
1110 | " arg", | ||
1111 | ), | ||
1106 | ), | 1112 | ), |
1107 | ( | 1113 | ( |
1108 | "&arg", | 1114 | "arg", |
1109 | Some( | 1115 | Some( |
1110 | " &arg", | 1116 | " arg", |
1111 | ), | 1117 | ), |
1112 | ), | 1118 | ), |
1113 | ] | 1119 | ] |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 6b774073d..cd0c91481 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -13,7 +13,7 @@ use project_model::ProjectManifest; | |||
13 | use rust_analyzer::{config::Config, lsp_ext, main_loop}; | 13 | use rust_analyzer::{config::Config, lsp_ext, main_loop}; |
14 | use serde::Serialize; | 14 | use serde::Serialize; |
15 | use serde_json::{json, to_string_pretty, Value}; | 15 | use serde_json::{json, to_string_pretty, Value}; |
16 | use test_utils::{find_mismatch, Fixture}; | 16 | use test_utils::Fixture; |
17 | use vfs::AbsPathBuf; | 17 | use vfs::AbsPathBuf; |
18 | 18 | ||
19 | use crate::testdir::TestDir; | 19 | use crate::testdir::TestDir; |
@@ -279,3 +279,98 @@ fn recv_timeout(receiver: &Receiver<Message>) -> Result<Option<Message>, Timeout | |||
279 | recv(after(timeout)) -> _ => Err(Timeout), | 279 | recv(after(timeout)) -> _ => Err(Timeout), |
280 | } | 280 | } |
281 | } | 281 | } |
282 | |||
283 | // Comparison functionality borrowed from cargo: | ||
284 | |||
285 | /// Compares JSON object for approximate equality. | ||
286 | /// You can use `[..]` wildcard in strings (useful for OS dependent things such | ||
287 | /// as paths). You can use a `"{...}"` string literal as a wildcard for | ||
288 | /// arbitrary nested JSON. Arrays are sorted before comparison. | ||
289 | fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { | ||
290 | match (expected, actual) { | ||
291 | (Value::Number(l), Value::Number(r)) if l == r => None, | ||
292 | (Value::Bool(l), Value::Bool(r)) if l == r => None, | ||
293 | (Value::String(l), Value::String(r)) if lines_match(l, r) => None, | ||
294 | (Value::Array(l), Value::Array(r)) => { | ||
295 | if l.len() != r.len() { | ||
296 | return Some((expected, actual)); | ||
297 | } | ||
298 | |||
299 | let mut l = l.iter().collect::<Vec<_>>(); | ||
300 | let mut r = r.iter().collect::<Vec<_>>(); | ||
301 | |||
302 | l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) { | ||
303 | Some(i) => { | ||
304 | r.remove(i); | ||
305 | false | ||
306 | } | ||
307 | None => true, | ||
308 | }); | ||
309 | |||
310 | if !l.is_empty() { | ||
311 | assert!(!r.is_empty()); | ||
312 | Some((&l[0], &r[0])) | ||
313 | } else { | ||
314 | assert_eq!(r.len(), 0); | ||
315 | None | ||
316 | } | ||
317 | } | ||
318 | (Value::Object(l), Value::Object(r)) => { | ||
319 | fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> { | ||
320 | let mut entries = obj.iter().collect::<Vec<_>>(); | ||
321 | entries.sort_by_key(|it| it.0); | ||
322 | entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>() | ||
323 | } | ||
324 | |||
325 | let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); | ||
326 | if !same_keys { | ||
327 | return Some((expected, actual)); | ||
328 | } | ||
329 | |||
330 | let l = sorted_values(l); | ||
331 | let r = sorted_values(r); | ||
332 | |||
333 | l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next() | ||
334 | } | ||
335 | (Value::Null, Value::Null) => None, | ||
336 | // magic string literal "{...}" acts as wildcard for any sub-JSON | ||
337 | (Value::String(l), _) if l == "{...}" => None, | ||
338 | _ => Some((expected, actual)), | ||
339 | } | ||
340 | } | ||
341 | |||
342 | /// Compare a line with an expected pattern. | ||
343 | /// - Use `[..]` as a wildcard to match 0 or more characters on the same line | ||
344 | /// (similar to `.*` in a regex). | ||
345 | fn lines_match(expected: &str, actual: &str) -> bool { | ||
346 | // Let's not deal with / vs \ (windows...) | ||
347 | // First replace backslash-escaped backslashes with forward slashes | ||
348 | // which can occur in, for example, JSON output | ||
349 | let expected = expected.replace(r"\\", "/").replace(r"\", "/"); | ||
350 | let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); | ||
351 | for (i, part) in expected.split("[..]").enumerate() { | ||
352 | match actual.find(part) { | ||
353 | Some(j) => { | ||
354 | if i == 0 && j != 0 { | ||
355 | return false; | ||
356 | } | ||
357 | actual = &actual[j + part.len()..]; | ||
358 | } | ||
359 | None => return false, | ||
360 | } | ||
361 | } | ||
362 | actual.is_empty() || expected.ends_with("[..]") | ||
363 | } | ||
364 | |||
365 | #[test] | ||
366 | fn lines_match_works() { | ||
367 | assert!(lines_match("a b", "a b")); | ||
368 | assert!(lines_match("a[..]b", "a b")); | ||
369 | assert!(lines_match("a[..]", "a b")); | ||
370 | assert!(lines_match("[..]", "a b")); | ||
371 | assert!(lines_match("[..]b", "a b")); | ||
372 | |||
373 | assert!(!lines_match("[..]b", "c")); | ||
374 | assert!(!lines_match("b", "c")); | ||
375 | assert!(!lines_match("b", "cb")); | ||
376 | } | ||
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 9ee3a8586..33bde099b 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -11,6 +11,7 @@ edition = "2018" | |||
11 | doctest = false | 11 | doctest = false |
12 | 12 | ||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = "1.1" | ||
14 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
15 | rowan = "0.12.2" | 16 | rowan = "0.12.2" |
16 | rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } |
@@ -24,10 +25,10 @@ serde = { version = "1.0.106", features = ["derive"] } | |||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 25 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | text_edit = { path = "../text_edit", version = "0.0.0" } | 26 | text_edit = { path = "../text_edit", version = "0.0.0" } |
26 | parser = { path = "../parser", version = "0.0.0" } | 27 | parser = { path = "../parser", version = "0.0.0" } |
27 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
28 | profile = { path = "../profile", version = "0.0.0" } | 28 | profile = { path = "../profile", version = "0.0.0" } |
29 | 29 | ||
30 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | test_utils = { path = "../test_utils" } | ||
31 | walkdir = "2.3.1" | 32 | walkdir = "2.3.1" |
32 | rayon = "1" | 33 | rayon = "1" |
33 | expect-test = "1.1" | 34 | expect-test = "1.1" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 2ff92f9f6..b13252eec 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -10,7 +10,6 @@ use std::{ | |||
10 | use indexmap::IndexMap; | 10 | use indexmap::IndexMap; |
11 | use itertools::Itertools; | 11 | use itertools::Itertools; |
12 | use rustc_hash::FxHashMap; | 12 | use rustc_hash::FxHashMap; |
13 | use test_utils::mark; | ||
14 | use text_edit::TextEditBuilder; | 13 | use text_edit::TextEditBuilder; |
15 | 14 | ||
16 | use crate::{ | 15 | use crate::{ |
@@ -184,7 +183,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
184 | let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { | 183 | let (lhs, rhs) = match lhs.as_node().zip(rhs.as_node()) { |
185 | Some((lhs, rhs)) => (lhs, rhs), | 184 | Some((lhs, rhs)) => (lhs, rhs), |
186 | _ => { | 185 | _ => { |
187 | mark::hit!(diff_node_token_replace); | 186 | cov_mark::hit!(diff_node_token_replace); |
188 | diff.replacements.insert(lhs, rhs); | 187 | diff.replacements.insert(lhs, rhs); |
189 | return; | 188 | return; |
190 | } | 189 | } |
@@ -202,19 +201,19 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
202 | (None, Some(element)) => { | 201 | (None, Some(element)) => { |
203 | let insert_pos = match last_lhs.clone() { | 202 | let insert_pos = match last_lhs.clone() { |
204 | Some(prev) => { | 203 | Some(prev) => { |
205 | mark::hit!(diff_insert); | 204 | cov_mark::hit!(diff_insert); |
206 | TreeDiffInsertPos::After(prev) | 205 | TreeDiffInsertPos::After(prev) |
207 | } | 206 | } |
208 | // first iteration, insert into out parent as the first child | 207 | // first iteration, insert into out parent as the first child |
209 | None => { | 208 | None => { |
210 | mark::hit!(diff_insert_as_first_child); | 209 | cov_mark::hit!(diff_insert_as_first_child); |
211 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) | 210 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) |
212 | } | 211 | } |
213 | }; | 212 | }; |
214 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); | 213 | diff.insertions.entry(insert_pos).or_insert_with(Vec::new).push(element); |
215 | } | 214 | } |
216 | (Some(element), None) => { | 215 | (Some(element), None) => { |
217 | mark::hit!(diff_delete); | 216 | cov_mark::hit!(diff_delete); |
218 | diff.deletions.push(element); | 217 | diff.deletions.push(element); |
219 | } | 218 | } |
220 | (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} | 219 | (Some(ref lhs_ele), Some(ref rhs_ele)) if syntax_element_eq(lhs_ele, rhs_ele) => {} |
@@ -228,7 +227,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
228 | let mut insert = false; | 227 | let mut insert = false; |
229 | while let Some(rhs_child) = rhs_children_clone.next() { | 228 | while let Some(rhs_child) = rhs_children_clone.next() { |
230 | if syntax_element_eq(&lhs_ele, &rhs_child) { | 229 | if syntax_element_eq(&lhs_ele, &rhs_child) { |
231 | mark::hit!(diff_insertions); | 230 | cov_mark::hit!(diff_insertions); |
232 | insert = true; | 231 | insert = true; |
233 | break; | 232 | break; |
234 | } else { | 233 | } else { |
@@ -240,7 +239,7 @@ pub fn diff(from: &SyntaxNode, to: &SyntaxNode) -> TreeDiff { | |||
240 | let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { | 239 | let insert_pos = if let Some(prev) = last_lhs.clone().filter(|_| insert) { |
241 | TreeDiffInsertPos::After(prev) | 240 | TreeDiffInsertPos::After(prev) |
242 | } else { | 241 | } else { |
243 | mark::hit!(insert_first_child); | 242 | cov_mark::hit!(insert_first_child); |
244 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) | 243 | TreeDiffInsertPos::AsFirstChild(lhs.clone().into()) |
245 | }; | 244 | }; |
246 | 245 | ||
@@ -635,14 +634,13 @@ mod tests { | |||
635 | use expect_test::{expect, Expect}; | 634 | use expect_test::{expect, Expect}; |
636 | use itertools::Itertools; | 635 | use itertools::Itertools; |
637 | use parser::SyntaxKind; | 636 | use parser::SyntaxKind; |
638 | use test_utils::mark; | ||
639 | use text_edit::TextEdit; | 637 | use text_edit::TextEdit; |
640 | 638 | ||
641 | use crate::{AstNode, SyntaxElement}; | 639 | use crate::{AstNode, SyntaxElement}; |
642 | 640 | ||
643 | #[test] | 641 | #[test] |
644 | fn replace_node_token() { | 642 | fn replace_node_token() { |
645 | mark::check!(diff_node_token_replace); | 643 | cov_mark::check!(diff_node_token_replace); |
646 | check_diff( | 644 | check_diff( |
647 | r#"use node;"#, | 645 | r#"use node;"#, |
648 | r#"ident"#, | 646 | r#"ident"#, |
@@ -666,7 +664,7 @@ mod tests { | |||
666 | 664 | ||
667 | #[test] | 665 | #[test] |
668 | fn replace_parent() { | 666 | fn replace_parent() { |
669 | mark::check!(diff_insert_as_first_child); | 667 | cov_mark::check!(diff_insert_as_first_child); |
670 | check_diff( | 668 | check_diff( |
671 | r#""#, | 669 | r#""#, |
672 | r#"use foo::bar;"#, | 670 | r#"use foo::bar;"#, |
@@ -689,7 +687,7 @@ mod tests { | |||
689 | 687 | ||
690 | #[test] | 688 | #[test] |
691 | fn insert_last() { | 689 | fn insert_last() { |
692 | mark::check!(diff_insert); | 690 | cov_mark::check!(diff_insert); |
693 | check_diff( | 691 | check_diff( |
694 | r#" | 692 | r#" |
695 | use foo; | 693 | use foo; |
@@ -774,7 +772,7 @@ use baz;"#, | |||
774 | 772 | ||
775 | #[test] | 773 | #[test] |
776 | fn first_child_insertion() { | 774 | fn first_child_insertion() { |
777 | mark::check!(insert_first_child); | 775 | cov_mark::check!(insert_first_child); |
778 | check_diff( | 776 | check_diff( |
779 | r#"fn main() { | 777 | r#"fn main() { |
780 | stdi | 778 | stdi |
@@ -804,7 +802,7 @@ use baz;"#, | |||
804 | 802 | ||
805 | #[test] | 803 | #[test] |
806 | fn delete_last() { | 804 | fn delete_last() { |
807 | mark::check!(diff_delete); | 805 | cov_mark::check!(diff_delete); |
808 | check_diff( | 806 | check_diff( |
809 | r#"use foo; | 807 | r#"use foo; |
810 | use bar;"#, | 808 | use bar;"#, |
@@ -828,7 +826,7 @@ use baz;"#, | |||
828 | 826 | ||
829 | #[test] | 827 | #[test] |
830 | fn delete_middle() { | 828 | fn delete_middle() { |
831 | mark::check!(diff_insertions); | 829 | cov_mark::check!(diff_insertions); |
832 | check_diff( | 830 | check_diff( |
833 | r#" | 831 | r#" |
834 | use expect_test::{expect, Expect}; | 832 | use expect_test::{expect, Expect}; |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index b6c5de658..70ba8adb4 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -91,6 +91,10 @@ pub fn path_from_segments( | |||
91 | }) | 91 | }) |
92 | } | 92 | } |
93 | 93 | ||
94 | pub fn path_from_text(text: &str) -> ast::Path { | ||
95 | ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) | ||
96 | } | ||
97 | |||
94 | pub fn glob_use_tree() -> ast::UseTree { | 98 | pub fn glob_use_tree() -> ast::UseTree { |
95 | ast_from_text("use *;") | 99 | ast_from_text("use *;") |
96 | } | 100 | } |
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 11294c5b2..09e212e8c 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs | |||
@@ -37,6 +37,7 @@ pub mod algo; | |||
37 | pub mod ast; | 37 | pub mod ast; |
38 | #[doc(hidden)] | 38 | #[doc(hidden)] |
39 | pub mod fuzz; | 39 | pub mod fuzz; |
40 | pub mod utils; | ||
40 | 41 | ||
41 | use std::{marker::PhantomData, sync::Arc}; | 42 | use std::{marker::PhantomData, sync::Arc}; |
42 | 43 | ||
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index b2c06e24f..ba0ccfaed 100644 --- a/crates/syntax/src/tests.rs +++ b/crates/syntax/src/tests.rs | |||
@@ -7,7 +7,7 @@ use std::{ | |||
7 | use ast::NameOwner; | 7 | use ast::NameOwner; |
8 | use expect_test::expect_file; | 8 | use expect_test::expect_file; |
9 | use rayon::prelude::*; | 9 | use rayon::prelude::*; |
10 | use test_utils::{bench, bench_fixture, project_dir, skip_slow_tests}; | 10 | use test_utils::{bench, bench_fixture, project_root, skip_slow_tests}; |
11 | 11 | ||
12 | use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token}; | 12 | use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token}; |
13 | 13 | ||
@@ -153,7 +153,7 @@ fn reparse_fuzz_tests() { | |||
153 | /// Test that Rust-analyzer can parse and validate the rust-analyzer | 153 | /// Test that Rust-analyzer can parse and validate the rust-analyzer |
154 | #[test] | 154 | #[test] |
155 | fn self_hosting_parsing() { | 155 | fn self_hosting_parsing() { |
156 | let dir = project_dir().join("crates"); | 156 | let dir = project_root().join("crates"); |
157 | let files = walkdir::WalkDir::new(dir) | 157 | let files = walkdir::WalkDir::new(dir) |
158 | .into_iter() | 158 | .into_iter() |
159 | .filter_entry(|entry| { | 159 | .filter_entry(|entry| { |
@@ -193,7 +193,7 @@ fn self_hosting_parsing() { | |||
193 | } | 193 | } |
194 | 194 | ||
195 | fn test_data_dir() -> PathBuf { | 195 | fn test_data_dir() -> PathBuf { |
196 | project_dir().join("crates/syntax/test_data") | 196 | project_root().join("crates/syntax/test_data") |
197 | } | 197 | } |
198 | 198 | ||
199 | fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { | 199 | fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) { |
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs new file mode 100644 index 000000000..f4c02518b --- /dev/null +++ b/crates/syntax/src/utils.rs | |||
@@ -0,0 +1,43 @@ | |||
1 | //! A set of utils methods to reuse on other abstraction levels | ||
2 | |||
3 | use itertools::Itertools; | ||
4 | |||
5 | use crate::{ast, match_ast, AstNode}; | ||
6 | |||
7 | pub fn path_to_string_stripping_turbo_fish(path: &ast::Path) -> String { | ||
8 | path.syntax() | ||
9 | .children() | ||
10 | .filter_map(|node| { | ||
11 | match_ast! { | ||
12 | match node { | ||
13 | ast::PathSegment(it) => { | ||
14 | Some(it.name_ref()?.to_string()) | ||
15 | }, | ||
16 | ast::Path(it) => { | ||
17 | Some(path_to_string_stripping_turbo_fish(&it)) | ||
18 | }, | ||
19 | _ => None, | ||
20 | } | ||
21 | } | ||
22 | }) | ||
23 | .join("::") | ||
24 | } | ||
25 | |||
26 | #[cfg(test)] | ||
27 | mod tests { | ||
28 | use super::path_to_string_stripping_turbo_fish; | ||
29 | use crate::ast::make; | ||
30 | |||
31 | #[test] | ||
32 | fn turbofishes_are_stripped() { | ||
33 | assert_eq!("Vec", path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>")),); | ||
34 | assert_eq!( | ||
35 | "Vec::new", | ||
36 | path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::<i32>::new")), | ||
37 | ); | ||
38 | assert_eq!( | ||
39 | "Vec::new", | ||
40 | path_to_string_stripping_turbo_fish(&make::path_from_text("Vec::new()")), | ||
41 | ); | ||
42 | } | ||
43 | } | ||
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 2a65000b8..87bab7a08 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml | |||
@@ -13,7 +13,6 @@ doctest = false | |||
13 | # Avoid adding deps here, this crate is widely used in tests it should compile fast! | 13 | # Avoid adding deps here, this crate is widely used in tests it should compile fast! |
14 | dissimilar = "1.0.2" | 14 | dissimilar = "1.0.2" |
15 | text-size = "1.0.0" | 15 | text-size = "1.0.0" |
16 | serde_json = "1.0.48" | ||
17 | rustc-hash = "1.1.0" | 16 | rustc-hash = "1.1.0" |
18 | 17 | ||
19 | stdx = { path = "../stdx", version = "0.0.0" } | 18 | stdx = { path = "../stdx", version = "0.0.0" } |
diff --git a/crates/test_utils/src/bench_fixture.rs b/crates/test_utils/src/bench_fixture.rs index d775e2cc9..3a37c4473 100644 --- a/crates/test_utils/src/bench_fixture.rs +++ b/crates/test_utils/src/bench_fixture.rs | |||
@@ -4,7 +4,7 @@ use std::fs; | |||
4 | 4 | ||
5 | use stdx::format_to; | 5 | use stdx::format_to; |
6 | 6 | ||
7 | use crate::project_dir; | 7 | use crate::project_root; |
8 | 8 | ||
9 | pub fn big_struct() -> String { | 9 | pub fn big_struct() -> String { |
10 | let n = 1_000; | 10 | let n = 1_000; |
@@ -32,11 +32,11 @@ struct S{} {{ | |||
32 | } | 32 | } |
33 | 33 | ||
34 | pub fn glorious_old_parser() -> String { | 34 | pub fn glorious_old_parser() -> String { |
35 | let path = project_dir().join("bench_data/glorious_old_parser"); | 35 | let path = project_root().join("bench_data/glorious_old_parser"); |
36 | fs::read_to_string(&path).unwrap() | 36 | fs::read_to_string(&path).unwrap() |
37 | } | 37 | } |
38 | 38 | ||
39 | pub fn numerous_macro_rules() -> String { | 39 | pub fn numerous_macro_rules() -> String { |
40 | let path = project_dir().join("bench_data/numerous_macro_rules"); | 40 | let path = project_root().join("bench_data/numerous_macro_rules"); |
41 | fs::read_to_string(&path).unwrap() | 41 | fs::read_to_string(&path).unwrap() |
42 | } | 42 | } |
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index 5be4a64fc..c5f859790 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs | |||
@@ -6,20 +6,17 @@ | |||
6 | //! * Extracting markup (mainly, `$0` markers) out of fixture strings. | 6 | //! * Extracting markup (mainly, `$0` markers) out of fixture strings. |
7 | //! * marks (see the eponymous module). | 7 | //! * marks (see the eponymous module). |
8 | 8 | ||
9 | #[macro_use] | ||
10 | pub mod mark; | ||
11 | pub mod bench_fixture; | 9 | pub mod bench_fixture; |
12 | mod fixture; | 10 | mod fixture; |
13 | 11 | ||
14 | use std::{ | 12 | use std::{ |
15 | convert::{TryFrom, TryInto}, | 13 | convert::{TryFrom, TryInto}, |
16 | env, fs, | 14 | env, fs, |
17 | path::PathBuf, | 15 | path::{Path, PathBuf}, |
18 | }; | 16 | }; |
19 | 17 | ||
20 | use profile::StopWatch; | 18 | use profile::StopWatch; |
21 | use serde_json::Value; | 19 | use stdx::{is_ci, lines_with_ends}; |
22 | use stdx::lines_with_ends; | ||
23 | use text_size::{TextRange, TextSize}; | 20 | use text_size::{TextRange, TextSize}; |
24 | 21 | ||
25 | pub use dissimilar::diff as __diff; | 22 | pub use dissimilar::diff as __diff; |
@@ -281,101 +278,6 @@ fn main() { | |||
281 | ); | 278 | ); |
282 | } | 279 | } |
283 | 280 | ||
284 | // Comparison functionality borrowed from cargo: | ||
285 | |||
286 | /// Compare a line with an expected pattern. | ||
287 | /// - Use `[..]` as a wildcard to match 0 or more characters on the same line | ||
288 | /// (similar to `.*` in a regex). | ||
289 | pub fn lines_match(expected: &str, actual: &str) -> bool { | ||
290 | // Let's not deal with / vs \ (windows...) | ||
291 | // First replace backslash-escaped backslashes with forward slashes | ||
292 | // which can occur in, for example, JSON output | ||
293 | let expected = expected.replace(r"\\", "/").replace(r"\", "/"); | ||
294 | let mut actual: &str = &actual.replace(r"\\", "/").replace(r"\", "/"); | ||
295 | for (i, part) in expected.split("[..]").enumerate() { | ||
296 | match actual.find(part) { | ||
297 | Some(j) => { | ||
298 | if i == 0 && j != 0 { | ||
299 | return false; | ||
300 | } | ||
301 | actual = &actual[j + part.len()..]; | ||
302 | } | ||
303 | None => return false, | ||
304 | } | ||
305 | } | ||
306 | actual.is_empty() || expected.ends_with("[..]") | ||
307 | } | ||
308 | |||
309 | #[test] | ||
310 | fn lines_match_works() { | ||
311 | assert!(lines_match("a b", "a b")); | ||
312 | assert!(lines_match("a[..]b", "a b")); | ||
313 | assert!(lines_match("a[..]", "a b")); | ||
314 | assert!(lines_match("[..]", "a b")); | ||
315 | assert!(lines_match("[..]b", "a b")); | ||
316 | |||
317 | assert!(!lines_match("[..]b", "c")); | ||
318 | assert!(!lines_match("b", "c")); | ||
319 | assert!(!lines_match("b", "cb")); | ||
320 | } | ||
321 | |||
322 | /// Compares JSON object for approximate equality. | ||
323 | /// You can use `[..]` wildcard in strings (useful for OS dependent things such | ||
324 | /// as paths). You can use a `"{...}"` string literal as a wildcard for | ||
325 | /// arbitrary nested JSON. Arrays are sorted before comparison. | ||
326 | pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { | ||
327 | match (expected, actual) { | ||
328 | (Value::Number(l), Value::Number(r)) if l == r => None, | ||
329 | (Value::Bool(l), Value::Bool(r)) if l == r => None, | ||
330 | (Value::String(l), Value::String(r)) if lines_match(l, r) => None, | ||
331 | (Value::Array(l), Value::Array(r)) => { | ||
332 | if l.len() != r.len() { | ||
333 | return Some((expected, actual)); | ||
334 | } | ||
335 | |||
336 | let mut l = l.iter().collect::<Vec<_>>(); | ||
337 | let mut r = r.iter().collect::<Vec<_>>(); | ||
338 | |||
339 | l.retain(|l| match r.iter().position(|r| find_mismatch(l, r).is_none()) { | ||
340 | Some(i) => { | ||
341 | r.remove(i); | ||
342 | false | ||
343 | } | ||
344 | None => true, | ||
345 | }); | ||
346 | |||
347 | if !l.is_empty() { | ||
348 | assert!(!r.is_empty()); | ||
349 | Some((&l[0], &r[0])) | ||
350 | } else { | ||
351 | assert_eq!(r.len(), 0); | ||
352 | None | ||
353 | } | ||
354 | } | ||
355 | (Value::Object(l), Value::Object(r)) => { | ||
356 | fn sorted_values(obj: &serde_json::Map<String, Value>) -> Vec<&Value> { | ||
357 | let mut entries = obj.iter().collect::<Vec<_>>(); | ||
358 | entries.sort_by_key(|it| it.0); | ||
359 | entries.into_iter().map(|(_k, v)| v).collect::<Vec<_>>() | ||
360 | } | ||
361 | |||
362 | let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k)); | ||
363 | if !same_keys { | ||
364 | return Some((expected, actual)); | ||
365 | } | ||
366 | |||
367 | let l = sorted_values(l); | ||
368 | let r = sorted_values(r); | ||
369 | |||
370 | l.into_iter().zip(r).filter_map(|(l, r)| find_mismatch(l, r)).next() | ||
371 | } | ||
372 | (Value::Null, Value::Null) => None, | ||
373 | // magic string literal "{...}" acts as wildcard for any sub-JSON | ||
374 | (Value::String(l), _) if l == "{...}" => None, | ||
375 | _ => Some((expected, actual)), | ||
376 | } | ||
377 | } | ||
378 | |||
379 | /// Returns `false` if slow tests should not run, otherwise returns `true` and | 281 | /// Returns `false` if slow tests should not run, otherwise returns `true` and |
380 | /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag | 282 | /// also creates a file at `./target/.slow_tests_cookie` which serves as a flag |
381 | /// that slow tests did run. | 283 | /// that slow tests did run. |
@@ -384,14 +286,14 @@ pub fn skip_slow_tests() -> bool { | |||
384 | if should_skip { | 286 | if should_skip { |
385 | eprintln!("ignoring slow test") | 287 | eprintln!("ignoring slow test") |
386 | } else { | 288 | } else { |
387 | let path = project_dir().join("./target/.slow_tests_cookie"); | 289 | let path = project_root().join("./target/.slow_tests_cookie"); |
388 | fs::write(&path, ".").unwrap(); | 290 | fs::write(&path, ".").unwrap(); |
389 | } | 291 | } |
390 | should_skip | 292 | should_skip |
391 | } | 293 | } |
392 | 294 | ||
393 | /// Returns the path to the root directory of `rust-analyzer` project. | 295 | /// Returns the path to the root directory of `rust-analyzer` project. |
394 | pub fn project_dir() -> PathBuf { | 296 | pub fn project_root() -> PathBuf { |
395 | let dir = env!("CARGO_MANIFEST_DIR"); | 297 | let dir = env!("CARGO_MANIFEST_DIR"); |
396 | PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() | 298 | PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() |
397 | } | 299 | } |
@@ -449,3 +351,39 @@ pub fn bench(label: &'static str) -> impl Drop { | |||
449 | 351 | ||
450 | Bencher { sw: StopWatch::start(), label } | 352 | Bencher { sw: StopWatch::start(), label } |
451 | } | 353 | } |
354 | |||
355 | /// Checks that the `file` has the specified `contents`. If that is not the | ||
356 | /// case, updates the file and then fails the test. | ||
357 | pub fn ensure_file_contents(file: &Path, contents: &str) { | ||
358 | if let Err(()) = try_ensure_file_contents(file, contents) { | ||
359 | panic!("Some files were not up-to-date"); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /// Checks that the `file` has the specified `contents`. If that is not the | ||
364 | /// case, updates the file and return an Error. | ||
365 | pub fn try_ensure_file_contents(file: &Path, contents: &str) -> Result<(), ()> { | ||
366 | match std::fs::read_to_string(file) { | ||
367 | Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => { | ||
368 | return Ok(()) | ||
369 | } | ||
370 | _ => (), | ||
371 | } | ||
372 | let display_path = file.strip_prefix(&project_root()).unwrap_or(file); | ||
373 | eprintln!( | ||
374 | "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", | ||
375 | display_path.display() | ||
376 | ); | ||
377 | if is_ci() { | ||
378 | eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n"); | ||
379 | } | ||
380 | if let Some(parent) = file.parent() { | ||
381 | let _ = std::fs::create_dir_all(parent); | ||
382 | } | ||
383 | std::fs::write(file, contents).unwrap(); | ||
384 | Err(()) | ||
385 | } | ||
386 | |||
387 | fn normalize_newlines(s: &str) -> String { | ||
388 | s.replace("\r\n", "\n") | ||
389 | } | ||
diff --git a/crates/test_utils/src/mark.rs b/crates/test_utils/src/mark.rs deleted file mode 100644 index 97f5a93ad..000000000 --- a/crates/test_utils/src/mark.rs +++ /dev/null | |||
@@ -1,78 +0,0 @@ | |||
1 | //! This module implements manually tracked test coverage, which is useful for | ||
2 | //! quickly finding a test responsible for testing a particular bit of code. | ||
3 | //! | ||
4 | //! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html> | ||
5 | //! for details, but the TL;DR is that you write your test as | ||
6 | //! | ||
7 | //! ``` | ||
8 | //! #[test] | ||
9 | //! fn test_foo() { | ||
10 | //! mark::check!(test_foo); | ||
11 | //! } | ||
12 | //! ``` | ||
13 | //! | ||
14 | //! and in the code under test you write | ||
15 | //! | ||
16 | //! ``` | ||
17 | //! # use test_utils::mark; | ||
18 | //! # fn some_condition() -> bool { true } | ||
19 | //! fn foo() { | ||
20 | //! if some_condition() { | ||
21 | //! mark::hit!(test_foo); | ||
22 | //! } | ||
23 | //! } | ||
24 | //! ``` | ||
25 | //! | ||
26 | //! This module then checks that executing the test indeed covers the specified | ||
27 | //! function. This is useful if you come back to the `foo` function ten years | ||
28 | //! later and wonder where the test are: now you can grep for `test_foo`. | ||
29 | use std::sync::atomic::{AtomicUsize, Ordering}; | ||
30 | |||
31 | #[macro_export] | ||
32 | macro_rules! _hit { | ||
33 | ($ident:ident) => {{ | ||
34 | #[cfg(test)] | ||
35 | { | ||
36 | extern "C" { | ||
37 | #[no_mangle] | ||
38 | static $ident: std::sync::atomic::AtomicUsize; | ||
39 | } | ||
40 | unsafe { | ||
41 | $ident.fetch_add(1, std::sync::atomic::Ordering::SeqCst); | ||
42 | } | ||
43 | } | ||
44 | }}; | ||
45 | } | ||
46 | pub use _hit as hit; | ||
47 | |||
48 | #[macro_export] | ||
49 | macro_rules! _check { | ||
50 | ($ident:ident) => { | ||
51 | #[no_mangle] | ||
52 | static $ident: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0); | ||
53 | let _checker = $crate::mark::MarkChecker::new(&$ident); | ||
54 | }; | ||
55 | } | ||
56 | pub use _check as check; | ||
57 | |||
58 | pub struct MarkChecker { | ||
59 | mark: &'static AtomicUsize, | ||
60 | value_on_entry: usize, | ||
61 | } | ||
62 | |||
63 | impl MarkChecker { | ||
64 | pub fn new(mark: &'static AtomicUsize) -> MarkChecker { | ||
65 | let value_on_entry = mark.load(Ordering::Relaxed); | ||
66 | MarkChecker { mark, value_on_entry } | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl Drop for MarkChecker { | ||
71 | fn drop(&mut self) { | ||
72 | if std::thread::panicking() { | ||
73 | return; | ||
74 | } | ||
75 | let value_on_exit = self.mark.load(Ordering::Relaxed); | ||
76 | assert!(value_on_exit > self.value_on_entry, "mark was not hit") | ||
77 | } | ||
78 | } | ||
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index ead12616e..e2237ca95 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -46,7 +46,7 @@ This is *the* entry point, but it front-loads a lot of complexity, so its fine t | |||
46 | 46 | ||
47 | `crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. | 47 | `crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP. |
48 | 48 | ||
49 | `Analysis` and `AnalysisHost` types define the main API. | 49 | `Analysis` and `AnalysisHost` types define the main API for consumers of IDE services. |
50 | 50 | ||
51 | ## Code Map | 51 | ## Code Map |
52 | 52 | ||
@@ -308,9 +308,8 @@ This sections talks about the things which are everywhere and nowhere in particu | |||
308 | ### Code generation | 308 | ### Code generation |
309 | 309 | ||
310 | Some of the components of this repository are generated through automatic processes. | 310 | Some of the components of this repository are generated through automatic processes. |
311 | `cargo xtask codegen` runs all generation tasks. | 311 | Generated code is updated automatically on `cargo test`. |
312 | Generated code is generally committed to the git repository. | 312 | Generated code is generally committed to the git repository. |
313 | There are tests to check that the generated code is fresh. | ||
314 | 313 | ||
315 | In particular, we generate: | 314 | In particular, we generate: |
316 | 315 | ||
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 164c8482e..dd3ecc18d 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -39,7 +39,7 @@ If a language client does not know about `rust-analyzer`'s configuration options | |||
39 | 39 | ||
40 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/724 | 40 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/724 |
41 | 41 | ||
42 | **Client Capability:** `{ "snippetTextEdit": boolean }` | 42 | **Experimental Client Capability:** `{ "snippetTextEdit": boolean }` |
43 | 43 | ||
44 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: | 44 | If this capability is set, `WorkspaceEdit`s returned from `codeAction` requests might contain `SnippetTextEdit`s instead of usual `TextEdit`s: |
45 | 45 | ||
@@ -72,7 +72,7 @@ At the moment, rust-analyzer guarantees that only a single edit will have `Inser | |||
72 | 72 | ||
73 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/994 | 73 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/994 |
74 | 74 | ||
75 | **Client Capability:** `{ "codeActionGroup": boolean }` | 75 | **Experimental Client Capability:** `{ "codeActionGroup": boolean }` |
76 | 76 | ||
77 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: | 77 | If this capability is set, `CodeAction` returned from the server contain an additional field, `group`: |
78 | 78 | ||
@@ -119,7 +119,7 @@ Invoking code action at this position will yield two code actions for importing | |||
119 | 119 | ||
120 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 | 120 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1002 |
121 | 121 | ||
122 | **Server Capability:** `{ "parentModule": boolean }` | 122 | **Experimental Server Capability:** `{ "parentModule": boolean }` |
123 | 123 | ||
124 | This request is sent from client to server to handle "Goto Parent Module" editor action. | 124 | This request is sent from client to server to handle "Goto Parent Module" editor action. |
125 | 125 | ||
@@ -153,7 +153,7 @@ mod foo; | |||
153 | 153 | ||
154 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 | 154 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/992 |
155 | 155 | ||
156 | **Server Capability:** `{ "joinLines": boolean }` | 156 | **Experimental Server Capability:** `{ "joinLines": boolean }` |
157 | 157 | ||
158 | This request is sent from client to server to handle "Join Lines" editor action. | 158 | This request is sent from client to server to handle "Join Lines" editor action. |
159 | 159 | ||
@@ -200,7 +200,7 @@ fn main() { | |||
200 | 200 | ||
201 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 | 201 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/1001 |
202 | 202 | ||
203 | **Server Capability:** `{ "onEnter": boolean }` | 203 | **Experimental Server Capability:** `{ "onEnter": boolean }` |
204 | 204 | ||
205 | This request is sent from client to server to handle <kbd>Enter</kbd> keypress. | 205 | This request is sent from client to server to handle <kbd>Enter</kbd> keypress. |
206 | 206 | ||
@@ -251,7 +251,7 @@ As proper cursor positioning is raison-d'etat for `onEnter`, it uses `SnippetTex | |||
251 | 251 | ||
252 | ## Structural Search Replace (SSR) | 252 | ## Structural Search Replace (SSR) |
253 | 253 | ||
254 | **Server Capability:** `{ "ssr": boolean }` | 254 | **Experimental Server Capability:** `{ "ssr": boolean }` |
255 | 255 | ||
256 | This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. | 256 | This request is sent from client to server to handle structural search replace -- automated syntax tree based transformation of the source. |
257 | 257 | ||
@@ -293,7 +293,7 @@ SSR with query `foo($a, $b) ==>> ($a).foo($b)` will transform, eg `foo(y + 5, z) | |||
293 | 293 | ||
294 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/999 | 294 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/999 |
295 | 295 | ||
296 | **Server Capability:** `{ "matchingBrace": boolean }` | 296 | **Experimental Server Capability:** `{ "matchingBrace": boolean }` |
297 | 297 | ||
298 | This request is sent from client to server to handle "Matching Brace" editor action. | 298 | This request is sent from client to server to handle "Matching Brace" editor action. |
299 | 299 | ||
@@ -338,7 +338,7 @@ Moreover, it would be cool if editors didn't need to implement even basic langua | |||
338 | 338 | ||
339 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/944 | 339 | **Issue:** https://github.com/microsoft/language-server-protocol/issues/944 |
340 | 340 | ||
341 | **Server Capability:** `{ "runnables": { "kinds": string[] } }` | 341 | **Experimental Server Capability:** `{ "runnables": { "kinds": string[] } }` |
342 | 342 | ||
343 | This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). | 343 | This request is sent from client to server to get the list of things that can be run (tests, binaries, `cargo check -p`). |
344 | 344 | ||
@@ -421,7 +421,7 @@ Reloads project information (that is, re-executes `cargo metadata`). | |||
421 | 421 | ||
422 | ## Status Notification | 422 | ## Status Notification |
423 | 423 | ||
424 | **Client Capability:** `{ "statusNotification": boolean }` | 424 | **Experimental Client Capability:** `{ "statusNotification": boolean }` |
425 | 425 | ||
426 | **Method:** `rust-analyzer/status` | 426 | **Method:** `rust-analyzer/status` |
427 | 427 | ||
@@ -519,7 +519,7 @@ interface InlayHint { | |||
519 | 519 | ||
520 | ## Hover Actions | 520 | ## Hover Actions |
521 | 521 | ||
522 | **Client Capability:** `{ "hoverActions": boolean }` | 522 | **Experimental Client Capability:** `{ "hoverActions": boolean }` |
523 | 523 | ||
524 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: | 524 | If this capability is set, `Hover` request returned from the server might contain an additional field, `actions`: |
525 | 525 | ||
diff --git a/docs/dev/style.md b/docs/dev/style.md index 93ad98f20..46bd8b9b2 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -145,7 +145,7 @@ Formatting ensures that you can use your editor's "number of selected characters | |||
145 | ## Marked Tests | 145 | ## Marked Tests |
146 | 146 | ||
147 | Use | 147 | Use |
148 | [`mark::hit! / mark::check!`](https://github.com/rust-analyzer/rust-analyzer/blob/71fe719dd5247ed8615641d9303d7ca1aa201c2f/crates/test_utils/src/mark.rs) | 148 | [`cov_mark::hit! / cov_mark::check!`](https://github.com/matklad/cov-mark) |
149 | when testing specific conditions. | 149 | when testing specific conditions. |
150 | Do not place several marks into a single test or condition. | 150 | Do not place several marks into a single test or condition. |
151 | Do not reuse marks between several tests. | 151 | Do not reuse marks between several tests. |
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index f91e04c31..042ba2d54 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc | |||
@@ -1,112 +1,322 @@ | |||
1 | [[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`):: | 1 | [[rust-analyzer.assist.importMergeBehavior]]rust-analyzer.assist.importMergeBehavior (default: `"full"`):: |
2 | The strategy to use when inserting new imports or merging imports. | 2 | + |
3 | -- | ||
4 | The strategy to use when inserting new imports or merging imports. | ||
5 | -- | ||
3 | [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: | 6 | [[rust-analyzer.assist.importPrefix]]rust-analyzer.assist.importPrefix (default: `"plain"`):: |
4 | The path structure for newly inserted paths to use. | 7 | + |
8 | -- | ||
9 | The path structure for newly inserted paths to use. | ||
10 | -- | ||
11 | [[rust-analyzer.assist.importGroup]]rust-analyzer.assist.importGroup (default: `true`):: | ||
12 | + | ||
13 | -- | ||
14 | Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. | ||
15 | -- | ||
5 | [[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: | 16 | [[rust-analyzer.callInfo.full]]rust-analyzer.callInfo.full (default: `true`):: |
6 | Show function name and docs in parameter hints. | 17 | + |
18 | -- | ||
19 | Show function name and docs in parameter hints. | ||
20 | -- | ||
7 | [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: | 21 | [[rust-analyzer.cargo.autoreload]]rust-analyzer.cargo.autoreload (default: `true`):: |
8 | Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes. | 22 | + |
23 | -- | ||
24 | Automatically refresh project info via `cargo metadata` on | ||
25 | `Cargo.toml` changes. | ||
26 | -- | ||
9 | [[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: | 27 | [[rust-analyzer.cargo.allFeatures]]rust-analyzer.cargo.allFeatures (default: `false`):: |
10 | Activate all available features (`--all-features`). | 28 | + |
29 | -- | ||
30 | Activate all available features (`--all-features`). | ||
31 | -- | ||
11 | [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: | 32 | [[rust-analyzer.cargo.features]]rust-analyzer.cargo.features (default: `[]`):: |
12 | List of features to activate. | 33 | + |
13 | [[rust-analyzer.cargo.loadOutDirsFromCheck]]rust-analyzer.cargo.loadOutDirsFromCheck (default: `false`):: | 34 | -- |
14 | Run `cargo check` on startup to get the correct value for package OUT_DIRs. | 35 | List of features to activate. |
36 | -- | ||
37 | [[rust-analyzer.cargo.runBuildScripts]]rust-analyzer.cargo.runBuildScripts (default: `true`):: | ||
38 | + | ||
39 | -- | ||
40 | Run build scripts (`build.rs`) for more precise code analysis. | ||
41 | -- | ||
15 | [[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: | 42 | [[rust-analyzer.cargo.noDefaultFeatures]]rust-analyzer.cargo.noDefaultFeatures (default: `false`):: |
16 | Do not activate the `default` feature. | 43 | + |
44 | -- | ||
45 | Do not activate the `default` feature. | ||
46 | -- | ||
17 | [[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: | 47 | [[rust-analyzer.cargo.target]]rust-analyzer.cargo.target (default: `null`):: |
18 | Compilation target (target triple). | 48 | + |
49 | -- | ||
50 | Compilation target (target triple). | ||
51 | -- | ||
19 | [[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: | 52 | [[rust-analyzer.cargo.noSysroot]]rust-analyzer.cargo.noSysroot (default: `false`):: |
20 | Internal config for debugging, disables loading of sysroot crates. | 53 | + |
54 | -- | ||
55 | Internal config for debugging, disables loading of sysroot crates. | ||
56 | -- | ||
21 | [[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: | 57 | [[rust-analyzer.checkOnSave.enable]]rust-analyzer.checkOnSave.enable (default: `true`):: |
22 | Run specified `cargo check` command for diagnostics on save. | 58 | + |
59 | -- | ||
60 | Run specified `cargo check` command for diagnostics on save. | ||
61 | -- | ||
23 | [[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: | 62 | [[rust-analyzer.checkOnSave.allFeatures]]rust-analyzer.checkOnSave.allFeatures (default: `null`):: |
24 | Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`. | 63 | + |
64 | -- | ||
65 | Check with all features (`--all-features`). | ||
66 | Defaults to `#rust-analyzer.cargo.allFeatures#`. | ||
67 | -- | ||
25 | [[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: | 68 | [[rust-analyzer.checkOnSave.allTargets]]rust-analyzer.checkOnSave.allTargets (default: `true`):: |
26 | Check all targets and tests (`--all-targets`). | 69 | + |
70 | -- | ||
71 | Check all targets and tests (`--all-targets`). | ||
72 | -- | ||
27 | [[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: | 73 | [[rust-analyzer.checkOnSave.command]]rust-analyzer.checkOnSave.command (default: `"check"`):: |
28 | Cargo command to use for `cargo check`. | 74 | + |
75 | -- | ||
76 | Cargo command to use for `cargo check`. | ||
77 | -- | ||
29 | [[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: | 78 | [[rust-analyzer.checkOnSave.noDefaultFeatures]]rust-analyzer.checkOnSave.noDefaultFeatures (default: `null`):: |
30 | Do not activate the `default` feature. | 79 | + |
80 | -- | ||
81 | Do not activate the `default` feature. | ||
82 | -- | ||
31 | [[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: | 83 | [[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: |
32 | Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`. | 84 | + |
85 | -- | ||
86 | Check for a specific target. Defaults to | ||
87 | `#rust-analyzer.cargo.target#`. | ||
88 | -- | ||
33 | [[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`):: | 89 | [[rust-analyzer.checkOnSave.extraArgs]]rust-analyzer.checkOnSave.extraArgs (default: `[]`):: |
34 | Extra arguments for `cargo check`. | 90 | + |
91 | -- | ||
92 | Extra arguments for `cargo check`. | ||
93 | -- | ||
35 | [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: | 94 | [[rust-analyzer.checkOnSave.features]]rust-analyzer.checkOnSave.features (default: `null`):: |
36 | List of features to activate. Defaults to `#rust-analyzer.cargo.features#`. | 95 | + |
96 | -- | ||
97 | List of features to activate. Defaults to | ||
98 | `#rust-analyzer.cargo.features#`. | ||
99 | -- | ||
37 | [[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`):: | 100 | [[rust-analyzer.checkOnSave.overrideCommand]]rust-analyzer.checkOnSave.overrideCommand (default: `null`):: |
38 | Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option. | 101 | + |
102 | -- | ||
103 | Advanced option, fully override the command rust-analyzer uses for | ||
104 | checking. The command should include `--message-format=json` or | ||
105 | similar option. | ||
106 | -- | ||
39 | [[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`):: | 107 | [[rust-analyzer.completion.addCallArgumentSnippets]]rust-analyzer.completion.addCallArgumentSnippets (default: `true`):: |
40 | Whether to add argument snippets when completing functions. | 108 | + |
109 | -- | ||
110 | Whether to add argument snippets when completing functions. | ||
111 | -- | ||
41 | [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: | 112 | [[rust-analyzer.completion.addCallParenthesis]]rust-analyzer.completion.addCallParenthesis (default: `true`):: |
42 | Whether to add parenthesis when completing functions. | 113 | + |
114 | -- | ||
115 | Whether to add parenthesis when completing functions. | ||
116 | -- | ||
43 | [[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: | 117 | [[rust-analyzer.completion.postfix.enable]]rust-analyzer.completion.postfix.enable (default: `true`):: |
44 | Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | 118 | + |
119 | -- | ||
120 | Whether to show postfix snippets like `dbg`, `if`, `not`, etc. | ||
121 | -- | ||
45 | [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: | 122 | [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: |
46 | Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | 123 | + |
124 | -- | ||
125 | Toggles the additional completions that automatically add imports when completed. | ||
126 | Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. | ||
127 | -- | ||
47 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: | 128 | [[rust-analyzer.diagnostics.enable]]rust-analyzer.diagnostics.enable (default: `true`):: |
48 | Whether to show native rust-analyzer diagnostics. | 129 | + |
130 | -- | ||
131 | Whether to show native rust-analyzer diagnostics. | ||
132 | -- | ||
49 | [[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`):: | 133 | [[rust-analyzer.diagnostics.enableExperimental]]rust-analyzer.diagnostics.enableExperimental (default: `true`):: |
50 | Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual. | 134 | + |
135 | -- | ||
136 | Whether to show experimental rust-analyzer diagnostics that might | ||
137 | have more false positives than usual. | ||
138 | -- | ||
51 | [[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: | 139 | [[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`):: |
52 | List of rust-analyzer diagnostics to disable. | 140 | + |
141 | -- | ||
142 | List of rust-analyzer diagnostics to disable. | ||
143 | -- | ||
53 | [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: | 144 | [[rust-analyzer.diagnostics.warningsAsHint]]rust-analyzer.diagnostics.warningsAsHint (default: `[]`):: |
54 | List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`. | 145 | + |
146 | -- | ||
147 | List of warnings that should be displayed with info severity. | ||
148 | |||
149 | The warnings will be indicated by a blue squiggly underline in code | ||
150 | and a blue icon in the `Problems Panel`. | ||
151 | -- | ||
55 | [[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: | 152 | [[rust-analyzer.diagnostics.warningsAsInfo]]rust-analyzer.diagnostics.warningsAsInfo (default: `[]`):: |
56 | List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`. | 153 | + |
154 | -- | ||
155 | List of warnings that should be displayed with hint severity. | ||
156 | |||
157 | The warnings will be indicated by faded text or three dots in code | ||
158 | and will not show up in the `Problems Panel`. | ||
159 | -- | ||
57 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: | 160 | [[rust-analyzer.files.watcher]]rust-analyzer.files.watcher (default: `"client"`):: |
58 | Controls file watching implementation. | 161 | + |
162 | -- | ||
163 | Controls file watching implementation. | ||
164 | -- | ||
59 | [[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: | 165 | [[rust-analyzer.files.excludeDirs]]rust-analyzer.files.excludeDirs (default: `[]`):: |
60 | These directories will be ignored by rust-analyzer. | 166 | + |
167 | -- | ||
168 | These directories will be ignored by rust-analyzer. | ||
169 | -- | ||
61 | [[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: | 170 | [[rust-analyzer.hoverActions.debug]]rust-analyzer.hoverActions.debug (default: `true`):: |
62 | Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 171 | + |
172 | -- | ||
173 | Whether to show `Debug` action. Only applies when | ||
174 | `#rust-analyzer.hoverActions.enable#` is set. | ||
175 | -- | ||
63 | [[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`):: | 176 | [[rust-analyzer.hoverActions.enable]]rust-analyzer.hoverActions.enable (default: `true`):: |
64 | Whether to show HoverActions in Rust files. | 177 | + |
178 | -- | ||
179 | Whether to show HoverActions in Rust files. | ||
180 | -- | ||
65 | [[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`):: | 181 | [[rust-analyzer.hoverActions.gotoTypeDef]]rust-analyzer.hoverActions.gotoTypeDef (default: `true`):: |
66 | Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 182 | + |
183 | -- | ||
184 | Whether to show `Go to Type Definition` action. Only applies when | ||
185 | `#rust-analyzer.hoverActions.enable#` is set. | ||
186 | -- | ||
67 | [[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`):: | 187 | [[rust-analyzer.hoverActions.implementations]]rust-analyzer.hoverActions.implementations (default: `true`):: |
68 | Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 188 | + |
189 | -- | ||
190 | Whether to show `Implementations` action. Only applies when | ||
191 | `#rust-analyzer.hoverActions.enable#` is set. | ||
192 | -- | ||
69 | [[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: | 193 | [[rust-analyzer.hoverActions.run]]rust-analyzer.hoverActions.run (default: `true`):: |
70 | Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set. | 194 | + |
195 | -- | ||
196 | Whether to show `Run` action. Only applies when | ||
197 | `#rust-analyzer.hoverActions.enable#` is set. | ||
198 | -- | ||
71 | [[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`):: | 199 | [[rust-analyzer.hoverActions.linksInHover]]rust-analyzer.hoverActions.linksInHover (default: `true`):: |
72 | Use markdown syntax for links in hover. | 200 | + |
201 | -- | ||
202 | Use markdown syntax for links in hover. | ||
203 | -- | ||
73 | [[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: | 204 | [[rust-analyzer.inlayHints.chainingHints]]rust-analyzer.inlayHints.chainingHints (default: `true`):: |
74 | Whether to show inlay type hints for method chains. | 205 | + |
206 | -- | ||
207 | Whether to show inlay type hints for method chains. | ||
208 | -- | ||
75 | [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`):: | 209 | [[rust-analyzer.inlayHints.maxLength]]rust-analyzer.inlayHints.maxLength (default: `null`):: |
76 | Maximum length for inlay hints. Default is unlimited. | 210 | + |
211 | -- | ||
212 | Maximum length for inlay hints. Default is unlimited. | ||
213 | -- | ||
77 | [[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`):: | 214 | [[rust-analyzer.inlayHints.parameterHints]]rust-analyzer.inlayHints.parameterHints (default: `true`):: |
78 | Whether to show function parameter name inlay hints at the call site. | 215 | + |
216 | -- | ||
217 | Whether to show function parameter name inlay hints at the call | ||
218 | site. | ||
219 | -- | ||
79 | [[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`):: | 220 | [[rust-analyzer.inlayHints.typeHints]]rust-analyzer.inlayHints.typeHints (default: `true`):: |
80 | Whether to show inlay type hints for variables. | 221 | + |
222 | -- | ||
223 | Whether to show inlay type hints for variables. | ||
224 | -- | ||
81 | [[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`):: | 225 | [[rust-analyzer.lens.debug]]rust-analyzer.lens.debug (default: `true`):: |
82 | Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 226 | + |
227 | -- | ||
228 | Whether to show `Debug` lens. Only applies when | ||
229 | `#rust-analyzer.lens.enable#` is set. | ||
230 | -- | ||
83 | [[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: | 231 | [[rust-analyzer.lens.enable]]rust-analyzer.lens.enable (default: `true`):: |
84 | Whether to show CodeLens in Rust files. | 232 | + |
233 | -- | ||
234 | Whether to show CodeLens in Rust files. | ||
235 | -- | ||
85 | [[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`):: | 236 | [[rust-analyzer.lens.implementations]]rust-analyzer.lens.implementations (default: `true`):: |
86 | Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 237 | + |
238 | -- | ||
239 | Whether to show `Implementations` lens. Only applies when | ||
240 | `#rust-analyzer.lens.enable#` is set. | ||
241 | -- | ||
87 | [[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`):: | 242 | [[rust-analyzer.lens.run]]rust-analyzer.lens.run (default: `true`):: |
88 | Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 243 | + |
244 | -- | ||
245 | Whether to show `Run` lens. Only applies when | ||
246 | `#rust-analyzer.lens.enable#` is set. | ||
247 | -- | ||
89 | [[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: | 248 | [[rust-analyzer.lens.methodReferences]]rust-analyzer.lens.methodReferences (default: `false`):: |
90 | Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 249 | + |
250 | -- | ||
251 | Whether to show `Method References` lens. Only applies when | ||
252 | `#rust-analyzer.lens.enable#` is set. | ||
253 | -- | ||
91 | [[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: | 254 | [[rust-analyzer.lens.references]]rust-analyzer.lens.references (default: `false`):: |
92 | Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set. | 255 | + |
256 | -- | ||
257 | Whether to show `References` lens. Only applies when | ||
258 | `#rust-analyzer.lens.enable#` is set. | ||
259 | -- | ||
93 | [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: | 260 | [[rust-analyzer.linkedProjects]]rust-analyzer.linkedProjects (default: `[]`):: |
94 | Disable project auto-discovery in favor of explicitly specified set of projects.\n\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format. | 261 | + |
262 | -- | ||
263 | Disable project auto-discovery in favor of explicitly specified set | ||
264 | of projects. | ||
265 | |||
266 | Elements must be paths pointing to `Cargo.toml`, | ||
267 | `rust-project.json`, or JSON objects in `rust-project.json` format. | ||
268 | -- | ||
95 | [[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: | 269 | [[rust-analyzer.lruCapacity]]rust-analyzer.lruCapacity (default: `null`):: |
96 | Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. | 270 | + |
271 | -- | ||
272 | Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. | ||
273 | -- | ||
97 | [[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: | 274 | [[rust-analyzer.notifications.cargoTomlNotFound]]rust-analyzer.notifications.cargoTomlNotFound (default: `true`):: |
98 | Whether to show `can't find Cargo.toml` error message. | 275 | + |
276 | -- | ||
277 | Whether to show `can't find Cargo.toml` error message. | ||
278 | -- | ||
99 | [[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: | 279 | [[rust-analyzer.procMacro.enable]]rust-analyzer.procMacro.enable (default: `false`):: |
100 | Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be enabled. | 280 | + |
281 | -- | ||
282 | Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`. | ||
283 | -- | ||
101 | [[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: | 284 | [[rust-analyzer.procMacro.server]]rust-analyzer.procMacro.server (default: `null`):: |
102 | Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests). | 285 | + |
286 | -- | ||
287 | Internal config, path to proc-macro server executable (typically, | ||
288 | this is rust-analyzer itself, but we override this in tests). | ||
289 | -- | ||
103 | [[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: | 290 | [[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`):: |
104 | Command to be executed instead of 'cargo' for runnables. | 291 | + |
292 | -- | ||
293 | Command to be executed instead of 'cargo' for runnables. | ||
294 | -- | ||
105 | [[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: | 295 | [[rust-analyzer.runnables.cargoExtraArgs]]rust-analyzer.runnables.cargoExtraArgs (default: `[]`):: |
106 | Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be `--release`. | 296 | + |
297 | -- | ||
298 | Additional arguments to be passed to cargo for runnables such as | ||
299 | tests or binaries. For example, it may be `--release`. | ||
300 | -- | ||
107 | [[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: | 301 | [[rust-analyzer.rustcSource]]rust-analyzer.rustcSource (default: `null`):: |
108 | Path to the rust compiler sources, for usage in rustc_private projects, or "discover" to try to automatically find it. | 302 | + |
303 | -- | ||
304 | Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private | ||
305 | projects, or "discover" to try to automatically find it. | ||
306 | |||
307 | Any project which uses rust-analyzer with the rustcPrivate | ||
308 | crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. | ||
309 | |||
310 | This option is not reloaded automatically; you must restart rust-analyzer for it to take effect. | ||
311 | -- | ||
109 | [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: | 312 | [[rust-analyzer.rustfmt.extraArgs]]rust-analyzer.rustfmt.extraArgs (default: `[]`):: |
110 | Additional arguments to `rustfmt`. | 313 | + |
314 | -- | ||
315 | Additional arguments to `rustfmt`. | ||
316 | -- | ||
111 | [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: | 317 | [[rust-analyzer.rustfmt.overrideCommand]]rust-analyzer.rustfmt.overrideCommand (default: `null`):: |
112 | Advanced option, fully override the command rust-analyzer uses for formatting. | 318 | + |
319 | -- | ||
320 | Advanced option, fully override the command rust-analyzer uses for | ||
321 | formatting. | ||
322 | -- | ||
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 9f28237ff..4f2217546 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -226,6 +226,8 @@ The are several LSP client implementations for vim or neovim: | |||
226 | * inlay hints for variables and method chaining, _Neovim Only_ | 226 | * inlay hints for variables and method chaining, _Neovim Only_ |
227 | * semantic highlighting is not implemented yet | 227 | * semantic highlighting is not implemented yet |
228 | 228 | ||
229 | Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful. | ||
230 | |||
229 | ==== LanguageClient-neovim | 231 | ==== LanguageClient-neovim |
230 | 232 | ||
231 | 1. Install LanguageClient-neovim by following the instructions | 233 | 1. Install LanguageClient-neovim by following the instructions |
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 9d0d1d4ec..198c17556 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -9,6 +9,7 @@ | |||
9 | "version": "0.4.0-dev", | 9 | "version": "0.4.0-dev", |
10 | "license": "MIT OR Apache-2.0", | 10 | "license": "MIT OR Apache-2.0", |
11 | "dependencies": { | 11 | "dependencies": { |
12 | "https-proxy-agent": "^5.0.0", | ||
12 | "node-fetch": "^2.6.1", | 13 | "node-fetch": "^2.6.1", |
13 | "vscode-languageclient": "^7.1.0-next.4" | 14 | "vscode-languageclient": "^7.1.0-next.4" |
14 | }, | 15 | }, |
@@ -515,7 +516,6 @@ | |||
515 | "version": "6.0.2", | 516 | "version": "6.0.2", |
516 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", | 517 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", |
517 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", | 518 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", |
518 | "dev": true, | ||
519 | "dependencies": { | 519 | "dependencies": { |
520 | "debug": "4" | 520 | "debug": "4" |
521 | }, | 521 | }, |
@@ -830,6 +830,7 @@ | |||
830 | "dependencies": { | 830 | "dependencies": { |
831 | "anymatch": "~3.1.1", | 831 | "anymatch": "~3.1.1", |
832 | "braces": "~3.0.2", | 832 | "braces": "~3.0.2", |
833 | "fsevents": "~2.3.1", | ||
833 | "glob-parent": "~5.1.0", | 834 | "glob-parent": "~5.1.0", |
834 | "is-binary-path": "~2.1.0", | 835 | "is-binary-path": "~2.1.0", |
835 | "is-glob": "~4.0.1", | 836 | "is-glob": "~4.0.1", |
@@ -959,7 +960,6 @@ | |||
959 | "version": "4.3.1", | 960 | "version": "4.3.1", |
960 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", | 961 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", |
961 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", | 962 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", |
962 | "dev": true, | ||
963 | "dependencies": { | 963 | "dependencies": { |
964 | "ms": "2.1.2" | 964 | "ms": "2.1.2" |
965 | }, | 965 | }, |
@@ -1759,7 +1759,6 @@ | |||
1759 | "version": "5.0.0", | 1759 | "version": "5.0.0", |
1760 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", | 1760 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", |
1761 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", | 1761 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", |
1762 | "dev": true, | ||
1763 | "dependencies": { | 1762 | "dependencies": { |
1764 | "agent-base": "6", | 1763 | "agent-base": "6", |
1765 | "debug": "4" | 1764 | "debug": "4" |
@@ -2236,8 +2235,7 @@ | |||
2236 | "node_modules/ms": { | 2235 | "node_modules/ms": { |
2237 | "version": "2.1.2", | 2236 | "version": "2.1.2", |
2238 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | 2237 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", |
2239 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", | 2238 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" |
2240 | "dev": true | ||
2241 | }, | 2239 | }, |
2242 | "node_modules/mute-stream": { | 2240 | "node_modules/mute-stream": { |
2243 | "version": "0.0.8", | 2241 | "version": "0.0.8", |
@@ -2682,6 +2680,9 @@ | |||
2682 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz", | 2680 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.1.tgz", |
2683 | "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==", | 2681 | "integrity": "sha512-9rfr0Z6j+vE+eayfNVFr1KZ+k+jiUl2+0e4quZafy1x6SFCjzFspfRSO2ZZQeWeX9noeDTUDgg6eCENiEPFvQg==", |
2684 | "dev": true, | 2682 | "dev": true, |
2683 | "dependencies": { | ||
2684 | "fsevents": "~2.3.1" | ||
2685 | }, | ||
2685 | "bin": { | 2686 | "bin": { |
2686 | "rollup": "dist/bin/rollup" | 2687 | "rollup": "dist/bin/rollup" |
2687 | }, | 2688 | }, |
@@ -3843,7 +3844,6 @@ | |||
3843 | "version": "6.0.2", | 3844 | "version": "6.0.2", |
3844 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", | 3845 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", |
3845 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", | 3846 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", |
3846 | "dev": true, | ||
3847 | "requires": { | 3847 | "requires": { |
3848 | "debug": "4" | 3848 | "debug": "4" |
3849 | } | 3849 | } |
@@ -4190,7 +4190,6 @@ | |||
4190 | "version": "4.3.1", | 4190 | "version": "4.3.1", |
4191 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", | 4191 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", |
4192 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", | 4192 | "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", |
4193 | "dev": true, | ||
4194 | "requires": { | 4193 | "requires": { |
4195 | "ms": "2.1.2" | 4194 | "ms": "2.1.2" |
4196 | } | 4195 | } |
@@ -4798,7 +4797,6 @@ | |||
4798 | "version": "5.0.0", | 4797 | "version": "5.0.0", |
4799 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", | 4798 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", |
4800 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", | 4799 | "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", |
4801 | "dev": true, | ||
4802 | "requires": { | 4800 | "requires": { |
4803 | "agent-base": "6", | 4801 | "agent-base": "6", |
4804 | "debug": "4" | 4802 | "debug": "4" |
@@ -5175,8 +5173,7 @@ | |||
5175 | "ms": { | 5173 | "ms": { |
5176 | "version": "2.1.2", | 5174 | "version": "2.1.2", |
5177 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | 5175 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", |
5178 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", | 5176 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" |
5179 | "dev": true | ||
5180 | }, | 5177 | }, |
5181 | "mute-stream": { | 5178 | "mute-stream": { |
5182 | "version": "0.0.8", | 5179 | "version": "0.0.8", |
diff --git a/editors/code/package.json b/editors/code/package.json index e3e0ebff0..b29f006f0 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -35,6 +35,7 @@ | |||
35 | "test": "node ./out/tests/runTests.js" | 35 | "test": "node ./out/tests/runTests.js" |
36 | }, | 36 | }, |
37 | "dependencies": { | 37 | "dependencies": { |
38 | "https-proxy-agent": "^5.0.0", | ||
38 | "node-fetch": "^2.6.1", | 39 | "node-fetch": "^2.6.1", |
39 | "vscode-languageclient": "^7.1.0-next.4" | 40 | "vscode-languageclient": "^7.1.0-next.4" |
40 | }, | 41 | }, |
@@ -385,13 +386,18 @@ | |||
385 | "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." | 386 | "Force import paths to be absolute by always starting them with `crate` or the crate name they refer to." |
386 | ] | 387 | ] |
387 | }, | 388 | }, |
389 | "rust-analyzer.assist.importGroup": { | ||
390 | "markdownDescription": "Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.", | ||
391 | "default": true, | ||
392 | "type": "boolean" | ||
393 | }, | ||
388 | "rust-analyzer.callInfo.full": { | 394 | "rust-analyzer.callInfo.full": { |
389 | "markdownDescription": "Show function name and docs in parameter hints.", | 395 | "markdownDescription": "Show function name and docs in parameter hints.", |
390 | "default": true, | 396 | "default": true, |
391 | "type": "boolean" | 397 | "type": "boolean" |
392 | }, | 398 | }, |
393 | "rust-analyzer.cargo.autoreload": { | 399 | "rust-analyzer.cargo.autoreload": { |
394 | "markdownDescription": "Automatically refresh project info via `cargo metadata` on `Cargo.toml` changes.", | 400 | "markdownDescription": "Automatically refresh project info via `cargo metadata` on\n`Cargo.toml` changes.", |
395 | "default": true, | 401 | "default": true, |
396 | "type": "boolean" | 402 | "type": "boolean" |
397 | }, | 403 | }, |
@@ -408,9 +414,9 @@ | |||
408 | "type": "string" | 414 | "type": "string" |
409 | } | 415 | } |
410 | }, | 416 | }, |
411 | "rust-analyzer.cargo.loadOutDirsFromCheck": { | 417 | "rust-analyzer.cargo.runBuildScripts": { |
412 | "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs.", | 418 | "markdownDescription": "Run build scripts (`build.rs`) for more precise code analysis.", |
413 | "default": false, | 419 | "default": true, |
414 | "type": "boolean" | 420 | "type": "boolean" |
415 | }, | 421 | }, |
416 | "rust-analyzer.cargo.noDefaultFeatures": { | 422 | "rust-analyzer.cargo.noDefaultFeatures": { |
@@ -437,7 +443,7 @@ | |||
437 | "type": "boolean" | 443 | "type": "boolean" |
438 | }, | 444 | }, |
439 | "rust-analyzer.checkOnSave.allFeatures": { | 445 | "rust-analyzer.checkOnSave.allFeatures": { |
440 | "markdownDescription": "Check with all features (`--all-features`). Defaults to `#rust-analyzer.cargo.allFeatures#`.", | 446 | "markdownDescription": "Check with all features (`--all-features`).\nDefaults to `#rust-analyzer.cargo.allFeatures#`.", |
441 | "default": null, | 447 | "default": null, |
442 | "type": [ | 448 | "type": [ |
443 | "null", | 449 | "null", |
@@ -463,7 +469,7 @@ | |||
463 | ] | 469 | ] |
464 | }, | 470 | }, |
465 | "rust-analyzer.checkOnSave.target": { | 471 | "rust-analyzer.checkOnSave.target": { |
466 | "markdownDescription": "Check for a specific target. Defaults to `#rust-analyzer.cargo.target#`.", | 472 | "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.", |
467 | "default": null, | 473 | "default": null, |
468 | "type": [ | 474 | "type": [ |
469 | "null", | 475 | "null", |
@@ -479,7 +485,7 @@ | |||
479 | } | 485 | } |
480 | }, | 486 | }, |
481 | "rust-analyzer.checkOnSave.features": { | 487 | "rust-analyzer.checkOnSave.features": { |
482 | "markdownDescription": "List of features to activate. Defaults to `#rust-analyzer.cargo.features#`.", | 488 | "markdownDescription": "List of features to activate. Defaults to\n`#rust-analyzer.cargo.features#`.", |
483 | "default": null, | 489 | "default": null, |
484 | "type": [ | 490 | "type": [ |
485 | "null", | 491 | "null", |
@@ -490,7 +496,7 @@ | |||
490 | } | 496 | } |
491 | }, | 497 | }, |
492 | "rust-analyzer.checkOnSave.overrideCommand": { | 498 | "rust-analyzer.checkOnSave.overrideCommand": { |
493 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message-format=json` or similar option.", | 499 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nchecking. The command should include `--message-format=json` or\nsimilar option.", |
494 | "default": null, | 500 | "default": null, |
495 | "type": [ | 501 | "type": [ |
496 | "null", | 502 | "null", |
@@ -516,7 +522,7 @@ | |||
516 | "type": "boolean" | 522 | "type": "boolean" |
517 | }, | 523 | }, |
518 | "rust-analyzer.completion.autoimport.enable": { | 524 | "rust-analyzer.completion.autoimport.enable": { |
519 | "markdownDescription": "Toggles the additional completions that automatically add imports when completed. Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", | 525 | "markdownDescription": "Toggles the additional completions that automatically add imports when completed.\nNote that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled.", |
520 | "default": true, | 526 | "default": true, |
521 | "type": "boolean" | 527 | "type": "boolean" |
522 | }, | 528 | }, |
@@ -526,7 +532,7 @@ | |||
526 | "type": "boolean" | 532 | "type": "boolean" |
527 | }, | 533 | }, |
528 | "rust-analyzer.diagnostics.enableExperimental": { | 534 | "rust-analyzer.diagnostics.enableExperimental": { |
529 | "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might have more false positives than usual.", | 535 | "markdownDescription": "Whether to show experimental rust-analyzer diagnostics that might\nhave more false positives than usual.", |
530 | "default": true, | 536 | "default": true, |
531 | "type": "boolean" | 537 | "type": "boolean" |
532 | }, | 538 | }, |
@@ -540,7 +546,7 @@ | |||
540 | "uniqueItems": true | 546 | "uniqueItems": true |
541 | }, | 547 | }, |
542 | "rust-analyzer.diagnostics.warningsAsHint": { | 548 | "rust-analyzer.diagnostics.warningsAsHint": { |
543 | "markdownDescription": "List of warnings that should be displayed with info severity.\\n\\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the `Problems Panel`.", | 549 | "markdownDescription": "List of warnings that should be displayed with info severity.\n\nThe warnings will be indicated by a blue squiggly underline in code\nand a blue icon in the `Problems Panel`.", |
544 | "default": [], | 550 | "default": [], |
545 | "type": "array", | 551 | "type": "array", |
546 | "items": { | 552 | "items": { |
@@ -548,7 +554,7 @@ | |||
548 | } | 554 | } |
549 | }, | 555 | }, |
550 | "rust-analyzer.diagnostics.warningsAsInfo": { | 556 | "rust-analyzer.diagnostics.warningsAsInfo": { |
551 | "markdownDescription": "List of warnings that should be displayed with hint severity.\\n\\nThe warnings will be indicated by faded text or three dots in code and will not show up in the `Problems Panel`.", | 557 | "markdownDescription": "List of warnings that should be displayed with hint severity.\n\nThe warnings will be indicated by faded text or three dots in code\nand will not show up in the `Problems Panel`.", |
552 | "default": [], | 558 | "default": [], |
553 | "type": "array", | 559 | "type": "array", |
554 | "items": { | 560 | "items": { |
@@ -569,7 +575,7 @@ | |||
569 | } | 575 | } |
570 | }, | 576 | }, |
571 | "rust-analyzer.hoverActions.debug": { | 577 | "rust-analyzer.hoverActions.debug": { |
572 | "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 578 | "markdownDescription": "Whether to show `Debug` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
573 | "default": true, | 579 | "default": true, |
574 | "type": "boolean" | 580 | "type": "boolean" |
575 | }, | 581 | }, |
@@ -579,17 +585,17 @@ | |||
579 | "type": "boolean" | 585 | "type": "boolean" |
580 | }, | 586 | }, |
581 | "rust-analyzer.hoverActions.gotoTypeDef": { | 587 | "rust-analyzer.hoverActions.gotoTypeDef": { |
582 | "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 588 | "markdownDescription": "Whether to show `Go to Type Definition` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
583 | "default": true, | 589 | "default": true, |
584 | "type": "boolean" | 590 | "type": "boolean" |
585 | }, | 591 | }, |
586 | "rust-analyzer.hoverActions.implementations": { | 592 | "rust-analyzer.hoverActions.implementations": { |
587 | "markdownDescription": "Whether to show `Implementations` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 593 | "markdownDescription": "Whether to show `Implementations` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
588 | "default": true, | 594 | "default": true, |
589 | "type": "boolean" | 595 | "type": "boolean" |
590 | }, | 596 | }, |
591 | "rust-analyzer.hoverActions.run": { | 597 | "rust-analyzer.hoverActions.run": { |
592 | "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.", | 598 | "markdownDescription": "Whether to show `Run` action. Only applies when\n`#rust-analyzer.hoverActions.enable#` is set.", |
593 | "default": true, | 599 | "default": true, |
594 | "type": "boolean" | 600 | "type": "boolean" |
595 | }, | 601 | }, |
@@ -613,7 +619,7 @@ | |||
613 | "minimum": 0 | 619 | "minimum": 0 |
614 | }, | 620 | }, |
615 | "rust-analyzer.inlayHints.parameterHints": { | 621 | "rust-analyzer.inlayHints.parameterHints": { |
616 | "markdownDescription": "Whether to show function parameter name inlay hints at the call site.", | 622 | "markdownDescription": "Whether to show function parameter name inlay hints at the call\nsite.", |
617 | "default": true, | 623 | "default": true, |
618 | "type": "boolean" | 624 | "type": "boolean" |
619 | }, | 625 | }, |
@@ -623,7 +629,7 @@ | |||
623 | "type": "boolean" | 629 | "type": "boolean" |
624 | }, | 630 | }, |
625 | "rust-analyzer.lens.debug": { | 631 | "rust-analyzer.lens.debug": { |
626 | "markdownDescription": "Whether to show `Debug` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 632 | "markdownDescription": "Whether to show `Debug` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
627 | "default": true, | 633 | "default": true, |
628 | "type": "boolean" | 634 | "type": "boolean" |
629 | }, | 635 | }, |
@@ -633,27 +639,27 @@ | |||
633 | "type": "boolean" | 639 | "type": "boolean" |
634 | }, | 640 | }, |
635 | "rust-analyzer.lens.implementations": { | 641 | "rust-analyzer.lens.implementations": { |
636 | "markdownDescription": "Whether to show `Implementations` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 642 | "markdownDescription": "Whether to show `Implementations` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
637 | "default": true, | 643 | "default": true, |
638 | "type": "boolean" | 644 | "type": "boolean" |
639 | }, | 645 | }, |
640 | "rust-analyzer.lens.run": { | 646 | "rust-analyzer.lens.run": { |
641 | "markdownDescription": "Whether to show `Run` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 647 | "markdownDescription": "Whether to show `Run` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
642 | "default": true, | 648 | "default": true, |
643 | "type": "boolean" | 649 | "type": "boolean" |
644 | }, | 650 | }, |
645 | "rust-analyzer.lens.methodReferences": { | 651 | "rust-analyzer.lens.methodReferences": { |
646 | "markdownDescription": "Whether to show `Method References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 652 | "markdownDescription": "Whether to show `Method References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
647 | "default": false, | 653 | "default": false, |
648 | "type": "boolean" | 654 | "type": "boolean" |
649 | }, | 655 | }, |
650 | "rust-analyzer.lens.references": { | 656 | "rust-analyzer.lens.references": { |
651 | "markdownDescription": "Whether to show `References` lens. Only applies when `#rust-analyzer.lens.enable#` is set.", | 657 | "markdownDescription": "Whether to show `References` lens. Only applies when\n`#rust-analyzer.lens.enable#` is set.", |
652 | "default": false, | 658 | "default": false, |
653 | "type": "boolean" | 659 | "type": "boolean" |
654 | }, | 660 | }, |
655 | "rust-analyzer.linkedProjects": { | 661 | "rust-analyzer.linkedProjects": { |
656 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set of projects.\\n\\nElements must be paths pointing to `Cargo.toml`, `rust-project.json`, or JSON objects in `rust-project.json` format.", | 662 | "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.", |
657 | "default": [], | 663 | "default": [], |
658 | "type": "array", | 664 | "type": "array", |
659 | "items": { | 665 | "items": { |
@@ -678,12 +684,12 @@ | |||
678 | "type": "boolean" | 684 | "type": "boolean" |
679 | }, | 685 | }, |
680 | "rust-analyzer.procMacro.enable": { | 686 | "rust-analyzer.procMacro.enable": { |
681 | "markdownDescription": "Enable Proc macro support, `#rust-analyzer.cargo.loadOutDirsFromCheck#` must be enabled.", | 687 | "markdownDescription": "Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScripts#`.", |
682 | "default": false, | 688 | "default": false, |
683 | "type": "boolean" | 689 | "type": "boolean" |
684 | }, | 690 | }, |
685 | "rust-analyzer.procMacro.server": { | 691 | "rust-analyzer.procMacro.server": { |
686 | "markdownDescription": "Internal config, path to proc-macro server executable (typically, this is rust-analyzer itself, but we override this in tests).", | 692 | "markdownDescription": "Internal config, path to proc-macro server executable (typically,\nthis is rust-analyzer itself, but we override this in tests).", |
687 | "default": null, | 693 | "default": null, |
688 | "type": [ | 694 | "type": [ |
689 | "null", | 695 | "null", |
@@ -699,7 +705,7 @@ | |||
699 | ] | 705 | ] |
700 | }, | 706 | }, |
701 | "rust-analyzer.runnables.cargoExtraArgs": { | 707 | "rust-analyzer.runnables.cargoExtraArgs": { |
702 | "markdownDescription": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\\nFor example, it may be `--release`.", | 708 | "markdownDescription": "Additional arguments to be passed to cargo for runnables such as\ntests or binaries. For example, it may be `--release`.", |
703 | "default": [], | 709 | "default": [], |
704 | "type": "array", | 710 | "type": "array", |
705 | "items": { | 711 | "items": { |
@@ -707,7 +713,7 @@ | |||
707 | } | 713 | } |
708 | }, | 714 | }, |
709 | "rust-analyzer.rustcSource": { | 715 | "rust-analyzer.rustcSource": { |
710 | "markdownDescription": "Path to the rust compiler sources, for usage in rustc_private projects, or \"discover\" to try to automatically find it.", | 716 | "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option is not reloaded automatically; you must restart rust-analyzer for it to take effect.", |
711 | "default": null, | 717 | "default": null, |
712 | "type": [ | 718 | "type": [ |
713 | "null", | 719 | "null", |
@@ -723,7 +729,7 @@ | |||
723 | } | 729 | } |
724 | }, | 730 | }, |
725 | "rust-analyzer.rustfmt.overrideCommand": { | 731 | "rust-analyzer.rustfmt.overrideCommand": { |
726 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting.", | 732 | "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting.", |
727 | "default": null, | 733 | "default": null, |
728 | "type": [ | 734 | "type": [ |
729 | "null", | 735 | "null", |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index ddb5cfbd3..82f0a0566 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -100,6 +100,14 @@ export class Config { | |||
100 | get channel() { return this.get<UpdatesChannel>("updates.channel"); } | 100 | get channel() { return this.get<UpdatesChannel>("updates.channel"); } |
101 | get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } | 101 | get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } |
102 | get traceExtension() { return this.get<boolean>("trace.extension"); } | 102 | get traceExtension() { return this.get<boolean>("trace.extension"); } |
103 | get httpProxy() { | ||
104 | const httpProxy = vscode | ||
105 | .workspace | ||
106 | .getConfiguration('http') | ||
107 | .get<null | string>("proxy")!; | ||
108 | |||
109 | return httpProxy || process.env["https_proxy"] || process.env["HTTPS_PROXY"]; | ||
110 | } | ||
103 | 111 | ||
104 | get inlayHints() { | 112 | get inlayHints() { |
105 | return { | 113 | return { |
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 00393d6e8..1be4f1758 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -183,7 +183,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
183 | } | 183 | } |
184 | 184 | ||
185 | const release = await downloadWithRetryDialog(state, async () => { | 185 | const release = await downloadWithRetryDialog(state, async () => { |
186 | return await fetchRelease("nightly", state.githubToken); | 186 | return await fetchRelease("nightly", state.githubToken, config.httpProxy); |
187 | }).catch(async (e) => { | 187 | }).catch(async (e) => { |
188 | log.error(e); | 188 | log.error(e); |
189 | if (state.releaseId === undefined) { // Show error only for the initial download | 189 | if (state.releaseId === undefined) { // Show error only for the initial download |
@@ -209,6 +209,7 @@ async function bootstrapExtension(config: Config, state: PersistentState): Promi | |||
209 | url: artifact.browser_download_url, | 209 | url: artifact.browser_download_url, |
210 | dest, | 210 | dest, |
211 | progressTitle: "Downloading rust-analyzer extension", | 211 | progressTitle: "Downloading rust-analyzer extension", |
212 | httpProxy: config.httpProxy, | ||
212 | }); | 213 | }); |
213 | }); | 214 | }); |
214 | 215 | ||
@@ -331,7 +332,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
331 | 332 | ||
332 | const releaseTag = config.package.releaseTag; | 333 | const releaseTag = config.package.releaseTag; |
333 | const release = await downloadWithRetryDialog(state, async () => { | 334 | const release = await downloadWithRetryDialog(state, async () => { |
334 | return await fetchRelease(releaseTag, state.githubToken); | 335 | return await fetchRelease(releaseTag, state.githubToken, config.httpProxy); |
335 | }); | 336 | }); |
336 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); | 337 | const artifact = release.assets.find(artifact => artifact.name === `rust-analyzer-${platform}.gz`); |
337 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); | 338 | assert(!!artifact, `Bad release: ${JSON.stringify(release)}`); |
@@ -343,6 +344,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
343 | progressTitle: "Downloading rust-analyzer server", | 344 | progressTitle: "Downloading rust-analyzer server", |
344 | gunzip: true, | 345 | gunzip: true, |
345 | mode: 0o755, | 346 | mode: 0o755, |
347 | httpProxy: config.httpProxy, | ||
346 | }); | 348 | }); |
347 | }); | 349 | }); |
348 | 350 | ||
diff --git a/editors/code/src/net.ts b/editors/code/src/net.ts index d39dc1baf..07ebc615c 100644 --- a/editors/code/src/net.ts +++ b/editors/code/src/net.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | import fetch from "node-fetch"; | 1 | import fetch from "node-fetch"; |
2 | var HttpsProxyAgent = require('https-proxy-agent'); | ||
3 | |||
2 | import * as vscode from "vscode"; | 4 | import * as vscode from "vscode"; |
3 | import * as stream from "stream"; | 5 | import * as stream from "stream"; |
4 | import * as crypto from "crypto"; | 6 | import * as crypto from "crypto"; |
@@ -17,6 +19,7 @@ const REPO = "rust-analyzer"; | |||
17 | export async function fetchRelease( | 19 | export async function fetchRelease( |
18 | releaseTag: string, | 20 | releaseTag: string, |
19 | githubToken: string | null | undefined, | 21 | githubToken: string | null | undefined, |
22 | httpProxy: string | null | undefined, | ||
20 | ): Promise<GithubRelease> { | 23 | ): Promise<GithubRelease> { |
21 | 24 | ||
22 | const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; | 25 | const apiEndpointPath = `/repos/${OWNER}/${REPO}/releases/tags/${releaseTag}`; |
@@ -30,7 +33,14 @@ export async function fetchRelease( | |||
30 | headers.Authorization = "token " + githubToken; | 33 | headers.Authorization = "token " + githubToken; |
31 | } | 34 | } |
32 | 35 | ||
33 | const response = await fetch(requestUrl, { headers: headers }); | 36 | const response = await (() => { |
37 | if (httpProxy) { | ||
38 | log.debug(`Fetching release metadata via proxy: ${httpProxy}`); | ||
39 | return fetch(requestUrl, { headers: headers, agent: new HttpsProxyAgent(httpProxy) }); | ||
40 | } | ||
41 | |||
42 | return fetch(requestUrl, { headers: headers }); | ||
43 | })(); | ||
34 | 44 | ||
35 | if (!response.ok) { | 45 | if (!response.ok) { |
36 | log.error("Error fetching artifact release info", { | 46 | log.error("Error fetching artifact release info", { |
@@ -73,6 +83,7 @@ interface DownloadOpts { | |||
73 | dest: string; | 83 | dest: string; |
74 | mode?: number; | 84 | mode?: number; |
75 | gunzip?: boolean; | 85 | gunzip?: boolean; |
86 | httpProxy?: string; | ||
76 | } | 87 | } |
77 | 88 | ||
78 | export async function download(opts: DownloadOpts) { | 89 | export async function download(opts: DownloadOpts) { |
@@ -91,7 +102,7 @@ export async function download(opts: DownloadOpts) { | |||
91 | }, | 102 | }, |
92 | async (progress, _cancellationToken) => { | 103 | async (progress, _cancellationToken) => { |
93 | let lastPercentage = 0; | 104 | let lastPercentage = 0; |
94 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, (readBytes, totalBytes) => { | 105 | await downloadFile(opts.url, tempFile, opts.mode, !!opts.gunzip, opts.httpProxy, (readBytes, totalBytes) => { |
95 | const newPercentage = Math.round((readBytes / totalBytes) * 100); | 106 | const newPercentage = Math.round((readBytes / totalBytes) * 100); |
96 | if (newPercentage !== lastPercentage) { | 107 | if (newPercentage !== lastPercentage) { |
97 | progress.report({ | 108 | progress.report({ |
@@ -113,9 +124,17 @@ async function downloadFile( | |||
113 | destFilePath: fs.PathLike, | 124 | destFilePath: fs.PathLike, |
114 | mode: number | undefined, | 125 | mode: number | undefined, |
115 | gunzip: boolean, | 126 | gunzip: boolean, |
127 | httpProxy: string | null | undefined, | ||
116 | onProgress: (readBytes: number, totalBytes: number) => void | 128 | onProgress: (readBytes: number, totalBytes: number) => void |
117 | ): Promise<void> { | 129 | ): Promise<void> { |
118 | const res = await fetch(url); | 130 | const res = await (() => { |
131 | if (httpProxy) { | ||
132 | log.debug(`Downloading ${url} via proxy: ${httpProxy}`); | ||
133 | return fetch(url, { agent: new HttpsProxyAgent(httpProxy) }); | ||
134 | } | ||
135 | |||
136 | return fetch(url); | ||
137 | })(); | ||
119 | 138 | ||
120 | if (!res.ok) { | 139 | if (!res.ok) { |
121 | log.error("Error", res.status, "while downloading file from", url); | 140 | log.error("Error", res.status, "while downloading file from", url); |
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index b17dde598..e084f0df6 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml | |||
@@ -15,5 +15,5 @@ ungrammar = "=1.11" | |||
15 | walkdir = "2.3.1" | 15 | walkdir = "2.3.1" |
16 | write-json = "0.1.0" | 16 | write-json = "0.1.0" |
17 | xshell = "0.1" | 17 | xshell = "0.1" |
18 | xflags = "0.1.2" | 18 | xflags = "0.2.1" |
19 | # Avoid adding more dependencies to this crate | 19 | # Avoid adding more dependencies to this crate |
diff --git a/xtask/src/codegen.rs b/xtask/src/codegen.rs index 2f56c5ad0..2cf3c6fdc 100644 --- a/xtask/src/codegen.rs +++ b/xtask/src/codegen.rs | |||
@@ -7,68 +7,66 @@ | |||
7 | 7 | ||
8 | mod gen_syntax; | 8 | mod gen_syntax; |
9 | mod gen_parser_tests; | 9 | mod gen_parser_tests; |
10 | mod gen_lint_completions; | ||
10 | mod gen_assists_docs; | 11 | mod gen_assists_docs; |
11 | mod gen_feature_docs; | 12 | mod gen_feature_docs; |
12 | mod gen_lint_completions; | ||
13 | mod gen_diagnostic_docs; | 13 | mod gen_diagnostic_docs; |
14 | 14 | ||
15 | use std::{ | 15 | use std::{ |
16 | fmt, mem, | 16 | fmt, mem, |
17 | path::{Path, PathBuf}, | 17 | path::{Path, PathBuf}, |
18 | }; | 18 | }; |
19 | use xshell::{cmd, pushenv, read_file, write_file}; | 19 | use xshell::{cmd, pushenv}; |
20 | 20 | ||
21 | use crate::{ensure_rustfmt, flags, project_root, Result}; | 21 | use crate::{ensure_rustfmt, project_root, Result}; |
22 | 22 | ||
23 | pub(crate) use self::{ | 23 | pub(crate) use self::{ |
24 | gen_assists_docs::{generate_assists_docs, generate_assists_tests}, | 24 | gen_assists_docs::generate_assists_tests, gen_lint_completions::generate_lint_completions, |
25 | gen_diagnostic_docs::generate_diagnostic_docs, | 25 | gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax, |
26 | gen_feature_docs::generate_feature_docs, | ||
27 | gen_lint_completions::generate_lint_completions, | ||
28 | gen_parser_tests::generate_parser_tests, | ||
29 | gen_syntax::generate_syntax, | ||
30 | }; | 26 | }; |
31 | 27 | ||
32 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | 28 | pub(crate) fn docs() -> Result<()> { |
33 | pub(crate) enum Mode { | 29 | // We don't commit docs to the repo, so we can just overwrite them. |
34 | Overwrite, | 30 | gen_assists_docs::generate_assists_docs()?; |
35 | Verify, | 31 | gen_feature_docs::generate_feature_docs()?; |
32 | gen_diagnostic_docs::generate_diagnostic_docs()?; | ||
33 | Ok(()) | ||
36 | } | 34 | } |
37 | 35 | ||
38 | impl flags::Codegen { | 36 | #[allow(unused)] |
39 | pub(crate) fn run(self) -> Result<()> { | 37 | fn used() { |
40 | if self.features { | 38 | generate_parser_tests(); |
41 | generate_lint_completions(Mode::Overwrite)?; | 39 | generate_assists_tests(); |
42 | } | 40 | generate_syntax(); |
43 | generate_syntax(Mode::Overwrite)?; | 41 | generate_lint_completions(); |
44 | generate_parser_tests(Mode::Overwrite)?; | ||
45 | generate_assists_tests(Mode::Overwrite)?; | ||
46 | generate_assists_docs(Mode::Overwrite)?; | ||
47 | generate_feature_docs(Mode::Overwrite)?; | ||
48 | generate_diagnostic_docs(Mode::Overwrite)?; | ||
49 | Ok(()) | ||
50 | } | ||
51 | } | 42 | } |
52 | 43 | ||
53 | /// A helper to update file on disk if it has changed. | 44 | /// Checks that the `file` has the specified `contents`. If that is not the |
54 | /// With verify = false, | 45 | /// case, updates the file and then fails the test. |
55 | fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> { | 46 | pub(crate) fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> { |
56 | match read_file(path) { | 47 | match std::fs::read_to_string(file) { |
57 | Ok(old_contents) if normalize(&old_contents) == normalize(contents) => { | 48 | Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => { |
58 | return Ok(()); | 49 | return Ok(()) |
59 | } | 50 | } |
60 | _ => (), | 51 | _ => (), |
61 | } | 52 | } |
62 | if mode == Mode::Verify { | 53 | let display_path = file.strip_prefix(&project_root()).unwrap_or(file); |
63 | anyhow::bail!("`{}` is not up-to-date", path.display()); | 54 | eprintln!( |
55 | "\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n", | ||
56 | display_path.display() | ||
57 | ); | ||
58 | if std::env::var("CI").is_ok() { | ||
59 | eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n"); | ||
64 | } | 60 | } |
65 | eprintln!("updating {}", path.display()); | 61 | if let Some(parent) = file.parent() { |
66 | write_file(path, contents)?; | 62 | let _ = std::fs::create_dir_all(parent); |
67 | return Ok(()); | ||
68 | |||
69 | fn normalize(s: &str) -> String { | ||
70 | s.replace("\r\n", "\n") | ||
71 | } | 63 | } |
64 | std::fs::write(file, contents).unwrap(); | ||
65 | anyhow::bail!("some file were not up to date") | ||
66 | } | ||
67 | |||
68 | fn normalize_newlines(s: &str) -> String { | ||
69 | s.replace("\r\n", "\n") | ||
72 | } | 70 | } |
73 | 71 | ||
74 | const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; | 72 | const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`"; |
diff --git a/xtask/src/codegen/gen_assists_docs.rs b/xtask/src/codegen/gen_assists_docs.rs index c469b388d..158680993 100644 --- a/xtask/src/codegen/gen_assists_docs.rs +++ b/xtask/src/codegen/gen_assists_docs.rs | |||
@@ -2,22 +2,25 @@ | |||
2 | 2 | ||
3 | use std::{fmt, path::Path}; | 3 | use std::{fmt, path::Path}; |
4 | 4 | ||
5 | use xshell::write_file; | ||
6 | |||
5 | use crate::{ | 7 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE}, | 8 | codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, PREAMBLE}, |
7 | project_root, rust_files_in, Result, | 9 | project_root, rust_files_in, Result, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn generate_assists_tests(mode: Mode) -> Result<()> { | 12 | pub(crate) fn generate_assists_tests() -> Result<()> { |
11 | let assists = Assist::collect()?; | 13 | let assists = Assist::collect()?; |
12 | generate_tests(&assists, mode) | 14 | generate_tests(&assists) |
13 | } | 15 | } |
14 | 16 | ||
15 | pub(crate) fn generate_assists_docs(mode: Mode) -> Result<()> { | 17 | pub(crate) fn generate_assists_docs() -> Result<()> { |
16 | let assists = Assist::collect()?; | 18 | let assists = Assist::collect()?; |
17 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 19 | let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
18 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); | 20 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); |
19 | let dst = project_root().join("docs/user/generated_assists.adoc"); | 21 | let dst = project_root().join("docs/user/generated_assists.adoc"); |
20 | codegen::update(&dst, &contents, mode) | 22 | write_file(dst, &contents)?; |
23 | Ok(()) | ||
21 | } | 24 | } |
22 | 25 | ||
23 | #[derive(Debug)] | 26 | #[derive(Debug)] |
@@ -111,7 +114,7 @@ impl fmt::Display for Assist { | |||
111 | } | 114 | } |
112 | } | 115 | } |
113 | 116 | ||
114 | fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> { | 117 | fn generate_tests(assists: &[Assist]) -> Result<()> { |
115 | let mut buf = String::from("use super::check_doc_test;\n"); | 118 | let mut buf = String::from("use super::check_doc_test;\n"); |
116 | 119 | ||
117 | for assist in assists.iter() { | 120 | for assist in assists.iter() { |
@@ -135,7 +138,10 @@ r#####" | |||
135 | buf.push_str(&test) | 138 | buf.push_str(&test) |
136 | } | 139 | } |
137 | let buf = reformat(&buf)?; | 140 | let buf = reformat(&buf)?; |
138 | codegen::update(&project_root().join("crates/ide_assists/src/tests/generated.rs"), &buf, mode) | 141 | codegen::ensure_file_contents( |
142 | &project_root().join("crates/ide_assists/src/tests/generated.rs"), | ||
143 | &buf, | ||
144 | ) | ||
139 | } | 145 | } |
140 | 146 | ||
141 | fn hide_hash_comments(text: &str) -> String { | 147 | fn hide_hash_comments(text: &str) -> String { |
diff --git a/xtask/src/codegen/gen_diagnostic_docs.rs b/xtask/src/codegen/gen_diagnostic_docs.rs index a2561817b..9cf4d0a88 100644 --- a/xtask/src/codegen/gen_diagnostic_docs.rs +++ b/xtask/src/codegen/gen_diagnostic_docs.rs | |||
@@ -2,18 +2,20 @@ | |||
2 | 2 | ||
3 | use std::{fmt, path::PathBuf}; | 3 | use std::{fmt, path::PathBuf}; |
4 | 4 | ||
5 | use xshell::write_file; | ||
6 | |||
5 | use crate::{ | 7 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE}, | 8 | codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE}, |
7 | project_root, rust_files, Result, | 9 | project_root, rust_files, Result, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn generate_diagnostic_docs(mode: Mode) -> Result<()> { | 12 | pub(crate) fn generate_diagnostic_docs() -> Result<()> { |
11 | let diagnostics = Diagnostic::collect()?; | 13 | let diagnostics = Diagnostic::collect()?; |
12 | let contents = | 14 | let contents = |
13 | diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 15 | diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
14 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); | 16 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); |
15 | let dst = project_root().join("docs/user/generated_diagnostic.adoc"); | 17 | let dst = project_root().join("docs/user/generated_diagnostic.adoc"); |
16 | codegen::update(&dst, &contents, mode)?; | 18 | write_file(&dst, &contents)?; |
17 | Ok(()) | 19 | Ok(()) |
18 | } | 20 | } |
19 | 21 | ||
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index cad7ff477..c373d7d70 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs | |||
@@ -2,17 +2,19 @@ | |||
2 | 2 | ||
3 | use std::{fmt, path::PathBuf}; | 3 | use std::{fmt, path::PathBuf}; |
4 | 4 | ||
5 | use xshell::write_file; | ||
6 | |||
5 | use crate::{ | 7 | use crate::{ |
6 | codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE}, | 8 | codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE}, |
7 | project_root, rust_files, Result, | 9 | project_root, rust_files, Result, |
8 | }; | 10 | }; |
9 | 11 | ||
10 | pub(crate) fn generate_feature_docs(mode: Mode) -> Result<()> { | 12 | pub(crate) fn generate_feature_docs() -> Result<()> { |
11 | let features = Feature::collect()?; | 13 | let features = Feature::collect()?; |
12 | let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); | 14 | let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n"); |
13 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); | 15 | let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim()); |
14 | let dst = project_root().join("docs/user/generated_features.adoc"); | 16 | let dst = project_root().join("docs/user/generated_features.adoc"); |
15 | codegen::update(&dst, &contents, mode)?; | 17 | write_file(&dst, &contents)?; |
16 | Ok(()) | 18 | Ok(()) |
17 | } | 19 | } |
18 | 20 | ||
diff --git a/xtask/src/codegen/gen_lint_completions.rs b/xtask/src/codegen/gen_lint_completions.rs index b1c057037..24dbc6a39 100644 --- a/xtask/src/codegen/gen_lint_completions.rs +++ b/xtask/src/codegen/gen_lint_completions.rs | |||
@@ -5,13 +5,10 @@ use std::path::{Path, PathBuf}; | |||
5 | use walkdir::WalkDir; | 5 | use walkdir::WalkDir; |
6 | use xshell::{cmd, read_file}; | 6 | use xshell::{cmd, read_file}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::codegen::{ensure_file_contents, project_root, reformat, Result}; |
9 | codegen::{project_root, reformat, update, Mode, Result}, | ||
10 | run_rustfmt, | ||
11 | }; | ||
12 | 9 | ||
13 | pub(crate) fn generate_lint_completions(mode: Mode) -> Result<()> { | 10 | pub(crate) fn generate_lint_completions() -> Result<()> { |
14 | if !Path::new("./target/rust").exists() { | 11 | if !project_root().join("./target/rust").exists() { |
15 | cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; | 12 | cmd!("git clone --depth=1 https://github.com/rust-lang/rust ./target/rust").run()?; |
16 | } | 13 | } |
17 | 14 | ||
@@ -25,8 +22,7 @@ pub(crate) fn generate_lint_completions(mode: Mode) -> Result<()> { | |||
25 | 22 | ||
26 | let destination = | 23 | let destination = |
27 | project_root().join("crates/ide_completion/src/generated_lint_completions.rs"); | 24 | project_root().join("crates/ide_completion/src/generated_lint_completions.rs"); |
28 | update(destination.as_path(), &contents, mode)?; | 25 | ensure_file_contents(destination.as_path(), &contents)?; |
29 | run_rustfmt(mode)?; | ||
30 | 26 | ||
31 | Ok(()) | 27 | Ok(()) |
32 | } | 28 | } |
diff --git a/xtask/src/codegen/gen_parser_tests.rs b/xtask/src/codegen/gen_parser_tests.rs index cb8939063..096590653 100644 --- a/xtask/src/codegen/gen_parser_tests.rs +++ b/xtask/src/codegen/gen_parser_tests.rs | |||
@@ -8,13 +8,13 @@ use std::{ | |||
8 | }; | 8 | }; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | codegen::{extract_comment_blocks, update, Mode}, | 11 | codegen::{ensure_file_contents, extract_comment_blocks}, |
12 | project_root, Result, | 12 | project_root, Result, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | pub(crate) fn generate_parser_tests(mode: Mode) -> Result<()> { | 15 | pub(crate) fn generate_parser_tests() -> Result<()> { |
16 | let tests = tests_from_dir(&project_root().join(Path::new("crates/parser/src/grammar")))?; | 16 | let tests = tests_from_dir(&project_root().join(Path::new("crates/parser/src/grammar")))?; |
17 | fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> { | 17 | fn install_tests(tests: &HashMap<String, Test>, into: &str) -> Result<()> { |
18 | let tests_dir = project_root().join(into); | 18 | let tests_dir = project_root().join(into); |
19 | if !tests_dir.is_dir() { | 19 | if !tests_dir.is_dir() { |
20 | fs::create_dir_all(&tests_dir)?; | 20 | fs::create_dir_all(&tests_dir)?; |
@@ -35,12 +35,12 @@ pub(crate) fn generate_parser_tests(mode: Mode) -> Result<()> { | |||
35 | tests_dir.join(file_name) | 35 | tests_dir.join(file_name) |
36 | } | 36 | } |
37 | }; | 37 | }; |
38 | update(&path, &test.text, mode)?; | 38 | ensure_file_contents(&path, &test.text)?; |
39 | } | 39 | } |
40 | Ok(()) | 40 | Ok(()) |
41 | } | 41 | } |
42 | install_tests(&tests.ok, "crates/syntax/test_data/parser/inline/ok", mode)?; | 42 | install_tests(&tests.ok, "crates/syntax/test_data/parser/inline/ok")?; |
43 | install_tests(&tests.err, "crates/syntax/test_data/parser/inline/err", mode) | 43 | install_tests(&tests.err, "crates/syntax/test_data/parser/inline/err") |
44 | } | 44 | } |
45 | 45 | ||
46 | #[derive(Debug)] | 46 | #[derive(Debug)] |
diff --git a/xtask/src/codegen/gen_syntax.rs b/xtask/src/codegen/gen_syntax.rs index 191bc0e9d..80f26e8f5 100644 --- a/xtask/src/codegen/gen_syntax.rs +++ b/xtask/src/codegen/gen_syntax.rs | |||
@@ -14,25 +14,25 @@ use ungrammar::{rust_grammar, Grammar, Rule}; | |||
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC}, | 16 | ast_src::{AstEnumSrc, AstNodeSrc, AstSrc, Cardinality, Field, KindsSrc, KINDS_SRC}, |
17 | codegen::{reformat, update, Mode}, | 17 | codegen::{ensure_file_contents, reformat}, |
18 | project_root, Result, | 18 | project_root, Result, |
19 | }; | 19 | }; |
20 | 20 | ||
21 | pub(crate) fn generate_syntax(mode: Mode) -> Result<()> { | 21 | pub(crate) fn generate_syntax() -> Result<()> { |
22 | let grammar = rust_grammar(); | 22 | let grammar = rust_grammar(); |
23 | let ast = lower(&grammar); | 23 | let ast = lower(&grammar); |
24 | 24 | ||
25 | let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs"); | 25 | let syntax_kinds_file = project_root().join("crates/parser/src/syntax_kind/generated.rs"); |
26 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; | 26 | let syntax_kinds = generate_syntax_kinds(KINDS_SRC)?; |
27 | update(syntax_kinds_file.as_path(), &syntax_kinds, mode)?; | 27 | ensure_file_contents(syntax_kinds_file.as_path(), &syntax_kinds)?; |
28 | 28 | ||
29 | let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs"); | 29 | let ast_tokens_file = project_root().join("crates/syntax/src/ast/generated/tokens.rs"); |
30 | let contents = generate_tokens(&ast)?; | 30 | let contents = generate_tokens(&ast)?; |
31 | update(ast_tokens_file.as_path(), &contents, mode)?; | 31 | ensure_file_contents(ast_tokens_file.as_path(), &contents)?; |
32 | 32 | ||
33 | let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs"); | 33 | let ast_nodes_file = project_root().join("crates/syntax/src/ast/generated/nodes.rs"); |
34 | let contents = generate_nodes(KINDS_SRC, &ast)?; | 34 | let contents = generate_nodes(KINDS_SRC, &ast)?; |
35 | update(ast_nodes_file.as_path(), &contents, mode)?; | 35 | ensure_file_contents(ast_nodes_file.as_path(), &contents)?; |
36 | 36 | ||
37 | Ok(()) | 37 | Ok(()) |
38 | } | 38 | } |
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs index 5710fbdb5..48d1ad45e 100644 --- a/xtask/src/flags.rs +++ b/xtask/src/flags.rs | |||
@@ -1,6 +1,10 @@ | |||
1 | #![allow(unreachable_pub)] | 1 | #![allow(unreachable_pub)] |
2 | 2 | ||
3 | xflags::args_parser! { | 3 | use crate::install::{ClientOpt, Malloc, ServerOpt}; |
4 | |||
5 | xflags::xflags! { | ||
6 | src "./src/flags.rs" | ||
7 | |||
4 | /// Run custom build command. | 8 | /// Run custom build command. |
5 | cmd xtask { | 9 | cmd xtask { |
6 | default cmd help { | 10 | default cmd help { |
@@ -23,10 +27,6 @@ xflags::args_parser! { | |||
23 | optional --jemalloc | 27 | optional --jemalloc |
24 | } | 28 | } |
25 | 29 | ||
26 | cmd codegen { | ||
27 | optional --features | ||
28 | } | ||
29 | |||
30 | cmd lint {} | 30 | cmd lint {} |
31 | cmd fuzz-tests {} | 31 | cmd fuzz-tests {} |
32 | cmd pre-cache {} | 32 | cmd pre-cache {} |
@@ -53,7 +53,7 @@ xflags::args_parser! { | |||
53 | 53 | ||
54 | // generated start | 54 | // generated start |
55 | // The following code is generated by `xflags` macro. | 55 | // The following code is generated by `xflags` macro. |
56 | // Run `env XFLAGS_DUMP= cargo build` to regenerate. | 56 | // Run `env UPDATE_XFLAGS=1 cargo build` to regenerate. |
57 | #[derive(Debug)] | 57 | #[derive(Debug)] |
58 | pub struct Xtask { | 58 | pub struct Xtask { |
59 | pub subcommand: XtaskCmd, | 59 | pub subcommand: XtaskCmd, |
@@ -63,7 +63,6 @@ pub struct Xtask { | |||
63 | pub enum XtaskCmd { | 63 | pub enum XtaskCmd { |
64 | Help(Help), | 64 | Help(Help), |
65 | Install(Install), | 65 | Install(Install), |
66 | Codegen(Codegen), | ||
67 | Lint(Lint), | 66 | Lint(Lint), |
68 | FuzzTests(FuzzTests), | 67 | FuzzTests(FuzzTests), |
69 | PreCache(PreCache), | 68 | PreCache(PreCache), |
@@ -89,18 +88,13 @@ pub struct Install { | |||
89 | } | 88 | } |
90 | 89 | ||
91 | #[derive(Debug)] | 90 | #[derive(Debug)] |
92 | pub struct Codegen { | 91 | pub struct Lint; |
93 | pub features: bool, | ||
94 | } | ||
95 | |||
96 | #[derive(Debug)] | ||
97 | pub struct Lint {} | ||
98 | 92 | ||
99 | #[derive(Debug)] | 93 | #[derive(Debug)] |
100 | pub struct FuzzTests {} | 94 | pub struct FuzzTests; |
101 | 95 | ||
102 | #[derive(Debug)] | 96 | #[derive(Debug)] |
103 | pub struct PreCache {} | 97 | pub struct PreCache; |
104 | 98 | ||
105 | #[derive(Debug)] | 99 | #[derive(Debug)] |
106 | pub struct Release { | 100 | pub struct Release { |
@@ -129,11 +123,32 @@ pub struct Bb { | |||
129 | } | 123 | } |
130 | 124 | ||
131 | impl Xtask { | 125 | impl Xtask { |
132 | pub const HELP: &'static str = Self::_HELP; | 126 | pub const HELP: &'static str = Self::HELP_; |
133 | 127 | ||
134 | pub fn from_env() -> xflags::Result<Self> { | 128 | pub fn from_env() -> xflags::Result<Self> { |
135 | let mut p = xflags::rt::Parser::new_from_env(); | 129 | Self::from_env_() |
136 | Self::_parse(&mut p) | ||
137 | } | 130 | } |
138 | } | 131 | } |
139 | // generated end | 132 | // generated end |
133 | |||
134 | impl Install { | ||
135 | pub(crate) fn server(&self) -> Option<ServerOpt> { | ||
136 | if self.client && !self.server { | ||
137 | return None; | ||
138 | } | ||
139 | let malloc = if self.mimalloc { | ||
140 | Malloc::Mimalloc | ||
141 | } else if self.jemalloc { | ||
142 | Malloc::Jemalloc | ||
143 | } else { | ||
144 | Malloc::System | ||
145 | }; | ||
146 | Some(ServerOpt { malloc }) | ||
147 | } | ||
148 | pub(crate) fn client(&self) -> Option<ClientOpt> { | ||
149 | if !self.client && self.server { | ||
150 | return None; | ||
151 | } | ||
152 | Some(ClientOpt { code_bin: self.code_bin.clone() }) | ||
153 | } | ||
154 | } | ||
diff --git a/xtask/src/install.rs b/xtask/src/install.rs index ea2194248..177028b08 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs | |||
@@ -5,60 +5,32 @@ use std::{env, path::PathBuf, str}; | |||
5 | use anyhow::{bail, format_err, Context, Result}; | 5 | use anyhow::{bail, format_err, Context, Result}; |
6 | use xshell::{cmd, pushd}; | 6 | use xshell::{cmd, pushd}; |
7 | 7 | ||
8 | use crate::flags; | ||
9 | |||
8 | // Latest stable, feel free to send a PR if this lags behind. | 10 | // Latest stable, feel free to send a PR if this lags behind. |
9 | const REQUIRED_RUST_VERSION: u32 = 50; | 11 | const REQUIRED_RUST_VERSION: u32 = 50; |
10 | 12 | ||
11 | pub(crate) struct InstallCmd { | 13 | impl flags::Install { |
12 | pub(crate) client: Option<ClientOpt>, | 14 | pub(crate) fn run(self) -> Result<()> { |
13 | pub(crate) server: Option<ServerOpt>, | 15 | if cfg!(target_os = "macos") { |
14 | } | 16 | fix_path_for_mac().context("Fix path for mac")? |
15 | 17 | } | |
16 | #[derive(Clone, Copy)] | 18 | if let Some(server) = self.server() { |
17 | pub(crate) enum ClientOpt { | 19 | install_server(server).context("install server")?; |
18 | VsCode, | 20 | } |
19 | VsCodeExploration, | 21 | if let Some(client) = self.client() { |
20 | VsCodeInsiders, | 22 | install_client(client).context("install client")?; |
21 | VsCodium, | ||
22 | VsCodeOss, | ||
23 | Any, | ||
24 | } | ||
25 | |||
26 | impl ClientOpt { | ||
27 | pub(crate) const fn as_cmds(&self) -> &'static [&'static str] { | ||
28 | match self { | ||
29 | ClientOpt::VsCode => &["code"], | ||
30 | ClientOpt::VsCodeExploration => &["code-exploration"], | ||
31 | ClientOpt::VsCodeInsiders => &["code-insiders"], | ||
32 | ClientOpt::VsCodium => &["codium"], | ||
33 | ClientOpt::VsCodeOss => &["code-oss"], | ||
34 | ClientOpt::Any => &["code", "code-exploration", "code-insiders", "codium", "code-oss"], | ||
35 | } | 23 | } |
24 | Ok(()) | ||
36 | } | 25 | } |
37 | } | 26 | } |
38 | 27 | ||
39 | impl Default for ClientOpt { | 28 | #[derive(Clone)] |
40 | fn default() -> Self { | 29 | pub(crate) struct ClientOpt { |
41 | ClientOpt::Any | 30 | pub(crate) code_bin: Option<String>, |
42 | } | ||
43 | } | 31 | } |
44 | 32 | ||
45 | impl std::str::FromStr for ClientOpt { | 33 | const VS_CODES: &[&str] = &["code", "code-exploration", "code-insiders", "codium", "code-oss"]; |
46 | type Err = anyhow::Error; | ||
47 | |||
48 | fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
49 | [ | ||
50 | ClientOpt::VsCode, | ||
51 | ClientOpt::VsCodeExploration, | ||
52 | ClientOpt::VsCodeInsiders, | ||
53 | ClientOpt::VsCodium, | ||
54 | ClientOpt::VsCodeOss, | ||
55 | ] | ||
56 | .iter() | ||
57 | .copied() | ||
58 | .find(|c| [s] == c.as_cmds()) | ||
59 | .ok_or_else(|| anyhow::format_err!("no such client")) | ||
60 | } | ||
61 | } | ||
62 | 34 | ||
63 | pub(crate) struct ServerOpt { | 35 | pub(crate) struct ServerOpt { |
64 | pub(crate) malloc: Malloc, | 36 | pub(crate) malloc: Malloc, |
@@ -70,21 +42,6 @@ pub(crate) enum Malloc { | |||
70 | Jemalloc, | 42 | Jemalloc, |
71 | } | 43 | } |
72 | 44 | ||
73 | impl InstallCmd { | ||
74 | pub(crate) fn run(self) -> Result<()> { | ||
75 | if cfg!(target_os = "macos") { | ||
76 | fix_path_for_mac().context("Fix path for mac")? | ||
77 | } | ||
78 | if let Some(server) = self.server { | ||
79 | install_server(server).context("install server")?; | ||
80 | } | ||
81 | if let Some(client) = self.client { | ||
82 | install_client(client).context("install client")?; | ||
83 | } | ||
84 | Ok(()) | ||
85 | } | ||
86 | } | ||
87 | |||
88 | fn fix_path_for_mac() -> Result<()> { | 45 | fn fix_path_for_mac() -> Result<()> { |
89 | let mut vscode_path: Vec<PathBuf> = { | 46 | let mut vscode_path: Vec<PathBuf> = { |
90 | const COMMON_APP_PATH: &str = | 47 | const COMMON_APP_PATH: &str = |
@@ -121,21 +78,12 @@ fn fix_path_for_mac() -> Result<()> { | |||
121 | fn install_client(client_opt: ClientOpt) -> Result<()> { | 78 | fn install_client(client_opt: ClientOpt) -> Result<()> { |
122 | let _dir = pushd("./editors/code"); | 79 | let _dir = pushd("./editors/code"); |
123 | 80 | ||
124 | let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { | 81 | // Package extension. |
125 | client_opt.as_cmds().iter().copied().find(|bin| f(bin)).ok_or_else(|| { | 82 | if cfg!(unix) { |
126 | format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?") | ||
127 | }) | ||
128 | }; | ||
129 | |||
130 | let installed_extensions = if cfg!(unix) { | ||
131 | cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?; | 83 | cmd!("npm --version").run().context("`npm` is required to build the VS Code plugin")?; |
132 | cmd!("npm ci").run()?; | 84 | cmd!("npm ci").run()?; |
133 | 85 | ||
134 | cmd!("npm run package --scripts-prepend-node-path").run()?; | 86 | cmd!("npm run package --scripts-prepend-node-path").run()?; |
135 | |||
136 | let code = find_code(|bin| cmd!("{bin} --version").read().is_ok())?; | ||
137 | cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?; | ||
138 | cmd!("{code} --list-extensions").read()? | ||
139 | } else { | 87 | } else { |
140 | cmd!("cmd.exe /c npm --version") | 88 | cmd!("cmd.exe /c npm --version") |
141 | .run() | 89 | .run() |
@@ -143,8 +91,36 @@ fn install_client(client_opt: ClientOpt) -> Result<()> { | |||
143 | cmd!("cmd.exe /c npm ci").run()?; | 91 | cmd!("cmd.exe /c npm ci").run()?; |
144 | 92 | ||
145 | cmd!("cmd.exe /c npm run package").run()?; | 93 | cmd!("cmd.exe /c npm run package").run()?; |
94 | }; | ||
95 | |||
96 | // Find the appropriate VS Code binary. | ||
97 | let lifetime_extender; | ||
98 | let candidates: &[&str] = match client_opt.code_bin.as_deref() { | ||
99 | Some(it) => { | ||
100 | lifetime_extender = [it]; | ||
101 | &lifetime_extender[..] | ||
102 | } | ||
103 | None => VS_CODES, | ||
104 | }; | ||
105 | let code = candidates | ||
106 | .iter() | ||
107 | .copied() | ||
108 | .find(|&bin| { | ||
109 | if cfg!(unix) { | ||
110 | cmd!("{bin} --version").read().is_ok() | ||
111 | } else { | ||
112 | cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok() | ||
113 | } | ||
114 | }) | ||
115 | .ok_or_else(|| { | ||
116 | format_err!("Can't execute `{} --version`. Perhaps it is not in $PATH?", candidates[0]) | ||
117 | })?; | ||
146 | 118 | ||
147 | let code = find_code(|bin| cmd!("cmd.exe /c {bin}.cmd --version").read().is_ok())?; | 119 | // Install & verify. |
120 | let installed_extensions = if cfg!(unix) { | ||
121 | cmd!("{code} --install-extension rust-analyzer.vsix --force").run()?; | ||
122 | cmd!("{code} --list-extensions").read()? | ||
123 | } else { | ||
148 | cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?; | 124 | cmd!("cmd.exe /c {code}.cmd --install-extension rust-analyzer.vsix --force").run()?; |
149 | cmd!("cmd.exe /c {code}.cmd --list-extensions").read()? | 125 | cmd!("cmd.exe /c {code}.cmd --list-extensions").read()? |
150 | }; | 126 | }; |
diff --git a/xtask/src/main.rs b/xtask/src/main.rs index e419db7a7..35cc7c108 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs | |||
@@ -28,11 +28,7 @@ use std::{ | |||
28 | use walkdir::{DirEntry, WalkDir}; | 28 | use walkdir::{DirEntry, WalkDir}; |
29 | use xshell::{cmd, cp, pushd, pushenv}; | 29 | use xshell::{cmd, cp, pushd, pushenv}; |
30 | 30 | ||
31 | use crate::{ | 31 | use crate::dist::DistCmd; |
32 | codegen::Mode, | ||
33 | dist::DistCmd, | ||
34 | install::{InstallCmd, Malloc, ServerOpt}, | ||
35 | }; | ||
36 | 32 | ||
37 | fn main() -> Result<()> { | 33 | fn main() -> Result<()> { |
38 | let _d = pushd(project_root())?; | 34 | let _d = pushd(project_root())?; |
@@ -43,32 +39,7 @@ fn main() -> Result<()> { | |||
43 | println!("{}", flags::Xtask::HELP); | 39 | println!("{}", flags::Xtask::HELP); |
44 | return Ok(()); | 40 | return Ok(()); |
45 | } | 41 | } |
46 | flags::XtaskCmd::Install(flags) => { | 42 | flags::XtaskCmd::Install(cmd) => cmd.run(), |
47 | if flags.server && flags.client { | ||
48 | eprintln!( | ||
49 | "error: The argument `--server` cannot be used with `--client`\n\n\ | ||
50 | For more information try --help" | ||
51 | ); | ||
52 | return Ok(()); | ||
53 | } | ||
54 | |||
55 | let malloc = if flags.mimalloc { | ||
56 | Malloc::Mimalloc | ||
57 | } else if flags.jemalloc { | ||
58 | Malloc::Jemalloc | ||
59 | } else { | ||
60 | Malloc::System | ||
61 | }; | ||
62 | |||
63 | let client_bin = flags.code_bin.map(|it| it.parse()).transpose()?; | ||
64 | |||
65 | InstallCmd { | ||
66 | client: if flags.server { None } else { client_bin }, | ||
67 | server: if flags.client { None } else { Some(ServerOpt { malloc }) }, | ||
68 | } | ||
69 | .run() | ||
70 | } | ||
71 | flags::XtaskCmd::Codegen(cmd) => cmd.run(), | ||
72 | flags::XtaskCmd::Lint(_) => run_clippy(), | 43 | flags::XtaskCmd::Lint(_) => run_clippy(), |
73 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), | 44 | flags::XtaskCmd::FuzzTests(_) => run_fuzzer(), |
74 | flags::XtaskCmd::PreCache(cmd) => cmd.run(), | 45 | flags::XtaskCmd::PreCache(cmd) => cmd.run(), |
@@ -113,18 +84,6 @@ fn rust_files_in(path: &Path) -> impl Iterator<Item = PathBuf> { | |||
113 | files_in(path, "rs") | 84 | files_in(path, "rs") |
114 | } | 85 | } |
115 | 86 | ||
116 | fn run_rustfmt(mode: Mode) -> Result<()> { | ||
117 | let _dir = pushd(project_root())?; | ||
118 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); | ||
119 | ensure_rustfmt()?; | ||
120 | let check = match mode { | ||
121 | Mode::Overwrite => &[][..], | ||
122 | Mode::Verify => &["--", "--check"], | ||
123 | }; | ||
124 | cmd!("cargo fmt {check...}").run()?; | ||
125 | Ok(()) | ||
126 | } | ||
127 | |||
128 | fn ensure_rustfmt() -> Result<()> { | 87 | fn ensure_rustfmt() -> Result<()> { |
129 | let out = cmd!("rustfmt --version").read()?; | 88 | let out = cmd!("rustfmt --version").read()?; |
130 | if !out.contains("stable") { | 89 | if !out.contains("stable") { |
diff --git a/xtask/src/release.rs b/xtask/src/release.rs index d8d86fd63..dde5d14ee 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs | |||
@@ -2,7 +2,7 @@ use std::fmt::Write; | |||
2 | 2 | ||
3 | use xshell::{cmd, cp, pushd, read_dir, write_file}; | 3 | use xshell::{cmd, cp, pushd, read_dir, write_file}; |
4 | 4 | ||
5 | use crate::{codegen, date_iso, flags, is_release_tag, project_root, Mode, Result}; | 5 | use crate::{codegen, date_iso, flags, is_release_tag, project_root, Result}; |
6 | 6 | ||
7 | impl flags::Release { | 7 | impl flags::Release { |
8 | pub(crate) fn run(self) -> Result<()> { | 8 | pub(crate) fn run(self) -> Result<()> { |
@@ -12,8 +12,7 @@ impl flags::Release { | |||
12 | cmd!("git reset --hard tags/nightly").run()?; | 12 | cmd!("git reset --hard tags/nightly").run()?; |
13 | cmd!("git push").run()?; | 13 | cmd!("git push").run()?; |
14 | } | 14 | } |
15 | codegen::generate_assists_docs(Mode::Overwrite)?; | 15 | codegen::docs()?; |
16 | codegen::generate_feature_docs(Mode::Overwrite)?; | ||
17 | 16 | ||
18 | let website_root = project_root().join("../rust-analyzer.github.io"); | 17 | let website_root = project_root().join("../rust-analyzer.github.io"); |
19 | let changelog_dir = website_root.join("./thisweek/_posts"); | 18 | let changelog_dir = website_root.join("./thisweek/_posts"); |
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs index 349ca14d0..1352d1218 100644 --- a/xtask/src/tidy.rs +++ b/xtask/src/tidy.rs | |||
@@ -3,48 +3,48 @@ use std::{ | |||
3 | path::{Path, PathBuf}, | 3 | path::{Path, PathBuf}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use xshell::{cmd, read_file}; | 6 | use xshell::{cmd, pushd, pushenv, read_file}; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{cargo_files, codegen, project_root, rust_files}; |
9 | cargo_files, | ||
10 | codegen::{self, Mode}, | ||
11 | project_root, run_rustfmt, rust_files, | ||
12 | }; | ||
13 | 9 | ||
14 | #[test] | 10 | #[test] |
15 | fn generated_grammar_is_fresh() { | 11 | fn generate_grammar() { |
16 | if let Err(error) = codegen::generate_syntax(Mode::Verify) { | 12 | codegen::generate_syntax().unwrap() |
17 | panic!("{}. Please update it by running `cargo xtask codegen`", error); | ||
18 | } | ||
19 | } | 13 | } |
20 | 14 | ||
21 | #[test] | 15 | #[test] |
22 | fn generated_tests_are_fresh() { | 16 | fn generate_parser_tests() { |
23 | if let Err(error) = codegen::generate_parser_tests(Mode::Verify) { | 17 | codegen::generate_parser_tests().unwrap() |
24 | panic!("{}. Please update tests by running `cargo xtask codegen`", error); | ||
25 | } | ||
26 | } | 18 | } |
27 | 19 | ||
28 | #[test] | 20 | #[test] |
29 | fn generated_assists_are_fresh() { | 21 | fn generate_assists_tests() { |
30 | if let Err(error) = codegen::generate_assists_tests(Mode::Verify) { | 22 | codegen::generate_assists_tests().unwrap(); |
31 | panic!("{}. Please update assists by running `cargo xtask codegen`", error); | 23 | } |
32 | } | 24 | |
25 | /// This clones rustc repo, and so is not worth to keep up-to-date. We update | ||
26 | /// manually by un-ignoring the test from time to time. | ||
27 | #[test] | ||
28 | #[ignore] | ||
29 | fn generate_lint_completions() { | ||
30 | codegen::generate_lint_completions().unwrap() | ||
33 | } | 31 | } |
34 | 32 | ||
35 | #[test] | 33 | #[test] |
36 | fn check_code_formatting() { | 34 | fn check_code_formatting() { |
37 | if let Err(error) = run_rustfmt(Mode::Verify) { | 35 | let _dir = pushd(project_root()).unwrap(); |
38 | panic!("{}. Please format the code by running `cargo format`", error); | 36 | let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); |
37 | crate::ensure_rustfmt().unwrap(); | ||
38 | let res = cmd!("cargo fmt -- --check").run(); | ||
39 | if !res.is_ok() { | ||
40 | let _ = cmd!("cargo fmt").run(); | ||
39 | } | 41 | } |
42 | res.unwrap() | ||
40 | } | 43 | } |
41 | 44 | ||
42 | #[test] | 45 | #[test] |
43 | fn smoke_test_docs_generation() { | 46 | fn smoke_test_generate_documentation() { |
44 | // We don't commit docs to the repo, so we can just overwrite in tests. | 47 | codegen::docs().unwrap() |
45 | codegen::generate_assists_docs(Mode::Overwrite).unwrap(); | ||
46 | codegen::generate_feature_docs(Mode::Overwrite).unwrap(); | ||
47 | codegen::generate_diagnostic_docs(Mode::Overwrite).unwrap(); | ||
48 | } | 48 | } |
49 | 49 | ||
50 | #[test] | 50 | #[test] |