diff options
75 files changed, 1632 insertions, 1062 deletions
diff --git a/Cargo.lock b/Cargo.lock index 74e5a8273..49022502d 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -82,9 +82,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | |||
82 | 82 | ||
83 | [[package]] | 83 | [[package]] |
84 | name = "backtrace" | 84 | name = "backtrace" |
85 | version = "0.3.50" | 85 | version = "0.3.51" |
86 | source = "registry+https://github.com/rust-lang/crates.io-index" | 86 | source = "registry+https://github.com/rust-lang/crates.io-index" |
87 | checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293" | 87 | checksum = "ec1931848a574faa8f7c71a12ea00453ff5effbb5f51afe7f77d7a48cace6ac1" |
88 | dependencies = [ | 88 | dependencies = [ |
89 | "addr2line", | 89 | "addr2line", |
90 | "cfg-if", | 90 | "cfg-if", |
@@ -214,9 +214,9 @@ dependencies = [ | |||
214 | 214 | ||
215 | [[package]] | 215 | [[package]] |
216 | name = "chrono" | 216 | name = "chrono" |
217 | version = "0.4.18" | 217 | version = "0.4.19" |
218 | source = "registry+https://github.com/rust-lang/crates.io-index" | 218 | source = "registry+https://github.com/rust-lang/crates.io-index" |
219 | checksum = "d021fddb7bd3e734370acfa4a83f34095571d8570c039f1420d77540f68d5772" | 219 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" |
220 | dependencies = [ | 220 | dependencies = [ |
221 | "libc", | 221 | "libc", |
222 | "num-integer", | 222 | "num-integer", |
@@ -365,9 +365,9 @@ checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" | |||
365 | 365 | ||
366 | [[package]] | 366 | [[package]] |
367 | name = "flate2" | 367 | name = "flate2" |
368 | version = "1.0.17" | 368 | version = "1.0.18" |
369 | source = "registry+https://github.com/rust-lang/crates.io-index" | 369 | source = "registry+https://github.com/rust-lang/crates.io-index" |
370 | checksum = "766d0e77a2c1502169d4a93ff3b8c15a71fd946cd0126309752104e5f3c46d94" | 370 | checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee" |
371 | dependencies = [ | 371 | dependencies = [ |
372 | "cfg-if", | 372 | "cfg-if", |
373 | "crc32fast", | 373 | "crc32fast", |
@@ -453,9 +453,9 @@ dependencies = [ | |||
453 | 453 | ||
454 | [[package]] | 454 | [[package]] |
455 | name = "hashbrown" | 455 | name = "hashbrown" |
456 | version = "0.9.0" | 456 | version = "0.9.1" |
457 | source = "registry+https://github.com/rust-lang/crates.io-index" | 457 | source = "registry+https://github.com/rust-lang/crates.io-index" |
458 | checksum = "00d63df3d41950fb462ed38308eea019113ad1508da725bbedcd0fa5a85ef5f7" | 458 | checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" |
459 | 459 | ||
460 | [[package]] | 460 | [[package]] |
461 | name = "heck" | 461 | name = "heck" |
@@ -725,9 +725,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" | |||
725 | 725 | ||
726 | [[package]] | 726 | [[package]] |
727 | name = "libc" | 727 | name = "libc" |
728 | version = "0.2.77" | 728 | version = "0.2.78" |
729 | source = "registry+https://github.com/rust-lang/crates.io-index" | 729 | source = "registry+https://github.com/rust-lang/crates.io-index" |
730 | checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235" | 730 | checksum = "aa7087f49d294270db4e1928fc110c976cd4b9e5a16348e0a1df09afa99e6c98" |
731 | 731 | ||
732 | [[package]] | 732 | [[package]] |
733 | name = "libloading" | 733 | name = "libloading" |
@@ -1070,6 +1070,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||
1070 | checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" | 1070 | checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" |
1071 | 1071 | ||
1072 | [[package]] | 1072 | [[package]] |
1073 | name = "pin-project-lite" | ||
1074 | version = "0.1.10" | ||
1075 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1076 | checksum = "e555d9e657502182ac97b539fb3dae8b79cda19e3e4f8ffb5e8de4f18df93c95" | ||
1077 | |||
1078 | [[package]] | ||
1073 | name = "plain" | 1079 | name = "plain" |
1074 | version = "0.2.3" | 1080 | version = "0.2.3" |
1075 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1077,9 +1083,9 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" | |||
1077 | 1083 | ||
1078 | [[package]] | 1084 | [[package]] |
1079 | name = "proc-macro2" | 1085 | name = "proc-macro2" |
1080 | version = "1.0.23" | 1086 | version = "1.0.24" |
1081 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1087 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1082 | checksum = "51ef7cd2518ead700af67bf9d1a658d90b6037d77110fd9c0445429d0ba1c6c9" | 1088 | checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" |
1083 | dependencies = [ | 1089 | dependencies = [ |
1084 | "unicode-xid", | 1090 | "unicode-xid", |
1085 | ] | 1091 | ] |
@@ -1180,9 +1186,9 @@ dependencies = [ | |||
1180 | 1186 | ||
1181 | [[package]] | 1187 | [[package]] |
1182 | name = "rayon" | 1188 | name = "rayon" |
1183 | version = "1.4.0" | 1189 | version = "1.4.1" |
1184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1190 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1185 | checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" | 1191 | checksum = "dcf6960dc9a5b4ee8d3e4c5787b4a112a8818e0290a42ff664ad60692fdf2032" |
1186 | dependencies = [ | 1192 | dependencies = [ |
1187 | "autocfg", | 1193 | "autocfg", |
1188 | "crossbeam-deque", | 1194 | "crossbeam-deque", |
@@ -1294,9 +1300,9 @@ dependencies = [ | |||
1294 | 1300 | ||
1295 | [[package]] | 1301 | [[package]] |
1296 | name = "rustc-ap-rustc_lexer" | 1302 | name = "rustc-ap-rustc_lexer" |
1297 | version = "673.0.0" | 1303 | version = "681.0.0" |
1298 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1304 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1299 | checksum = "f6b71fa1285bdefe5fb61e59b63d6cc246abf337f4acafdd620d721bc488e671" | 1305 | checksum = "01e579a90506e9d9c9a098f380cad55b9ecf9e7be9fa96cb67b31f52045f41a8" |
1300 | dependencies = [ | 1306 | dependencies = [ |
1301 | "unicode-xid", | 1307 | "unicode-xid", |
1302 | ] | 1308 | ] |
@@ -1427,9 +1433,9 @@ dependencies = [ | |||
1427 | 1433 | ||
1428 | [[package]] | 1434 | [[package]] |
1429 | name = "serde_json" | 1435 | name = "serde_json" |
1430 | version = "1.0.57" | 1436 | version = "1.0.58" |
1431 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1437 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1432 | checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" | 1438 | checksum = "a230ea9107ca2220eea9d46de97eddcb04cd00e92d13dda78e478dd33fa82bd4" |
1433 | dependencies = [ | 1439 | dependencies = [ |
1434 | "itoa", | 1440 | "itoa", |
1435 | "ryu", | 1441 | "ryu", |
@@ -1623,11 +1629,12 @@ dependencies = [ | |||
1623 | 1629 | ||
1624 | [[package]] | 1630 | [[package]] |
1625 | name = "tracing" | 1631 | name = "tracing" |
1626 | version = "0.1.19" | 1632 | version = "0.1.21" |
1627 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1633 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1628 | checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" | 1634 | checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" |
1629 | dependencies = [ | 1635 | dependencies = [ |
1630 | "cfg-if", | 1636 | "cfg-if", |
1637 | "pin-project-lite", | ||
1631 | "tracing-attributes", | 1638 | "tracing-attributes", |
1632 | "tracing-core", | 1639 | "tracing-core", |
1633 | ] | 1640 | ] |
@@ -1645,9 +1652,9 @@ dependencies = [ | |||
1645 | 1652 | ||
1646 | [[package]] | 1653 | [[package]] |
1647 | name = "tracing-core" | 1654 | name = "tracing-core" |
1648 | version = "0.1.16" | 1655 | version = "0.1.17" |
1649 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1650 | checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a" | 1657 | checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" |
1651 | dependencies = [ | 1658 | dependencies = [ |
1652 | "lazy_static", | 1659 | "lazy_static", |
1653 | ] | 1660 | ] |
@@ -1696,9 +1703,9 @@ dependencies = [ | |||
1696 | 1703 | ||
1697 | [[package]] | 1704 | [[package]] |
1698 | name = "tracing-tree" | 1705 | name = "tracing-tree" |
1699 | version = "0.1.5" | 1706 | version = "0.1.6" |
1700 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1707 | source = "registry+https://github.com/rust-lang/crates.io-index" |
1701 | checksum = "e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f" | 1708 | checksum = "43aac8afb493b08e1e1904956f7407c1e671b9c83b26a17e1bd83d6a3520e350" |
1702 | dependencies = [ | 1709 | dependencies = [ |
1703 | "ansi_term", | 1710 | "ansi_term", |
1704 | "atty", | 1711 | "atty", |
diff --git a/crates/assists/src/ast_transform.rs b/crates/assists/src/ast_transform.rs index 835da3bb2..4307e0191 100644 --- a/crates/assists/src/ast_transform.rs +++ b/crates/assists/src/ast_transform.rs | |||
@@ -5,12 +5,13 @@ use hir::{HirDisplay, PathResolution, SemanticsScope}; | |||
5 | use syntax::{ | 5 | use syntax::{ |
6 | algo::SyntaxRewriter, | 6 | algo::SyntaxRewriter, |
7 | ast::{self, AstNode}, | 7 | ast::{self, AstNode}, |
8 | SyntaxNode, | ||
8 | }; | 9 | }; |
9 | 10 | ||
10 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | 11 | pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { |
11 | SyntaxRewriter::from_fn(|element| match element { | 12 | SyntaxRewriter::from_fn(|element| match element { |
12 | syntax::SyntaxElement::Node(n) => { | 13 | syntax::SyntaxElement::Node(n) => { |
13 | let replacement = transformer.get_substitution(&n)?; | 14 | let replacement = transformer.get_substitution(&n, transformer)?; |
14 | Some(replacement.into()) | 15 | Some(replacement.into()) |
15 | } | 16 | } |
16 | _ => None, | 17 | _ => None, |
@@ -47,32 +48,35 @@ pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { | |||
47 | /// We'd want to somehow express this concept simpler, but so far nobody got to | 48 | /// We'd want to somehow express this concept simpler, but so far nobody got to |
48 | /// simplifying this! | 49 | /// simplifying this! |
49 | pub trait AstTransform<'a> { | 50 | pub trait AstTransform<'a> { |
50 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode>; | 51 | fn get_substitution( |
52 | &self, | ||
53 | node: &SyntaxNode, | ||
54 | recur: &dyn AstTransform<'a>, | ||
55 | ) -> Option<SyntaxNode>; | ||
51 | 56 | ||
52 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a>; | ||
53 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> | 57 | fn or<T: AstTransform<'a> + 'a>(self, other: T) -> Box<dyn AstTransform<'a> + 'a> |
54 | where | 58 | where |
55 | Self: Sized + 'a, | 59 | Self: Sized + 'a, |
56 | { | 60 | { |
57 | self.chain_before(Box::new(other)) | 61 | Box::new(Or(Box::new(self), Box::new(other))) |
58 | } | 62 | } |
59 | } | 63 | } |
60 | 64 | ||
61 | struct NullTransformer; | 65 | struct Or<'a>(Box<dyn AstTransform<'a> + 'a>, Box<dyn AstTransform<'a> + 'a>); |
62 | 66 | ||
63 | impl<'a> AstTransform<'a> for NullTransformer { | 67 | impl<'a> AstTransform<'a> for Or<'a> { |
64 | fn get_substitution(&self, _node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | 68 | fn get_substitution( |
65 | None | 69 | &self, |
66 | } | 70 | node: &SyntaxNode, |
67 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | 71 | recur: &dyn AstTransform<'a>, |
68 | other | 72 | ) -> Option<SyntaxNode> { |
73 | self.0.get_substitution(node, recur).or_else(|| self.1.get_substitution(node, recur)) | ||
69 | } | 74 | } |
70 | } | 75 | } |
71 | 76 | ||
72 | pub struct SubstituteTypeParams<'a> { | 77 | pub struct SubstituteTypeParams<'a> { |
73 | source_scope: &'a SemanticsScope<'a>, | 78 | source_scope: &'a SemanticsScope<'a>, |
74 | substs: FxHashMap<hir::TypeParam, ast::Type>, | 79 | substs: FxHashMap<hir::TypeParam, ast::Type>, |
75 | previous: Box<dyn AstTransform<'a> + 'a>, | ||
76 | } | 80 | } |
77 | 81 | ||
78 | impl<'a> SubstituteTypeParams<'a> { | 82 | impl<'a> SubstituteTypeParams<'a> { |
@@ -111,11 +115,7 @@ impl<'a> SubstituteTypeParams<'a> { | |||
111 | } | 115 | } |
112 | }) | 116 | }) |
113 | .collect(); | 117 | .collect(); |
114 | return SubstituteTypeParams { | 118 | return SubstituteTypeParams { source_scope, substs: substs_by_param }; |
115 | source_scope, | ||
116 | substs: substs_by_param, | ||
117 | previous: Box::new(NullTransformer), | ||
118 | }; | ||
119 | 119 | ||
120 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the | 120 | // FIXME: It would probably be nicer if we could get this via HIR (i.e. get the |
121 | // trait ref, and then go from the types in the substs back to the syntax). | 121 | // trait ref, and then go from the types in the substs back to the syntax). |
@@ -140,7 +140,14 @@ impl<'a> SubstituteTypeParams<'a> { | |||
140 | Some(result) | 140 | Some(result) |
141 | } | 141 | } |
142 | } | 142 | } |
143 | fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | 143 | } |
144 | |||
145 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | ||
146 | fn get_substitution( | ||
147 | &self, | ||
148 | node: &SyntaxNode, | ||
149 | _recur: &dyn AstTransform<'a>, | ||
150 | ) -> Option<SyntaxNode> { | ||
144 | let type_ref = ast::Type::cast(node.clone())?; | 151 | let type_ref = ast::Type::cast(node.clone())?; |
145 | let path = match &type_ref { | 152 | let path = match &type_ref { |
146 | ast::Type::PathType(path_type) => path_type.path()?, | 153 | ast::Type::PathType(path_type) => path_type.path()?, |
@@ -154,27 +161,23 @@ impl<'a> SubstituteTypeParams<'a> { | |||
154 | } | 161 | } |
155 | } | 162 | } |
156 | 163 | ||
157 | impl<'a> AstTransform<'a> for SubstituteTypeParams<'a> { | ||
158 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | ||
159 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | ||
160 | } | ||
161 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | ||
162 | Box::new(SubstituteTypeParams { previous: other, ..self }) | ||
163 | } | ||
164 | } | ||
165 | |||
166 | pub struct QualifyPaths<'a> { | 164 | pub struct QualifyPaths<'a> { |
167 | target_scope: &'a SemanticsScope<'a>, | 165 | target_scope: &'a SemanticsScope<'a>, |
168 | source_scope: &'a SemanticsScope<'a>, | 166 | source_scope: &'a SemanticsScope<'a>, |
169 | previous: Box<dyn AstTransform<'a> + 'a>, | ||
170 | } | 167 | } |
171 | 168 | ||
172 | impl<'a> QualifyPaths<'a> { | 169 | impl<'a> QualifyPaths<'a> { |
173 | pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { | 170 | pub fn new(target_scope: &'a SemanticsScope<'a>, source_scope: &'a SemanticsScope<'a>) -> Self { |
174 | Self { target_scope, source_scope, previous: Box::new(NullTransformer) } | 171 | Self { target_scope, source_scope } |
175 | } | 172 | } |
173 | } | ||
176 | 174 | ||
177 | fn get_substitution_inner(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | 175 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { |
176 | fn get_substitution( | ||
177 | &self, | ||
178 | node: &SyntaxNode, | ||
179 | recur: &dyn AstTransform<'a>, | ||
180 | ) -> Option<SyntaxNode> { | ||
178 | // FIXME handle value ns? | 181 | // FIXME handle value ns? |
179 | let from = self.target_scope.module()?; | 182 | let from = self.target_scope.module()?; |
180 | let p = ast::Path::cast(node.clone())?; | 183 | let p = ast::Path::cast(node.clone())?; |
@@ -191,7 +194,7 @@ impl<'a> QualifyPaths<'a> { | |||
191 | let type_args = p | 194 | let type_args = p |
192 | .segment() | 195 | .segment() |
193 | .and_then(|s| s.generic_arg_list()) | 196 | .and_then(|s| s.generic_arg_list()) |
194 | .map(|arg_list| apply(self, arg_list)); | 197 | .map(|arg_list| apply(recur, arg_list)); |
195 | if let Some(type_args) = type_args { | 198 | if let Some(type_args) = type_args { |
196 | let last_segment = path.segment().unwrap(); | 199 | let last_segment = path.segment().unwrap(); |
197 | path = path.with_segment(last_segment.with_generic_args(type_args)) | 200 | path = path.with_segment(last_segment.with_generic_args(type_args)) |
@@ -208,15 +211,6 @@ impl<'a> QualifyPaths<'a> { | |||
208 | } | 211 | } |
209 | } | 212 | } |
210 | 213 | ||
211 | impl<'a> AstTransform<'a> for QualifyPaths<'a> { | ||
212 | fn get_substitution(&self, node: &syntax::SyntaxNode) -> Option<syntax::SyntaxNode> { | ||
213 | self.get_substitution_inner(node).or_else(|| self.previous.get_substitution(node)) | ||
214 | } | ||
215 | fn chain_before(self, other: Box<dyn AstTransform<'a> + 'a>) -> Box<dyn AstTransform<'a> + 'a> { | ||
216 | Box::new(QualifyPaths { previous: other, ..self }) | ||
217 | } | ||
218 | } | ||
219 | |||
220 | pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { | 214 | pub(crate) fn path_to_ast(path: hir::ModPath) -> ast::Path { |
221 | let parse = ast::SourceFile::parse(&path.to_string()); | 215 | let parse = ast::SourceFile::parse(&path.to_string()); |
222 | parse | 216 | parse |
diff --git a/crates/assists/src/handlers/add_missing_impl_members.rs b/crates/assists/src/handlers/add_missing_impl_members.rs index 1ac5fefd6..51b5a2eb0 100644 --- a/crates/assists/src/handlers/add_missing_impl_members.rs +++ b/crates/assists/src/handlers/add_missing_impl_members.rs | |||
@@ -146,7 +146,7 @@ fn add_missing_impl_members_inner( | |||
146 | 146 | ||
147 | let target = impl_def.syntax().text_range(); | 147 | let target = impl_def.syntax().text_range(); |
148 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { | 148 | acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| { |
149 | let impl_item_list = impl_def.assoc_item_list().unwrap_or(make::assoc_item_list()); | 149 | let impl_item_list = impl_def.assoc_item_list().unwrap_or_else(make::assoc_item_list); |
150 | 150 | ||
151 | let n_existing_items = impl_item_list.assoc_items().count(); | 151 | let n_existing_items = impl_item_list.assoc_items().count(); |
152 | let source_scope = ctx.sema.scope_for_def(trait_); | 152 | let source_scope = ctx.sema.scope_for_def(trait_); |
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index d1eadaa99..f5f03ef36 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -91,7 +91,7 @@ fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVari | |||
91 | .module(db) | 91 | .module(db) |
92 | .scope(db, None) | 92 | .scope(db, None) |
93 | .into_iter() | 93 | .into_iter() |
94 | .any(|(name, _)| name.to_string() == variant_name.to_string()) | 94 | .any(|(name, _)| name.to_string() == variant_name) |
95 | } | 95 | } |
96 | 96 | ||
97 | #[allow(dead_code)] | 97 | #[allow(dead_code)] |
diff --git a/crates/assists/src/utils/insert_use.rs b/crates/assists/src/utils/insert_use.rs index 5719b06af..f6025c99a 100644 --- a/crates/assists/src/utils/insert_use.rs +++ b/crates/assists/src/utils/insert_use.rs | |||
@@ -4,13 +4,14 @@ use std::{ | |||
4 | iter::{self, successors}, | 4 | iter::{self, successors}, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use ast::{ | 7 | use itertools::{EitherOrBoth, Itertools}; |
8 | edit::{AstNodeEdit, IndentLevel}, | ||
9 | PathSegmentKind, VisibilityOwner, | ||
10 | }; | ||
11 | use syntax::{ | 8 | use syntax::{ |
12 | algo, | 9 | algo, |
13 | ast::{self, make, AstNode}, | 10 | ast::{ |
11 | self, | ||
12 | edit::{AstNodeEdit, IndentLevel}, | ||
13 | make, AstNode, PathSegmentKind, VisibilityOwner, | ||
14 | }, | ||
14 | InsertPosition, SyntaxElement, SyntaxNode, | 15 | InsertPosition, SyntaxElement, SyntaxNode, |
15 | }; | 16 | }; |
16 | 17 | ||
@@ -174,7 +175,7 @@ pub(crate) fn try_merge_trees( | |||
174 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; | 175 | let (lhs_prefix, rhs_prefix) = common_prefix(&lhs_path, &rhs_path)?; |
175 | let lhs = lhs.split_prefix(&lhs_prefix); | 176 | let lhs = lhs.split_prefix(&lhs_prefix); |
176 | let rhs = rhs.split_prefix(&rhs_prefix); | 177 | let rhs = rhs.split_prefix(&rhs_prefix); |
177 | recursive_merge(&lhs, &rhs, merge).map(|(merged, _)| merged) | 178 | recursive_merge(&lhs, &rhs, merge) |
178 | } | 179 | } |
179 | 180 | ||
180 | /// Recursively "zips" together lhs and rhs. | 181 | /// Recursively "zips" together lhs and rhs. |
@@ -182,22 +183,24 @@ fn recursive_merge( | |||
182 | lhs: &ast::UseTree, | 183 | lhs: &ast::UseTree, |
183 | rhs: &ast::UseTree, | 184 | rhs: &ast::UseTree, |
184 | merge: MergeBehaviour, | 185 | merge: MergeBehaviour, |
185 | ) -> Option<(ast::UseTree, bool)> { | 186 | ) -> Option<ast::UseTree> { |
186 | let mut use_trees = lhs | 187 | let mut use_trees = lhs |
187 | .use_tree_list() | 188 | .use_tree_list() |
188 | .into_iter() | 189 | .into_iter() |
189 | .flat_map(|list| list.use_trees()) | 190 | .flat_map(|list| list.use_trees()) |
190 | // check if any of the use trees are nested, if they are and the behaviour is `last` we are not allowed to merge this | 191 | // we use Option here to early return from this function(this is not the same as a `filter` op) |
191 | // so early exit the iterator by using Option's Intoiterator impl | 192 | .map(|tree| match merge.is_tree_allowed(&tree) { |
192 | .map(|tree| match merge == MergeBehaviour::Last && tree.use_tree_list().is_some() { | 193 | true => Some(tree), |
193 | true => None, | 194 | false => None, |
194 | false => Some(tree), | ||
195 | }) | 195 | }) |
196 | .collect::<Option<Vec<_>>>()?; | 196 | .collect::<Option<Vec<_>>>()?; |
197 | use_trees.sort_unstable_by(|a, b| path_cmp_opt(a.path(), b.path())); | 197 | use_trees.sort_unstable_by(|a, b| path_cmp_for_sort(a.path(), b.path())); |
198 | for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { | 198 | for rhs_t in rhs.use_tree_list().into_iter().flat_map(|list| list.use_trees()) { |
199 | if !merge.is_tree_allowed(&rhs_t) { | ||
200 | return None; | ||
201 | } | ||
199 | let rhs_path = rhs_t.path(); | 202 | let rhs_path = rhs_t.path(); |
200 | match use_trees.binary_search_by(|p| path_cmp_opt(p.path(), rhs_path.clone())) { | 203 | match use_trees.binary_search_by(|p| path_cmp_bin_search(p.path(), rhs_path.clone())) { |
201 | Ok(idx) => { | 204 | Ok(idx) => { |
202 | let lhs_t = &mut use_trees[idx]; | 205 | let lhs_t = &mut use_trees[idx]; |
203 | let lhs_path = lhs_t.path()?; | 206 | let lhs_path = lhs_t.path()?; |
@@ -239,17 +242,9 @@ fn recursive_merge( | |||
239 | } | 242 | } |
240 | let lhs = lhs_t.split_prefix(&lhs_prefix); | 243 | let lhs = lhs_t.split_prefix(&lhs_prefix); |
241 | let rhs = rhs_t.split_prefix(&rhs_prefix); | 244 | let rhs = rhs_t.split_prefix(&rhs_prefix); |
242 | let this_has_children = use_trees.len() > 0; | ||
243 | match recursive_merge(&lhs, &rhs, merge) { | 245 | match recursive_merge(&lhs, &rhs, merge) { |
244 | Some((_, has_multiple_children)) | 246 | Some(use_tree) => use_trees[idx] = use_tree, |
245 | if merge == MergeBehaviour::Last | 247 | None => return None, |
246 | && this_has_children | ||
247 | && has_multiple_children => | ||
248 | { | ||
249 | return None | ||
250 | } | ||
251 | Some((use_tree, _)) => use_trees[idx] = use_tree, | ||
252 | None => use_trees.insert(idx, rhs_t), | ||
253 | } | 248 | } |
254 | } | 249 | } |
255 | Err(_) | 250 | Err(_) |
@@ -264,8 +259,7 @@ fn recursive_merge( | |||
264 | } | 259 | } |
265 | } | 260 | } |
266 | } | 261 | } |
267 | let has_multiple_children = use_trees.len() > 1; | 262 | Some(lhs.with_use_tree_list(make::use_tree_list(use_trees))) |
268 | Some((lhs.with_use_tree_list(make::use_tree_list(use_trees)), has_multiple_children)) | ||
269 | } | 263 | } |
270 | 264 | ||
271 | /// Traverses both paths until they differ, returning the common prefix of both. | 265 | /// Traverses both paths until they differ, returning the common prefix of both. |
@@ -308,41 +302,83 @@ fn segment_iter(path: &ast::Path) -> impl Iterator<Item = ast::PathSegment> + Cl | |||
308 | successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) | 302 | successors(first_segment(path), |p| p.parent_path().parent_path().and_then(|p| p.segment())) |
309 | } | 303 | } |
310 | 304 | ||
305 | fn path_len(path: ast::Path) -> usize { | ||
306 | segment_iter(&path).count() | ||
307 | } | ||
308 | |||
311 | /// Orders paths in the following way: | 309 | /// Orders paths in the following way: |
312 | /// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers | 310 | /// the sole self token comes first, after that come uppercase identifiers, then lowercase identifiers |
313 | // FIXME: rustfmt sort lowercase idents before uppercase, in general we want to have the same ordering rustfmt has | 311 | // FIXME: rustfmt sorts lowercase idents before uppercase, in general we want to have the same ordering rustfmt has |
314 | // which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports. | 312 | // which is `self` and `super` first, then identifier imports with lowercase ones first, then glob imports and at last list imports. |
315 | // Example foo::{self, foo, baz, Baz, Qux, *, {Bar}} | 313 | // Example foo::{self, foo, baz, Baz, Qux, *, {Bar}} |
316 | fn path_cmp(a: &ast::Path, b: &ast::Path) -> Ordering { | 314 | fn path_cmp_for_sort(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering { |
317 | match (path_is_self(a), path_is_self(b)) { | 315 | match (a, b) { |
318 | (true, true) => Ordering::Equal, | 316 | (None, None) => Ordering::Equal, |
319 | (true, false) => Ordering::Less, | 317 | (None, Some(_)) => Ordering::Less, |
320 | (false, true) => Ordering::Greater, | 318 | (Some(_), None) => Ordering::Greater, |
321 | (false, false) => { | 319 | (Some(ref a), Some(ref b)) => match (path_is_self(a), path_is_self(b)) { |
322 | let a = segment_iter(a); | 320 | (true, true) => Ordering::Equal, |
323 | let b = segment_iter(b); | 321 | (true, false) => Ordering::Less, |
324 | // cmp_by would be useful for us here but that is currently unstable | 322 | (false, true) => Ordering::Greater, |
325 | // cmp doesnt work due the lifetimes on text's return type | 323 | (false, false) => path_cmp_short(a, b), |
326 | a.zip(b) | 324 | }, |
327 | .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref())) | ||
328 | .find_map(|(a, b)| match a.text().cmp(b.text()) { | ||
329 | ord @ Ordering::Greater | ord @ Ordering::Less => Some(ord), | ||
330 | Ordering::Equal => None, | ||
331 | }) | ||
332 | .unwrap_or(Ordering::Equal) | ||
333 | } | ||
334 | } | 325 | } |
335 | } | 326 | } |
336 | 327 | ||
337 | fn path_cmp_opt(a: Option<ast::Path>, b: Option<ast::Path>) -> Ordering { | 328 | /// Path comparison func for binary searching for merging. |
338 | match (a, b) { | 329 | fn path_cmp_bin_search(lhs: Option<ast::Path>, rhs: Option<ast::Path>) -> Ordering { |
330 | match (lhs, rhs) { | ||
339 | (None, None) => Ordering::Equal, | 331 | (None, None) => Ordering::Equal, |
340 | (None, Some(_)) => Ordering::Less, | 332 | (None, Some(_)) => Ordering::Less, |
341 | (Some(_), None) => Ordering::Greater, | 333 | (Some(_), None) => Ordering::Greater, |
342 | (Some(a), Some(b)) => path_cmp(&a, &b), | 334 | (Some(ref a), Some(ref b)) => path_cmp_short(a, b), |
343 | } | 335 | } |
344 | } | 336 | } |
345 | 337 | ||
338 | /// Short circuiting comparison, if both paths are equal until one of them ends they are considered | ||
339 | /// equal | ||
340 | fn path_cmp_short(a: &ast::Path, b: &ast::Path) -> Ordering { | ||
341 | let a = segment_iter(a); | ||
342 | let b = segment_iter(b); | ||
343 | // cmp_by would be useful for us here but that is currently unstable | ||
344 | // cmp doesnt work due the lifetimes on text's return type | ||
345 | a.zip(b) | ||
346 | .find_map(|(a, b)| match path_segment_cmp(&a, &b) { | ||
347 | Ordering::Equal => None, | ||
348 | ord => Some(ord), | ||
349 | }) | ||
350 | .unwrap_or(Ordering::Equal) | ||
351 | } | ||
352 | |||
353 | /// Compares to paths, if one ends earlier than the other the has_tl parameters decide which is | ||
354 | /// greater as a a path that has a tree list should be greater, while one that just ends without | ||
355 | /// a tree list should be considered less. | ||
356 | fn use_tree_path_cmp(a: &ast::Path, a_has_tl: bool, b: &ast::Path, b_has_tl: bool) -> Ordering { | ||
357 | let a_segments = segment_iter(a); | ||
358 | let b_segments = segment_iter(b); | ||
359 | // cmp_by would be useful for us here but that is currently unstable | ||
360 | // cmp doesnt work due the lifetimes on text's return type | ||
361 | a_segments | ||
362 | .zip_longest(b_segments) | ||
363 | .find_map(|zipped| match zipped { | ||
364 | EitherOrBoth::Both(ref a, ref b) => match path_segment_cmp(a, b) { | ||
365 | Ordering::Equal => None, | ||
366 | ord => Some(ord), | ||
367 | }, | ||
368 | EitherOrBoth::Left(_) if !b_has_tl => Some(Ordering::Greater), | ||
369 | EitherOrBoth::Left(_) => Some(Ordering::Less), | ||
370 | EitherOrBoth::Right(_) if !a_has_tl => Some(Ordering::Less), | ||
371 | EitherOrBoth::Right(_) => Some(Ordering::Greater), | ||
372 | }) | ||
373 | .unwrap_or(Ordering::Equal) | ||
374 | } | ||
375 | |||
376 | fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering { | ||
377 | let a = a.name_ref(); | ||
378 | let b = b.name_ref(); | ||
379 | a.as_ref().map(ast::NameRef::text).cmp(&b.as_ref().map(ast::NameRef::text)) | ||
380 | } | ||
381 | |||
346 | /// What type of merges are allowed. | 382 | /// What type of merges are allowed. |
347 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | 383 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
348 | pub enum MergeBehaviour { | 384 | pub enum MergeBehaviour { |
@@ -352,6 +388,19 @@ pub enum MergeBehaviour { | |||
352 | Last, | 388 | Last, |
353 | } | 389 | } |
354 | 390 | ||
391 | impl MergeBehaviour { | ||
392 | #[inline] | ||
393 | fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool { | ||
394 | match self { | ||
395 | MergeBehaviour::Full => true, | ||
396 | // only simple single segment paths are allowed | ||
397 | MergeBehaviour::Last => { | ||
398 | tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) | ||
399 | } | ||
400 | } | ||
401 | } | ||
402 | } | ||
403 | |||
355 | #[derive(Eq, PartialEq, PartialOrd, Ord)] | 404 | #[derive(Eq, PartialEq, PartialOrd, Ord)] |
356 | enum ImportGroup { | 405 | enum ImportGroup { |
357 | // the order here defines the order of new group inserts | 406 | // the order here defines the order of new group inserts |
@@ -379,7 +428,6 @@ impl ImportGroup { | |||
379 | PathSegmentKind::Name(name) => match name.text().as_str() { | 428 | PathSegmentKind::Name(name) => match name.text().as_str() { |
380 | "std" => ImportGroup::Std, | 429 | "std" => ImportGroup::Std, |
381 | "core" => ImportGroup::Std, | 430 | "core" => ImportGroup::Std, |
382 | // FIXME: can be ThisModule as well | ||
383 | _ => ImportGroup::ExternCrate, | 431 | _ => ImportGroup::ExternCrate, |
384 | }, | 432 | }, |
385 | PathSegmentKind::Type { .. } => unreachable!(), | 433 | PathSegmentKind::Type { .. } => unreachable!(), |
@@ -405,30 +453,30 @@ fn find_insert_position( | |||
405 | .as_syntax_node() | 453 | .as_syntax_node() |
406 | .children() | 454 | .children() |
407 | .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) | 455 | .filter_map(|node| ast::Use::cast(node.clone()).zip(Some(node))) |
408 | .flat_map(|(use_, node)| use_.use_tree().and_then(|tree| tree.path()).zip(Some(node))); | 456 | .flat_map(|(use_, node)| { |
457 | let tree = use_.use_tree()?; | ||
458 | let path = tree.path()?; | ||
459 | let has_tl = tree.use_tree_list().is_some(); | ||
460 | Some((path, has_tl, node)) | ||
461 | }); | ||
409 | // Iterator that discards anything thats not in the required grouping | 462 | // Iterator that discards anything thats not in the required grouping |
410 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits | 463 | // This implementation allows the user to rearrange their import groups as this only takes the first group that fits |
411 | let group_iter = path_node_iter | 464 | let group_iter = path_node_iter |
412 | .clone() | 465 | .clone() |
413 | .skip_while(|(path, _)| ImportGroup::new(path) != group) | 466 | .skip_while(|(path, ..)| ImportGroup::new(path) != group) |
414 | .take_while(|(path, _)| ImportGroup::new(path) == group); | 467 | .take_while(|(path, ..)| ImportGroup::new(path) == group); |
415 | 468 | ||
416 | let segments = segment_iter(&insert_path); | ||
417 | // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place | 469 | // track the last element we iterated over, if this is still None after the iteration then that means we never iterated in the first place |
418 | let mut last = None; | 470 | let mut last = None; |
419 | // find the element that would come directly after our new import | 471 | // find the element that would come directly after our new import |
420 | let post_insert = | 472 | let post_insert = group_iter.inspect(|(.., node)| last = Some(node.clone())).find( |
421 | group_iter.inspect(|(_, node)| last = Some(node.clone())).find(|(path, _)| { | 473 | |&(ref path, has_tl, _)| { |
422 | let check_segments = segment_iter(&path); | 474 | use_tree_path_cmp(&insert_path, false, path, has_tl) != Ordering::Greater |
423 | segments | 475 | }, |
424 | .clone() | 476 | ); |
425 | .zip(check_segments) | ||
426 | .flat_map(|(seg, seg2)| seg.name_ref().zip(seg2.name_ref())) | ||
427 | .all(|(l, r)| l.text() <= r.text()) | ||
428 | }); | ||
429 | match post_insert { | 477 | match post_insert { |
430 | // insert our import before that element | 478 | // insert our import before that element |
431 | Some((_, node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), | 479 | Some((.., node)) => (InsertPosition::Before(node.into()), AddBlankLine::After), |
432 | // there is no element after our new import, so append it to the end of the group | 480 | // there is no element after our new import, so append it to the end of the group |
433 | None => match last { | 481 | None => match last { |
434 | Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before), | 482 | Some(node) => (InsertPosition::After(node.into()), AddBlankLine::Before), |
@@ -438,10 +486,10 @@ fn find_insert_position( | |||
438 | let mut last = None; | 486 | let mut last = None; |
439 | // find the group that comes after where we want to insert | 487 | // find the group that comes after where we want to insert |
440 | let post_group = path_node_iter | 488 | let post_group = path_node_iter |
441 | .inspect(|(_, node)| last = Some(node.clone())) | 489 | .inspect(|(.., node)| last = Some(node.clone())) |
442 | .find(|(p, _)| ImportGroup::new(p) > group); | 490 | .find(|(p, ..)| ImportGroup::new(p) > group); |
443 | match post_group { | 491 | match post_group { |
444 | Some((_, node)) => { | 492 | Some((.., node)) => { |
445 | (InsertPosition::Before(node.into()), AddBlankLine::AfterTwice) | 493 | (InsertPosition::Before(node.into()), AddBlankLine::AfterTwice) |
446 | } | 494 | } |
447 | // there is no such group, so append after the last one | 495 | // there is no such group, so append after the last one |
@@ -676,6 +724,11 @@ use std::io;", | |||
676 | } | 724 | } |
677 | 725 | ||
678 | #[test] | 726 | #[test] |
727 | fn merge_last_into_self() { | ||
728 | check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};"); | ||
729 | } | ||
730 | |||
731 | #[test] | ||
679 | fn merge_groups_full() { | 732 | fn merge_groups_full() { |
680 | check_full( | 733 | check_full( |
681 | "std::io", | 734 | "std::io", |
@@ -819,8 +872,23 @@ use std::io;", | |||
819 | } | 872 | } |
820 | 873 | ||
821 | #[test] | 874 | #[test] |
822 | fn merge_last_too_long() { | 875 | fn skip_merge_last_too_long() { |
823 | check_last("foo::bar", r"use foo::bar::baz::Qux;", r"use foo::bar::{self, baz::Qux};"); | 876 | check_last( |
877 | "foo::bar", | ||
878 | r"use foo::bar::baz::Qux;", | ||
879 | r"use foo::bar; | ||
880 | use foo::bar::baz::Qux;", | ||
881 | ); | ||
882 | } | ||
883 | |||
884 | #[test] | ||
885 | fn skip_merge_last_too_long2() { | ||
886 | check_last( | ||
887 | "foo::bar::baz::Qux", | ||
888 | r"use foo::bar;", | ||
889 | r"use foo::bar; | ||
890 | use foo::bar::baz::Qux;", | ||
891 | ); | ||
824 | } | 892 | } |
825 | 893 | ||
826 | #[test] | 894 | #[test] |
diff --git a/crates/base_db/src/change.rs b/crates/base_db/src/change.rs new file mode 100644 index 000000000..043e03bba --- /dev/null +++ b/crates/base_db/src/change.rs | |||
@@ -0,0 +1,97 @@ | |||
1 | //! Defines a unit of change that can applied to the database to get the next | ||
2 | //! state. Changes are transactional. | ||
3 | |||
4 | use std::{fmt, sync::Arc}; | ||
5 | |||
6 | use rustc_hash::FxHashSet; | ||
7 | use salsa::Durability; | ||
8 | use vfs::FileId; | ||
9 | |||
10 | use crate::{CrateGraph, SourceDatabaseExt, SourceRoot, SourceRootId}; | ||
11 | |||
12 | /// Encapsulate a bunch of raw `.set` calls on the database. | ||
13 | #[derive(Default)] | ||
14 | pub struct Change { | ||
15 | pub roots: Option<Vec<SourceRoot>>, | ||
16 | pub files_changed: Vec<(FileId, Option<Arc<String>>)>, | ||
17 | pub crate_graph: Option<CrateGraph>, | ||
18 | } | ||
19 | |||
20 | impl fmt::Debug for Change { | ||
21 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
22 | let mut d = fmt.debug_struct("AnalysisChange"); | ||
23 | if let Some(roots) = &self.roots { | ||
24 | d.field("roots", roots); | ||
25 | } | ||
26 | if !self.files_changed.is_empty() { | ||
27 | d.field("files_changed", &self.files_changed.len()); | ||
28 | } | ||
29 | if self.crate_graph.is_some() { | ||
30 | d.field("crate_graph", &self.crate_graph); | ||
31 | } | ||
32 | d.finish() | ||
33 | } | ||
34 | } | ||
35 | |||
36 | impl Change { | ||
37 | pub fn new() -> Change { | ||
38 | Change::default() | ||
39 | } | ||
40 | |||
41 | pub fn set_roots(&mut self, roots: Vec<SourceRoot>) { | ||
42 | self.roots = Some(roots); | ||
43 | } | ||
44 | |||
45 | pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) { | ||
46 | self.files_changed.push((file_id, new_text)) | ||
47 | } | ||
48 | |||
49 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { | ||
50 | self.crate_graph = Some(graph); | ||
51 | } | ||
52 | |||
53 | pub fn apply(self, db: &mut dyn SourceDatabaseExt) { | ||
54 | let _p = profile::span("RootDatabase::apply_change"); | ||
55 | // db.request_cancellation(); | ||
56 | // log::info!("apply_change {:?}", change); | ||
57 | if let Some(roots) = self.roots { | ||
58 | let mut local_roots = FxHashSet::default(); | ||
59 | let mut library_roots = FxHashSet::default(); | ||
60 | for (idx, root) in roots.into_iter().enumerate() { | ||
61 | let root_id = SourceRootId(idx as u32); | ||
62 | let durability = durability(&root); | ||
63 | if root.is_library { | ||
64 | library_roots.insert(root_id); | ||
65 | } else { | ||
66 | local_roots.insert(root_id); | ||
67 | } | ||
68 | for file_id in root.iter() { | ||
69 | db.set_file_source_root_with_durability(file_id, root_id, durability); | ||
70 | } | ||
71 | db.set_source_root_with_durability(root_id, Arc::new(root), durability); | ||
72 | } | ||
73 | // db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); | ||
74 | // db.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH); | ||
75 | } | ||
76 | |||
77 | for (file_id, text) in self.files_changed { | ||
78 | let source_root_id = db.file_source_root(file_id); | ||
79 | let source_root = db.source_root(source_root_id); | ||
80 | let durability = durability(&source_root); | ||
81 | // XXX: can't actually remove the file, just reset the text | ||
82 | let text = text.unwrap_or_default(); | ||
83 | db.set_file_text_with_durability(file_id, text, durability) | ||
84 | } | ||
85 | if let Some(crate_graph) = self.crate_graph { | ||
86 | db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | fn durability(source_root: &SourceRoot) -> Durability { | ||
92 | if source_root.is_library { | ||
93 | Durability::HIGH | ||
94 | } else { | ||
95 | Durability::LOW | ||
96 | } | ||
97 | } | ||
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 5ff8ead0e..b7286fc7d 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -65,24 +65,26 @@ use test_utils::{extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER} | |||
65 | use vfs::{file_set::FileSet, VfsPath}; | 65 | use vfs::{file_set::FileSet, VfsPath}; |
66 | 66 | ||
67 | use crate::{ | 67 | use crate::{ |
68 | input::CrateName, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, SourceDatabaseExt, | 68 | input::CrateName, Change, CrateGraph, CrateId, Edition, Env, FileId, FilePosition, |
69 | SourceRoot, SourceRootId, | 69 | SourceDatabaseExt, SourceRoot, SourceRootId, |
70 | }; | 70 | }; |
71 | 71 | ||
72 | pub const WORKSPACE: SourceRootId = SourceRootId(0); | 72 | pub const WORKSPACE: SourceRootId = SourceRootId(0); |
73 | 73 | ||
74 | pub trait WithFixture: Default + SourceDatabaseExt + 'static { | 74 | pub trait WithFixture: Default + SourceDatabaseExt + 'static { |
75 | fn with_single_file(text: &str) -> (Self, FileId) { | 75 | fn with_single_file(text: &str) -> (Self, FileId) { |
76 | let fixture = ChangeFixture::parse(text); | ||
76 | let mut db = Self::default(); | 77 | let mut db = Self::default(); |
77 | let (_, files) = with_files(&mut db, text); | 78 | fixture.change.apply(&mut db); |
78 | assert_eq!(files.len(), 1); | 79 | assert_eq!(fixture.files.len(), 1); |
79 | (db, files[0]) | 80 | (db, fixture.files[0]) |
80 | } | 81 | } |
81 | 82 | ||
82 | fn with_files(ra_fixture: &str) -> Self { | 83 | fn with_files(ra_fixture: &str) -> Self { |
84 | let fixture = ChangeFixture::parse(ra_fixture); | ||
83 | let mut db = Self::default(); | 85 | let mut db = Self::default(); |
84 | let (pos, _) = with_files(&mut db, ra_fixture); | 86 | fixture.change.apply(&mut db); |
85 | assert!(pos.is_none()); | 87 | assert!(fixture.file_position.is_none()); |
86 | db | 88 | db |
87 | } | 89 | } |
88 | 90 | ||
@@ -96,9 +98,10 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
96 | } | 98 | } |
97 | 99 | ||
98 | fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { | 100 | fn with_range_or_offset(ra_fixture: &str) -> (Self, FileId, RangeOrOffset) { |
101 | let fixture = ChangeFixture::parse(ra_fixture); | ||
99 | let mut db = Self::default(); | 102 | let mut db = Self::default(); |
100 | let (pos, _) = with_files(&mut db, ra_fixture); | 103 | fixture.change.apply(&mut db); |
101 | let (file_id, range_or_offset) = pos.unwrap(); | 104 | let (file_id, range_or_offset) = fixture.file_position.unwrap(); |
102 | (db, file_id, range_or_offset) | 105 | (db, file_id, range_or_offset) |
103 | } | 106 | } |
104 | 107 | ||
@@ -113,89 +116,95 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
113 | 116 | ||
114 | impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} | 117 | impl<DB: SourceDatabaseExt + Default + 'static> WithFixture for DB {} |
115 | 118 | ||
116 | fn with_files( | 119 | pub struct ChangeFixture { |
117 | db: &mut dyn SourceDatabaseExt, | 120 | pub file_position: Option<(FileId, RangeOrOffset)>, |
118 | fixture: &str, | 121 | pub files: Vec<FileId>, |
119 | ) -> (Option<(FileId, RangeOrOffset)>, Vec<FileId>) { | 122 | pub change: Change, |
120 | let fixture = Fixture::parse(fixture); | 123 | } |
121 | |||
122 | let mut files = Vec::new(); | ||
123 | let mut crate_graph = CrateGraph::default(); | ||
124 | let mut crates = FxHashMap::default(); | ||
125 | let mut crate_deps = Vec::new(); | ||
126 | let mut default_crate_root: Option<FileId> = None; | ||
127 | |||
128 | let mut file_set = FileSet::default(); | ||
129 | let source_root_id = WORKSPACE; | ||
130 | let source_root_prefix = "/".to_string(); | ||
131 | let mut file_id = FileId(0); | ||
132 | |||
133 | let mut file_position = None; | ||
134 | |||
135 | for entry in fixture { | ||
136 | let text = if entry.text.contains(CURSOR_MARKER) { | ||
137 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
138 | assert!(file_position.is_none()); | ||
139 | file_position = Some((file_id, range_or_offset)); | ||
140 | text.to_string() | ||
141 | } else { | ||
142 | entry.text.clone() | ||
143 | }; | ||
144 | 124 | ||
145 | let meta = FileMeta::from(entry); | 125 | impl ChangeFixture { |
146 | assert!(meta.path.starts_with(&source_root_prefix)); | 126 | pub fn parse(ra_fixture: &str) -> ChangeFixture { |
127 | let fixture = Fixture::parse(ra_fixture); | ||
128 | let mut change = Change::new(); | ||
129 | |||
130 | let mut files = Vec::new(); | ||
131 | let mut crate_graph = CrateGraph::default(); | ||
132 | let mut crates = FxHashMap::default(); | ||
133 | let mut crate_deps = Vec::new(); | ||
134 | let mut default_crate_root: Option<FileId> = None; | ||
135 | let mut default_cfg = CfgOptions::default(); | ||
136 | |||
137 | let mut file_set = FileSet::default(); | ||
138 | let source_root_prefix = "/".to_string(); | ||
139 | let mut file_id = FileId(0); | ||
140 | |||
141 | let mut file_position = None; | ||
142 | |||
143 | for entry in fixture { | ||
144 | let text = if entry.text.contains(CURSOR_MARKER) { | ||
145 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
146 | assert!(file_position.is_none()); | ||
147 | file_position = Some((file_id, range_or_offset)); | ||
148 | text.to_string() | ||
149 | } else { | ||
150 | entry.text.clone() | ||
151 | }; | ||
152 | |||
153 | let meta = FileMeta::from(entry); | ||
154 | assert!(meta.path.starts_with(&source_root_prefix)); | ||
155 | |||
156 | if let Some(krate) = meta.krate { | ||
157 | let crate_id = crate_graph.add_crate_root( | ||
158 | file_id, | ||
159 | meta.edition, | ||
160 | Some(krate.clone()), | ||
161 | meta.cfg, | ||
162 | meta.env, | ||
163 | Default::default(), | ||
164 | ); | ||
165 | let crate_name = CrateName::new(&krate).unwrap(); | ||
166 | let prev = crates.insert(crate_name.clone(), crate_id); | ||
167 | assert!(prev.is_none()); | ||
168 | for dep in meta.deps { | ||
169 | let dep = CrateName::new(&dep).unwrap(); | ||
170 | crate_deps.push((crate_name.clone(), dep)) | ||
171 | } | ||
172 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | ||
173 | assert!(default_crate_root.is_none()); | ||
174 | default_crate_root = Some(file_id); | ||
175 | default_cfg = meta.cfg; | ||
176 | } | ||
177 | |||
178 | change.change_file(file_id, Some(Arc::new(text))); | ||
179 | let path = VfsPath::new_virtual_path(meta.path); | ||
180 | file_set.insert(file_id, path.into()); | ||
181 | files.push(file_id); | ||
182 | file_id.0 += 1; | ||
183 | } | ||
147 | 184 | ||
148 | if let Some(krate) = meta.krate { | 185 | if crates.is_empty() { |
149 | let crate_id = crate_graph.add_crate_root( | 186 | let crate_root = default_crate_root.unwrap(); |
150 | file_id, | 187 | crate_graph.add_crate_root( |
151 | meta.edition, | 188 | crate_root, |
152 | Some(krate.clone()), | 189 | Edition::Edition2018, |
153 | meta.cfg, | 190 | Some("test".to_string()), |
154 | meta.env, | 191 | default_cfg, |
192 | Env::default(), | ||
155 | Default::default(), | 193 | Default::default(), |
156 | ); | 194 | ); |
157 | let crate_name = CrateName::new(&krate).unwrap(); | 195 | } else { |
158 | let prev = crates.insert(crate_name.clone(), crate_id); | 196 | for (from, to) in crate_deps { |
159 | assert!(prev.is_none()); | 197 | let from_id = crates[&from]; |
160 | for dep in meta.deps { | 198 | let to_id = crates[&to]; |
161 | let dep = CrateName::new(&dep).unwrap(); | 199 | crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); |
162 | crate_deps.push((crate_name.clone(), dep)) | ||
163 | } | 200 | } |
164 | } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { | ||
165 | assert!(default_crate_root.is_none()); | ||
166 | default_crate_root = Some(file_id); | ||
167 | } | 201 | } |
168 | 202 | ||
169 | db.set_file_text(file_id, Arc::new(text)); | 203 | change.set_roots(vec![SourceRoot::new_local(file_set)]); |
170 | db.set_file_source_root(file_id, source_root_id); | 204 | change.set_crate_graph(crate_graph); |
171 | let path = VfsPath::new_virtual_path(meta.path); | ||
172 | file_set.insert(file_id, path.into()); | ||
173 | files.push(file_id); | ||
174 | file_id.0 += 1; | ||
175 | } | ||
176 | 205 | ||
177 | if crates.is_empty() { | 206 | ChangeFixture { file_position, files, change } |
178 | let crate_root = default_crate_root.unwrap(); | ||
179 | crate_graph.add_crate_root( | ||
180 | crate_root, | ||
181 | Edition::Edition2018, | ||
182 | None, | ||
183 | CfgOptions::default(), | ||
184 | Env::default(), | ||
185 | Default::default(), | ||
186 | ); | ||
187 | } else { | ||
188 | for (from, to) in crate_deps { | ||
189 | let from_id = crates[&from]; | ||
190 | let to_id = crates[&to]; | ||
191 | crate_graph.add_dep(from_id, CrateName::new(&to).unwrap(), to_id).unwrap(); | ||
192 | } | ||
193 | } | 207 | } |
194 | |||
195 | db.set_source_root(source_root_id, Arc::new(SourceRoot::new_local(file_set))); | ||
196 | db.set_crate_graph(Arc::new(crate_graph)); | ||
197 | |||
198 | (file_position, files) | ||
199 | } | 208 | } |
200 | 209 | ||
201 | struct FileMeta { | 210 | struct FileMeta { |
diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index ee3415850..e38aa7257 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! base_db defines basic database traits. The concrete DB is defined by ide. | 1 | //! base_db defines basic database traits. The concrete DB is defined by ide. |
2 | mod cancellation; | 2 | mod cancellation; |
3 | mod input; | 3 | mod input; |
4 | mod change; | ||
4 | pub mod fixture; | 5 | pub mod fixture; |
5 | 6 | ||
6 | use std::{panic, sync::Arc}; | 7 | use std::{panic, sync::Arc}; |
@@ -10,6 +11,7 @@ use syntax::{ast, Parse, SourceFile, TextRange, TextSize}; | |||
10 | 11 | ||
11 | pub use crate::{ | 12 | pub use crate::{ |
12 | cancellation::Canceled, | 13 | cancellation::Canceled, |
14 | change::Change, | ||
13 | input::{ | 15 | input::{ |
14 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, | 16 | CrateData, CrateGraph, CrateId, CrateName, Dependency, Edition, Env, FileId, ProcMacroId, |
15 | SourceRoot, SourceRootId, | 17 | SourceRoot, SourceRootId, |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 567fd91af..5721a66c4 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -145,7 +145,7 @@ impl Crate { | |||
145 | } | 145 | } |
146 | }).flat_map(|t| t).next(); | 146 | }).flat_map(|t| t).next(); |
147 | 147 | ||
148 | doc_url.map(|s| s.trim_matches('"').trim_end_matches("/").to_owned() + "/") | 148 | doc_url.map(|s| s.trim_matches('"').trim_end_matches('/').to_owned() + "/") |
149 | } | 149 | } |
150 | } | 150 | } |
151 | 151 | ||
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index 100e25ffc..c8cd04264 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -1229,9 +1229,10 @@ impl ModCollector<'_, '_> { | |||
1229 | } else { | 1229 | } else { |
1230 | let derive = attrs.by_key("proc_macro_derive"); | 1230 | let derive = attrs.by_key("proc_macro_derive"); |
1231 | if let Some(arg) = derive.tt_values().next() { | 1231 | if let Some(arg) = derive.tt_values().next() { |
1232 | if let [TokenTree::Leaf(Leaf::Ident(trait_name))] = &*arg.token_trees { | 1232 | if let [TokenTree::Leaf(Leaf::Ident(trait_name)), ..] = &*arg.token_trees { |
1233 | trait_name.as_name() | 1233 | trait_name.as_name() |
1234 | } else { | 1234 | } else { |
1235 | log::trace!("malformed `#[proc_macro_derive]`: {}", arg); | ||
1235 | return; | 1236 | return; |
1236 | } | 1237 | } |
1237 | } else { | 1238 | } else { |
diff --git a/crates/hir_def/src/nameres/tests/macros.rs b/crates/hir_def/src/nameres/tests/macros.rs index 0851c3b7d..305fca0f9 100644 --- a/crates/hir_def/src/nameres/tests/macros.rs +++ b/crates/hir_def/src/nameres/tests/macros.rs | |||
@@ -688,13 +688,20 @@ fn resolves_proc_macros() { | |||
688 | pub fn derive_macro(_item: TokenStream) -> TokenStream { | 688 | pub fn derive_macro(_item: TokenStream) -> TokenStream { |
689 | TokenStream | 689 | TokenStream |
690 | } | 690 | } |
691 | |||
692 | #[proc_macro_derive(AnotherTrait, attributes(helper_attr))] | ||
693 | pub fn derive_macro_2(_item: TokenStream) -> TokenStream { | ||
694 | TokenStream | ||
695 | } | ||
691 | ", | 696 | ", |
692 | expect![[r#" | 697 | expect![[r#" |
693 | crate | 698 | crate |
699 | AnotherTrait: m | ||
694 | DummyTrait: m | 700 | DummyTrait: m |
695 | TokenStream: t v | 701 | TokenStream: t v |
696 | attribute_macro: v m | 702 | attribute_macro: v m |
697 | derive_macro: v | 703 | derive_macro: v |
704 | derive_macro_2: v | ||
698 | function_like_macro: v m | 705 | function_like_macro: v m |
699 | "#]], | 706 | "#]], |
700 | ); | 707 | ); |
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 58e26b94c..d2cf2cc7d 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs | |||
@@ -139,7 +139,7 @@ impl CallLocations { | |||
139 | mod tests { | 139 | mod tests { |
140 | use base_db::FilePosition; | 140 | use base_db::FilePosition; |
141 | 141 | ||
142 | use crate::mock_analysis::analysis_and_position; | 142 | use crate::fixture; |
143 | 143 | ||
144 | fn check_hierarchy( | 144 | fn check_hierarchy( |
145 | ra_fixture: &str, | 145 | ra_fixture: &str, |
@@ -147,7 +147,7 @@ mod tests { | |||
147 | expected_incoming: &[&str], | 147 | expected_incoming: &[&str], |
148 | expected_outgoing: &[&str], | 148 | expected_outgoing: &[&str], |
149 | ) { | 149 | ) { |
150 | let (analysis, pos) = analysis_and_position(ra_fixture); | 150 | let (analysis, pos) = fixture::position(ra_fixture); |
151 | 151 | ||
152 | let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; | 152 | let mut navs = analysis.call_hierarchy(pos).unwrap().unwrap().info; |
153 | assert_eq!(navs.len(), 1); | 153 | assert_eq!(navs.len(), 1); |
@@ -181,8 +181,8 @@ fn caller() { | |||
181 | call<|>ee(); | 181 | call<|>ee(); |
182 | } | 182 | } |
183 | "#, | 183 | "#, |
184 | "callee FN FileId(1) 0..14 3..9", | 184 | "callee FN FileId(0) 0..14 3..9", |
185 | &["caller FN FileId(1) 15..44 18..24 : [33..39]"], | 185 | &["caller FN FileId(0) 15..44 18..24 : [33..39]"], |
186 | &[], | 186 | &[], |
187 | ); | 187 | ); |
188 | } | 188 | } |
@@ -197,8 +197,8 @@ fn caller() { | |||
197 | callee(); | 197 | callee(); |
198 | } | 198 | } |
199 | "#, | 199 | "#, |
200 | "callee FN FileId(1) 0..14 3..9", | 200 | "callee FN FileId(0) 0..14 3..9", |
201 | &["caller FN FileId(1) 15..44 18..24 : [33..39]"], | 201 | &["caller FN FileId(0) 15..44 18..24 : [33..39]"], |
202 | &[], | 202 | &[], |
203 | ); | 203 | ); |
204 | } | 204 | } |
@@ -214,8 +214,8 @@ fn caller() { | |||
214 | callee(); | 214 | callee(); |
215 | } | 215 | } |
216 | "#, | 216 | "#, |
217 | "callee FN FileId(1) 0..14 3..9", | 217 | "callee FN FileId(0) 0..14 3..9", |
218 | &["caller FN FileId(1) 15..58 18..24 : [33..39, 47..53]"], | 218 | &["caller FN FileId(0) 15..58 18..24 : [33..39, 47..53]"], |
219 | &[], | 219 | &[], |
220 | ); | 220 | ); |
221 | } | 221 | } |
@@ -234,10 +234,10 @@ fn caller2() { | |||
234 | callee(); | 234 | callee(); |
235 | } | 235 | } |
236 | "#, | 236 | "#, |
237 | "callee FN FileId(1) 0..14 3..9", | 237 | "callee FN FileId(0) 0..14 3..9", |
238 | &[ | 238 | &[ |
239 | "caller1 FN FileId(1) 15..45 18..25 : [34..40]", | 239 | "caller1 FN FileId(0) 15..45 18..25 : [34..40]", |
240 | "caller2 FN FileId(1) 47..77 50..57 : [66..72]", | 240 | "caller2 FN FileId(0) 47..77 50..57 : [66..72]", |
241 | ], | 241 | ], |
242 | &[], | 242 | &[], |
243 | ); | 243 | ); |
@@ -263,10 +263,10 @@ mod tests { | |||
263 | } | 263 | } |
264 | } | 264 | } |
265 | "#, | 265 | "#, |
266 | "callee FN FileId(1) 0..14 3..9", | 266 | "callee FN FileId(0) 0..14 3..9", |
267 | &[ | 267 | &[ |
268 | "caller1 FN FileId(1) 15..45 18..25 : [34..40]", | 268 | "caller1 FN FileId(0) 15..45 18..25 : [34..40]", |
269 | "test_caller FN FileId(1) 95..149 110..121 : [134..140]", | 269 | "test_caller FN FileId(0) 95..149 110..121 : [134..140]", |
270 | ], | 270 | ], |
271 | &[], | 271 | &[], |
272 | ); | 272 | ); |
@@ -287,8 +287,8 @@ fn caller() { | |||
287 | //- /foo/mod.rs | 287 | //- /foo/mod.rs |
288 | pub fn callee() {} | 288 | pub fn callee() {} |
289 | "#, | 289 | "#, |
290 | "callee FN FileId(2) 0..18 7..13", | 290 | "callee FN FileId(1) 0..18 7..13", |
291 | &["caller FN FileId(1) 27..56 30..36 : [45..51]"], | 291 | &["caller FN FileId(0) 27..56 30..36 : [45..51]"], |
292 | &[], | 292 | &[], |
293 | ); | 293 | ); |
294 | } | 294 | } |
@@ -304,9 +304,9 @@ fn call<|>er() { | |||
304 | callee(); | 304 | callee(); |
305 | } | 305 | } |
306 | "#, | 306 | "#, |
307 | "caller FN FileId(1) 15..58 18..24", | 307 | "caller FN FileId(0) 15..58 18..24", |
308 | &[], | 308 | &[], |
309 | &["callee FN FileId(1) 0..14 3..9 : [33..39, 47..53]"], | 309 | &["callee FN FileId(0) 0..14 3..9 : [33..39, 47..53]"], |
310 | ); | 310 | ); |
311 | } | 311 | } |
312 | 312 | ||
@@ -325,9 +325,9 @@ fn call<|>er() { | |||
325 | //- /foo/mod.rs | 325 | //- /foo/mod.rs |
326 | pub fn callee() {} | 326 | pub fn callee() {} |
327 | "#, | 327 | "#, |
328 | "caller FN FileId(1) 27..56 30..36", | 328 | "caller FN FileId(0) 27..56 30..36", |
329 | &[], | 329 | &[], |
330 | &["callee FN FileId(2) 0..18 7..13 : [45..51]"], | 330 | &["callee FN FileId(1) 0..18 7..13 : [45..51]"], |
331 | ); | 331 | ); |
332 | } | 332 | } |
333 | 333 | ||
@@ -348,9 +348,9 @@ fn caller3() { | |||
348 | 348 | ||
349 | } | 349 | } |
350 | "#, | 350 | "#, |
351 | "caller2 FN FileId(1) 33..64 36..43", | 351 | "caller2 FN FileId(0) 33..64 36..43", |
352 | &["caller1 FN FileId(1) 0..31 3..10 : [19..26]"], | 352 | &["caller1 FN FileId(0) 0..31 3..10 : [19..26]"], |
353 | &["caller3 FN FileId(1) 66..83 69..76 : [52..59]"], | 353 | &["caller3 FN FileId(0) 66..83 69..76 : [52..59]"], |
354 | ); | 354 | ); |
355 | } | 355 | } |
356 | 356 | ||
@@ -368,9 +368,9 @@ fn main() { | |||
368 | a<|>() | 368 | a<|>() |
369 | } | 369 | } |
370 | "#, | 370 | "#, |
371 | "a FN FileId(1) 0..18 3..4", | 371 | "a FN FileId(0) 0..18 3..4", |
372 | &["main FN FileId(1) 31..52 34..38 : [47..48]"], | 372 | &["main FN FileId(0) 31..52 34..38 : [47..48]"], |
373 | &["b FN FileId(1) 20..29 23..24 : [13..14]"], | 373 | &["b FN FileId(0) 20..29 23..24 : [13..14]"], |
374 | ); | 374 | ); |
375 | 375 | ||
376 | check_hierarchy( | 376 | check_hierarchy( |
@@ -385,8 +385,8 @@ fn main() { | |||
385 | a() | 385 | a() |
386 | } | 386 | } |
387 | "#, | 387 | "#, |
388 | "b FN FileId(1) 20..29 23..24", | 388 | "b FN FileId(0) 20..29 23..24", |
389 | &["a FN FileId(1) 0..18 3..4 : [13..14]"], | 389 | &["a FN FileId(0) 0..18 3..4 : [13..14]"], |
390 | &[], | 390 | &[], |
391 | ); | 391 | ); |
392 | } | 392 | } |
diff --git a/crates/ide/src/call_info.rs b/crates/ide/src/call_info.rs index 7e99c6b72..d7b2b926e 100644 --- a/crates/ide/src/call_info.rs +++ b/crates/ide/src/call_info.rs | |||
@@ -232,10 +232,10 @@ mod tests { | |||
232 | use expect_test::{expect, Expect}; | 232 | use expect_test::{expect, Expect}; |
233 | use test_utils::mark; | 233 | use test_utils::mark; |
234 | 234 | ||
235 | use crate::mock_analysis::analysis_and_position; | 235 | use crate::fixture; |
236 | 236 | ||
237 | fn check(ra_fixture: &str, expect: Expect) { | 237 | fn check(ra_fixture: &str, expect: Expect) { |
238 | let (analysis, position) = analysis_and_position(ra_fixture); | 238 | let (analysis, position) = fixture::position(ra_fixture); |
239 | let call_info = analysis.call_info(position).unwrap(); | 239 | let call_info = analysis.call_info(position).unwrap(); |
240 | let actual = match call_info { | 240 | let actual = match call_info { |
241 | Some(call_info) => { | 241 | Some(call_info) => { |
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs index daea2aa95..697f691b0 100644 --- a/crates/ide/src/completion.rs +++ b/crates/ide/src/completion.rs | |||
@@ -133,7 +133,7 @@ pub(crate) fn completions( | |||
133 | #[cfg(test)] | 133 | #[cfg(test)] |
134 | mod tests { | 134 | mod tests { |
135 | use crate::completion::completion_config::CompletionConfig; | 135 | use crate::completion::completion_config::CompletionConfig; |
136 | use crate::mock_analysis::analysis_and_position; | 136 | use crate::fixture; |
137 | 137 | ||
138 | struct DetailAndDocumentation<'a> { | 138 | struct DetailAndDocumentation<'a> { |
139 | detail: &'a str, | 139 | detail: &'a str, |
@@ -141,7 +141,7 @@ mod tests { | |||
141 | } | 141 | } |
142 | 142 | ||
143 | fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { | 143 | fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) { |
144 | let (analysis, position) = analysis_and_position(ra_fixture); | 144 | let (analysis, position) = fixture::position(ra_fixture); |
145 | let config = CompletionConfig::default(); | 145 | let config = CompletionConfig::default(); |
146 | let completions = analysis.completions(&config, position).unwrap().unwrap(); | 146 | let completions = analysis.completions(&config, position).unwrap().unwrap(); |
147 | for item in completions { | 147 | for item in completions { |
diff --git a/crates/ide/src/completion/complete_keyword.rs b/crates/ide/src/completion/complete_keyword.rs index 5645b41fa..e59747095 100644 --- a/crates/ide/src/completion/complete_keyword.rs +++ b/crates/ide/src/completion/complete_keyword.rs | |||
@@ -495,13 +495,13 @@ Some multi-line comment<|> | |||
495 | fn test_completion_await_impls_future() { | 495 | fn test_completion_await_impls_future() { |
496 | check( | 496 | check( |
497 | r#" | 497 | r#" |
498 | //- /main.rs | 498 | //- /main.rs crate:main deps:std |
499 | use std::future::*; | 499 | use std::future::*; |
500 | struct A {} | 500 | struct A {} |
501 | impl Future for A {} | 501 | impl Future for A {} |
502 | fn foo(a: A) { a.<|> } | 502 | fn foo(a: A) { a.<|> } |
503 | 503 | ||
504 | //- /std/lib.rs | 504 | //- /std/lib.rs crate:std |
505 | pub mod future { | 505 | pub mod future { |
506 | #[lang = "future_trait"] | 506 | #[lang = "future_trait"] |
507 | pub trait Future {} | 507 | pub trait Future {} |
@@ -514,14 +514,14 @@ pub mod future { | |||
514 | 514 | ||
515 | check( | 515 | check( |
516 | r#" | 516 | r#" |
517 | //- /main.rs | 517 | //- /main.rs crate:main deps:std |
518 | use std::future::*; | 518 | use std::future::*; |
519 | fn foo() { | 519 | fn foo() { |
520 | let a = async {}; | 520 | let a = async {}; |
521 | a.<|> | 521 | a.<|> |
522 | } | 522 | } |
523 | 523 | ||
524 | //- /std/lib.rs | 524 | //- /std/lib.rs crate:std |
525 | pub mod future { | 525 | pub mod future { |
526 | #[lang = "future_trait"] | 526 | #[lang = "future_trait"] |
527 | pub trait Future { | 527 | pub trait Future { |
diff --git a/crates/ide/src/completion/complete_mod.rs b/crates/ide/src/completion/complete_mod.rs index 3cfc2e131..c7a99bdc3 100644 --- a/crates/ide/src/completion/complete_mod.rs +++ b/crates/ide/src/completion/complete_mod.rs | |||
@@ -300,7 +300,7 @@ mod tests { | |||
300 | // "#, | 300 | // "#, |
301 | // expect![[r#" | 301 | // expect![[r#" |
302 | // md bar; | 302 | // md bar; |
303 | // "#]], | 303 | // "#]],foo |
304 | // ); | 304 | // ); |
305 | // } | 305 | // } |
306 | 306 | ||
@@ -308,7 +308,7 @@ mod tests { | |||
308 | fn already_declared_bin_module_completion_omitted() { | 308 | fn already_declared_bin_module_completion_omitted() { |
309 | check( | 309 | check( |
310 | r#" | 310 | r#" |
311 | //- /src/bin.rs | 311 | //- /src/bin.rs crate:main |
312 | fn main() {} | 312 | fn main() {} |
313 | //- /src/bin/foo.rs | 313 | //- /src/bin/foo.rs |
314 | mod <|> | 314 | mod <|> |
diff --git a/crates/ide/src/completion/complete_postfix.rs b/crates/ide/src/completion/complete_postfix.rs index 26a5af5b9..db5319618 100644 --- a/crates/ide/src/completion/complete_postfix.rs +++ b/crates/ide/src/completion/complete_postfix.rs | |||
@@ -1,11 +1,15 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | |||
3 | mod format_like; | ||
4 | |||
2 | use assists::utils::TryEnum; | 5 | use assists::utils::TryEnum; |
3 | use syntax::{ | 6 | use syntax::{ |
4 | ast::{self, AstNode}, | 7 | ast::{self, AstNode, AstToken}, |
5 | TextRange, TextSize, | 8 | TextRange, TextSize, |
6 | }; | 9 | }; |
7 | use text_edit::TextEdit; | 10 | use text_edit::TextEdit; |
8 | 11 | ||
12 | use self::format_like::add_format_like_completions; | ||
9 | use crate::{ | 13 | use crate::{ |
10 | completion::{ | 14 | completion::{ |
11 | completion_config::SnippetCap, | 15 | completion_config::SnippetCap, |
@@ -207,6 +211,12 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
207 | &format!("${{1}}({})", receiver_text), | 211 | &format!("${{1}}({})", receiver_text), |
208 | ) | 212 | ) |
209 | .add_to(acc); | 213 | .add_to(acc); |
214 | |||
215 | if let ast::Expr::Literal(literal) = dot_receiver.clone() { | ||
216 | if let Some(literal_text) = ast::String::cast(literal.token()) { | ||
217 | add_format_like_completions(acc, ctx, &dot_receiver, cap, &literal_text); | ||
218 | } | ||
219 | } | ||
210 | } | 220 | } |
211 | 221 | ||
212 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { | 222 | fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String { |
@@ -392,4 +402,53 @@ fn main() { | |||
392 | check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#); | 402 | check_edit("dbg", r#"fn main() { &&42.<|> }"#, r#"fn main() { dbg!(&&42) }"#); |
393 | check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#); | 403 | check_edit("refm", r#"fn main() { &&42.<|> }"#, r#"fn main() { &&&mut 42 }"#); |
394 | } | 404 | } |
405 | |||
406 | #[test] | ||
407 | fn postfix_completion_for_format_like_strings() { | ||
408 | check_edit( | ||
409 | "fmt", | ||
410 | r#"fn main() { "{some_var:?}".<|> }"#, | ||
411 | r#"fn main() { format!("{:?}", some_var) }"#, | ||
412 | ); | ||
413 | check_edit( | ||
414 | "panic", | ||
415 | r#"fn main() { "Panic with {a}".<|> }"#, | ||
416 | r#"fn main() { panic!("Panic with {}", a) }"#, | ||
417 | ); | ||
418 | check_edit( | ||
419 | "println", | ||
420 | r#"fn main() { "{ 2+2 } { SomeStruct { val: 1, other: 32 } :?}".<|> }"#, | ||
421 | r#"fn main() { println!("{} {:?}", 2+2, SomeStruct { val: 1, other: 32 }) }"#, | ||
422 | ); | ||
423 | check_edit( | ||
424 | "loge", | ||
425 | r#"fn main() { "{2+2}".<|> }"#, | ||
426 | r#"fn main() { log::error!("{}", 2+2) }"#, | ||
427 | ); | ||
428 | check_edit( | ||
429 | "logt", | ||
430 | r#"fn main() { "{2+2}".<|> }"#, | ||
431 | r#"fn main() { log::trace!("{}", 2+2) }"#, | ||
432 | ); | ||
433 | check_edit( | ||
434 | "logd", | ||
435 | r#"fn main() { "{2+2}".<|> }"#, | ||
436 | r#"fn main() { log::debug!("{}", 2+2) }"#, | ||
437 | ); | ||
438 | check_edit( | ||
439 | "logi", | ||
440 | r#"fn main() { "{2+2}".<|> }"#, | ||
441 | r#"fn main() { log::info!("{}", 2+2) }"#, | ||
442 | ); | ||
443 | check_edit( | ||
444 | "logw", | ||
445 | r#"fn main() { "{2+2}".<|> }"#, | ||
446 | r#"fn main() { log::warn!("{}", 2+2) }"#, | ||
447 | ); | ||
448 | check_edit( | ||
449 | "loge", | ||
450 | r#"fn main() { "{2+2}".<|> }"#, | ||
451 | r#"fn main() { log::error!("{}", 2+2) }"#, | ||
452 | ); | ||
453 | } | ||
395 | } | 454 | } |
diff --git a/crates/ide/src/completion/complete_postfix/format_like.rs b/crates/ide/src/completion/complete_postfix/format_like.rs new file mode 100644 index 000000000..81c33bf3a --- /dev/null +++ b/crates/ide/src/completion/complete_postfix/format_like.rs | |||
@@ -0,0 +1,277 @@ | |||
1 | // Feature: Format String Completion. | ||
2 | // | ||
3 | // `"Result {result} is {2 + 2}"` is expanded to the `"Result {} is {}", result, 2 + 2`. | ||
4 | // | ||
5 | // The following postfix snippets are available: | ||
6 | // | ||
7 | // - `format` -> `format!(...)` | ||
8 | // - `panic` -> `panic!(...)` | ||
9 | // - `println` -> `println!(...)` | ||
10 | // - `log`: | ||
11 | // + `logd` -> `log::debug!(...)` | ||
12 | // + `logt` -> `log::trace!(...)` | ||
13 | // + `logi` -> `log::info!(...)` | ||
14 | // + `logw` -> `log::warn!(...)` | ||
15 | // + `loge` -> `log::error!(...)` | ||
16 | |||
17 | use crate::completion::{ | ||
18 | complete_postfix::postfix_snippet, completion_config::SnippetCap, | ||
19 | completion_context::CompletionContext, completion_item::Completions, | ||
20 | }; | ||
21 | use syntax::ast::{self, AstToken}; | ||
22 | |||
23 | /// Mapping ("postfix completion item" => "macro to use") | ||
24 | static KINDS: &[(&str, &str)] = &[ | ||
25 | ("fmt", "format!"), | ||
26 | ("panic", "panic!"), | ||
27 | ("println", "println!"), | ||
28 | ("logd", "log::debug!"), | ||
29 | ("logt", "log::trace!"), | ||
30 | ("logi", "log::info!"), | ||
31 | ("logw", "log::warn!"), | ||
32 | ("loge", "log::error!"), | ||
33 | ]; | ||
34 | |||
35 | pub(super) fn add_format_like_completions( | ||
36 | acc: &mut Completions, | ||
37 | ctx: &CompletionContext, | ||
38 | dot_receiver: &ast::Expr, | ||
39 | cap: SnippetCap, | ||
40 | receiver_text: &ast::String, | ||
41 | ) { | ||
42 | let input = match string_literal_contents(receiver_text) { | ||
43 | // It's not a string literal, do not parse input. | ||
44 | Some(input) => input, | ||
45 | None => return, | ||
46 | }; | ||
47 | |||
48 | let mut parser = FormatStrParser::new(input); | ||
49 | |||
50 | if parser.parse().is_ok() { | ||
51 | for (label, macro_name) in KINDS { | ||
52 | let snippet = parser.into_suggestion(macro_name); | ||
53 | |||
54 | postfix_snippet(ctx, cap, &dot_receiver, label, macro_name, &snippet).add_to(acc); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | |||
59 | /// Checks whether provided item is a string literal. | ||
60 | fn string_literal_contents(item: &ast::String) -> Option<String> { | ||
61 | let item = item.text(); | ||
62 | if item.len() >= 2 && item.starts_with("\"") && item.ends_with("\"") { | ||
63 | return Some(item[1..item.len() - 1].to_owned()); | ||
64 | } | ||
65 | |||
66 | None | ||
67 | } | ||
68 | |||
69 | /// Parser for a format-like string. It is more allowing in terms of string contents, | ||
70 | /// as we expect variable placeholders to be filled with expressions. | ||
71 | #[derive(Debug)] | ||
72 | pub struct FormatStrParser { | ||
73 | input: String, | ||
74 | output: String, | ||
75 | extracted_expressions: Vec<String>, | ||
76 | state: State, | ||
77 | parsed: bool, | ||
78 | } | ||
79 | |||
80 | #[derive(Debug, Clone, Copy, PartialEq)] | ||
81 | enum State { | ||
82 | NotExpr, | ||
83 | MaybeExpr, | ||
84 | Expr, | ||
85 | MaybeIncorrect, | ||
86 | FormatOpts, | ||
87 | } | ||
88 | |||
89 | impl FormatStrParser { | ||
90 | pub fn new(input: String) -> Self { | ||
91 | Self { | ||
92 | input: input.into(), | ||
93 | output: String::new(), | ||
94 | extracted_expressions: Vec::new(), | ||
95 | state: State::NotExpr, | ||
96 | parsed: false, | ||
97 | } | ||
98 | } | ||
99 | |||
100 | pub fn parse(&mut self) -> Result<(), ()> { | ||
101 | let mut current_expr = String::new(); | ||
102 | |||
103 | let mut placeholder_id = 1; | ||
104 | |||
105 | // Count of open braces inside of an expression. | ||
106 | // We assume that user knows what they're doing, thus we treat it like a correct pattern, e.g. | ||
107 | // "{MyStruct { val_a: 0, val_b: 1 }}". | ||
108 | let mut inexpr_open_count = 0; | ||
109 | |||
110 | for chr in self.input.chars() { | ||
111 | match (self.state, chr) { | ||
112 | (State::NotExpr, '{') => { | ||
113 | self.output.push(chr); | ||
114 | self.state = State::MaybeExpr; | ||
115 | } | ||
116 | (State::NotExpr, '}') => { | ||
117 | self.output.push(chr); | ||
118 | self.state = State::MaybeIncorrect; | ||
119 | } | ||
120 | (State::NotExpr, _) => { | ||
121 | self.output.push(chr); | ||
122 | } | ||
123 | (State::MaybeIncorrect, '}') => { | ||
124 | // It's okay, we met "}}". | ||
125 | self.output.push(chr); | ||
126 | self.state = State::NotExpr; | ||
127 | } | ||
128 | (State::MaybeIncorrect, _) => { | ||
129 | // Error in the string. | ||
130 | return Err(()); | ||
131 | } | ||
132 | (State::MaybeExpr, '{') => { | ||
133 | self.output.push(chr); | ||
134 | self.state = State::NotExpr; | ||
135 | } | ||
136 | (State::MaybeExpr, '}') => { | ||
137 | // This is an empty sequence '{}'. Replace it with placeholder. | ||
138 | self.output.push(chr); | ||
139 | self.extracted_expressions.push(format!("${}", placeholder_id)); | ||
140 | placeholder_id += 1; | ||
141 | self.state = State::NotExpr; | ||
142 | } | ||
143 | (State::MaybeExpr, _) => { | ||
144 | current_expr.push(chr); | ||
145 | self.state = State::Expr; | ||
146 | } | ||
147 | (State::Expr, '}') => { | ||
148 | if inexpr_open_count == 0 { | ||
149 | self.output.push(chr); | ||
150 | self.extracted_expressions.push(current_expr.trim().into()); | ||
151 | current_expr = String::new(); | ||
152 | self.state = State::NotExpr; | ||
153 | } else { | ||
154 | // We're closing one brace met before inside of the expression. | ||
155 | current_expr.push(chr); | ||
156 | inexpr_open_count -= 1; | ||
157 | } | ||
158 | } | ||
159 | (State::Expr, ':') => { | ||
160 | if inexpr_open_count == 0 { | ||
161 | // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" | ||
162 | self.output.push(chr); | ||
163 | self.extracted_expressions.push(current_expr.trim().into()); | ||
164 | current_expr = String::new(); | ||
165 | self.state = State::FormatOpts; | ||
166 | } else { | ||
167 | // We're inside of braced expression, assume that it's a struct field name/value delimeter. | ||
168 | current_expr.push(chr); | ||
169 | } | ||
170 | } | ||
171 | (State::Expr, '{') => { | ||
172 | current_expr.push(chr); | ||
173 | inexpr_open_count += 1; | ||
174 | } | ||
175 | (State::Expr, _) => { | ||
176 | current_expr.push(chr); | ||
177 | } | ||
178 | (State::FormatOpts, '}') => { | ||
179 | self.output.push(chr); | ||
180 | self.state = State::NotExpr; | ||
181 | } | ||
182 | (State::FormatOpts, _) => { | ||
183 | self.output.push(chr); | ||
184 | } | ||
185 | } | ||
186 | } | ||
187 | |||
188 | if self.state != State::NotExpr { | ||
189 | return Err(()); | ||
190 | } | ||
191 | |||
192 | self.parsed = true; | ||
193 | Ok(()) | ||
194 | } | ||
195 | |||
196 | pub fn into_suggestion(&self, macro_name: &str) -> String { | ||
197 | assert!(self.parsed, "Attempt to get a suggestion from not parsed expression"); | ||
198 | |||
199 | let expressions_as_string = self.extracted_expressions.join(", "); | ||
200 | format!(r#"{}("{}", {})"#, macro_name, self.output, expressions_as_string) | ||
201 | } | ||
202 | } | ||
203 | |||
204 | #[cfg(test)] | ||
205 | mod tests { | ||
206 | use super::*; | ||
207 | use expect_test::{expect, Expect}; | ||
208 | |||
209 | fn check(input: &str, expect: &Expect) { | ||
210 | let mut parser = FormatStrParser::new((*input).to_owned()); | ||
211 | let outcome_repr = if parser.parse().is_ok() { | ||
212 | // Parsing should be OK, expected repr is "string; expr_1, expr_2". | ||
213 | if parser.extracted_expressions.is_empty() { | ||
214 | parser.output | ||
215 | } else { | ||
216 | format!("{}; {}", parser.output, parser.extracted_expressions.join(", ")) | ||
217 | } | ||
218 | } else { | ||
219 | // Parsing should fail, expected repr is "-". | ||
220 | "-".to_owned() | ||
221 | }; | ||
222 | |||
223 | expect.assert_eq(&outcome_repr); | ||
224 | } | ||
225 | |||
226 | #[test] | ||
227 | fn format_str_parser() { | ||
228 | let test_vector = &[ | ||
229 | ("no expressions", expect![["no expressions"]]), | ||
230 | ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]), | ||
231 | ("{expr:?}", expect![["{:?}; expr"]]), | ||
232 | ("{malformed", expect![["-"]]), | ||
233 | ("malformed}", expect![["-"]]), | ||
234 | ("{{correct", expect![["{{correct"]]), | ||
235 | ("correct}}", expect![["correct}}"]]), | ||
236 | ("{correct}}}", expect![["{}}}; correct"]]), | ||
237 | ("{correct}}}}}", expect![["{}}}}}; correct"]]), | ||
238 | ("{incorrect}}", expect![["-"]]), | ||
239 | ("placeholders {} {}", expect![["placeholders {} {}; $1, $2"]]), | ||
240 | ("mixed {} {2 + 2} {}", expect![["mixed {} {} {}; $1, 2 + 2, $2"]]), | ||
241 | ( | ||
242 | "{SomeStruct { val_a: 0, val_b: 1 }}", | ||
243 | expect![["{}; SomeStruct { val_a: 0, val_b: 1 }"]], | ||
244 | ), | ||
245 | ("{expr:?} is {2.32f64:.5}", expect![["{:?} is {:.5}; expr, 2.32f64"]]), | ||
246 | ( | ||
247 | "{SomeStruct { val_a: 0, val_b: 1 }:?}", | ||
248 | expect![["{:?}; SomeStruct { val_a: 0, val_b: 1 }"]], | ||
249 | ), | ||
250 | ("{ 2 + 2 }", expect![["{}; 2 + 2"]]), | ||
251 | ]; | ||
252 | |||
253 | for (input, output) in test_vector { | ||
254 | check(input, output) | ||
255 | } | ||
256 | } | ||
257 | |||
258 | #[test] | ||
259 | fn test_into_suggestion() { | ||
260 | let test_vector = &[ | ||
261 | ("println!", "{}", r#"println!("{}", $1)"#), | ||
262 | ( | ||
263 | "log::info!", | ||
264 | "{} {expr} {} {2 + 2}", | ||
265 | r#"log::info!("{} {} {} {}", $1, expr, $2, 2 + 2)"#, | ||
266 | ), | ||
267 | ("format!", "{expr:?}", r#"format!("{:?}", expr)"#), | ||
268 | ]; | ||
269 | |||
270 | for (kind, input, output) in test_vector { | ||
271 | let mut parser = FormatStrParser::new((*input).to_owned()); | ||
272 | parser.parse().expect("Parsing must succeed"); | ||
273 | |||
274 | assert_eq!(&parser.into_suggestion(*kind), output); | ||
275 | } | ||
276 | } | ||
277 | } | ||
diff --git a/crates/ide/src/completion/complete_qualified_path.rs b/crates/ide/src/completion/complete_qualified_path.rs index 00e89f0fd..2fafedd47 100644 --- a/crates/ide/src/completion/complete_qualified_path.rs +++ b/crates/ide/src/completion/complete_qualified_path.rs | |||
@@ -422,10 +422,10 @@ fn foo() { let _ = U::<|> } | |||
422 | fn completes_use_paths_across_crates() { | 422 | fn completes_use_paths_across_crates() { |
423 | check( | 423 | check( |
424 | r#" | 424 | r#" |
425 | //- /main.rs | 425 | //- /main.rs crate:main deps:foo |
426 | use foo::<|>; | 426 | use foo::<|>; |
427 | 427 | ||
428 | //- /foo/lib.rs | 428 | //- /foo/lib.rs crate:foo |
429 | pub mod bar { pub struct S; } | 429 | pub mod bar { pub struct S; } |
430 | "#, | 430 | "#, |
431 | expect![[r#" | 431 | expect![[r#" |
diff --git a/crates/ide/src/completion/complete_unqualified_path.rs b/crates/ide/src/completion/complete_unqualified_path.rs index 8eda4b64d..8b6757195 100644 --- a/crates/ide/src/completion/complete_unqualified_path.rs +++ b/crates/ide/src/completion/complete_unqualified_path.rs | |||
@@ -267,14 +267,34 @@ fn quux() { <|> } | |||
267 | ); | 267 | ); |
268 | } | 268 | } |
269 | 269 | ||
270 | /// Regression test for issue #6091. | ||
271 | #[test] | ||
272 | fn correctly_completes_module_items_prefixed_with_underscore() { | ||
273 | check_edit( | ||
274 | "_alpha", | ||
275 | r#" | ||
276 | fn main() { | ||
277 | _<|> | ||
278 | } | ||
279 | fn _alpha() {} | ||
280 | "#, | ||
281 | r#" | ||
282 | fn main() { | ||
283 | _alpha()$0 | ||
284 | } | ||
285 | fn _alpha() {} | ||
286 | "#, | ||
287 | ) | ||
288 | } | ||
289 | |||
270 | #[test] | 290 | #[test] |
271 | fn completes_extern_prelude() { | 291 | fn completes_extern_prelude() { |
272 | check( | 292 | check( |
273 | r#" | 293 | r#" |
274 | //- /lib.rs | 294 | //- /lib.rs crate:main deps:other_crate |
275 | use <|>; | 295 | use <|>; |
276 | 296 | ||
277 | //- /other_crate/lib.rs | 297 | //- /other_crate/lib.rs crate:other_crate |
278 | // nothing here | 298 | // nothing here |
279 | "#, | 299 | "#, |
280 | expect![[r#" | 300 | expect![[r#" |
@@ -350,10 +370,10 @@ fn foo() { | |||
350 | fn completes_prelude() { | 370 | fn completes_prelude() { |
351 | check( | 371 | check( |
352 | r#" | 372 | r#" |
353 | //- /main.rs | 373 | //- /main.rs crate:main deps:std |
354 | fn foo() { let x: <|> } | 374 | fn foo() { let x: <|> } |
355 | 375 | ||
356 | //- /std/lib.rs | 376 | //- /std/lib.rs crate:std |
357 | #[prelude_import] | 377 | #[prelude_import] |
358 | use prelude::*; | 378 | use prelude::*; |
359 | 379 | ||
@@ -371,16 +391,16 @@ mod prelude { struct Option; } | |||
371 | fn completes_std_prelude_if_core_is_defined() { | 391 | fn completes_std_prelude_if_core_is_defined() { |
372 | check( | 392 | check( |
373 | r#" | 393 | r#" |
374 | //- /main.rs | 394 | //- /main.rs crate:main deps:core,std |
375 | fn foo() { let x: <|> } | 395 | fn foo() { let x: <|> } |
376 | 396 | ||
377 | //- /core/lib.rs | 397 | //- /core/lib.rs crate:core |
378 | #[prelude_import] | 398 | #[prelude_import] |
379 | use prelude::*; | 399 | use prelude::*; |
380 | 400 | ||
381 | mod prelude { struct Option; } | 401 | mod prelude { struct Option; } |
382 | 402 | ||
383 | //- /std/lib.rs | 403 | //- /std/lib.rs crate:std deps:core |
384 | #[prelude_import] | 404 | #[prelude_import] |
385 | use prelude::*; | 405 | use prelude::*; |
386 | 406 | ||
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 671b13328..8dea8a4bf 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -221,10 +221,11 @@ impl<'a> CompletionContext<'a> { | |||
221 | Some(ctx) | 221 | Some(ctx) |
222 | } | 222 | } |
223 | 223 | ||
224 | // The range of the identifier that is being completed. | 224 | /// The range of the identifier that is being completed. |
225 | pub(crate) fn source_range(&self) -> TextRange { | 225 | pub(crate) fn source_range(&self) -> TextRange { |
226 | // check kind of macro-expanded token, but use range of original token | 226 | // check kind of macro-expanded token, but use range of original token |
227 | if self.token.kind() == IDENT || self.token.kind().is_keyword() { | 227 | let kind = self.token.kind(); |
228 | if kind == IDENT || kind == UNDERSCORE || kind.is_keyword() { | ||
228 | mark::hit!(completes_if_prefix_is_keyword); | 229 | mark::hit!(completes_if_prefix_is_keyword); |
229 | self.original_token.text_range() | 230 | self.original_token.text_range() |
230 | } else { | 231 | } else { |
@@ -469,7 +470,7 @@ impl<'a> CompletionContext<'a> { | |||
469 | } | 470 | } |
470 | } else { | 471 | } else { |
471 | false | 472 | false |
472 | } | 473 | }; |
473 | } | 474 | } |
474 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { | 475 | if let Some(method_call_expr) = ast::MethodCallExpr::cast(parent) { |
475 | // As above | 476 | // As above |
diff --git a/crates/ide/src/completion/presentation.rs b/crates/ide/src/completion/presentation.rs index 987cbfa7a..a5172b87e 100644 --- a/crates/ide/src/completion/presentation.rs +++ b/crates/ide/src/completion/presentation.rs | |||
@@ -1172,9 +1172,9 @@ fn foo(xs: Vec<i128>) | |||
1172 | check_edit( | 1172 | check_edit( |
1173 | "frobnicate!", | 1173 | "frobnicate!", |
1174 | r#" | 1174 | r#" |
1175 | //- /main.rs | 1175 | //- /main.rs crate:main deps:foo |
1176 | use foo::<|>; | 1176 | use foo::<|>; |
1177 | //- /foo/lib.rs | 1177 | //- /foo/lib.rs crate:foo |
1178 | #[macro_export] | 1178 | #[macro_export] |
1179 | macro_rules frobnicate { () => () } | 1179 | macro_rules frobnicate { () => () } |
1180 | "#, | 1180 | "#, |
diff --git a/crates/ide/src/completion/test_utils.rs b/crates/ide/src/completion/test_utils.rs index 1452d7e9e..feb8cd2a6 100644 --- a/crates/ide/src/completion/test_utils.rs +++ b/crates/ide/src/completion/test_utils.rs | |||
@@ -8,8 +8,7 @@ use test_utils::assert_eq_text; | |||
8 | 8 | ||
9 | use crate::{ | 9 | use crate::{ |
10 | completion::{completion_item::CompletionKind, CompletionConfig}, | 10 | completion::{completion_item::CompletionKind, CompletionConfig}, |
11 | mock_analysis::analysis_and_position, | 11 | fixture, CompletionItem, |
12 | CompletionItem, | ||
13 | }; | 12 | }; |
14 | 13 | ||
15 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { | 14 | pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { |
@@ -80,7 +79,7 @@ pub(crate) fn check_edit_with_config( | |||
80 | ra_fixture_after: &str, | 79 | ra_fixture_after: &str, |
81 | ) { | 80 | ) { |
82 | let ra_fixture_after = trim_indent(ra_fixture_after); | 81 | let ra_fixture_after = trim_indent(ra_fixture_after); |
83 | let (analysis, position) = analysis_and_position(ra_fixture_before); | 82 | let (analysis, position) = fixture::position(ra_fixture_before); |
84 | let completions: Vec<CompletionItem> = | 83 | let completions: Vec<CompletionItem> = |
85 | analysis.completions(&config, position).unwrap().unwrap().into(); | 84 | analysis.completions(&config, position).unwrap().unwrap().into(); |
86 | let (completion,) = completions | 85 | let (completion,) = completions |
@@ -94,7 +93,7 @@ pub(crate) fn check_edit_with_config( | |||
94 | } | 93 | } |
95 | 94 | ||
96 | pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { | 95 | pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { |
97 | let (analysis, pos) = analysis_and_position(code); | 96 | let (analysis, pos) = fixture::position(code); |
98 | analysis | 97 | analysis |
99 | .with_db(|db| { | 98 | .with_db(|db| { |
100 | let sema = Semantics::new(db); | 99 | let sema = Semantics::new(db); |
@@ -109,6 +108,6 @@ pub(crate) fn get_all_completion_items( | |||
109 | config: CompletionConfig, | 108 | config: CompletionConfig, |
110 | code: &str, | 109 | code: &str, |
111 | ) -> Vec<CompletionItem> { | 110 | ) -> Vec<CompletionItem> { |
112 | let (analysis, position) = analysis_and_position(code); | 111 | let (analysis, position) = fixture::position(code); |
113 | analysis.completions(&config, position).unwrap().unwrap().into() | 112 | analysis.completions(&config, position).unwrap().unwrap().into() |
114 | } | 113 | } |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index dc815a483..f5d627b6e 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -218,10 +218,7 @@ mod tests { | |||
218 | use stdx::trim_indent; | 218 | use stdx::trim_indent; |
219 | use test_utils::assert_eq_text; | 219 | use test_utils::assert_eq_text; |
220 | 220 | ||
221 | use crate::{ | 221 | use crate::{fixture, DiagnosticsConfig}; |
222 | mock_analysis::{analysis_and_position, single_file, MockAnalysis}, | ||
223 | DiagnosticsConfig, | ||
224 | }; | ||
225 | 222 | ||
226 | /// Takes a multi-file input fixture with annotated cursor positions, | 223 | /// Takes a multi-file input fixture with annotated cursor positions, |
227 | /// and checks that: | 224 | /// and checks that: |
@@ -231,7 +228,7 @@ mod tests { | |||
231 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { | 228 | fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { |
232 | let after = trim_indent(ra_fixture_after); | 229 | let after = trim_indent(ra_fixture_after); |
233 | 230 | ||
234 | let (analysis, file_position) = analysis_and_position(ra_fixture_before); | 231 | let (analysis, file_position) = fixture::position(ra_fixture_before); |
235 | let diagnostic = analysis | 232 | let diagnostic = analysis |
236 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) | 233 | .diagnostics(&DiagnosticsConfig::default(), file_position.file_id) |
237 | .unwrap() | 234 | .unwrap() |
@@ -260,7 +257,7 @@ mod tests { | |||
260 | /// which has a fix that can apply to other files. | 257 | /// which has a fix that can apply to other files. |
261 | fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { | 258 | fn check_apply_diagnostic_fix_in_other_file(ra_fixture_before: &str, ra_fixture_after: &str) { |
262 | let ra_fixture_after = &trim_indent(ra_fixture_after); | 259 | let ra_fixture_after = &trim_indent(ra_fixture_after); |
263 | let (analysis, file_pos) = analysis_and_position(ra_fixture_before); | 260 | let (analysis, file_pos) = fixture::position(ra_fixture_before); |
264 | let current_file_id = file_pos.file_id; | 261 | let current_file_id = file_pos.file_id; |
265 | let diagnostic = analysis | 262 | let diagnostic = analysis |
266 | .diagnostics(&DiagnosticsConfig::default(), current_file_id) | 263 | .diagnostics(&DiagnosticsConfig::default(), current_file_id) |
@@ -282,9 +279,7 @@ mod tests { | |||
282 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics | 279 | /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics |
283 | /// apply to the file containing the cursor. | 280 | /// apply to the file containing the cursor. |
284 | fn check_no_diagnostics(ra_fixture: &str) { | 281 | fn check_no_diagnostics(ra_fixture: &str) { |
285 | let mock = MockAnalysis::with_files(ra_fixture); | 282 | let (analysis, files) = fixture::files(ra_fixture); |
286 | let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>(); | ||
287 | let analysis = mock.analysis(); | ||
288 | let diagnostics = files | 283 | let diagnostics = files |
289 | .into_iter() | 284 | .into_iter() |
290 | .flat_map(|file_id| { | 285 | .flat_map(|file_id| { |
@@ -295,7 +290,7 @@ mod tests { | |||
295 | } | 290 | } |
296 | 291 | ||
297 | fn check_expect(ra_fixture: &str, expect: Expect) { | 292 | fn check_expect(ra_fixture: &str, expect: Expect) { |
298 | let (analysis, file_id) = single_file(ra_fixture); | 293 | let (analysis, file_id) = fixture::file(ra_fixture); |
299 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); | 294 | let diagnostics = analysis.diagnostics(&DiagnosticsConfig::default(), file_id).unwrap(); |
300 | expect.assert_debug_eq(&diagnostics) | 295 | expect.assert_debug_eq(&diagnostics) |
301 | } | 296 | } |
@@ -304,7 +299,7 @@ mod tests { | |||
304 | fn test_wrap_return_type() { | 299 | fn test_wrap_return_type() { |
305 | check_fix( | 300 | check_fix( |
306 | r#" | 301 | r#" |
307 | //- /main.rs | 302 | //- /main.rs crate:main deps:core |
308 | use core::result::Result::{self, Ok, Err}; | 303 | use core::result::Result::{self, Ok, Err}; |
309 | 304 | ||
310 | fn div(x: i32, y: i32) -> Result<i32, ()> { | 305 | fn div(x: i32, y: i32) -> Result<i32, ()> { |
@@ -313,7 +308,7 @@ fn div(x: i32, y: i32) -> Result<i32, ()> { | |||
313 | } | 308 | } |
314 | x / y<|> | 309 | x / y<|> |
315 | } | 310 | } |
316 | //- /core/lib.rs | 311 | //- /core/lib.rs crate:core |
317 | pub mod result { | 312 | pub mod result { |
318 | pub enum Result<T, E> { Ok(T), Err(E) } | 313 | pub enum Result<T, E> { Ok(T), Err(E) } |
319 | } | 314 | } |
@@ -335,7 +330,7 @@ fn div(x: i32, y: i32) -> Result<i32, ()> { | |||
335 | fn test_wrap_return_type_handles_generic_functions() { | 330 | fn test_wrap_return_type_handles_generic_functions() { |
336 | check_fix( | 331 | check_fix( |
337 | r#" | 332 | r#" |
338 | //- /main.rs | 333 | //- /main.rs crate:main deps:core |
339 | use core::result::Result::{self, Ok, Err}; | 334 | use core::result::Result::{self, Ok, Err}; |
340 | 335 | ||
341 | fn div<T>(x: T) -> Result<T, i32> { | 336 | fn div<T>(x: T) -> Result<T, i32> { |
@@ -344,7 +339,7 @@ fn div<T>(x: T) -> Result<T, i32> { | |||
344 | } | 339 | } |
345 | <|>x | 340 | <|>x |
346 | } | 341 | } |
347 | //- /core/lib.rs | 342 | //- /core/lib.rs crate:core |
348 | pub mod result { | 343 | pub mod result { |
349 | pub enum Result<T, E> { Ok(T), Err(E) } | 344 | pub enum Result<T, E> { Ok(T), Err(E) } |
350 | } | 345 | } |
@@ -366,7 +361,7 @@ fn div<T>(x: T) -> Result<T, i32> { | |||
366 | fn test_wrap_return_type_handles_type_aliases() { | 361 | fn test_wrap_return_type_handles_type_aliases() { |
367 | check_fix( | 362 | check_fix( |
368 | r#" | 363 | r#" |
369 | //- /main.rs | 364 | //- /main.rs crate:main deps:core |
370 | use core::result::Result::{self, Ok, Err}; | 365 | use core::result::Result::{self, Ok, Err}; |
371 | 366 | ||
372 | type MyResult<T> = Result<T, ()>; | 367 | type MyResult<T> = Result<T, ()>; |
@@ -377,7 +372,7 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
377 | } | 372 | } |
378 | x <|>/ y | 373 | x <|>/ y |
379 | } | 374 | } |
380 | //- /core/lib.rs | 375 | //- /core/lib.rs crate:core |
381 | pub mod result { | 376 | pub mod result { |
382 | pub enum Result<T, E> { Ok(T), Err(E) } | 377 | pub enum Result<T, E> { Ok(T), Err(E) } |
383 | } | 378 | } |
@@ -401,12 +396,12 @@ fn div(x: i32, y: i32) -> MyResult<i32> { | |||
401 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { | 396 | fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { |
402 | check_no_diagnostics( | 397 | check_no_diagnostics( |
403 | r#" | 398 | r#" |
404 | //- /main.rs | 399 | //- /main.rs crate:main deps:core |
405 | use core::result::Result::{self, Ok, Err}; | 400 | use core::result::Result::{self, Ok, Err}; |
406 | 401 | ||
407 | fn foo() -> Result<(), i32> { 0 } | 402 | fn foo() -> Result<(), i32> { 0 } |
408 | 403 | ||
409 | //- /core/lib.rs | 404 | //- /core/lib.rs crate:core |
410 | pub mod result { | 405 | pub mod result { |
411 | pub enum Result<T, E> { Ok(T), Err(E) } | 406 | pub enum Result<T, E> { Ok(T), Err(E) } |
412 | } | 407 | } |
@@ -418,14 +413,14 @@ pub mod result { | |||
418 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { | 413 | fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { |
419 | check_no_diagnostics( | 414 | check_no_diagnostics( |
420 | r#" | 415 | r#" |
421 | //- /main.rs | 416 | //- /main.rs crate:main deps:core |
422 | use core::result::Result::{self, Ok, Err}; | 417 | use core::result::Result::{self, Ok, Err}; |
423 | 418 | ||
424 | enum SomeOtherEnum { Ok(i32), Err(String) } | 419 | enum SomeOtherEnum { Ok(i32), Err(String) } |
425 | 420 | ||
426 | fn foo() -> SomeOtherEnum { 0 } | 421 | fn foo() -> SomeOtherEnum { 0 } |
427 | 422 | ||
428 | //- /core/lib.rs | 423 | //- /core/lib.rs crate:core |
429 | pub mod result { | 424 | pub mod result { |
430 | pub enum Result<T, E> { Ok(T), Err(E) } | 425 | pub enum Result<T, E> { Ok(T), Err(E) } |
431 | } | 426 | } |
@@ -567,7 +562,7 @@ fn test_fn() { | |||
567 | file_system_edits: [ | 562 | file_system_edits: [ |
568 | CreateFile { | 563 | CreateFile { |
569 | anchor: FileId( | 564 | anchor: FileId( |
570 | 1, | 565 | 0, |
571 | ), | 566 | ), |
572 | dst: "foo.rs", | 567 | dst: "foo.rs", |
573 | }, | 568 | }, |
@@ -787,7 +782,7 @@ struct Foo { | |||
787 | let mut config = DiagnosticsConfig::default(); | 782 | let mut config = DiagnosticsConfig::default(); |
788 | config.disabled.insert("unresolved-module".into()); | 783 | config.disabled.insert("unresolved-module".into()); |
789 | 784 | ||
790 | let (analysis, file_id) = single_file(r#"mod foo;"#); | 785 | let (analysis, file_id) = fixture::file(r#"mod foo;"#); |
791 | 786 | ||
792 | let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); | 787 | let diagnostics = analysis.diagnostics(&config, file_id).unwrap(); |
793 | assert!(diagnostics.is_empty()); | 788 | assert!(diagnostics.is_empty()); |
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 1ee80c2dd..cf9d617dc 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -423,11 +423,11 @@ pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> | |||
423 | mod tests { | 423 | mod tests { |
424 | use expect_test::expect; | 424 | use expect_test::expect; |
425 | 425 | ||
426 | use crate::{mock_analysis::single_file, Query}; | 426 | use crate::{fixture, Query}; |
427 | 427 | ||
428 | #[test] | 428 | #[test] |
429 | fn test_nav_for_symbol() { | 429 | fn test_nav_for_symbol() { |
430 | let (analysis, _) = single_file( | 430 | let (analysis, _) = fixture::file( |
431 | r#" | 431 | r#" |
432 | enum FooInner { } | 432 | enum FooInner { } |
433 | fn foo() { enum FooInner { } } | 433 | fn foo() { enum FooInner { } } |
@@ -439,7 +439,7 @@ fn foo() { enum FooInner { } } | |||
439 | [ | 439 | [ |
440 | NavigationTarget { | 440 | NavigationTarget { |
441 | file_id: FileId( | 441 | file_id: FileId( |
442 | 1, | 442 | 0, |
443 | ), | 443 | ), |
444 | full_range: 0..17, | 444 | full_range: 0..17, |
445 | focus_range: Some( | 445 | focus_range: Some( |
@@ -455,7 +455,7 @@ fn foo() { enum FooInner { } } | |||
455 | }, | 455 | }, |
456 | NavigationTarget { | 456 | NavigationTarget { |
457 | file_id: FileId( | 457 | file_id: FileId( |
458 | 1, | 458 | 0, |
459 | ), | 459 | ), |
460 | full_range: 29..46, | 460 | full_range: 29..46, |
461 | focus_range: Some( | 461 | focus_range: Some( |
@@ -478,7 +478,7 @@ fn foo() { enum FooInner { } } | |||
478 | 478 | ||
479 | #[test] | 479 | #[test] |
480 | fn test_world_symbols_are_case_sensitive() { | 480 | fn test_world_symbols_are_case_sensitive() { |
481 | let (analysis, _) = single_file( | 481 | let (analysis, _) = fixture::file( |
482 | r#" | 482 | r#" |
483 | fn foo() {} | 483 | fn foo() {} |
484 | struct Foo; | 484 | struct Foo; |
diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index 8a285bcf7..8d75e0f05 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs | |||
@@ -122,10 +122,10 @@ fn insert_whitespaces(syn: SyntaxNode) -> String { | |||
122 | mod tests { | 122 | mod tests { |
123 | use expect_test::{expect, Expect}; | 123 | use expect_test::{expect, Expect}; |
124 | 124 | ||
125 | use crate::mock_analysis::analysis_and_position; | 125 | use crate::fixture; |
126 | 126 | ||
127 | fn check(ra_fixture: &str, expect: Expect) { | 127 | fn check(ra_fixture: &str, expect: Expect) { |
128 | let (analysis, pos) = analysis_and_position(ra_fixture); | 128 | let (analysis, pos) = fixture::position(ra_fixture); |
129 | let expansion = analysis.expand_macro(pos).unwrap().unwrap(); | 129 | let expansion = analysis.expand_macro(pos).unwrap().unwrap(); |
130 | let actual = format!("{}\n{}", expansion.name, expansion.expansion); | 130 | let actual = format!("{}\n{}", expansion.name, expansion.expansion); |
131 | expect.assert_eq(&actual); | 131 | expect.assert_eq(&actual); |
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs index 34563a026..3ee0af8ad 100644 --- a/crates/ide/src/extend_selection.rs +++ b/crates/ide/src/extend_selection.rs | |||
@@ -315,12 +315,12 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment { | |||
315 | 315 | ||
316 | #[cfg(test)] | 316 | #[cfg(test)] |
317 | mod tests { | 317 | mod tests { |
318 | use crate::mock_analysis::analysis_and_position; | 318 | use crate::fixture; |
319 | 319 | ||
320 | use super::*; | 320 | use super::*; |
321 | 321 | ||
322 | fn do_check(before: &str, afters: &[&str]) { | 322 | fn do_check(before: &str, afters: &[&str]) { |
323 | let (analysis, position) = analysis_and_position(&before); | 323 | let (analysis, position) = fixture::position(&before); |
324 | let before = analysis.file_text(position.file_id).unwrap(); | 324 | let before = analysis.file_text(position.file_id).unwrap(); |
325 | let range = TextRange::empty(position.offset); | 325 | let range = TextRange::empty(position.offset); |
326 | let mut frange = FileRange { file_id: position.file_id, range }; | 326 | let mut frange = FileRange { file_id: position.file_id, range }; |
diff --git a/crates/ide/src/fixture.rs b/crates/ide/src/fixture.rs new file mode 100644 index 000000000..ed06689f0 --- /dev/null +++ b/crates/ide/src/fixture.rs | |||
@@ -0,0 +1,70 @@ | |||
1 | //! Utilities for creating `Analysis` instances for tests. | ||
2 | use base_db::fixture::ChangeFixture; | ||
3 | use test_utils::{extract_annotations, RangeOrOffset}; | ||
4 | |||
5 | use crate::{Analysis, AnalysisHost, FileId, FilePosition, FileRange}; | ||
6 | |||
7 | /// Creates analysis for a single file. | ||
8 | pub(crate) fn file(ra_fixture: &str) -> (Analysis, FileId) { | ||
9 | let mut host = AnalysisHost::default(); | ||
10 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
11 | host.db.apply_change(change_fixture.change); | ||
12 | (host.analysis(), change_fixture.files[0]) | ||
13 | } | ||
14 | |||
15 | /// Creates analysis for many files. | ||
16 | pub(crate) fn files(ra_fixture: &str) -> (Analysis, Vec<FileId>) { | ||
17 | let mut host = AnalysisHost::default(); | ||
18 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
19 | host.db.apply_change(change_fixture.change); | ||
20 | (host.analysis(), change_fixture.files) | ||
21 | } | ||
22 | |||
23 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | ||
24 | pub(crate) fn position(ra_fixture: &str) -> (Analysis, FilePosition) { | ||
25 | let mut host = AnalysisHost::default(); | ||
26 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
27 | host.db.apply_change(change_fixture.change); | ||
28 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); | ||
29 | let offset = match range_or_offset { | ||
30 | RangeOrOffset::Range(_) => panic!(), | ||
31 | RangeOrOffset::Offset(it) => it, | ||
32 | }; | ||
33 | (host.analysis(), FilePosition { file_id, offset }) | ||
34 | } | ||
35 | |||
36 | /// Creates analysis for a single file, returns range marked with a pair of <|>. | ||
37 | pub(crate) fn range(ra_fixture: &str) -> (Analysis, FileRange) { | ||
38 | let mut host = AnalysisHost::default(); | ||
39 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
40 | host.db.apply_change(change_fixture.change); | ||
41 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); | ||
42 | let range = match range_or_offset { | ||
43 | RangeOrOffset::Range(it) => it, | ||
44 | RangeOrOffset::Offset(_) => panic!(), | ||
45 | }; | ||
46 | (host.analysis(), FileRange { file_id, range }) | ||
47 | } | ||
48 | |||
49 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | ||
50 | pub(crate) fn annotations(ra_fixture: &str) -> (Analysis, FilePosition, Vec<(FileRange, String)>) { | ||
51 | let mut host = AnalysisHost::default(); | ||
52 | let change_fixture = ChangeFixture::parse(ra_fixture); | ||
53 | host.db.apply_change(change_fixture.change); | ||
54 | let (file_id, range_or_offset) = change_fixture.file_position.expect("expected a marker (<|>)"); | ||
55 | let offset = match range_or_offset { | ||
56 | RangeOrOffset::Range(_) => panic!(), | ||
57 | RangeOrOffset::Offset(it) => it, | ||
58 | }; | ||
59 | |||
60 | let annotations = change_fixture | ||
61 | .files | ||
62 | .iter() | ||
63 | .flat_map(|&file_id| { | ||
64 | let file_text = host.analysis().file_text(file_id).unwrap(); | ||
65 | let annotations = extract_annotations(&file_text); | ||
66 | annotations.into_iter().map(move |(range, data)| (FileRange { file_id, range }, data)) | ||
67 | }) | ||
68 | .collect(); | ||
69 | (host.analysis(), FilePosition { file_id, offset }, annotations) | ||
70 | } | ||
diff --git a/crates/ide/src/fn_references.rs b/crates/ide/src/fn_references.rs index 1989a562b..459f201ed 100644 --- a/crates/ide/src/fn_references.rs +++ b/crates/ide/src/fn_references.rs | |||
@@ -25,15 +25,14 @@ fn method_range(item: SyntaxNode, file_id: FileId) -> Option<FileRange> { | |||
25 | 25 | ||
26 | #[cfg(test)] | 26 | #[cfg(test)] |
27 | mod tests { | 27 | mod tests { |
28 | use crate::mock_analysis::analysis_and_position; | 28 | use crate::fixture; |
29 | use crate::{FileRange, TextSize}; | 29 | use crate::{FileRange, TextSize}; |
30 | use std::ops::RangeInclusive; | 30 | use std::ops::RangeInclusive; |
31 | 31 | ||
32 | #[test] | 32 | #[test] |
33 | fn test_find_all_methods() { | 33 | fn test_find_all_methods() { |
34 | let (analysis, pos) = analysis_and_position( | 34 | let (analysis, pos) = fixture::position( |
35 | r#" | 35 | r#" |
36 | //- /lib.rs | ||
37 | fn private_fn() {<|>} | 36 | fn private_fn() {<|>} |
38 | 37 | ||
39 | pub fn pub_fn() {} | 38 | pub fn pub_fn() {} |
@@ -48,9 +47,8 @@ mod tests { | |||
48 | 47 | ||
49 | #[test] | 48 | #[test] |
50 | fn test_find_trait_methods() { | 49 | fn test_find_trait_methods() { |
51 | let (analysis, pos) = analysis_and_position( | 50 | let (analysis, pos) = fixture::position( |
52 | r#" | 51 | r#" |
53 | //- /lib.rs | ||
54 | trait Foo { | 52 | trait Foo { |
55 | fn bar() {<|>} | 53 | fn bar() {<|>} |
56 | fn baz() {} | 54 | fn baz() {} |
@@ -64,7 +62,7 @@ mod tests { | |||
64 | 62 | ||
65 | #[test] | 63 | #[test] |
66 | fn test_skip_tests() { | 64 | fn test_skip_tests() { |
67 | let (analysis, pos) = analysis_and_position( | 65 | let (analysis, pos) = fixture::position( |
68 | r#" | 66 | r#" |
69 | //- /lib.rs | 67 | //- /lib.rs |
70 | #[test] | 68 | #[test] |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 15e9b7fad..582bf4837 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -103,12 +103,11 @@ mod tests { | |||
103 | use base_db::FileRange; | 103 | use base_db::FileRange; |
104 | use syntax::{TextRange, TextSize}; | 104 | use syntax::{TextRange, TextSize}; |
105 | 105 | ||
106 | use crate::mock_analysis::MockAnalysis; | 106 | use crate::fixture; |
107 | 107 | ||
108 | fn check(ra_fixture: &str) { | 108 | fn check(ra_fixture: &str) { |
109 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | 109 | let (analysis, position, mut annotations) = fixture::annotations(ra_fixture); |
110 | let (mut expected, data) = mock.annotation(); | 110 | let (mut expected, data) = annotations.pop().unwrap(); |
111 | let analysis = mock.analysis(); | ||
112 | match data.as_str() { | 111 | match data.as_str() { |
113 | "" => (), | 112 | "" => (), |
114 | "file" => { | 113 | "file" => { |
@@ -133,9 +132,9 @@ mod tests { | |||
133 | fn goto_def_for_extern_crate() { | 132 | fn goto_def_for_extern_crate() { |
134 | check( | 133 | check( |
135 | r#" | 134 | r#" |
136 | //- /main.rs | 135 | //- /main.rs crate:main deps:std |
137 | extern crate std<|>; | 136 | extern crate std<|>; |
138 | //- /std/lib.rs | 137 | //- /std/lib.rs crate:std |
139 | // empty | 138 | // empty |
140 | //^ file | 139 | //^ file |
141 | "#, | 140 | "#, |
@@ -146,9 +145,9 @@ mod tests { | |||
146 | fn goto_def_for_renamed_extern_crate() { | 145 | fn goto_def_for_renamed_extern_crate() { |
147 | check( | 146 | check( |
148 | r#" | 147 | r#" |
149 | //- /main.rs | 148 | //- /main.rs crate:main deps:std |
150 | extern crate std as abc<|>; | 149 | extern crate std as abc<|>; |
151 | //- /std/lib.rs | 150 | //- /std/lib.rs crate:std |
152 | // empty | 151 | // empty |
153 | //^ file | 152 | //^ file |
154 | "#, | 153 | "#, |
@@ -342,10 +341,10 @@ fn bar() { | |||
342 | fn goto_def_for_use_alias() { | 341 | fn goto_def_for_use_alias() { |
343 | check( | 342 | check( |
344 | r#" | 343 | r#" |
345 | //- /lib.rs | 344 | //- /lib.rs crate:main deps:foo |
346 | use foo as bar<|>; | 345 | use foo as bar<|>; |
347 | 346 | ||
348 | //- /foo/lib.rs | 347 | //- /foo/lib.rs crate:foo |
349 | // empty | 348 | // empty |
350 | //^ file | 349 | //^ file |
351 | "#, | 350 | "#, |
@@ -356,10 +355,10 @@ use foo as bar<|>; | |||
356 | fn goto_def_for_use_alias_foo_macro() { | 355 | fn goto_def_for_use_alias_foo_macro() { |
357 | check( | 356 | check( |
358 | r#" | 357 | r#" |
359 | //- /lib.rs | 358 | //- /lib.rs crate:main deps:foo |
360 | use foo::foo as bar<|>; | 359 | use foo::foo as bar<|>; |
361 | 360 | ||
362 | //- /foo/lib.rs | 361 | //- /foo/lib.rs crate:foo |
363 | #[macro_export] | 362 | #[macro_export] |
364 | macro_rules! foo { () => { () } } | 363 | macro_rules! foo { () => { () } } |
365 | //^^^ | 364 | //^^^ |
@@ -371,7 +370,6 @@ macro_rules! foo { () => { () } } | |||
371 | fn goto_def_for_methods() { | 370 | fn goto_def_for_methods() { |
372 | check( | 371 | check( |
373 | r#" | 372 | r#" |
374 | //- /lib.rs | ||
375 | struct Foo; | 373 | struct Foo; |
376 | impl Foo { | 374 | impl Foo { |
377 | fn frobnicate(&self) { } | 375 | fn frobnicate(&self) { } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index f503f4ec5..6c586bbd1 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -76,12 +76,10 @@ fn impls_for_trait( | |||
76 | mod tests { | 76 | mod tests { |
77 | use base_db::FileRange; | 77 | use base_db::FileRange; |
78 | 78 | ||
79 | use crate::mock_analysis::MockAnalysis; | 79 | use crate::fixture; |
80 | 80 | ||
81 | fn check(ra_fixture: &str) { | 81 | fn check(ra_fixture: &str) { |
82 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | 82 | let (analysis, position, annotations) = fixture::annotations(ra_fixture); |
83 | let annotations = mock.annotations(); | ||
84 | let analysis = mock.analysis(); | ||
85 | 83 | ||
86 | let navs = analysis.goto_implementation(position).unwrap().unwrap().info; | 84 | let navs = analysis.goto_implementation(position).unwrap().unwrap().info; |
87 | 85 | ||
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 4a151b150..6d0df04dd 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs | |||
@@ -56,13 +56,12 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
56 | mod tests { | 56 | mod tests { |
57 | use base_db::FileRange; | 57 | use base_db::FileRange; |
58 | 58 | ||
59 | use crate::mock_analysis::MockAnalysis; | 59 | use crate::fixture; |
60 | 60 | ||
61 | fn check(ra_fixture: &str) { | 61 | fn check(ra_fixture: &str) { |
62 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | 62 | let (analysis, position, mut annotations) = fixture::annotations(ra_fixture); |
63 | let (expected, data) = mock.annotation(); | 63 | let (expected, data) = annotations.pop().unwrap(); |
64 | assert!(data.is_empty()); | 64 | assert!(data.is_empty()); |
65 | let analysis = mock.analysis(); | ||
66 | 65 | ||
67 | let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info; | 66 | let mut navs = analysis.goto_type_definition(position).unwrap().unwrap().info; |
68 | assert_eq!(navs.len(), 1); | 67 | assert_eq!(navs.len(), 1); |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index bb9f12cd3..9cf02f0a3 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -377,17 +377,17 @@ mod tests { | |||
377 | use base_db::FileLoader; | 377 | use base_db::FileLoader; |
378 | use expect_test::{expect, Expect}; | 378 | use expect_test::{expect, Expect}; |
379 | 379 | ||
380 | use crate::mock_analysis::analysis_and_position; | 380 | use crate::fixture; |
381 | 381 | ||
382 | use super::*; | 382 | use super::*; |
383 | 383 | ||
384 | fn check_hover_no_result(ra_fixture: &str) { | 384 | fn check_hover_no_result(ra_fixture: &str) { |
385 | let (analysis, position) = analysis_and_position(ra_fixture); | 385 | let (analysis, position) = fixture::position(ra_fixture); |
386 | assert!(analysis.hover(position, true).unwrap().is_none()); | 386 | assert!(analysis.hover(position, true).unwrap().is_none()); |
387 | } | 387 | } |
388 | 388 | ||
389 | fn check(ra_fixture: &str, expect: Expect) { | 389 | fn check(ra_fixture: &str, expect: Expect) { |
390 | let (analysis, position) = analysis_and_position(ra_fixture); | 390 | let (analysis, position) = fixture::position(ra_fixture); |
391 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 391 | let hover = analysis.hover(position, true).unwrap().unwrap(); |
392 | 392 | ||
393 | let content = analysis.db.file_text(position.file_id); | 393 | let content = analysis.db.file_text(position.file_id); |
@@ -398,7 +398,7 @@ mod tests { | |||
398 | } | 398 | } |
399 | 399 | ||
400 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { | 400 | fn check_hover_no_links(ra_fixture: &str, expect: Expect) { |
401 | let (analysis, position) = analysis_and_position(ra_fixture); | 401 | let (analysis, position) = fixture::position(ra_fixture); |
402 | let hover = analysis.hover(position, false).unwrap().unwrap(); | 402 | let hover = analysis.hover(position, false).unwrap().unwrap(); |
403 | 403 | ||
404 | let content = analysis.db.file_text(position.file_id); | 404 | let content = analysis.db.file_text(position.file_id); |
@@ -409,7 +409,7 @@ mod tests { | |||
409 | } | 409 | } |
410 | 410 | ||
411 | fn check_actions(ra_fixture: &str, expect: Expect) { | 411 | fn check_actions(ra_fixture: &str, expect: Expect) { |
412 | let (analysis, position) = analysis_and_position(ra_fixture); | 412 | let (analysis, position) = fixture::position(ra_fixture); |
413 | let hover = analysis.hover(position, true).unwrap().unwrap(); | 413 | let hover = analysis.hover(position, true).unwrap().unwrap(); |
414 | expect.assert_debug_eq(&hover.info.actions) | 414 | expect.assert_debug_eq(&hover.info.actions) |
415 | } | 415 | } |
@@ -963,7 +963,7 @@ impl Thing { | |||
963 | "#]], | 963 | "#]], |
964 | ) | 964 | ) |
965 | } /* FIXME: revive these tests | 965 | } /* FIXME: revive these tests |
966 | let (analysis, position) = analysis_and_position( | 966 | let (analysis, position) = fixture::position( |
967 | " | 967 | " |
968 | struct Thing { x: u32 } | 968 | struct Thing { x: u32 } |
969 | impl Thing { | 969 | impl Thing { |
@@ -977,7 +977,7 @@ impl Thing { | |||
977 | let hover = analysis.hover(position).unwrap().unwrap(); | 977 | let hover = analysis.hover(position).unwrap().unwrap(); |
978 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing")); | 978 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("Thing")); |
979 | 979 | ||
980 | let (analysis, position) = analysis_and_position( | 980 | let (analysis, position) = fixture::position( |
981 | " | 981 | " |
982 | enum Thing { A } | 982 | enum Thing { A } |
983 | impl Thing { | 983 | impl Thing { |
@@ -990,7 +990,7 @@ impl Thing { | |||
990 | let hover = analysis.hover(position).unwrap().unwrap(); | 990 | let hover = analysis.hover(position).unwrap().unwrap(); |
991 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); | 991 | assert_eq!(trim_markup(&hover.info.markup.as_str()), ("enum Thing")); |
992 | 992 | ||
993 | let (analysis, position) = analysis_and_position( | 993 | let (analysis, position) = fixture::position( |
994 | " | 994 | " |
995 | enum Thing { A } | 995 | enum Thing { A } |
996 | impl Thing { | 996 | impl Thing { |
@@ -1275,7 +1275,7 @@ fn bar() { fo<|>o(); } | |||
1275 | Implementaion( | 1275 | Implementaion( |
1276 | FilePosition { | 1276 | FilePosition { |
1277 | file_id: FileId( | 1277 | file_id: FileId( |
1278 | 1, | 1278 | 0, |
1279 | ), | 1279 | ), |
1280 | offset: 13, | 1280 | offset: 13, |
1281 | }, | 1281 | }, |
@@ -1289,9 +1289,9 @@ fn bar() { fo<|>o(); } | |||
1289 | fn test_hover_extern_crate() { | 1289 | fn test_hover_extern_crate() { |
1290 | check( | 1290 | check( |
1291 | r#" | 1291 | r#" |
1292 | //- /main.rs | 1292 | //- /main.rs crate:main deps:std |
1293 | extern crate st<|>d; | 1293 | extern crate st<|>d; |
1294 | //- /std/lib.rs | 1294 | //- /std/lib.rs crate:std |
1295 | //! Standard library for this test | 1295 | //! Standard library for this test |
1296 | //! | 1296 | //! |
1297 | //! Printed? | 1297 | //! Printed? |
@@ -1307,9 +1307,9 @@ extern crate st<|>d; | |||
1307 | ); | 1307 | ); |
1308 | check( | 1308 | check( |
1309 | r#" | 1309 | r#" |
1310 | //- /main.rs | 1310 | //- /main.rs crate:main deps:std |
1311 | extern crate std as ab<|>c; | 1311 | extern crate std as ab<|>c; |
1312 | //- /std/lib.rs | 1312 | //- /std/lib.rs crate:std |
1313 | //! Standard library for this test | 1313 | //! Standard library for this test |
1314 | //! | 1314 | //! |
1315 | //! Printed? | 1315 | //! Printed? |
@@ -1989,7 +1989,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
1989 | Implementaion( | 1989 | Implementaion( |
1990 | FilePosition { | 1990 | FilePosition { |
1991 | file_id: FileId( | 1991 | file_id: FileId( |
1992 | 1, | 1992 | 0, |
1993 | ), | 1993 | ), |
1994 | offset: 6, | 1994 | offset: 6, |
1995 | }, | 1995 | }, |
@@ -2008,7 +2008,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2008 | Implementaion( | 2008 | Implementaion( |
2009 | FilePosition { | 2009 | FilePosition { |
2010 | file_id: FileId( | 2010 | file_id: FileId( |
2011 | 1, | 2011 | 0, |
2012 | ), | 2012 | ), |
2013 | offset: 7, | 2013 | offset: 7, |
2014 | }, | 2014 | }, |
@@ -2027,7 +2027,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2027 | Implementaion( | 2027 | Implementaion( |
2028 | FilePosition { | 2028 | FilePosition { |
2029 | file_id: FileId( | 2029 | file_id: FileId( |
2030 | 1, | 2030 | 0, |
2031 | ), | 2031 | ), |
2032 | offset: 6, | 2032 | offset: 6, |
2033 | }, | 2033 | }, |
@@ -2046,7 +2046,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2046 | Implementaion( | 2046 | Implementaion( |
2047 | FilePosition { | 2047 | FilePosition { |
2048 | file_id: FileId( | 2048 | file_id: FileId( |
2049 | 1, | 2049 | 0, |
2050 | ), | 2050 | ), |
2051 | offset: 5, | 2051 | offset: 5, |
2052 | }, | 2052 | }, |
@@ -2069,7 +2069,7 @@ fn foo_<|>test() {} | |||
2069 | Runnable { | 2069 | Runnable { |
2070 | nav: NavigationTarget { | 2070 | nav: NavigationTarget { |
2071 | file_id: FileId( | 2071 | file_id: FileId( |
2072 | 1, | 2072 | 0, |
2073 | ), | 2073 | ), |
2074 | full_range: 0..24, | 2074 | full_range: 0..24, |
2075 | focus_range: Some( | 2075 | focus_range: Some( |
@@ -2112,7 +2112,7 @@ mod tests<|> { | |||
2112 | Runnable { | 2112 | Runnable { |
2113 | nav: NavigationTarget { | 2113 | nav: NavigationTarget { |
2114 | file_id: FileId( | 2114 | file_id: FileId( |
2115 | 1, | 2115 | 0, |
2116 | ), | 2116 | ), |
2117 | full_range: 0..46, | 2117 | full_range: 0..46, |
2118 | focus_range: Some( | 2118 | focus_range: Some( |
@@ -2151,7 +2151,7 @@ fn main() { let s<|>t = S{ f1:0 }; } | |||
2151 | mod_path: "test::S", | 2151 | mod_path: "test::S", |
2152 | nav: NavigationTarget { | 2152 | nav: NavigationTarget { |
2153 | file_id: FileId( | 2153 | file_id: FileId( |
2154 | 1, | 2154 | 0, |
2155 | ), | 2155 | ), |
2156 | full_range: 0..19, | 2156 | full_range: 0..19, |
2157 | focus_range: Some( | 2157 | focus_range: Some( |
@@ -2190,7 +2190,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; } | |||
2190 | mod_path: "test::S", | 2190 | mod_path: "test::S", |
2191 | nav: NavigationTarget { | 2191 | nav: NavigationTarget { |
2192 | file_id: FileId( | 2192 | file_id: FileId( |
2193 | 1, | 2193 | 0, |
2194 | ), | 2194 | ), |
2195 | full_range: 17..37, | 2195 | full_range: 17..37, |
2196 | focus_range: Some( | 2196 | focus_range: Some( |
@@ -2209,7 +2209,7 @@ fn main() { let s<|>t = S{ f1:Arg(0) }; } | |||
2209 | mod_path: "test::Arg", | 2209 | mod_path: "test::Arg", |
2210 | nav: NavigationTarget { | 2210 | nav: NavigationTarget { |
2211 | file_id: FileId( | 2211 | file_id: FileId( |
2212 | 1, | 2212 | 0, |
2213 | ), | 2213 | ), |
2214 | full_range: 0..16, | 2214 | full_range: 0..16, |
2215 | focus_range: Some( | 2215 | focus_range: Some( |
@@ -2248,7 +2248,7 @@ fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } | |||
2248 | mod_path: "test::S", | 2248 | mod_path: "test::S", |
2249 | nav: NavigationTarget { | 2249 | nav: NavigationTarget { |
2250 | file_id: FileId( | 2250 | file_id: FileId( |
2251 | 1, | 2251 | 0, |
2252 | ), | 2252 | ), |
2253 | full_range: 17..37, | 2253 | full_range: 17..37, |
2254 | focus_range: Some( | 2254 | focus_range: Some( |
@@ -2267,7 +2267,7 @@ fn main() { let s<|>t = S{ f1: S{ f1: Arg(0) } }; } | |||
2267 | mod_path: "test::Arg", | 2267 | mod_path: "test::Arg", |
2268 | nav: NavigationTarget { | 2268 | nav: NavigationTarget { |
2269 | file_id: FileId( | 2269 | file_id: FileId( |
2270 | 1, | 2270 | 0, |
2271 | ), | 2271 | ), |
2272 | full_range: 0..16, | 2272 | full_range: 0..16, |
2273 | focus_range: Some( | 2273 | focus_range: Some( |
@@ -2309,7 +2309,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
2309 | mod_path: "test::A", | 2309 | mod_path: "test::A", |
2310 | nav: NavigationTarget { | 2310 | nav: NavigationTarget { |
2311 | file_id: FileId( | 2311 | file_id: FileId( |
2312 | 1, | 2312 | 0, |
2313 | ), | 2313 | ), |
2314 | full_range: 0..14, | 2314 | full_range: 0..14, |
2315 | focus_range: Some( | 2315 | focus_range: Some( |
@@ -2328,7 +2328,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
2328 | mod_path: "test::B", | 2328 | mod_path: "test::B", |
2329 | nav: NavigationTarget { | 2329 | nav: NavigationTarget { |
2330 | file_id: FileId( | 2330 | file_id: FileId( |
2331 | 1, | 2331 | 0, |
2332 | ), | 2332 | ), |
2333 | full_range: 15..29, | 2333 | full_range: 15..29, |
2334 | focus_range: Some( | 2334 | focus_range: Some( |
@@ -2347,7 +2347,7 @@ fn main() { let s<|>t = (A(1), B(2), M::C(3) ); } | |||
2347 | mod_path: "test::M::C", | 2347 | mod_path: "test::M::C", |
2348 | nav: NavigationTarget { | 2348 | nav: NavigationTarget { |
2349 | file_id: FileId( | 2349 | file_id: FileId( |
2350 | 1, | 2350 | 0, |
2351 | ), | 2351 | ), |
2352 | full_range: 42..60, | 2352 | full_range: 42..60, |
2353 | focus_range: Some( | 2353 | focus_range: Some( |
@@ -2386,7 +2386,7 @@ fn main() { let s<|>t = foo(); } | |||
2386 | mod_path: "test::Foo", | 2386 | mod_path: "test::Foo", |
2387 | nav: NavigationTarget { | 2387 | nav: NavigationTarget { |
2388 | file_id: FileId( | 2388 | file_id: FileId( |
2389 | 1, | 2389 | 0, |
2390 | ), | 2390 | ), |
2391 | full_range: 0..12, | 2391 | full_range: 0..12, |
2392 | focus_range: Some( | 2392 | focus_range: Some( |
@@ -2426,7 +2426,7 @@ fn main() { let s<|>t = foo(); } | |||
2426 | mod_path: "test::Foo", | 2426 | mod_path: "test::Foo", |
2427 | nav: NavigationTarget { | 2427 | nav: NavigationTarget { |
2428 | file_id: FileId( | 2428 | file_id: FileId( |
2429 | 1, | 2429 | 0, |
2430 | ), | 2430 | ), |
2431 | full_range: 0..15, | 2431 | full_range: 0..15, |
2432 | focus_range: Some( | 2432 | focus_range: Some( |
@@ -2445,7 +2445,7 @@ fn main() { let s<|>t = foo(); } | |||
2445 | mod_path: "test::S", | 2445 | mod_path: "test::S", |
2446 | nav: NavigationTarget { | 2446 | nav: NavigationTarget { |
2447 | file_id: FileId( | 2447 | file_id: FileId( |
2448 | 1, | 2448 | 0, |
2449 | ), | 2449 | ), |
2450 | full_range: 16..25, | 2450 | full_range: 16..25, |
2451 | focus_range: Some( | 2451 | focus_range: Some( |
@@ -2485,7 +2485,7 @@ fn main() { let s<|>t = foo(); } | |||
2485 | mod_path: "test::Foo", | 2485 | mod_path: "test::Foo", |
2486 | nav: NavigationTarget { | 2486 | nav: NavigationTarget { |
2487 | file_id: FileId( | 2487 | file_id: FileId( |
2488 | 1, | 2488 | 0, |
2489 | ), | 2489 | ), |
2490 | full_range: 0..12, | 2490 | full_range: 0..12, |
2491 | focus_range: Some( | 2491 | focus_range: Some( |
@@ -2504,7 +2504,7 @@ fn main() { let s<|>t = foo(); } | |||
2504 | mod_path: "test::Bar", | 2504 | mod_path: "test::Bar", |
2505 | nav: NavigationTarget { | 2505 | nav: NavigationTarget { |
2506 | file_id: FileId( | 2506 | file_id: FileId( |
2507 | 1, | 2507 | 0, |
2508 | ), | 2508 | ), |
2509 | full_range: 13..25, | 2509 | full_range: 13..25, |
2510 | focus_range: Some( | 2510 | focus_range: Some( |
@@ -2547,7 +2547,7 @@ fn main() { let s<|>t = foo(); } | |||
2547 | mod_path: "test::Foo", | 2547 | mod_path: "test::Foo", |
2548 | nav: NavigationTarget { | 2548 | nav: NavigationTarget { |
2549 | file_id: FileId( | 2549 | file_id: FileId( |
2550 | 1, | 2550 | 0, |
2551 | ), | 2551 | ), |
2552 | full_range: 0..15, | 2552 | full_range: 0..15, |
2553 | focus_range: Some( | 2553 | focus_range: Some( |
@@ -2566,7 +2566,7 @@ fn main() { let s<|>t = foo(); } | |||
2566 | mod_path: "test::Bar", | 2566 | mod_path: "test::Bar", |
2567 | nav: NavigationTarget { | 2567 | nav: NavigationTarget { |
2568 | file_id: FileId( | 2568 | file_id: FileId( |
2569 | 1, | 2569 | 0, |
2570 | ), | 2570 | ), |
2571 | full_range: 16..31, | 2571 | full_range: 16..31, |
2572 | focus_range: Some( | 2572 | focus_range: Some( |
@@ -2585,7 +2585,7 @@ fn main() { let s<|>t = foo(); } | |||
2585 | mod_path: "test::S1", | 2585 | mod_path: "test::S1", |
2586 | nav: NavigationTarget { | 2586 | nav: NavigationTarget { |
2587 | file_id: FileId( | 2587 | file_id: FileId( |
2588 | 1, | 2588 | 0, |
2589 | ), | 2589 | ), |
2590 | full_range: 32..44, | 2590 | full_range: 32..44, |
2591 | focus_range: Some( | 2591 | focus_range: Some( |
@@ -2604,7 +2604,7 @@ fn main() { let s<|>t = foo(); } | |||
2604 | mod_path: "test::S2", | 2604 | mod_path: "test::S2", |
2605 | nav: NavigationTarget { | 2605 | nav: NavigationTarget { |
2606 | file_id: FileId( | 2606 | file_id: FileId( |
2607 | 1, | 2607 | 0, |
2608 | ), | 2608 | ), |
2609 | full_range: 45..57, | 2609 | full_range: 45..57, |
2610 | focus_range: Some( | 2610 | focus_range: Some( |
@@ -2641,7 +2641,7 @@ fn foo(ar<|>g: &impl Foo) {} | |||
2641 | mod_path: "test::Foo", | 2641 | mod_path: "test::Foo", |
2642 | nav: NavigationTarget { | 2642 | nav: NavigationTarget { |
2643 | file_id: FileId( | 2643 | file_id: FileId( |
2644 | 1, | 2644 | 0, |
2645 | ), | 2645 | ), |
2646 | full_range: 0..12, | 2646 | full_range: 0..12, |
2647 | focus_range: Some( | 2647 | focus_range: Some( |
@@ -2681,7 +2681,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2681 | mod_path: "test::Foo", | 2681 | mod_path: "test::Foo", |
2682 | nav: NavigationTarget { | 2682 | nav: NavigationTarget { |
2683 | file_id: FileId( | 2683 | file_id: FileId( |
2684 | 1, | 2684 | 0, |
2685 | ), | 2685 | ), |
2686 | full_range: 0..12, | 2686 | full_range: 0..12, |
2687 | focus_range: Some( | 2687 | focus_range: Some( |
@@ -2700,7 +2700,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2700 | mod_path: "test::Bar", | 2700 | mod_path: "test::Bar", |
2701 | nav: NavigationTarget { | 2701 | nav: NavigationTarget { |
2702 | file_id: FileId( | 2702 | file_id: FileId( |
2703 | 1, | 2703 | 0, |
2704 | ), | 2704 | ), |
2705 | full_range: 13..28, | 2705 | full_range: 13..28, |
2706 | focus_range: Some( | 2706 | focus_range: Some( |
@@ -2719,7 +2719,7 @@ fn foo(ar<|>g: &impl Foo + Bar<S>) {} | |||
2719 | mod_path: "test::S", | 2719 | mod_path: "test::S", |
2720 | nav: NavigationTarget { | 2720 | nav: NavigationTarget { |
2721 | file_id: FileId( | 2721 | file_id: FileId( |
2722 | 1, | 2722 | 0, |
2723 | ), | 2723 | ), |
2724 | full_range: 29..39, | 2724 | full_range: 29..39, |
2725 | focus_range: Some( | 2725 | focus_range: Some( |
@@ -2764,7 +2764,7 @@ mod future { | |||
2764 | mod_path: "test::future::Future", | 2764 | mod_path: "test::future::Future", |
2765 | nav: NavigationTarget { | 2765 | nav: NavigationTarget { |
2766 | file_id: FileId( | 2766 | file_id: FileId( |
2767 | 1, | 2767 | 0, |
2768 | ), | 2768 | ), |
2769 | full_range: 101..163, | 2769 | full_range: 101..163, |
2770 | focus_range: Some( | 2770 | focus_range: Some( |
@@ -2783,7 +2783,7 @@ mod future { | |||
2783 | mod_path: "test::S", | 2783 | mod_path: "test::S", |
2784 | nav: NavigationTarget { | 2784 | nav: NavigationTarget { |
2785 | file_id: FileId( | 2785 | file_id: FileId( |
2786 | 1, | 2786 | 0, |
2787 | ), | 2787 | ), |
2788 | full_range: 0..9, | 2788 | full_range: 0..9, |
2789 | focus_range: Some( | 2789 | focus_range: Some( |
@@ -2821,7 +2821,7 @@ fn foo(ar<|>g: &impl Foo<S>) {} | |||
2821 | mod_path: "test::Foo", | 2821 | mod_path: "test::Foo", |
2822 | nav: NavigationTarget { | 2822 | nav: NavigationTarget { |
2823 | file_id: FileId( | 2823 | file_id: FileId( |
2824 | 1, | 2824 | 0, |
2825 | ), | 2825 | ), |
2826 | full_range: 0..15, | 2826 | full_range: 0..15, |
2827 | focus_range: Some( | 2827 | focus_range: Some( |
@@ -2840,7 +2840,7 @@ fn foo(ar<|>g: &impl Foo<S>) {} | |||
2840 | mod_path: "test::S", | 2840 | mod_path: "test::S", |
2841 | nav: NavigationTarget { | 2841 | nav: NavigationTarget { |
2842 | file_id: FileId( | 2842 | file_id: FileId( |
2843 | 1, | 2843 | 0, |
2844 | ), | 2844 | ), |
2845 | full_range: 16..27, | 2845 | full_range: 16..27, |
2846 | focus_range: Some( | 2846 | focus_range: Some( |
@@ -2883,7 +2883,7 @@ fn main() { let s<|>t = foo(); } | |||
2883 | mod_path: "test::B", | 2883 | mod_path: "test::B", |
2884 | nav: NavigationTarget { | 2884 | nav: NavigationTarget { |
2885 | file_id: FileId( | 2885 | file_id: FileId( |
2886 | 1, | 2886 | 0, |
2887 | ), | 2887 | ), |
2888 | full_range: 42..55, | 2888 | full_range: 42..55, |
2889 | focus_range: Some( | 2889 | focus_range: Some( |
@@ -2902,7 +2902,7 @@ fn main() { let s<|>t = foo(); } | |||
2902 | mod_path: "test::Foo", | 2902 | mod_path: "test::Foo", |
2903 | nav: NavigationTarget { | 2903 | nav: NavigationTarget { |
2904 | file_id: FileId( | 2904 | file_id: FileId( |
2905 | 1, | 2905 | 0, |
2906 | ), | 2906 | ), |
2907 | full_range: 0..12, | 2907 | full_range: 0..12, |
2908 | focus_range: Some( | 2908 | focus_range: Some( |
@@ -2939,7 +2939,7 @@ fn foo(ar<|>g: &dyn Foo) {} | |||
2939 | mod_path: "test::Foo", | 2939 | mod_path: "test::Foo", |
2940 | nav: NavigationTarget { | 2940 | nav: NavigationTarget { |
2941 | file_id: FileId( | 2941 | file_id: FileId( |
2942 | 1, | 2942 | 0, |
2943 | ), | 2943 | ), |
2944 | full_range: 0..12, | 2944 | full_range: 0..12, |
2945 | focus_range: Some( | 2945 | focus_range: Some( |
@@ -2977,7 +2977,7 @@ fn foo(ar<|>g: &dyn Foo<S>) {} | |||
2977 | mod_path: "test::Foo", | 2977 | mod_path: "test::Foo", |
2978 | nav: NavigationTarget { | 2978 | nav: NavigationTarget { |
2979 | file_id: FileId( | 2979 | file_id: FileId( |
2980 | 1, | 2980 | 0, |
2981 | ), | 2981 | ), |
2982 | full_range: 0..15, | 2982 | full_range: 0..15, |
2983 | focus_range: Some( | 2983 | focus_range: Some( |
@@ -2996,7 +2996,7 @@ fn foo(ar<|>g: &dyn Foo<S>) {} | |||
2996 | mod_path: "test::S", | 2996 | mod_path: "test::S", |
2997 | nav: NavigationTarget { | 2997 | nav: NavigationTarget { |
2998 | file_id: FileId( | 2998 | file_id: FileId( |
2999 | 1, | 2999 | 0, |
3000 | ), | 3000 | ), |
3001 | full_range: 16..27, | 3001 | full_range: 16..27, |
3002 | focus_range: Some( | 3002 | focus_range: Some( |
@@ -3037,7 +3037,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3037 | mod_path: "test::ImplTrait", | 3037 | mod_path: "test::ImplTrait", |
3038 | nav: NavigationTarget { | 3038 | nav: NavigationTarget { |
3039 | file_id: FileId( | 3039 | file_id: FileId( |
3040 | 1, | 3040 | 0, |
3041 | ), | 3041 | ), |
3042 | full_range: 0..21, | 3042 | full_range: 0..21, |
3043 | focus_range: Some( | 3043 | focus_range: Some( |
@@ -3056,7 +3056,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3056 | mod_path: "test::B", | 3056 | mod_path: "test::B", |
3057 | nav: NavigationTarget { | 3057 | nav: NavigationTarget { |
3058 | file_id: FileId( | 3058 | file_id: FileId( |
3059 | 1, | 3059 | 0, |
3060 | ), | 3060 | ), |
3061 | full_range: 43..57, | 3061 | full_range: 43..57, |
3062 | focus_range: Some( | 3062 | focus_range: Some( |
@@ -3075,7 +3075,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3075 | mod_path: "test::DynTrait", | 3075 | mod_path: "test::DynTrait", |
3076 | nav: NavigationTarget { | 3076 | nav: NavigationTarget { |
3077 | file_id: FileId( | 3077 | file_id: FileId( |
3078 | 1, | 3078 | 0, |
3079 | ), | 3079 | ), |
3080 | full_range: 22..42, | 3080 | full_range: 22..42, |
3081 | focus_range: Some( | 3081 | focus_range: Some( |
@@ -3094,7 +3094,7 @@ fn foo(a<|>rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {} | |||
3094 | mod_path: "test::S", | 3094 | mod_path: "test::S", |
3095 | nav: NavigationTarget { | 3095 | nav: NavigationTarget { |
3096 | file_id: FileId( | 3096 | file_id: FileId( |
3097 | 1, | 3097 | 0, |
3098 | ), | 3098 | ), |
3099 | full_range: 58..69, | 3099 | full_range: 58..69, |
3100 | focus_range: Some( | 3100 | focus_range: Some( |
@@ -3142,7 +3142,7 @@ fn main() { let s<|>t = test().get(); } | |||
3142 | mod_path: "test::Foo", | 3142 | mod_path: "test::Foo", |
3143 | nav: NavigationTarget { | 3143 | nav: NavigationTarget { |
3144 | file_id: FileId( | 3144 | file_id: FileId( |
3145 | 1, | 3145 | 0, |
3146 | ), | 3146 | ), |
3147 | full_range: 0..62, | 3147 | full_range: 0..62, |
3148 | focus_range: Some( | 3148 | focus_range: Some( |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 583f39d85..1d7e8de56 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -189,7 +189,7 @@ fn get_bind_pat_hints( | |||
189 | 189 | ||
190 | let ty = sema.type_of_pat(&pat.clone().into())?; | 190 | let ty = sema.type_of_pat(&pat.clone().into())?; |
191 | 191 | ||
192 | if should_not_display_type_hint(sema.db, &pat, &ty) { | 192 | if should_not_display_type_hint(sema, &pat, &ty) { |
193 | return None; | 193 | return None; |
194 | } | 194 | } |
195 | 195 | ||
@@ -215,10 +215,12 @@ fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &Typ | |||
215 | } | 215 | } |
216 | 216 | ||
217 | fn should_not_display_type_hint( | 217 | fn should_not_display_type_hint( |
218 | db: &RootDatabase, | 218 | sema: &Semantics<RootDatabase>, |
219 | bind_pat: &ast::IdentPat, | 219 | bind_pat: &ast::IdentPat, |
220 | pat_ty: &Type, | 220 | pat_ty: &Type, |
221 | ) -> bool { | 221 | ) -> bool { |
222 | let db = sema.db; | ||
223 | |||
222 | if pat_ty.is_unknown() { | 224 | if pat_ty.is_unknown() { |
223 | return true; | 225 | return true; |
224 | } | 226 | } |
@@ -249,6 +251,15 @@ fn should_not_display_type_hint( | |||
249 | return it.condition().and_then(|condition| condition.pat()).is_some() | 251 | return it.condition().and_then(|condition| condition.pat()).is_some() |
250 | && pat_is_enum_variant(db, bind_pat, pat_ty); | 252 | && pat_is_enum_variant(db, bind_pat, pat_ty); |
251 | }, | 253 | }, |
254 | ast::ForExpr(it) => { | ||
255 | // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit). | ||
256 | // Type of expr should be iterable. | ||
257 | return it.in_token().is_none() || | ||
258 | it.iterable() | ||
259 | .and_then(|iterable_expr|sema.type_of_expr(&iterable_expr)) | ||
260 | .map(|iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit()) | ||
261 | .unwrap_or(true) | ||
262 | }, | ||
252 | _ => (), | 263 | _ => (), |
253 | } | 264 | } |
254 | } | 265 | } |
@@ -339,14 +350,14 @@ mod tests { | |||
339 | use expect_test::{expect, Expect}; | 350 | use expect_test::{expect, Expect}; |
340 | use test_utils::extract_annotations; | 351 | use test_utils::extract_annotations; |
341 | 352 | ||
342 | use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file}; | 353 | use crate::{fixture, inlay_hints::InlayHintsConfig}; |
343 | 354 | ||
344 | fn check(ra_fixture: &str) { | 355 | fn check(ra_fixture: &str) { |
345 | check_with_config(InlayHintsConfig::default(), ra_fixture); | 356 | check_with_config(InlayHintsConfig::default(), ra_fixture); |
346 | } | 357 | } |
347 | 358 | ||
348 | fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { | 359 | fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) { |
349 | let (analysis, file_id) = single_file(ra_fixture); | 360 | let (analysis, file_id) = fixture::file(ra_fixture); |
350 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); | 361 | let expected = extract_annotations(&*analysis.file_text(file_id).unwrap()); |
351 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); | 362 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); |
352 | let actual = | 363 | let actual = |
@@ -355,7 +366,7 @@ mod tests { | |||
355 | } | 366 | } |
356 | 367 | ||
357 | fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { | 368 | fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { |
358 | let (analysis, file_id) = single_file(ra_fixture); | 369 | let (analysis, file_id) = fixture::file(ra_fixture); |
359 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); | 370 | let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap(); |
360 | expect.assert_debug_eq(&inlay_hints) | 371 | expect.assert_debug_eq(&inlay_hints) |
361 | } | 372 | } |
@@ -496,19 +507,6 @@ fn main() { | |||
496 | } | 507 | } |
497 | 508 | ||
498 | #[test] | 509 | #[test] |
499 | fn for_expression() { | ||
500 | check( | ||
501 | r#" | ||
502 | fn main() { | ||
503 | let mut start = 0; | ||
504 | //^^^^^^^^^ i32 | ||
505 | for increment in 0..2 { start += increment; } | ||
506 | //^^^^^^^^^ i32 | ||
507 | }"#, | ||
508 | ); | ||
509 | } | ||
510 | |||
511 | #[test] | ||
512 | fn if_expr() { | 510 | fn if_expr() { |
513 | check( | 511 | check( |
514 | r#" | 512 | r#" |
@@ -924,4 +922,108 @@ fn main() { | |||
924 | "#]], | 922 | "#]], |
925 | ); | 923 | ); |
926 | } | 924 | } |
925 | |||
926 | #[test] | ||
927 | fn incomplete_for_no_hint() { | ||
928 | check( | ||
929 | r#" | ||
930 | fn main() { | ||
931 | let data = &[1i32, 2, 3]; | ||
932 | //^^^^ &[i32; _] | ||
933 | for i | ||
934 | }"#, | ||
935 | ); | ||
936 | check( | ||
937 | r#" | ||
938 | //- /main.rs crate:main deps:core | ||
939 | pub struct Vec<T> {} | ||
940 | |||
941 | impl<T> Vec<T> { | ||
942 | pub fn new() -> Self { Vec {} } | ||
943 | pub fn push(&mut self, t: T) {} | ||
944 | } | ||
945 | |||
946 | impl<T> IntoIterator for Vec<T> { | ||
947 | type Item=T; | ||
948 | } | ||
949 | |||
950 | fn main() { | ||
951 | let mut data = Vec::new(); | ||
952 | //^^^^^^^^ Vec<&str> | ||
953 | data.push("foo"); | ||
954 | for i in | ||
955 | |||
956 | println!("Unit expr"); | ||
957 | } | ||
958 | |||
959 | //- /core.rs crate:core | ||
960 | #[prelude_import] use iter::*; | ||
961 | mod iter { | ||
962 | trait IntoIterator { | ||
963 | type Item; | ||
964 | } | ||
965 | } | ||
966 | //- /alloc.rs crate:alloc deps:core | ||
967 | mod collections { | ||
968 | struct Vec<T> {} | ||
969 | impl<T> Vec<T> { | ||
970 | fn new() -> Self { Vec {} } | ||
971 | fn push(&mut self, t: T) { } | ||
972 | } | ||
973 | impl<T> IntoIterator for Vec<T> { | ||
974 | type Item=T; | ||
975 | } | ||
976 | } | ||
977 | "#, | ||
978 | ); | ||
979 | } | ||
980 | |||
981 | #[test] | ||
982 | fn complete_for_hint() { | ||
983 | check( | ||
984 | r#" | ||
985 | //- /main.rs crate:main deps:core | ||
986 | pub struct Vec<T> {} | ||
987 | |||
988 | impl<T> Vec<T> { | ||
989 | pub fn new() -> Self { Vec {} } | ||
990 | pub fn push(&mut self, t: T) {} | ||
991 | } | ||
992 | |||
993 | impl<T> IntoIterator for Vec<T> { | ||
994 | type Item=T; | ||
995 | } | ||
996 | |||
997 | fn main() { | ||
998 | let mut data = Vec::new(); | ||
999 | //^^^^^^^^ Vec<&str> | ||
1000 | data.push("foo"); | ||
1001 | for i in data { | ||
1002 | //^ &str | ||
1003 | let z = i; | ||
1004 | //^ &str | ||
1005 | } | ||
1006 | } | ||
1007 | |||
1008 | //- /core.rs crate:core | ||
1009 | #[prelude_import] use iter::*; | ||
1010 | mod iter { | ||
1011 | trait IntoIterator { | ||
1012 | type Item; | ||
1013 | } | ||
1014 | } | ||
1015 | //- /alloc.rs crate:alloc deps:core | ||
1016 | mod collections { | ||
1017 | struct Vec<T> {} | ||
1018 | impl<T> Vec<T> { | ||
1019 | fn new() -> Self { Vec {} } | ||
1020 | fn push(&mut self, t: T) { } | ||
1021 | } | ||
1022 | impl<T> IntoIterator for Vec<T> { | ||
1023 | type Item=T; | ||
1024 | } | ||
1025 | } | ||
1026 | "#, | ||
1027 | ); | ||
1028 | } | ||
927 | } | 1029 | } |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 31f2bcba3..1aa673cf8 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -15,7 +15,8 @@ macro_rules! eprintln { | |||
15 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | 15 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; |
16 | } | 16 | } |
17 | 17 | ||
18 | pub mod mock_analysis; | 18 | #[cfg(test)] |
19 | mod fixture; | ||
19 | 20 | ||
20 | mod markup; | 21 | mod markup; |
21 | mod prime_caches; | 22 | mod prime_caches; |
@@ -86,12 +87,11 @@ pub use assists::{ | |||
86 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, | 87 | utils::MergeBehaviour, Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist, |
87 | }; | 88 | }; |
88 | pub use base_db::{ | 89 | pub use base_db::{ |
89 | Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, | 90 | Canceled, Change, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, |
90 | SourceRootId, | 91 | SourceRootId, |
91 | }; | 92 | }; |
92 | pub use hir::{Documentation, Semantics}; | 93 | pub use hir::{Documentation, Semantics}; |
93 | pub use ide_db::{ | 94 | pub use ide_db::{ |
94 | change::AnalysisChange, | ||
95 | label::Label, | 95 | label::Label, |
96 | line_index::{LineCol, LineIndex}, | 96 | line_index::{LineCol, LineIndex}, |
97 | search::SearchScope, | 97 | search::SearchScope, |
@@ -140,14 +140,10 @@ impl AnalysisHost { | |||
140 | 140 | ||
141 | /// Applies changes to the current state of the world. If there are | 141 | /// Applies changes to the current state of the world. If there are |
142 | /// outstanding snapshots, they will be canceled. | 142 | /// outstanding snapshots, they will be canceled. |
143 | pub fn apply_change(&mut self, change: AnalysisChange) { | 143 | pub fn apply_change(&mut self, change: Change) { |
144 | self.db.apply_change(change) | 144 | self.db.apply_change(change) |
145 | } | 145 | } |
146 | 146 | ||
147 | pub fn maybe_collect_garbage(&mut self) { | ||
148 | self.db.maybe_collect_garbage(); | ||
149 | } | ||
150 | |||
151 | pub fn collect_garbage(&mut self) { | 147 | pub fn collect_garbage(&mut self) { |
152 | self.db.collect_garbage(); | 148 | self.db.collect_garbage(); |
153 | } | 149 | } |
@@ -198,7 +194,7 @@ impl Analysis { | |||
198 | file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string())); | 194 | file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string())); |
199 | let source_root = SourceRoot::new_local(file_set); | 195 | let source_root = SourceRoot::new_local(file_set); |
200 | 196 | ||
201 | let mut change = AnalysisChange::new(); | 197 | let mut change = Change::new(); |
202 | change.set_roots(vec![source_root]); | 198 | change.set_roots(vec![source_root]); |
203 | let mut crate_graph = CrateGraph::default(); | 199 | let mut crate_graph = CrateGraph::default(); |
204 | // FIXME: cfg options | 200 | // FIXME: cfg options |
@@ -220,8 +216,8 @@ impl Analysis { | |||
220 | } | 216 | } |
221 | 217 | ||
222 | /// Debug info about the current state of the analysis. | 218 | /// Debug info about the current state of the analysis. |
223 | pub fn status(&self) -> Cancelable<String> { | 219 | pub fn status(&self, file_id: Option<FileId>) -> Cancelable<String> { |
224 | self.with_db(|db| status::status(&*db)) | 220 | self.with_db(|db| status::status(&*db, file_id)) |
225 | } | 221 | } |
226 | 222 | ||
227 | pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> { | 223 | pub fn prime_caches(&self, files: Vec<FileId>) -> Cancelable<()> { |
diff --git a/crates/ide/src/link_rewrite.rs b/crates/ide/src/link_rewrite.rs index 107787bb9..a16f90e17 100644 --- a/crates/ide/src/link_rewrite.rs +++ b/crates/ide/src/link_rewrite.rs | |||
@@ -120,7 +120,7 @@ fn rewrite_intra_doc_link( | |||
120 | 120 | ||
121 | /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). | 121 | /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`). |
122 | fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<String> { | 122 | fn rewrite_url_link(db: &RootDatabase, def: ModuleDef, target: &str) -> Option<String> { |
123 | if !(target.contains("#") || target.contains(".html")) { | 123 | if !(target.contains('#') || target.contains(".html")) { |
124 | return None; | 124 | return None; |
125 | } | 125 | } |
126 | 126 | ||
@@ -190,7 +190,7 @@ fn strip_prefixes_suffixes(mut s: &str) -> &str { | |||
190 | prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix)); | 190 | prefixes.clone().for_each(|prefix| s = s.trim_start_matches(*prefix)); |
191 | suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix)); | 191 | suffixes.clone().for_each(|suffix| s = s.trim_end_matches(*suffix)); |
192 | }); | 192 | }); |
193 | s.trim_start_matches("@").trim() | 193 | s.trim_start_matches('@').trim() |
194 | } | 194 | } |
195 | 195 | ||
196 | static TYPES: ([&str; 7], [&str; 0]) = | 196 | static TYPES: ([&str; 7], [&str; 0]) = |
diff --git a/crates/ide/src/mock_analysis.rs b/crates/ide/src/mock_analysis.rs deleted file mode 100644 index 235796dbc..000000000 --- a/crates/ide/src/mock_analysis.rs +++ /dev/null | |||
@@ -1,176 +0,0 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | use std::sync::Arc; | ||
3 | |||
4 | use base_db::{CrateName, FileSet, SourceRoot, VfsPath}; | ||
5 | use cfg::CfgOptions; | ||
6 | use test_utils::{ | ||
7 | extract_annotations, extract_range_or_offset, Fixture, RangeOrOffset, CURSOR_MARKER, | ||
8 | }; | ||
9 | |||
10 | use crate::{ | ||
11 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, Edition, FileId, FilePosition, FileRange, | ||
12 | }; | ||
13 | |||
14 | /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis | ||
15 | /// from a set of in-memory files. | ||
16 | #[derive(Debug, Default)] | ||
17 | pub struct MockAnalysis { | ||
18 | files: Vec<Fixture>, | ||
19 | } | ||
20 | |||
21 | impl MockAnalysis { | ||
22 | /// Creates `MockAnalysis` using a fixture data in the following format: | ||
23 | /// | ||
24 | /// ```not_rust | ||
25 | /// //- /main.rs | ||
26 | /// mod foo; | ||
27 | /// fn main() {} | ||
28 | /// | ||
29 | /// //- /foo.rs | ||
30 | /// struct Baz; | ||
31 | /// ``` | ||
32 | pub fn with_files(ra_fixture: &str) -> MockAnalysis { | ||
33 | let (res, pos) = MockAnalysis::with_fixture(ra_fixture); | ||
34 | assert!(pos.is_none()); | ||
35 | res | ||
36 | } | ||
37 | |||
38 | /// Same as `with_files`, but requires that a single file contains a `<|>` marker, | ||
39 | /// whose position is also returned. | ||
40 | pub fn with_files_and_position(fixture: &str) -> (MockAnalysis, FilePosition) { | ||
41 | let (res, position) = MockAnalysis::with_fixture(fixture); | ||
42 | let (file_id, range_or_offset) = position.expect("expected a marker (<|>)"); | ||
43 | let offset = match range_or_offset { | ||
44 | RangeOrOffset::Range(_) => panic!(), | ||
45 | RangeOrOffset::Offset(it) => it, | ||
46 | }; | ||
47 | (res, FilePosition { file_id, offset }) | ||
48 | } | ||
49 | |||
50 | fn with_fixture(fixture: &str) -> (MockAnalysis, Option<(FileId, RangeOrOffset)>) { | ||
51 | let mut position = None; | ||
52 | let mut res = MockAnalysis::default(); | ||
53 | for mut entry in Fixture::parse(fixture) { | ||
54 | if entry.text.contains(CURSOR_MARKER) { | ||
55 | assert!(position.is_none(), "only one marker (<|>) per fixture is allowed"); | ||
56 | let (range_or_offset, text) = extract_range_or_offset(&entry.text); | ||
57 | entry.text = text; | ||
58 | let file_id = res.add_file_fixture(entry); | ||
59 | position = Some((file_id, range_or_offset)); | ||
60 | } else { | ||
61 | res.add_file_fixture(entry); | ||
62 | } | ||
63 | } | ||
64 | (res, position) | ||
65 | } | ||
66 | |||
67 | fn add_file_fixture(&mut self, fixture: Fixture) -> FileId { | ||
68 | let file_id = FileId((self.files.len() + 1) as u32); | ||
69 | self.files.push(fixture); | ||
70 | file_id | ||
71 | } | ||
72 | |||
73 | pub fn id_of(&self, path: &str) -> FileId { | ||
74 | let (file_id, _) = | ||
75 | self.files().find(|(_, data)| path == data.path).expect("no file in this mock"); | ||
76 | file_id | ||
77 | } | ||
78 | pub fn annotations(&self) -> Vec<(FileRange, String)> { | ||
79 | self.files() | ||
80 | .flat_map(|(file_id, fixture)| { | ||
81 | let annotations = extract_annotations(&fixture.text); | ||
82 | annotations | ||
83 | .into_iter() | ||
84 | .map(move |(range, data)| (FileRange { file_id, range }, data)) | ||
85 | }) | ||
86 | .collect() | ||
87 | } | ||
88 | pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ { | ||
89 | self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture)) | ||
90 | } | ||
91 | pub fn annotation(&self) -> (FileRange, String) { | ||
92 | let mut all = self.annotations(); | ||
93 | assert_eq!(all.len(), 1); | ||
94 | all.pop().unwrap() | ||
95 | } | ||
96 | pub fn analysis_host(self) -> AnalysisHost { | ||
97 | let mut host = AnalysisHost::default(); | ||
98 | let mut change = AnalysisChange::new(); | ||
99 | let mut file_set = FileSet::default(); | ||
100 | let mut crate_graph = CrateGraph::default(); | ||
101 | let mut root_crate = None; | ||
102 | for (i, data) in self.files.into_iter().enumerate() { | ||
103 | let path = data.path; | ||
104 | assert!(path.starts_with('/')); | ||
105 | |||
106 | let mut cfg = CfgOptions::default(); | ||
107 | data.cfg_atoms.iter().for_each(|it| cfg.insert_atom(it.into())); | ||
108 | data.cfg_key_values.iter().for_each(|(k, v)| cfg.insert_key_value(k.into(), v.into())); | ||
109 | let edition: Edition = | ||
110 | data.edition.and_then(|it| it.parse().ok()).unwrap_or(Edition::Edition2018); | ||
111 | |||
112 | let file_id = FileId(i as u32 + 1); | ||
113 | let env = data.env.into_iter().collect(); | ||
114 | if path == "/lib.rs" || path == "/main.rs" { | ||
115 | root_crate = Some(crate_graph.add_crate_root( | ||
116 | file_id, | ||
117 | edition, | ||
118 | Some("test".to_string()), | ||
119 | cfg, | ||
120 | env, | ||
121 | Default::default(), | ||
122 | )); | ||
123 | } else if path.ends_with("/lib.rs") { | ||
124 | let base = &path[..path.len() - "/lib.rs".len()]; | ||
125 | let crate_name = &base[base.rfind('/').unwrap() + '/'.len_utf8()..]; | ||
126 | let other_crate = crate_graph.add_crate_root( | ||
127 | file_id, | ||
128 | edition, | ||
129 | Some(crate_name.to_string()), | ||
130 | cfg, | ||
131 | env, | ||
132 | Default::default(), | ||
133 | ); | ||
134 | if let Some(root_crate) = root_crate { | ||
135 | crate_graph | ||
136 | .add_dep(root_crate, CrateName::new(crate_name).unwrap(), other_crate) | ||
137 | .unwrap(); | ||
138 | } | ||
139 | } | ||
140 | let path = VfsPath::new_virtual_path(path.to_string()); | ||
141 | file_set.insert(file_id, path); | ||
142 | change.change_file(file_id, Some(Arc::new(data.text).to_owned())); | ||
143 | } | ||
144 | change.set_crate_graph(crate_graph); | ||
145 | change.set_roots(vec![SourceRoot::new_local(file_set)]); | ||
146 | host.apply_change(change); | ||
147 | host | ||
148 | } | ||
149 | pub fn analysis(self) -> Analysis { | ||
150 | self.analysis_host().analysis() | ||
151 | } | ||
152 | } | ||
153 | |||
154 | /// Creates analysis from a multi-file fixture, returns positions marked with <|>. | ||
155 | pub fn analysis_and_position(ra_fixture: &str) -> (Analysis, FilePosition) { | ||
156 | let (mock, position) = MockAnalysis::with_files_and_position(ra_fixture); | ||
157 | (mock.analysis(), position) | ||
158 | } | ||
159 | |||
160 | /// Creates analysis for a single file. | ||
161 | pub fn single_file(ra_fixture: &str) -> (Analysis, FileId) { | ||
162 | let mock = MockAnalysis::with_files(ra_fixture); | ||
163 | let file_id = mock.id_of("/main.rs"); | ||
164 | (mock.analysis(), file_id) | ||
165 | } | ||
166 | |||
167 | /// Creates analysis for a single file, returns range marked with a pair of <|>. | ||
168 | pub fn analysis_and_range(ra_fixture: &str) -> (Analysis, FileRange) { | ||
169 | let (res, position) = MockAnalysis::with_fixture(ra_fixture); | ||
170 | let (file_id, range_or_offset) = position.expect("expected a marker (<|>)"); | ||
171 | let range = match range_or_offset { | ||
172 | RangeOrOffset::Range(it) => it, | ||
173 | RangeOrOffset::Offset(_) => panic!(), | ||
174 | }; | ||
175 | (res.analysis(), FileRange { file_id, range }) | ||
176 | } | ||
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 59ed2967c..ef94acfec 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs | |||
@@ -63,19 +63,13 @@ pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { | |||
63 | 63 | ||
64 | #[cfg(test)] | 64 | #[cfg(test)] |
65 | mod tests { | 65 | mod tests { |
66 | use base_db::Env; | ||
67 | use cfg::CfgOptions; | ||
68 | use test_utils::mark; | 66 | use test_utils::mark; |
69 | 67 | ||
70 | use crate::{ | 68 | use crate::fixture::{self}; |
71 | mock_analysis::{analysis_and_position, MockAnalysis}, | ||
72 | AnalysisChange, CrateGraph, | ||
73 | Edition::Edition2018, | ||
74 | }; | ||
75 | 69 | ||
76 | #[test] | 70 | #[test] |
77 | fn test_resolve_parent_module() { | 71 | fn test_resolve_parent_module() { |
78 | let (analysis, pos) = analysis_and_position( | 72 | let (analysis, pos) = fixture::position( |
79 | " | 73 | " |
80 | //- /lib.rs | 74 | //- /lib.rs |
81 | mod foo; | 75 | mod foo; |
@@ -84,13 +78,13 @@ mod tests { | |||
84 | ", | 78 | ", |
85 | ); | 79 | ); |
86 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 80 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
87 | nav.assert_match("foo MODULE FileId(1) 0..8"); | 81 | nav.assert_match("foo MODULE FileId(0) 0..8"); |
88 | } | 82 | } |
89 | 83 | ||
90 | #[test] | 84 | #[test] |
91 | fn test_resolve_parent_module_on_module_decl() { | 85 | fn test_resolve_parent_module_on_module_decl() { |
92 | mark::check!(test_resolve_parent_module_on_module_decl); | 86 | mark::check!(test_resolve_parent_module_on_module_decl); |
93 | let (analysis, pos) = analysis_and_position( | 87 | let (analysis, pos) = fixture::position( |
94 | " | 88 | " |
95 | //- /lib.rs | 89 | //- /lib.rs |
96 | mod foo; | 90 | mod foo; |
@@ -103,12 +97,12 @@ mod tests { | |||
103 | ", | 97 | ", |
104 | ); | 98 | ); |
105 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 99 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
106 | nav.assert_match("foo MODULE FileId(1) 0..8"); | 100 | nav.assert_match("foo MODULE FileId(0) 0..8"); |
107 | } | 101 | } |
108 | 102 | ||
109 | #[test] | 103 | #[test] |
110 | fn test_resolve_parent_module_for_inline() { | 104 | fn test_resolve_parent_module_for_inline() { |
111 | let (analysis, pos) = analysis_and_position( | 105 | let (analysis, pos) = fixture::position( |
112 | " | 106 | " |
113 | //- /lib.rs | 107 | //- /lib.rs |
114 | mod foo { | 108 | mod foo { |
@@ -119,37 +113,19 @@ mod tests { | |||
119 | ", | 113 | ", |
120 | ); | 114 | ); |
121 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); | 115 | let nav = analysis.parent_module(pos).unwrap().pop().unwrap(); |
122 | nav.assert_match("baz MODULE FileId(1) 32..44"); | 116 | nav.assert_match("baz MODULE FileId(0) 32..44"); |
123 | } | 117 | } |
124 | 118 | ||
125 | #[test] | 119 | #[test] |
126 | fn test_resolve_crate_root() { | 120 | fn test_resolve_crate_root() { |
127 | let mock = MockAnalysis::with_files( | 121 | let (analysis, file_id) = fixture::file( |
128 | r#" | 122 | r#" |
129 | //- /bar.rs | 123 | //- /main.rs |
130 | mod foo; | 124 | mod foo; |
131 | //- /foo.rs | 125 | //- /foo.rs |
132 | // empty | 126 | <|> |
133 | "#, | 127 | "#, |
134 | ); | 128 | ); |
135 | let root_file = mock.id_of("/bar.rs"); | 129 | assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); |
136 | let mod_file = mock.id_of("/foo.rs"); | ||
137 | let mut host = mock.analysis_host(); | ||
138 | assert!(host.analysis().crate_for(mod_file).unwrap().is_empty()); | ||
139 | |||
140 | let mut crate_graph = CrateGraph::default(); | ||
141 | let crate_id = crate_graph.add_crate_root( | ||
142 | root_file, | ||
143 | Edition2018, | ||
144 | None, | ||
145 | CfgOptions::default(), | ||
146 | Env::default(), | ||
147 | Default::default(), | ||
148 | ); | ||
149 | let mut change = AnalysisChange::new(); | ||
150 | change.set_crate_graph(crate_graph); | ||
151 | host.apply_change(change); | ||
152 | |||
153 | assert_eq!(host.analysis().crate_for(mod_file).unwrap(), vec![crate_id]); | ||
154 | } | 130 | } |
155 | } | 131 | } |
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 722c8f406..e0830eb4f 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs | |||
@@ -190,14 +190,15 @@ fn get_struct_def_name_for_struct_literal_search( | |||
190 | 190 | ||
191 | #[cfg(test)] | 191 | #[cfg(test)] |
192 | mod tests { | 192 | mod tests { |
193 | use crate::{ | 193 | use base_db::FileId; |
194 | mock_analysis::{analysis_and_position, MockAnalysis}, | 194 | use expect_test::{expect, Expect}; |
195 | Declaration, Reference, ReferenceSearchResult, SearchScope, | 195 | use stdx::format_to; |
196 | }; | 196 | |
197 | use crate::{fixture, SearchScope}; | ||
197 | 198 | ||
198 | #[test] | 199 | #[test] |
199 | fn test_struct_literal_after_space() { | 200 | fn test_struct_literal_after_space() { |
200 | let refs = get_all_refs( | 201 | check( |
201 | r#" | 202 | r#" |
202 | struct Foo <|>{ | 203 | struct Foo <|>{ |
203 | a: i32, | 204 | a: i32, |
@@ -210,17 +211,17 @@ fn main() { | |||
210 | f = Foo {a: Foo::f()}; | 211 | f = Foo {a: Foo::f()}; |
211 | } | 212 | } |
212 | "#, | 213 | "#, |
213 | ); | 214 | expect![[r#" |
214 | check_result( | 215 | Foo STRUCT FileId(0) 0..26 7..10 Other |
215 | refs, | 216 | |
216 | "Foo STRUCT FileId(1) 0..26 7..10 Other", | 217 | FileId(0) 101..104 StructLiteral |
217 | &["FileId(1) 101..104 StructLiteral"], | 218 | "#]], |
218 | ); | 219 | ); |
219 | } | 220 | } |
220 | 221 | ||
221 | #[test] | 222 | #[test] |
222 | fn test_struct_literal_before_space() { | 223 | fn test_struct_literal_before_space() { |
223 | let refs = get_all_refs( | 224 | check( |
224 | r#" | 225 | r#" |
225 | struct Foo<|> {} | 226 | struct Foo<|> {} |
226 | fn main() { | 227 | fn main() { |
@@ -228,17 +229,18 @@ struct Foo<|> {} | |||
228 | f = Foo {}; | 229 | f = Foo {}; |
229 | } | 230 | } |
230 | "#, | 231 | "#, |
231 | ); | 232 | expect![[r#" |
232 | check_result( | 233 | Foo STRUCT FileId(0) 0..13 7..10 Other |
233 | refs, | 234 | |
234 | "Foo STRUCT FileId(1) 0..13 7..10 Other", | 235 | FileId(0) 41..44 Other |
235 | &["FileId(1) 41..44 Other", "FileId(1) 54..57 StructLiteral"], | 236 | FileId(0) 54..57 StructLiteral |
237 | "#]], | ||
236 | ); | 238 | ); |
237 | } | 239 | } |
238 | 240 | ||
239 | #[test] | 241 | #[test] |
240 | fn test_struct_literal_with_generic_type() { | 242 | fn test_struct_literal_with_generic_type() { |
241 | let refs = get_all_refs( | 243 | check( |
242 | r#" | 244 | r#" |
243 | struct Foo<T> <|>{} | 245 | struct Foo<T> <|>{} |
244 | fn main() { | 246 | fn main() { |
@@ -246,17 +248,17 @@ struct Foo<T> <|>{} | |||
246 | f = Foo {}; | 248 | f = Foo {}; |
247 | } | 249 | } |
248 | "#, | 250 | "#, |
249 | ); | 251 | expect![[r#" |
250 | check_result( | 252 | Foo STRUCT FileId(0) 0..16 7..10 Other |
251 | refs, | 253 | |
252 | "Foo STRUCT FileId(1) 0..16 7..10 Other", | 254 | FileId(0) 64..67 StructLiteral |
253 | &["FileId(1) 64..67 StructLiteral"], | 255 | "#]], |
254 | ); | 256 | ); |
255 | } | 257 | } |
256 | 258 | ||
257 | #[test] | 259 | #[test] |
258 | fn test_struct_literal_for_tuple() { | 260 | fn test_struct_literal_for_tuple() { |
259 | let refs = get_all_refs( | 261 | check( |
260 | r#" | 262 | r#" |
261 | struct Foo<|>(i32); | 263 | struct Foo<|>(i32); |
262 | 264 | ||
@@ -265,17 +267,17 @@ fn main() { | |||
265 | f = Foo(1); | 267 | f = Foo(1); |
266 | } | 268 | } |
267 | "#, | 269 | "#, |
268 | ); | 270 | expect![[r#" |
269 | check_result( | 271 | Foo STRUCT FileId(0) 0..16 7..10 Other |
270 | refs, | 272 | |
271 | "Foo STRUCT FileId(1) 0..16 7..10 Other", | 273 | FileId(0) 54..57 StructLiteral |
272 | &["FileId(1) 54..57 StructLiteral"], | 274 | "#]], |
273 | ); | 275 | ); |
274 | } | 276 | } |
275 | 277 | ||
276 | #[test] | 278 | #[test] |
277 | fn test_find_all_refs_for_local() { | 279 | fn test_find_all_refs_for_local() { |
278 | let refs = get_all_refs( | 280 | check( |
279 | r#" | 281 | r#" |
280 | fn main() { | 282 | fn main() { |
281 | let mut i = 1; | 283 | let mut i = 1; |
@@ -288,22 +290,20 @@ fn main() { | |||
288 | 290 | ||
289 | i = 5; | 291 | i = 5; |
290 | }"#, | 292 | }"#, |
291 | ); | 293 | expect![[r#" |
292 | check_result( | 294 | i IDENT_PAT FileId(0) 24..25 Other Write |
293 | refs, | 295 | |
294 | "i IDENT_PAT FileId(1) 24..25 Other Write", | 296 | FileId(0) 50..51 Other Write |
295 | &[ | 297 | FileId(0) 54..55 Other Read |
296 | "FileId(1) 50..51 Other Write", | 298 | FileId(0) 76..77 Other Write |
297 | "FileId(1) 54..55 Other Read", | 299 | FileId(0) 94..95 Other Write |
298 | "FileId(1) 76..77 Other Write", | 300 | "#]], |
299 | "FileId(1) 94..95 Other Write", | ||
300 | ], | ||
301 | ); | 301 | ); |
302 | } | 302 | } |
303 | 303 | ||
304 | #[test] | 304 | #[test] |
305 | fn search_filters_by_range() { | 305 | fn search_filters_by_range() { |
306 | let refs = get_all_refs( | 306 | check( |
307 | r#" | 307 | r#" |
308 | fn foo() { | 308 | fn foo() { |
309 | let spam<|> = 92; | 309 | let spam<|> = 92; |
@@ -314,41 +314,46 @@ fn bar() { | |||
314 | spam + spam | 314 | spam + spam |
315 | } | 315 | } |
316 | "#, | 316 | "#, |
317 | ); | 317 | expect![[r#" |
318 | check_result( | 318 | spam IDENT_PAT FileId(0) 19..23 Other |
319 | refs, | 319 | |
320 | "spam IDENT_PAT FileId(1) 19..23 Other", | 320 | FileId(0) 34..38 Other Read |
321 | &["FileId(1) 34..38 Other Read", "FileId(1) 41..45 Other Read"], | 321 | FileId(0) 41..45 Other Read |
322 | "#]], | ||
322 | ); | 323 | ); |
323 | } | 324 | } |
324 | 325 | ||
325 | #[test] | 326 | #[test] |
326 | fn test_find_all_refs_for_param_inside() { | 327 | fn test_find_all_refs_for_param_inside() { |
327 | let refs = get_all_refs( | 328 | check( |
328 | r#" | 329 | r#" |
329 | fn foo(i : u32) -> u32 { | 330 | fn foo(i : u32) -> u32 { i<|> } |
330 | i<|> | ||
331 | } | ||
332 | "#, | 331 | "#, |
332 | expect![[r#" | ||
333 | i IDENT_PAT FileId(0) 7..8 Other | ||
334 | |||
335 | FileId(0) 25..26 Other Read | ||
336 | "#]], | ||
333 | ); | 337 | ); |
334 | check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]); | ||
335 | } | 338 | } |
336 | 339 | ||
337 | #[test] | 340 | #[test] |
338 | fn test_find_all_refs_for_fn_param() { | 341 | fn test_find_all_refs_for_fn_param() { |
339 | let refs = get_all_refs( | 342 | check( |
340 | r#" | 343 | r#" |
341 | fn foo(i<|> : u32) -> u32 { | 344 | fn foo(i<|> : u32) -> u32 { i } |
342 | i | ||
343 | } | ||
344 | "#, | 345 | "#, |
346 | expect![[r#" | ||
347 | i IDENT_PAT FileId(0) 7..8 Other | ||
348 | |||
349 | FileId(0) 25..26 Other Read | ||
350 | "#]], | ||
345 | ); | 351 | ); |
346 | check_result(refs, "i IDENT_PAT FileId(1) 7..8 Other", &["FileId(1) 29..30 Other Read"]); | ||
347 | } | 352 | } |
348 | 353 | ||
349 | #[test] | 354 | #[test] |
350 | fn test_find_all_refs_field_name() { | 355 | fn test_find_all_refs_field_name() { |
351 | let refs = get_all_refs( | 356 | check( |
352 | r#" | 357 | r#" |
353 | //- /lib.rs | 358 | //- /lib.rs |
354 | struct Foo { | 359 | struct Foo { |
@@ -359,30 +364,33 @@ fn main(s: Foo) { | |||
359 | let f = s.spam; | 364 | let f = s.spam; |
360 | } | 365 | } |
361 | "#, | 366 | "#, |
362 | ); | 367 | expect![[r#" |
363 | check_result( | 368 | spam RECORD_FIELD FileId(0) 17..30 21..25 Other |
364 | refs, | 369 | |
365 | "spam RECORD_FIELD FileId(1) 17..30 21..25 Other", | 370 | FileId(0) 67..71 Other Read |
366 | &["FileId(1) 67..71 Other Read"], | 371 | "#]], |
367 | ); | 372 | ); |
368 | } | 373 | } |
369 | 374 | ||
370 | #[test] | 375 | #[test] |
371 | fn test_find_all_refs_impl_item_name() { | 376 | fn test_find_all_refs_impl_item_name() { |
372 | let refs = get_all_refs( | 377 | check( |
373 | r#" | 378 | r#" |
374 | struct Foo; | 379 | struct Foo; |
375 | impl Foo { | 380 | impl Foo { |
376 | fn f<|>(&self) { } | 381 | fn f<|>(&self) { } |
377 | } | 382 | } |
378 | "#, | 383 | "#, |
384 | expect![[r#" | ||
385 | f FN FileId(0) 27..43 30..31 Other | ||
386 | |||
387 | "#]], | ||
379 | ); | 388 | ); |
380 | check_result(refs, "f FN FileId(1) 27..43 30..31 Other", &[]); | ||
381 | } | 389 | } |
382 | 390 | ||
383 | #[test] | 391 | #[test] |
384 | fn test_find_all_refs_enum_var_name() { | 392 | fn test_find_all_refs_enum_var_name() { |
385 | let refs = get_all_refs( | 393 | check( |
386 | r#" | 394 | r#" |
387 | enum Foo { | 395 | enum Foo { |
388 | A, | 396 | A, |
@@ -390,13 +398,16 @@ enum Foo { | |||
390 | C, | 398 | C, |
391 | } | 399 | } |
392 | "#, | 400 | "#, |
401 | expect![[r#" | ||
402 | B VARIANT FileId(0) 22..23 22..23 Other | ||
403 | |||
404 | "#]], | ||
393 | ); | 405 | ); |
394 | check_result(refs, "B VARIANT FileId(1) 22..23 22..23 Other", &[]); | ||
395 | } | 406 | } |
396 | 407 | ||
397 | #[test] | 408 | #[test] |
398 | fn test_find_all_refs_two_modules() { | 409 | fn test_find_all_refs_two_modules() { |
399 | let (analysis, pos) = analysis_and_position( | 410 | check( |
400 | r#" | 411 | r#" |
401 | //- /lib.rs | 412 | //- /lib.rs |
402 | pub mod foo; | 413 | pub mod foo; |
@@ -428,12 +439,12 @@ fn f() { | |||
428 | let i = foo::Foo<|> { n: 5 }; | 439 | let i = foo::Foo<|> { n: 5 }; |
429 | } | 440 | } |
430 | "#, | 441 | "#, |
431 | ); | 442 | expect![[r#" |
432 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 443 | Foo STRUCT FileId(1) 17..51 28..31 Other |
433 | check_result( | 444 | |
434 | refs, | 445 | FileId(0) 53..56 StructLiteral |
435 | "Foo STRUCT FileId(2) 17..51 28..31 Other", | 446 | FileId(2) 79..82 StructLiteral |
436 | &["FileId(1) 53..56 StructLiteral", "FileId(3) 79..82 StructLiteral"], | 447 | "#]], |
437 | ); | 448 | ); |
438 | } | 449 | } |
439 | 450 | ||
@@ -442,7 +453,7 @@ fn f() { | |||
442 | // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. | 453 | // which is the whole `foo.rs`, and the second one is in `use foo::Foo`. |
443 | #[test] | 454 | #[test] |
444 | fn test_find_all_refs_decl_module() { | 455 | fn test_find_all_refs_decl_module() { |
445 | let (analysis, pos) = analysis_and_position( | 456 | check( |
446 | r#" | 457 | r#" |
447 | //- /lib.rs | 458 | //- /lib.rs |
448 | mod foo<|>; | 459 | mod foo<|>; |
@@ -458,14 +469,17 @@ pub struct Foo { | |||
458 | pub n: u32, | 469 | pub n: u32, |
459 | } | 470 | } |
460 | "#, | 471 | "#, |
472 | expect![[r#" | ||
473 | foo SOURCE_FILE FileId(1) 0..35 Other | ||
474 | |||
475 | FileId(0) 14..17 Other | ||
476 | "#]], | ||
461 | ); | 477 | ); |
462 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | ||
463 | check_result(refs, "foo SOURCE_FILE FileId(2) 0..35 Other", &["FileId(1) 14..17 Other"]); | ||
464 | } | 478 | } |
465 | 479 | ||
466 | #[test] | 480 | #[test] |
467 | fn test_find_all_refs_super_mod_vis() { | 481 | fn test_find_all_refs_super_mod_vis() { |
468 | let (analysis, pos) = analysis_and_position( | 482 | check( |
469 | r#" | 483 | r#" |
470 | //- /lib.rs | 484 | //- /lib.rs |
471 | mod foo; | 485 | mod foo; |
@@ -483,12 +497,12 @@ pub(super) struct Foo<|> { | |||
483 | pub n: u32, | 497 | pub n: u32, |
484 | } | 498 | } |
485 | "#, | 499 | "#, |
486 | ); | 500 | expect![[r#" |
487 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 501 | Foo STRUCT FileId(2) 0..41 18..21 Other |
488 | check_result( | 502 | |
489 | refs, | 503 | FileId(1) 20..23 Other |
490 | "Foo STRUCT FileId(3) 0..41 18..21 Other", | 504 | FileId(1) 47..50 StructLiteral |
491 | &["FileId(2) 20..23 Other", "FileId(2) 47..50 StructLiteral"], | 505 | "#]], |
492 | ); | 506 | ); |
493 | } | 507 | } |
494 | 508 | ||
@@ -508,29 +522,31 @@ pub(super) struct Foo<|> { | |||
508 | fn f() { super::quux(); } | 522 | fn f() { super::quux(); } |
509 | "#; | 523 | "#; |
510 | 524 | ||
511 | let (mock, pos) = MockAnalysis::with_files_and_position(code); | 525 | check_with_scope( |
512 | let bar = mock.id_of("/bar.rs"); | 526 | code, |
513 | let analysis = mock.analysis(); | 527 | None, |
528 | expect![[r#" | ||
529 | quux FN FileId(0) 19..35 26..30 Other | ||
514 | 530 | ||
515 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 531 | FileId(1) 16..20 StructLiteral |
516 | check_result( | 532 | FileId(2) 16..20 StructLiteral |
517 | refs, | 533 | "#]], |
518 | "quux FN FileId(1) 19..35 26..30 Other", | ||
519 | &["FileId(2) 16..20 StructLiteral", "FileId(3) 16..20 StructLiteral"], | ||
520 | ); | 534 | ); |
521 | 535 | ||
522 | let refs = | 536 | check_with_scope( |
523 | analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap(); | 537 | code, |
524 | check_result( | 538 | Some(SearchScope::single_file(FileId(2))), |
525 | refs, | 539 | expect![[r#" |
526 | "quux FN FileId(1) 19..35 26..30 Other", | 540 | quux FN FileId(0) 19..35 26..30 Other |
527 | &["FileId(3) 16..20 StructLiteral"], | 541 | |
542 | FileId(2) 16..20 StructLiteral | ||
543 | "#]], | ||
528 | ); | 544 | ); |
529 | } | 545 | } |
530 | 546 | ||
531 | #[test] | 547 | #[test] |
532 | fn test_find_all_refs_macro_def() { | 548 | fn test_find_all_refs_macro_def() { |
533 | let refs = get_all_refs( | 549 | check( |
534 | r#" | 550 | r#" |
535 | #[macro_export] | 551 | #[macro_export] |
536 | macro_rules! m1<|> { () => (()) } | 552 | macro_rules! m1<|> { () => (()) } |
@@ -540,34 +556,36 @@ fn foo() { | |||
540 | m1(); | 556 | m1(); |
541 | } | 557 | } |
542 | "#, | 558 | "#, |
543 | ); | 559 | expect![[r#" |
544 | check_result( | 560 | m1 MACRO_CALL FileId(0) 0..46 29..31 Other |
545 | refs, | 561 | |
546 | "m1 MACRO_CALL FileId(1) 0..46 29..31 Other", | 562 | FileId(0) 63..65 StructLiteral |
547 | &["FileId(1) 63..65 StructLiteral", "FileId(1) 73..75 StructLiteral"], | 563 | FileId(0) 73..75 StructLiteral |
564 | "#]], | ||
548 | ); | 565 | ); |
549 | } | 566 | } |
550 | 567 | ||
551 | #[test] | 568 | #[test] |
552 | fn test_basic_highlight_read_write() { | 569 | fn test_basic_highlight_read_write() { |
553 | let refs = get_all_refs( | 570 | check( |
554 | r#" | 571 | r#" |
555 | fn foo() { | 572 | fn foo() { |
556 | let mut i<|> = 0; | 573 | let mut i<|> = 0; |
557 | i = i + 1; | 574 | i = i + 1; |
558 | } | 575 | } |
559 | "#, | 576 | "#, |
560 | ); | 577 | expect![[r#" |
561 | check_result( | 578 | i IDENT_PAT FileId(0) 23..24 Other Write |
562 | refs, | 579 | |
563 | "i IDENT_PAT FileId(1) 23..24 Other Write", | 580 | FileId(0) 34..35 Other Write |
564 | &["FileId(1) 34..35 Other Write", "FileId(1) 38..39 Other Read"], | 581 | FileId(0) 38..39 Other Read |
582 | "#]], | ||
565 | ); | 583 | ); |
566 | } | 584 | } |
567 | 585 | ||
568 | #[test] | 586 | #[test] |
569 | fn test_basic_highlight_field_read_write() { | 587 | fn test_basic_highlight_field_read_write() { |
570 | let refs = get_all_refs( | 588 | check( |
571 | r#" | 589 | r#" |
572 | struct S { | 590 | struct S { |
573 | f: u32, | 591 | f: u32, |
@@ -578,38 +596,41 @@ fn foo() { | |||
578 | s.f<|> = 0; | 596 | s.f<|> = 0; |
579 | } | 597 | } |
580 | "#, | 598 | "#, |
581 | ); | 599 | expect![[r#" |
582 | check_result( | 600 | f RECORD_FIELD FileId(0) 15..21 15..16 Other |
583 | refs, | 601 | |
584 | "f RECORD_FIELD FileId(1) 15..21 15..16 Other", | 602 | FileId(0) 55..56 Other Read |
585 | &["FileId(1) 55..56 Other Read", "FileId(1) 68..69 Other Write"], | 603 | FileId(0) 68..69 Other Write |
604 | "#]], | ||
586 | ); | 605 | ); |
587 | } | 606 | } |
588 | 607 | ||
589 | #[test] | 608 | #[test] |
590 | fn test_basic_highlight_decl_no_write() { | 609 | fn test_basic_highlight_decl_no_write() { |
591 | let refs = get_all_refs( | 610 | check( |
592 | r#" | 611 | r#" |
593 | fn foo() { | 612 | fn foo() { |
594 | let i<|>; | 613 | let i<|>; |
595 | i = 1; | 614 | i = 1; |
596 | } | 615 | } |
597 | "#, | 616 | "#, |
617 | expect![[r#" | ||
618 | i IDENT_PAT FileId(0) 19..20 Other | ||
619 | |||
620 | FileId(0) 26..27 Other Write | ||
621 | "#]], | ||
598 | ); | 622 | ); |
599 | check_result(refs, "i IDENT_PAT FileId(1) 19..20 Other", &["FileId(1) 26..27 Other Write"]); | ||
600 | } | 623 | } |
601 | 624 | ||
602 | #[test] | 625 | #[test] |
603 | fn test_find_struct_function_refs_outside_module() { | 626 | fn test_find_struct_function_refs_outside_module() { |
604 | let refs = get_all_refs( | 627 | check( |
605 | r#" | 628 | r#" |
606 | mod foo { | 629 | mod foo { |
607 | pub struct Foo; | 630 | pub struct Foo; |
608 | 631 | ||
609 | impl Foo { | 632 | impl Foo { |
610 | pub fn new<|>() -> Foo { | 633 | pub fn new<|>() -> Foo { Foo } |
611 | Foo | ||
612 | } | ||
613 | } | 634 | } |
614 | } | 635 | } |
615 | 636 | ||
@@ -617,80 +638,62 @@ fn main() { | |||
617 | let _f = foo::Foo::new(); | 638 | let _f = foo::Foo::new(); |
618 | } | 639 | } |
619 | "#, | 640 | "#, |
620 | ); | 641 | expect![[r#" |
621 | check_result( | 642 | new FN FileId(0) 54..81 61..64 Other |
622 | refs, | 643 | |
623 | "new FN FileId(1) 54..101 61..64 Other", | 644 | FileId(0) 126..129 StructLiteral |
624 | &["FileId(1) 146..149 StructLiteral"], | 645 | "#]], |
625 | ); | 646 | ); |
626 | } | 647 | } |
627 | 648 | ||
628 | #[test] | 649 | #[test] |
629 | fn test_find_all_refs_nested_module() { | 650 | fn test_find_all_refs_nested_module() { |
630 | let code = r#" | 651 | check( |
631 | //- /lib.rs | 652 | r#" |
632 | mod foo { | 653 | //- /lib.rs |
633 | mod bar; | 654 | mod foo { mod bar; } |
634 | } | ||
635 | 655 | ||
636 | fn f<|>() {} | 656 | fn f<|>() {} |
637 | 657 | ||
638 | //- /foo/bar.rs | 658 | //- /foo/bar.rs |
639 | use crate::f; | 659 | use crate::f; |
640 | 660 | ||
641 | fn g() { | 661 | fn g() { f(); } |
642 | f(); | 662 | "#, |
643 | } | 663 | expect![[r#" |
644 | "#; | 664 | f FN FileId(0) 22..31 25..26 Other |
645 | 665 | ||
646 | let (analysis, pos) = analysis_and_position(code); | 666 | FileId(1) 11..12 Other |
647 | let refs = analysis.find_all_refs(pos, None).unwrap().unwrap(); | 667 | FileId(1) 24..25 StructLiteral |
648 | check_result( | 668 | "#]], |
649 | refs, | ||
650 | "f FN FileId(1) 26..35 29..30 Other", | ||
651 | &["FileId(2) 11..12 Other", "FileId(2) 28..29 StructLiteral"], | ||
652 | ); | 669 | ); |
653 | } | 670 | } |
654 | 671 | ||
655 | fn get_all_refs(ra_fixture: &str) -> ReferenceSearchResult { | 672 | fn check(ra_fixture: &str, expect: Expect) { |
656 | let (analysis, position) = analysis_and_position(ra_fixture); | 673 | check_with_scope(ra_fixture, None, expect) |
657 | analysis.find_all_refs(position, None).unwrap().unwrap() | ||
658 | } | 674 | } |
659 | 675 | ||
660 | fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) { | 676 | fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect: Expect) { |
661 | res.declaration().assert_match(expected_decl); | 677 | let (analysis, pos) = fixture::position(ra_fixture); |
662 | assert_eq!(res.references.len(), expected_refs.len()); | 678 | let refs = analysis.find_all_refs(pos, search_scope).unwrap().unwrap(); |
663 | res.references() | ||
664 | .iter() | ||
665 | .enumerate() | ||
666 | .for_each(|(i, r)| ref_assert_match(r, expected_refs[i])); | ||
667 | } | ||
668 | 679 | ||
669 | impl Declaration { | 680 | let mut actual = String::new(); |
670 | fn debug_render(&self) -> String { | 681 | { |
671 | let mut s = format!("{} {:?}", self.nav.debug_render(), self.kind); | 682 | let decl = refs.declaration; |
672 | if let Some(access) = self.access { | 683 | format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind); |
673 | s.push_str(&format!(" {:?}", access)); | 684 | if let Some(access) = decl.access { |
685 | format_to!(actual, " {:?}", access) | ||
674 | } | 686 | } |
675 | s | 687 | actual += "\n\n"; |
676 | } | 688 | } |
677 | 689 | ||
678 | fn assert_match(&self, expected: &str) { | 690 | for r in &refs.references { |
679 | let actual = self.debug_render(); | 691 | format_to!(actual, "{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); |
680 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | 692 | if let Some(access) = r.access { |
681 | } | 693 | format_to!(actual, " {:?}", access); |
682 | } | 694 | } |
683 | 695 | actual += "\n"; | |
684 | fn ref_debug_render(r: &Reference) -> String { | ||
685 | let mut s = format!("{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); | ||
686 | if let Some(access) = r.access { | ||
687 | s.push_str(&format!(" {:?}", access)); | ||
688 | } | 696 | } |
689 | s | 697 | expect.assert_eq(&actual) |
690 | } | ||
691 | |||
692 | fn ref_assert_match(r: &Reference, expected: &str) { | ||
693 | let actual = ref_debug_render(r); | ||
694 | test_utils::assert_eq_text!(expected.trim(), actual.trim(),); | ||
695 | } | 698 | } |
696 | } | 699 | } |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 301629763..8cbe1ae5a 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -275,11 +275,11 @@ mod tests { | |||
275 | use test_utils::{assert_eq_text, mark}; | 275 | use test_utils::{assert_eq_text, mark}; |
276 | use text_edit::TextEdit; | 276 | use text_edit::TextEdit; |
277 | 277 | ||
278 | use crate::{mock_analysis::analysis_and_position, FileId}; | 278 | use crate::{fixture, FileId}; |
279 | 279 | ||
280 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | 280 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { |
281 | let ra_fixture_after = &trim_indent(ra_fixture_after); | 281 | let ra_fixture_after = &trim_indent(ra_fixture_after); |
282 | let (analysis, position) = analysis_and_position(ra_fixture_before); | 282 | let (analysis, position) = fixture::position(ra_fixture_before); |
283 | let source_change = analysis.rename(position, new_name).unwrap(); | 283 | let source_change = analysis.rename(position, new_name).unwrap(); |
284 | let mut text_edit_builder = TextEdit::builder(); | 284 | let mut text_edit_builder = TextEdit::builder(); |
285 | let mut file_id: Option<FileId> = None; | 285 | let mut file_id: Option<FileId> = None; |
@@ -297,7 +297,7 @@ mod tests { | |||
297 | } | 297 | } |
298 | 298 | ||
299 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { | 299 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { |
300 | let (analysis, position) = analysis_and_position(ra_fixture); | 300 | let (analysis, position) = fixture::position(ra_fixture); |
301 | let source_change = analysis.rename(position, new_name).unwrap().unwrap(); | 301 | let source_change = analysis.rename(position, new_name).unwrap().unwrap(); |
302 | expect.assert_debug_eq(&source_change) | 302 | expect.assert_debug_eq(&source_change) |
303 | } | 303 | } |
@@ -314,7 +314,7 @@ mod tests { | |||
314 | 314 | ||
315 | #[test] | 315 | #[test] |
316 | fn test_rename_to_invalid_identifier() { | 316 | fn test_rename_to_invalid_identifier() { |
317 | let (analysis, position) = analysis_and_position(r#"fn main() { let i<|> = 1; }"#); | 317 | let (analysis, position) = fixture::position(r#"fn main() { let i<|> = 1; }"#); |
318 | let new_name = "invalid!"; | 318 | let new_name = "invalid!"; |
319 | let source_change = analysis.rename(position, new_name).unwrap(); | 319 | let source_change = analysis.rename(position, new_name).unwrap(); |
320 | assert!(source_change.is_none()); | 320 | assert!(source_change.is_none()); |
@@ -602,7 +602,7 @@ mod foo<|>; | |||
602 | source_file_edits: [ | 602 | source_file_edits: [ |
603 | SourceFileEdit { | 603 | SourceFileEdit { |
604 | file_id: FileId( | 604 | file_id: FileId( |
605 | 2, | 605 | 1, |
606 | ), | 606 | ), |
607 | edit: TextEdit { | 607 | edit: TextEdit { |
608 | indels: [ | 608 | indels: [ |
@@ -617,10 +617,10 @@ mod foo<|>; | |||
617 | file_system_edits: [ | 617 | file_system_edits: [ |
618 | MoveFile { | 618 | MoveFile { |
619 | src: FileId( | 619 | src: FileId( |
620 | 3, | 620 | 2, |
621 | ), | 621 | ), |
622 | anchor: FileId( | 622 | anchor: FileId( |
623 | 3, | 623 | 2, |
624 | ), | 624 | ), |
625 | dst: "foo2.rs", | 625 | dst: "foo2.rs", |
626 | }, | 626 | }, |
@@ -655,7 +655,7 @@ use crate::foo<|>::FooContent; | |||
655 | source_file_edits: [ | 655 | source_file_edits: [ |
656 | SourceFileEdit { | 656 | SourceFileEdit { |
657 | file_id: FileId( | 657 | file_id: FileId( |
658 | 1, | 658 | 0, |
659 | ), | 659 | ), |
660 | edit: TextEdit { | 660 | edit: TextEdit { |
661 | indels: [ | 661 | indels: [ |
@@ -668,7 +668,7 @@ use crate::foo<|>::FooContent; | |||
668 | }, | 668 | }, |
669 | SourceFileEdit { | 669 | SourceFileEdit { |
670 | file_id: FileId( | 670 | file_id: FileId( |
671 | 3, | 671 | 2, |
672 | ), | 672 | ), |
673 | edit: TextEdit { | 673 | edit: TextEdit { |
674 | indels: [ | 674 | indels: [ |
@@ -683,10 +683,10 @@ use crate::foo<|>::FooContent; | |||
683 | file_system_edits: [ | 683 | file_system_edits: [ |
684 | MoveFile { | 684 | MoveFile { |
685 | src: FileId( | 685 | src: FileId( |
686 | 2, | 686 | 1, |
687 | ), | 687 | ), |
688 | anchor: FileId( | 688 | anchor: FileId( |
689 | 2, | 689 | 1, |
690 | ), | 690 | ), |
691 | dst: "quux.rs", | 691 | dst: "quux.rs", |
692 | }, | 692 | }, |
@@ -715,7 +715,7 @@ mod fo<|>o; | |||
715 | source_file_edits: [ | 715 | source_file_edits: [ |
716 | SourceFileEdit { | 716 | SourceFileEdit { |
717 | file_id: FileId( | 717 | file_id: FileId( |
718 | 1, | 718 | 0, |
719 | ), | 719 | ), |
720 | edit: TextEdit { | 720 | edit: TextEdit { |
721 | indels: [ | 721 | indels: [ |
@@ -730,10 +730,10 @@ mod fo<|>o; | |||
730 | file_system_edits: [ | 730 | file_system_edits: [ |
731 | MoveFile { | 731 | MoveFile { |
732 | src: FileId( | 732 | src: FileId( |
733 | 2, | 733 | 1, |
734 | ), | 734 | ), |
735 | anchor: FileId( | 735 | anchor: FileId( |
736 | 2, | 736 | 1, |
737 | ), | 737 | ), |
738 | dst: "../foo2/mod.rs", | 738 | dst: "../foo2/mod.rs", |
739 | }, | 739 | }, |
@@ -763,7 +763,7 @@ mod outer { mod fo<|>o; } | |||
763 | source_file_edits: [ | 763 | source_file_edits: [ |
764 | SourceFileEdit { | 764 | SourceFileEdit { |
765 | file_id: FileId( | 765 | file_id: FileId( |
766 | 1, | 766 | 0, |
767 | ), | 767 | ), |
768 | edit: TextEdit { | 768 | edit: TextEdit { |
769 | indels: [ | 769 | indels: [ |
@@ -778,10 +778,10 @@ mod outer { mod fo<|>o; } | |||
778 | file_system_edits: [ | 778 | file_system_edits: [ |
779 | MoveFile { | 779 | MoveFile { |
780 | src: FileId( | 780 | src: FileId( |
781 | 2, | 781 | 1, |
782 | ), | 782 | ), |
783 | anchor: FileId( | 783 | anchor: FileId( |
784 | 2, | 784 | 1, |
785 | ), | 785 | ), |
786 | dst: "bar.rs", | 786 | dst: "bar.rs", |
787 | }, | 787 | }, |
@@ -834,7 +834,7 @@ pub mod foo<|>; | |||
834 | source_file_edits: [ | 834 | source_file_edits: [ |
835 | SourceFileEdit { | 835 | SourceFileEdit { |
836 | file_id: FileId( | 836 | file_id: FileId( |
837 | 2, | 837 | 1, |
838 | ), | 838 | ), |
839 | edit: TextEdit { | 839 | edit: TextEdit { |
840 | indels: [ | 840 | indels: [ |
@@ -847,7 +847,7 @@ pub mod foo<|>; | |||
847 | }, | 847 | }, |
848 | SourceFileEdit { | 848 | SourceFileEdit { |
849 | file_id: FileId( | 849 | file_id: FileId( |
850 | 1, | 850 | 0, |
851 | ), | 851 | ), |
852 | edit: TextEdit { | 852 | edit: TextEdit { |
853 | indels: [ | 853 | indels: [ |
@@ -862,10 +862,10 @@ pub mod foo<|>; | |||
862 | file_system_edits: [ | 862 | file_system_edits: [ |
863 | MoveFile { | 863 | MoveFile { |
864 | src: FileId( | 864 | src: FileId( |
865 | 3, | 865 | 2, |
866 | ), | 866 | ), |
867 | anchor: FileId( | 867 | anchor: FileId( |
868 | 3, | 868 | 2, |
869 | ), | 869 | ), |
870 | dst: "foo2.rs", | 870 | dst: "foo2.rs", |
871 | }, | 871 | }, |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index cfeff40c1..752ef2f21 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -292,7 +292,7 @@ fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { | |||
292 | mod tests { | 292 | mod tests { |
293 | use expect_test::{expect, Expect}; | 293 | use expect_test::{expect, Expect}; |
294 | 294 | ||
295 | use crate::mock_analysis::analysis_and_position; | 295 | use crate::fixture; |
296 | 296 | ||
297 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; | 297 | use super::{RunnableAction, BENCH, BIN, DOCTEST, TEST}; |
298 | 298 | ||
@@ -302,7 +302,7 @@ mod tests { | |||
302 | actions: &[&RunnableAction], | 302 | actions: &[&RunnableAction], |
303 | expect: Expect, | 303 | expect: Expect, |
304 | ) { | 304 | ) { |
305 | let (analysis, position) = analysis_and_position(ra_fixture); | 305 | let (analysis, position) = fixture::position(ra_fixture); |
306 | let runnables = analysis.runnables(position.file_id).unwrap(); | 306 | let runnables = analysis.runnables(position.file_id).unwrap(); |
307 | expect.assert_debug_eq(&runnables); | 307 | expect.assert_debug_eq(&runnables); |
308 | assert_eq!( | 308 | assert_eq!( |
@@ -335,7 +335,7 @@ fn bench() {} | |||
335 | Runnable { | 335 | Runnable { |
336 | nav: NavigationTarget { | 336 | nav: NavigationTarget { |
337 | file_id: FileId( | 337 | file_id: FileId( |
338 | 1, | 338 | 0, |
339 | ), | 339 | ), |
340 | full_range: 1..13, | 340 | full_range: 1..13, |
341 | focus_range: Some( | 341 | focus_range: Some( |
@@ -353,7 +353,7 @@ fn bench() {} | |||
353 | Runnable { | 353 | Runnable { |
354 | nav: NavigationTarget { | 354 | nav: NavigationTarget { |
355 | file_id: FileId( | 355 | file_id: FileId( |
356 | 1, | 356 | 0, |
357 | ), | 357 | ), |
358 | full_range: 15..39, | 358 | full_range: 15..39, |
359 | focus_range: Some( | 359 | focus_range: Some( |
@@ -378,7 +378,7 @@ fn bench() {} | |||
378 | Runnable { | 378 | Runnable { |
379 | nav: NavigationTarget { | 379 | nav: NavigationTarget { |
380 | file_id: FileId( | 380 | file_id: FileId( |
381 | 1, | 381 | 0, |
382 | ), | 382 | ), |
383 | full_range: 41..75, | 383 | full_range: 41..75, |
384 | focus_range: Some( | 384 | focus_range: Some( |
@@ -403,7 +403,7 @@ fn bench() {} | |||
403 | Runnable { | 403 | Runnable { |
404 | nav: NavigationTarget { | 404 | nav: NavigationTarget { |
405 | file_id: FileId( | 405 | file_id: FileId( |
406 | 1, | 406 | 0, |
407 | ), | 407 | ), |
408 | full_range: 77..99, | 408 | full_range: 77..99, |
409 | focus_range: Some( | 409 | focus_range: Some( |
@@ -494,7 +494,7 @@ fn should_have_no_runnable_6() {} | |||
494 | Runnable { | 494 | Runnable { |
495 | nav: NavigationTarget { | 495 | nav: NavigationTarget { |
496 | file_id: FileId( | 496 | file_id: FileId( |
497 | 1, | 497 | 0, |
498 | ), | 498 | ), |
499 | full_range: 1..13, | 499 | full_range: 1..13, |
500 | focus_range: Some( | 500 | focus_range: Some( |
@@ -512,7 +512,7 @@ fn should_have_no_runnable_6() {} | |||
512 | Runnable { | 512 | Runnable { |
513 | nav: NavigationTarget { | 513 | nav: NavigationTarget { |
514 | file_id: FileId( | 514 | file_id: FileId( |
515 | 1, | 515 | 0, |
516 | ), | 516 | ), |
517 | full_range: 15..74, | 517 | full_range: 15..74, |
518 | focus_range: None, | 518 | focus_range: None, |
@@ -532,7 +532,7 @@ fn should_have_no_runnable_6() {} | |||
532 | Runnable { | 532 | Runnable { |
533 | nav: NavigationTarget { | 533 | nav: NavigationTarget { |
534 | file_id: FileId( | 534 | file_id: FileId( |
535 | 1, | 535 | 0, |
536 | ), | 536 | ), |
537 | full_range: 76..148, | 537 | full_range: 76..148, |
538 | focus_range: None, | 538 | focus_range: None, |
@@ -552,7 +552,7 @@ fn should_have_no_runnable_6() {} | |||
552 | Runnable { | 552 | Runnable { |
553 | nav: NavigationTarget { | 553 | nav: NavigationTarget { |
554 | file_id: FileId( | 554 | file_id: FileId( |
555 | 1, | 555 | 0, |
556 | ), | 556 | ), |
557 | full_range: 150..254, | 557 | full_range: 150..254, |
558 | focus_range: None, | 558 | focus_range: None, |
@@ -596,7 +596,7 @@ impl Data { | |||
596 | Runnable { | 596 | Runnable { |
597 | nav: NavigationTarget { | 597 | nav: NavigationTarget { |
598 | file_id: FileId( | 598 | file_id: FileId( |
599 | 1, | 599 | 0, |
600 | ), | 600 | ), |
601 | full_range: 1..13, | 601 | full_range: 1..13, |
602 | focus_range: Some( | 602 | focus_range: Some( |
@@ -614,7 +614,7 @@ impl Data { | |||
614 | Runnable { | 614 | Runnable { |
615 | nav: NavigationTarget { | 615 | nav: NavigationTarget { |
616 | file_id: FileId( | 616 | file_id: FileId( |
617 | 1, | 617 | 0, |
618 | ), | 618 | ), |
619 | full_range: 44..98, | 619 | full_range: 44..98, |
620 | focus_range: None, | 620 | focus_range: None, |
@@ -653,7 +653,7 @@ mod test_mod { | |||
653 | Runnable { | 653 | Runnable { |
654 | nav: NavigationTarget { | 654 | nav: NavigationTarget { |
655 | file_id: FileId( | 655 | file_id: FileId( |
656 | 1, | 656 | 0, |
657 | ), | 657 | ), |
658 | full_range: 1..51, | 658 | full_range: 1..51, |
659 | focus_range: Some( | 659 | focus_range: Some( |
@@ -673,7 +673,7 @@ mod test_mod { | |||
673 | Runnable { | 673 | Runnable { |
674 | nav: NavigationTarget { | 674 | nav: NavigationTarget { |
675 | file_id: FileId( | 675 | file_id: FileId( |
676 | 1, | 676 | 0, |
677 | ), | 677 | ), |
678 | full_range: 20..49, | 678 | full_range: 20..49, |
679 | focus_range: Some( | 679 | focus_range: Some( |
@@ -733,7 +733,7 @@ mod root_tests { | |||
733 | Runnable { | 733 | Runnable { |
734 | nav: NavigationTarget { | 734 | nav: NavigationTarget { |
735 | file_id: FileId( | 735 | file_id: FileId( |
736 | 1, | 736 | 0, |
737 | ), | 737 | ), |
738 | full_range: 22..323, | 738 | full_range: 22..323, |
739 | focus_range: Some( | 739 | focus_range: Some( |
@@ -753,7 +753,7 @@ mod root_tests { | |||
753 | Runnable { | 753 | Runnable { |
754 | nav: NavigationTarget { | 754 | nav: NavigationTarget { |
755 | file_id: FileId( | 755 | file_id: FileId( |
756 | 1, | 756 | 0, |
757 | ), | 757 | ), |
758 | full_range: 51..192, | 758 | full_range: 51..192, |
759 | focus_range: Some( | 759 | focus_range: Some( |
@@ -773,7 +773,7 @@ mod root_tests { | |||
773 | Runnable { | 773 | Runnable { |
774 | nav: NavigationTarget { | 774 | nav: NavigationTarget { |
775 | file_id: FileId( | 775 | file_id: FileId( |
776 | 1, | 776 | 0, |
777 | ), | 777 | ), |
778 | full_range: 84..126, | 778 | full_range: 84..126, |
779 | focus_range: Some( | 779 | focus_range: Some( |
@@ -798,7 +798,7 @@ mod root_tests { | |||
798 | Runnable { | 798 | Runnable { |
799 | nav: NavigationTarget { | 799 | nav: NavigationTarget { |
800 | file_id: FileId( | 800 | file_id: FileId( |
801 | 1, | 801 | 0, |
802 | ), | 802 | ), |
803 | full_range: 140..182, | 803 | full_range: 140..182, |
804 | focus_range: Some( | 804 | focus_range: Some( |
@@ -823,7 +823,7 @@ mod root_tests { | |||
823 | Runnable { | 823 | Runnable { |
824 | nav: NavigationTarget { | 824 | nav: NavigationTarget { |
825 | file_id: FileId( | 825 | file_id: FileId( |
826 | 1, | 826 | 0, |
827 | ), | 827 | ), |
828 | full_range: 202..286, | 828 | full_range: 202..286, |
829 | focus_range: Some( | 829 | focus_range: Some( |
@@ -843,7 +843,7 @@ mod root_tests { | |||
843 | Runnable { | 843 | Runnable { |
844 | nav: NavigationTarget { | 844 | nav: NavigationTarget { |
845 | file_id: FileId( | 845 | file_id: FileId( |
846 | 1, | 846 | 0, |
847 | ), | 847 | ), |
848 | full_range: 235..276, | 848 | full_range: 235..276, |
849 | focus_range: Some( | 849 | focus_range: Some( |
@@ -886,7 +886,7 @@ fn test_foo1() {} | |||
886 | Runnable { | 886 | Runnable { |
887 | nav: NavigationTarget { | 887 | nav: NavigationTarget { |
888 | file_id: FileId( | 888 | file_id: FileId( |
889 | 1, | 889 | 0, |
890 | ), | 890 | ), |
891 | full_range: 1..50, | 891 | full_range: 1..50, |
892 | focus_range: Some( | 892 | focus_range: Some( |
@@ -934,7 +934,7 @@ fn test_foo1() {} | |||
934 | Runnable { | 934 | Runnable { |
935 | nav: NavigationTarget { | 935 | nav: NavigationTarget { |
936 | file_id: FileId( | 936 | file_id: FileId( |
937 | 1, | 937 | 0, |
938 | ), | 938 | ), |
939 | full_range: 1..72, | 939 | full_range: 1..72, |
940 | focus_range: Some( | 940 | focus_range: Some( |
diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index c23708181..0af84daa0 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs | |||
@@ -2,19 +2,19 @@ use std::{fmt, iter::FromIterator, sync::Arc}; | |||
2 | 2 | ||
3 | use base_db::{ | 3 | use base_db::{ |
4 | salsa::debug::{DebugQueryTable, TableEntry}, | 4 | salsa::debug::{DebugQueryTable, TableEntry}, |
5 | FileTextQuery, SourceRootId, | 5 | CrateId, FileId, FileTextQuery, SourceDatabase, SourceRootId, |
6 | }; | 6 | }; |
7 | use hir::MacroFile; | 7 | use hir::MacroFile; |
8 | use ide_db::{ | 8 | use ide_db::{ |
9 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, | 9 | symbol_index::{LibrarySymbolsQuery, SymbolIndex}, |
10 | RootDatabase, | 10 | RootDatabase, |
11 | }; | 11 | }; |
12 | use itertools::Itertools; | ||
12 | use profile::{memory_usage, Bytes}; | 13 | use profile::{memory_usage, Bytes}; |
13 | use rustc_hash::FxHashMap; | 14 | use rustc_hash::FxHashMap; |
15 | use stdx::format_to; | ||
14 | use syntax::{ast, Parse, SyntaxNode}; | 16 | use syntax::{ast, Parse, SyntaxNode}; |
15 | 17 | ||
16 | use crate::FileId; | ||
17 | |||
18 | fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | 18 | fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { |
19 | base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() | 19 | base_db::ParseQuery.in_db(db).entries::<SyntaxTreeStats>() |
20 | } | 20 | } |
@@ -31,20 +31,36 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { | |||
31 | // | 31 | // |
32 | // | VS Code | **Rust Analyzer: Status** | 32 | // | VS Code | **Rust Analyzer: Status** |
33 | // |=== | 33 | // |=== |
34 | pub(crate) fn status(db: &RootDatabase) -> String { | 34 | pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String { |
35 | let files_stats = FileTextQuery.in_db(db).entries::<FilesStats>(); | 35 | let mut buf = String::new(); |
36 | let syntax_tree_stats = syntax_tree_stats(db); | 36 | format_to!(buf, "{}\n", FileTextQuery.in_db(db).entries::<FilesStats>()); |
37 | let macro_syntax_tree_stats = macro_syntax_tree_stats(db); | 37 | format_to!(buf, "{}\n", LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>()); |
38 | let symbols_stats = LibrarySymbolsQuery.in_db(db).entries::<LibrarySymbolsStats>(); | 38 | format_to!(buf, "{}\n", syntax_tree_stats(db)); |
39 | format!( | 39 | format_to!(buf, "{} (macros)\n", macro_syntax_tree_stats(db)); |
40 | "{}\n{}\n{}\n{} (macros)\n\n\nmemory:\n{}\ngc {:?} seconds ago", | 40 | format_to!(buf, "{} total\n", memory_usage()); |
41 | files_stats, | 41 | |
42 | symbols_stats, | 42 | if let Some(file_id) = file_id { |
43 | syntax_tree_stats, | 43 | format_to!(buf, "\nfile info:\n"); |
44 | macro_syntax_tree_stats, | 44 | let krate = crate::parent_module::crate_for(db, file_id).pop(); |
45 | memory_usage(), | 45 | match krate { |
46 | db.last_gc.elapsed().as_secs(), | 46 | Some(krate) => { |
47 | ) | 47 | let crate_graph = db.crate_graph(); |
48 | let display_crate = |krate: CrateId| match &crate_graph[krate].display_name { | ||
49 | Some(it) => format!("{}({:?})", it, krate), | ||
50 | None => format!("{:?}", krate), | ||
51 | }; | ||
52 | format_to!(buf, "crate: {}\n", display_crate(krate)); | ||
53 | let deps = crate_graph[krate] | ||
54 | .dependencies | ||
55 | .iter() | ||
56 | .map(|dep| format!("{}={:?}", dep.name, dep.crate_id)) | ||
57 | .format(", "); | ||
58 | format_to!(buf, "deps: {}\n", deps); | ||
59 | } | ||
60 | None => format_to!(buf, "does not belong to any crate"), | ||
61 | } | ||
62 | } | ||
63 | buf | ||
48 | } | 64 | } |
49 | 65 | ||
50 | #[derive(Default)] | 66 | #[derive(Default)] |
@@ -121,7 +137,7 @@ struct LibrarySymbolsStats { | |||
121 | 137 | ||
122 | impl fmt::Display for LibrarySymbolsStats { | 138 | impl fmt::Display for LibrarySymbolsStats { |
123 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | 139 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { |
124 | write!(fmt, "{} ({}) symbols", self.total, self.size) | 140 | write!(fmt, "{} ({}) index symbols", self.total, self.size) |
125 | } | 141 | } |
126 | } | 142 | } |
127 | 143 | ||
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 211e62ea1..694c4b7fa 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs | |||
@@ -3,7 +3,7 @@ use std::fs; | |||
3 | use expect_test::{expect_file, ExpectFile}; | 3 | use expect_test::{expect_file, ExpectFile}; |
4 | use test_utils::project_dir; | 4 | use test_utils::project_dir; |
5 | 5 | ||
6 | use crate::{mock_analysis::single_file, FileRange, TextRange}; | 6 | use crate::{fixture, FileRange, TextRange}; |
7 | 7 | ||
8 | #[test] | 8 | #[test] |
9 | fn test_highlighting() { | 9 | fn test_highlighting() { |
@@ -178,7 +178,7 @@ fn accidentally_quadratic() { | |||
178 | let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); | 178 | let file = project_dir().join("crates/syntax/test_data/accidentally_quadratic"); |
179 | let src = fs::read_to_string(file).unwrap(); | 179 | let src = fs::read_to_string(file).unwrap(); |
180 | 180 | ||
181 | let (analysis, file_id) = single_file(&src); | 181 | let (analysis, file_id) = fixture::file(&src); |
182 | 182 | ||
183 | // let t = std::time::Instant::now(); | 183 | // let t = std::time::Instant::now(); |
184 | let _ = analysis.highlight(file_id).unwrap(); | 184 | let _ = analysis.highlight(file_id).unwrap(); |
@@ -187,7 +187,7 @@ fn accidentally_quadratic() { | |||
187 | 187 | ||
188 | #[test] | 188 | #[test] |
189 | fn test_ranges() { | 189 | fn test_ranges() { |
190 | let (analysis, file_id) = single_file( | 190 | let (analysis, file_id) = fixture::file( |
191 | r#" | 191 | r#" |
192 | #[derive(Clone, Debug)] | 192 | #[derive(Clone, Debug)] |
193 | struct Foo { | 193 | struct Foo { |
@@ -228,7 +228,7 @@ fn main() { | |||
228 | 228 | ||
229 | #[test] | 229 | #[test] |
230 | fn ranges_sorted() { | 230 | fn ranges_sorted() { |
231 | let (analysis, file_id) = single_file( | 231 | let (analysis, file_id) = fixture::file( |
232 | r#" | 232 | r#" |
233 | #[foo(bar = "bar")] | 233 | #[foo(bar = "bar")] |
234 | macro_rules! test {} | 234 | macro_rules! test {} |
@@ -462,12 +462,12 @@ macro_rules! noop { | |||
462 | fn test_extern_crate() { | 462 | fn test_extern_crate() { |
463 | check_highlighting( | 463 | check_highlighting( |
464 | r#" | 464 | r#" |
465 | //- /main.rs | 465 | //- /main.rs crate:main deps:std,alloc |
466 | extern crate std; | 466 | extern crate std; |
467 | extern crate alloc as abc; | 467 | extern crate alloc as abc; |
468 | //- /std/lib.rs | 468 | //- /std/lib.rs crate:std |
469 | pub struct S; | 469 | pub struct S; |
470 | //- /alloc/lib.rs | 470 | //- /alloc/lib.rs crate:alloc |
471 | pub struct A | 471 | pub struct A |
472 | "#, | 472 | "#, |
473 | expect_file!["./test_data/highlight_extern_crate.html"], | 473 | expect_file!["./test_data/highlight_extern_crate.html"], |
@@ -479,7 +479,7 @@ fn test_extern_crate() { | |||
479 | /// result as HTML, and compares it with the HTML file given as `snapshot`. | 479 | /// result as HTML, and compares it with the HTML file given as `snapshot`. |
480 | /// Note that the `snapshot` file is overwritten by the rendered HTML. | 480 | /// Note that the `snapshot` file is overwritten by the rendered HTML. |
481 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { | 481 | fn check_highlighting(ra_fixture: &str, expect: ExpectFile, rainbow: bool) { |
482 | let (analysis, file_id) = single_file(ra_fixture); | 482 | let (analysis, file_id) = fixture::file(ra_fixture); |
483 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); | 483 | let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap(); |
484 | expect.assert_eq(actual_html) | 484 | expect.assert_eq(actual_html) |
485 | } | 485 | } |
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs index f80044959..0eed2dbd7 100644 --- a/crates/ide/src/syntax_tree.rs +++ b/crates/ide/src/syntax_tree.rs | |||
@@ -104,12 +104,12 @@ fn syntax_tree_for_token(node: &SyntaxToken, text_range: TextRange) -> Option<St | |||
104 | mod tests { | 104 | mod tests { |
105 | use test_utils::assert_eq_text; | 105 | use test_utils::assert_eq_text; |
106 | 106 | ||
107 | use crate::mock_analysis::{analysis_and_range, single_file}; | 107 | use crate::fixture; |
108 | 108 | ||
109 | #[test] | 109 | #[test] |
110 | fn test_syntax_tree_without_range() { | 110 | fn test_syntax_tree_without_range() { |
111 | // Basic syntax | 111 | // Basic syntax |
112 | let (analysis, file_id) = single_file(r#"fn foo() {}"#); | 112 | let (analysis, file_id) = fixture::file(r#"fn foo() {}"#); |
113 | let syn = analysis.syntax_tree(file_id, None).unwrap(); | 113 | let syn = analysis.syntax_tree(file_id, None).unwrap(); |
114 | 114 | ||
115 | assert_eq_text!( | 115 | assert_eq_text!( |
@@ -132,7 +132,7 @@ [email protected] | |||
132 | .trim() | 132 | .trim() |
133 | ); | 133 | ); |
134 | 134 | ||
135 | let (analysis, file_id) = single_file( | 135 | let (analysis, file_id) = fixture::file( |
136 | r#" | 136 | r#" |
137 | fn test() { | 137 | fn test() { |
138 | assert!(" | 138 | assert!(" |
@@ -184,7 +184,7 @@ [email protected] | |||
184 | 184 | ||
185 | #[test] | 185 | #[test] |
186 | fn test_syntax_tree_with_range() { | 186 | fn test_syntax_tree_with_range() { |
187 | let (analysis, range) = analysis_and_range(r#"<|>fn foo() {}<|>"#.trim()); | 187 | let (analysis, range) = fixture::range(r#"<|>fn foo() {}<|>"#.trim()); |
188 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); | 188 | let syn = analysis.syntax_tree(range.file_id, Some(range.range)).unwrap(); |
189 | 189 | ||
190 | assert_eq_text!( | 190 | assert_eq_text!( |
@@ -206,7 +206,7 @@ [email protected] | |||
206 | .trim() | 206 | .trim() |
207 | ); | 207 | ); |
208 | 208 | ||
209 | let (analysis, range) = analysis_and_range( | 209 | let (analysis, range) = fixture::range( |
210 | r#"fn test() { | 210 | r#"fn test() { |
211 | <|>assert!(" | 211 | <|>assert!(" |
212 | fn foo() { | 212 | fn foo() { |
@@ -242,7 +242,7 @@ [email protected] | |||
242 | 242 | ||
243 | #[test] | 243 | #[test] |
244 | fn test_syntax_tree_inside_string() { | 244 | fn test_syntax_tree_inside_string() { |
245 | let (analysis, range) = analysis_and_range( | 245 | let (analysis, range) = fixture::range( |
246 | r#"fn test() { | 246 | r#"fn test() { |
247 | assert!(" | 247 | assert!(" |
248 | <|>fn foo() { | 248 | <|>fn foo() { |
@@ -276,7 +276,7 @@ [email protected] | |||
276 | ); | 276 | ); |
277 | 277 | ||
278 | // With a raw string | 278 | // With a raw string |
279 | let (analysis, range) = analysis_and_range( | 279 | let (analysis, range) = fixture::range( |
280 | r###"fn test() { | 280 | r###"fn test() { |
281 | assert!(r#" | 281 | assert!(r#" |
282 | <|>fn foo() { | 282 | <|>fn foo() { |
@@ -310,7 +310,7 @@ [email protected] | |||
310 | ); | 310 | ); |
311 | 311 | ||
312 | // With a raw string | 312 | // With a raw string |
313 | let (analysis, range) = analysis_and_range( | 313 | let (analysis, range) = fixture::range( |
314 | r###"fn test() { | 314 | r###"fn test() { |
315 | assert!(r<|>#" | 315 | assert!(r<|>#" |
316 | fn foo() { | 316 | fn foo() { |
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs index f7d46146c..a0dc4b9df 100644 --- a/crates/ide/src/typing/on_enter.rs +++ b/crates/ide/src/typing/on_enter.rs | |||
@@ -109,10 +109,10 @@ mod tests { | |||
109 | use stdx::trim_indent; | 109 | use stdx::trim_indent; |
110 | use test_utils::{assert_eq_text, mark}; | 110 | use test_utils::{assert_eq_text, mark}; |
111 | 111 | ||
112 | use crate::mock_analysis::analysis_and_position; | 112 | use crate::fixture; |
113 | 113 | ||
114 | fn apply_on_enter(before: &str) -> Option<String> { | 114 | fn apply_on_enter(before: &str) -> Option<String> { |
115 | let (analysis, position) = analysis_and_position(&before); | 115 | let (analysis, position) = fixture::position(&before); |
116 | let result = analysis.on_enter(position).unwrap()?; | 116 | let result = analysis.on_enter(position).unwrap()?; |
117 | 117 | ||
118 | let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); | 118 | let mut actual = analysis.file_text(position.file_id).unwrap().to_string(); |
diff --git a/crates/ide_db/src/change.rs b/crates/ide_db/src/apply_change.rs index 8b4fd7ab8..da16fa21d 100644 --- a/crates/ide_db/src/change.rs +++ b/crates/ide_db/src/apply_change.rs | |||
@@ -1,58 +1,16 @@ | |||
1 | //! Defines a unit of change that can applied to a state of IDE to get the next | 1 | //! Applies changes to the IDE state transactionally. |
2 | //! state. Changes are transactional. | ||
3 | 2 | ||
4 | use std::{fmt, sync::Arc, time}; | 3 | use std::{fmt, sync::Arc}; |
5 | 4 | ||
6 | use base_db::{ | 5 | use base_db::{ |
7 | salsa::{Database, Durability, SweepStrategy}, | 6 | salsa::{Database, Durability, SweepStrategy}, |
8 | CrateGraph, FileId, SourceDatabase, SourceDatabaseExt, SourceRoot, SourceRootId, | 7 | Change, FileId, SourceRootId, |
9 | }; | 8 | }; |
10 | use profile::{memory_usage, Bytes}; | 9 | use profile::{memory_usage, Bytes}; |
11 | use rustc_hash::FxHashSet; | 10 | use rustc_hash::FxHashSet; |
12 | 11 | ||
13 | use crate::{symbol_index::SymbolsDatabase, RootDatabase}; | 12 | use crate::{symbol_index::SymbolsDatabase, RootDatabase}; |
14 | 13 | ||
15 | #[derive(Default)] | ||
16 | pub struct AnalysisChange { | ||
17 | roots: Option<Vec<SourceRoot>>, | ||
18 | files_changed: Vec<(FileId, Option<Arc<String>>)>, | ||
19 | crate_graph: Option<CrateGraph>, | ||
20 | } | ||
21 | |||
22 | impl fmt::Debug for AnalysisChange { | ||
23 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | ||
24 | let mut d = fmt.debug_struct("AnalysisChange"); | ||
25 | if let Some(roots) = &self.roots { | ||
26 | d.field("roots", roots); | ||
27 | } | ||
28 | if !self.files_changed.is_empty() { | ||
29 | d.field("files_changed", &self.files_changed.len()); | ||
30 | } | ||
31 | if self.crate_graph.is_some() { | ||
32 | d.field("crate_graph", &self.crate_graph); | ||
33 | } | ||
34 | d.finish() | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl AnalysisChange { | ||
39 | pub fn new() -> AnalysisChange { | ||
40 | AnalysisChange::default() | ||
41 | } | ||
42 | |||
43 | pub fn set_roots(&mut self, roots: Vec<SourceRoot>) { | ||
44 | self.roots = Some(roots); | ||
45 | } | ||
46 | |||
47 | pub fn change_file(&mut self, file_id: FileId, new_text: Option<Arc<String>>) { | ||
48 | self.files_changed.push((file_id, new_text)) | ||
49 | } | ||
50 | |||
51 | pub fn set_crate_graph(&mut self, graph: CrateGraph) { | ||
52 | self.crate_graph = Some(graph); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | #[derive(Debug)] | 14 | #[derive(Debug)] |
57 | struct AddFile { | 15 | struct AddFile { |
58 | file_id: FileId, | 16 | file_id: FileId, |
@@ -81,59 +39,31 @@ impl fmt::Debug for RootChange { | |||
81 | } | 39 | } |
82 | } | 40 | } |
83 | 41 | ||
84 | const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); | ||
85 | |||
86 | impl RootDatabase { | 42 | impl RootDatabase { |
87 | pub fn request_cancellation(&mut self) { | 43 | pub fn request_cancellation(&mut self) { |
88 | let _p = profile::span("RootDatabase::request_cancellation"); | 44 | let _p = profile::span("RootDatabase::request_cancellation"); |
89 | self.salsa_runtime_mut().synthetic_write(Durability::LOW); | 45 | self.salsa_runtime_mut().synthetic_write(Durability::LOW); |
90 | } | 46 | } |
91 | 47 | ||
92 | pub fn apply_change(&mut self, change: AnalysisChange) { | 48 | pub fn apply_change(&mut self, change: Change) { |
93 | let _p = profile::span("RootDatabase::apply_change"); | 49 | let _p = profile::span("RootDatabase::apply_change"); |
94 | self.request_cancellation(); | 50 | self.request_cancellation(); |
95 | log::info!("apply_change {:?}", change); | 51 | log::info!("apply_change {:?}", change); |
96 | if let Some(roots) = change.roots { | 52 | if let Some(roots) = &change.roots { |
97 | let mut local_roots = FxHashSet::default(); | 53 | let mut local_roots = FxHashSet::default(); |
98 | let mut library_roots = FxHashSet::default(); | 54 | let mut library_roots = FxHashSet::default(); |
99 | for (idx, root) in roots.into_iter().enumerate() { | 55 | for (idx, root) in roots.iter().enumerate() { |
100 | let root_id = SourceRootId(idx as u32); | 56 | let root_id = SourceRootId(idx as u32); |
101 | let durability = durability(&root); | ||
102 | if root.is_library { | 57 | if root.is_library { |
103 | library_roots.insert(root_id); | 58 | library_roots.insert(root_id); |
104 | } else { | 59 | } else { |
105 | local_roots.insert(root_id); | 60 | local_roots.insert(root_id); |
106 | } | 61 | } |
107 | for file_id in root.iter() { | ||
108 | self.set_file_source_root_with_durability(file_id, root_id, durability); | ||
109 | } | ||
110 | self.set_source_root_with_durability(root_id, Arc::new(root), durability); | ||
111 | } | 62 | } |
112 | self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); | 63 | self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); |
113 | self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH); | 64 | self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH); |
114 | } | 65 | } |
115 | 66 | change.apply(self); | |
116 | for (file_id, text) in change.files_changed { | ||
117 | let source_root_id = self.file_source_root(file_id); | ||
118 | let source_root = self.source_root(source_root_id); | ||
119 | let durability = durability(&source_root); | ||
120 | // XXX: can't actually remove the file, just reset the text | ||
121 | let text = text.unwrap_or_default(); | ||
122 | self.set_file_text_with_durability(file_id, text, durability) | ||
123 | } | ||
124 | if let Some(crate_graph) = change.crate_graph { | ||
125 | self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | pub fn maybe_collect_garbage(&mut self) { | ||
130 | if cfg!(feature = "wasm") { | ||
131 | return; | ||
132 | } | ||
133 | |||
134 | if self.last_gc_check.elapsed() > GC_COOLDOWN { | ||
135 | self.last_gc_check = crate::wasm_shims::Instant::now(); | ||
136 | } | ||
137 | } | 67 | } |
138 | 68 | ||
139 | pub fn collect_garbage(&mut self) { | 69 | pub fn collect_garbage(&mut self) { |
@@ -142,7 +72,6 @@ impl RootDatabase { | |||
142 | } | 72 | } |
143 | 73 | ||
144 | let _p = profile::span("RootDatabase::collect_garbage"); | 74 | let _p = profile::span("RootDatabase::collect_garbage"); |
145 | self.last_gc = crate::wasm_shims::Instant::now(); | ||
146 | 75 | ||
147 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); | 76 | let sweep = SweepStrategy::default().discard_values().sweep_all_revisions(); |
148 | 77 | ||
@@ -308,11 +237,3 @@ impl RootDatabase { | |||
308 | acc | 237 | acc |
309 | } | 238 | } |
310 | } | 239 | } |
311 | |||
312 | fn durability(source_root: &SourceRoot) -> Durability { | ||
313 | if source_root.is_library { | ||
314 | Durability::HIGH | ||
315 | } else { | ||
316 | Durability::LOW | ||
317 | } | ||
318 | } | ||
diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index 70ada02f3..7eff247c7 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs | |||
@@ -2,15 +2,14 @@ | |||
2 | //! | 2 | //! |
3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. | 3 | //! It is mainly a `HirDatabase` for semantic analysis, plus a `SymbolsDatabase`, for fuzzy search. |
4 | 4 | ||
5 | mod apply_change; | ||
5 | pub mod label; | 6 | pub mod label; |
6 | pub mod line_index; | 7 | pub mod line_index; |
7 | pub mod symbol_index; | 8 | pub mod symbol_index; |
8 | pub mod change; | ||
9 | pub mod defs; | 9 | pub mod defs; |
10 | pub mod search; | 10 | pub mod search; |
11 | pub mod imports_locator; | 11 | pub mod imports_locator; |
12 | pub mod source_change; | 12 | pub mod source_change; |
13 | mod wasm_shims; | ||
14 | 13 | ||
15 | use std::{fmt, sync::Arc}; | 14 | use std::{fmt, sync::Arc}; |
16 | 15 | ||
@@ -36,8 +35,6 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; | |||
36 | )] | 35 | )] |
37 | pub struct RootDatabase { | 36 | pub struct RootDatabase { |
38 | storage: salsa::Storage<RootDatabase>, | 37 | storage: salsa::Storage<RootDatabase>, |
39 | pub last_gc: crate::wasm_shims::Instant, | ||
40 | pub last_gc_check: crate::wasm_shims::Instant, | ||
41 | } | 38 | } |
42 | 39 | ||
43 | impl fmt::Debug for RootDatabase { | 40 | impl fmt::Debug for RootDatabase { |
@@ -99,11 +96,7 @@ impl Default for RootDatabase { | |||
99 | 96 | ||
100 | impl RootDatabase { | 97 | impl RootDatabase { |
101 | pub fn new(lru_capacity: Option<usize>) -> RootDatabase { | 98 | pub fn new(lru_capacity: Option<usize>) -> RootDatabase { |
102 | let mut db = RootDatabase { | 99 | let mut db = RootDatabase { storage: salsa::Storage::default() }; |
103 | storage: salsa::Storage::default(), | ||
104 | last_gc: crate::wasm_shims::Instant::now(), | ||
105 | last_gc_check: crate::wasm_shims::Instant::now(), | ||
106 | }; | ||
107 | db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); | 100 | db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); |
108 | db.set_local_roots_with_durability(Default::default(), Durability::HIGH); | 101 | db.set_local_roots_with_durability(Default::default(), Durability::HIGH); |
109 | db.set_library_roots_with_durability(Default::default(), Durability::HIGH); | 102 | db.set_library_roots_with_durability(Default::default(), Durability::HIGH); |
@@ -121,11 +114,7 @@ impl RootDatabase { | |||
121 | 114 | ||
122 | impl salsa::ParallelDatabase for RootDatabase { | 115 | impl salsa::ParallelDatabase for RootDatabase { |
123 | fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { | 116 | fn snapshot(&self) -> salsa::Snapshot<RootDatabase> { |
124 | salsa::Snapshot::new(RootDatabase { | 117 | salsa::Snapshot::new(RootDatabase { storage: self.storage.snapshot() }) |
125 | storage: self.storage.snapshot(), | ||
126 | last_gc: self.last_gc, | ||
127 | last_gc_check: self.last_gc_check, | ||
128 | }) | ||
129 | } | 118 | } |
130 | } | 119 | } |
131 | 120 | ||
diff --git a/crates/ide_db/src/wasm_shims.rs b/crates/ide_db/src/wasm_shims.rs deleted file mode 100644 index 7af9f9d9b..000000000 --- a/crates/ide_db/src/wasm_shims.rs +++ /dev/null | |||
@@ -1,19 +0,0 @@ | |||
1 | //! A version of `std::time::Instant` that doesn't panic in WASM. | ||
2 | |||
3 | #[cfg(not(feature = "wasm"))] | ||
4 | pub use std::time::Instant; | ||
5 | |||
6 | #[cfg(feature = "wasm")] | ||
7 | #[derive(Clone, Copy, Debug)] | ||
8 | pub struct Instant; | ||
9 | |||
10 | #[cfg(feature = "wasm")] | ||
11 | impl Instant { | ||
12 | pub fn now() -> Self { | ||
13 | Self | ||
14 | } | ||
15 | |||
16 | pub fn elapsed(&self) -> std::time::Duration { | ||
17 | std::time::Duration::new(0, 0) | ||
18 | } | ||
19 | } | ||
diff --git a/crates/rust-analyzer/src/cli/analysis_bench.rs b/crates/rust-analyzer/src/cli/analysis_bench.rs index c312e0a2e..d1c095ba5 100644 --- a/crates/rust-analyzer/src/cli/analysis_bench.rs +++ b/crates/rust-analyzer/src/cli/analysis_bench.rs | |||
@@ -8,8 +8,7 @@ use base_db::{ | |||
8 | FileId, | 8 | FileId, |
9 | }; | 9 | }; |
10 | use ide::{ | 10 | use ide::{ |
11 | Analysis, AnalysisChange, AnalysisHost, CompletionConfig, DiagnosticsConfig, FilePosition, | 11 | Analysis, AnalysisHost, Change, CompletionConfig, DiagnosticsConfig, FilePosition, LineCol, |
12 | LineCol, | ||
13 | }; | 12 | }; |
14 | use vfs::AbsPathBuf; | 13 | use vfs::AbsPathBuf; |
15 | 14 | ||
@@ -143,7 +142,7 @@ fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, w | |||
143 | { | 142 | { |
144 | let mut text = host.analysis().file_text(file_id).unwrap().to_string(); | 143 | let mut text = host.analysis().file_text(file_id).unwrap().to_string(); |
145 | text.push_str("\n/* Hello world */\n"); | 144 | text.push_str("\n/* Hello world */\n"); |
146 | let mut change = AnalysisChange::new(); | 145 | let mut change = Change::new(); |
147 | change.change_file(file_id, Some(Arc::new(text))); | 146 | change.change_file(file_id, Some(Arc::new(text))); |
148 | host.apply_change(change); | 147 | host.apply_change(change); |
149 | } | 148 | } |
@@ -156,7 +155,7 @@ fn do_work<F: Fn(&Analysis) -> T, T>(host: &mut AnalysisHost, file_id: FileId, w | |||
156 | { | 155 | { |
157 | let mut text = host.analysis().file_text(file_id).unwrap().to_string(); | 156 | let mut text = host.analysis().file_text(file_id).unwrap().to_string(); |
158 | text.push_str("\npub fn _dummy() {}\n"); | 157 | text.push_str("\npub fn _dummy() {}\n"); |
159 | let mut change = AnalysisChange::new(); | 158 | let mut change = Change::new(); |
160 | change.change_file(file_id, Some(Arc::new(text))); | 159 | change.change_file(file_id, Some(Arc::new(text))); |
161 | host.apply_change(change); | 160 | host.apply_change(change); |
162 | } | 161 | } |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index c47cf6ef3..7ae1c9055 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -5,7 +5,7 @@ use std::{path::Path, sync::Arc}; | |||
5 | use anyhow::Result; | 5 | use anyhow::Result; |
6 | use base_db::CrateGraph; | 6 | use base_db::CrateGraph; |
7 | use crossbeam_channel::{unbounded, Receiver}; | 7 | use crossbeam_channel::{unbounded, Receiver}; |
8 | use ide::{AnalysisChange, AnalysisHost}; | 8 | use ide::{AnalysisHost, Change}; |
9 | use project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; | 9 | use project_model::{CargoConfig, ProcMacroClient, ProjectManifest, ProjectWorkspace}; |
10 | use vfs::{loader::Handle, AbsPath, AbsPathBuf}; | 10 | use vfs::{loader::Handle, AbsPath, AbsPathBuf}; |
11 | 11 | ||
@@ -62,7 +62,7 @@ fn load( | |||
62 | ) -> AnalysisHost { | 62 | ) -> AnalysisHost { |
63 | let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); | 63 | let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); |
64 | let mut host = AnalysisHost::new(lru_cap); | 64 | let mut host = AnalysisHost::new(lru_cap); |
65 | let mut analysis_change = AnalysisChange::new(); | 65 | let mut analysis_change = Change::new(); |
66 | 66 | ||
67 | // wait until Vfs has loaded all roots | 67 | // wait until Vfs has loaded all roots |
68 | for task in receiver { | 68 | for task in receiver { |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 42e1ad376..0ab4c37bf 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -38,6 +38,7 @@ pub struct Config { | |||
38 | pub cargo: CargoConfig, | 38 | pub cargo: CargoConfig, |
39 | pub rustfmt: RustfmtConfig, | 39 | pub rustfmt: RustfmtConfig, |
40 | pub flycheck: Option<FlycheckConfig>, | 40 | pub flycheck: Option<FlycheckConfig>, |
41 | pub runnables: RunnablesConfig, | ||
41 | 42 | ||
42 | pub inlay_hints: InlayHintsConfig, | 43 | pub inlay_hints: InlayHintsConfig, |
43 | pub completion: CompletionConfig, | 44 | pub completion: CompletionConfig, |
@@ -124,6 +125,15 @@ pub enum RustfmtConfig { | |||
124 | CustomCommand { command: String, args: Vec<String> }, | 125 | CustomCommand { command: String, args: Vec<String> }, |
125 | } | 126 | } |
126 | 127 | ||
128 | /// Configuration for runnable items, such as `main` function or tests. | ||
129 | #[derive(Debug, Clone, Default)] | ||
130 | pub struct RunnablesConfig { | ||
131 | /// Custom command to be executed instead of `cargo` for runnables. | ||
132 | pub override_cargo: Option<String>, | ||
133 | /// Additional arguments for the `cargo`, e.g. `--release`. | ||
134 | pub cargo_extra_args: Vec<String>, | ||
135 | } | ||
136 | |||
127 | #[derive(Debug, Clone, Default)] | 137 | #[derive(Debug, Clone, Default)] |
128 | pub struct ClientCapsConfig { | 138 | pub struct ClientCapsConfig { |
129 | pub location_link: bool, | 139 | pub location_link: bool, |
@@ -164,6 +174,7 @@ impl Config { | |||
164 | extra_args: Vec::new(), | 174 | extra_args: Vec::new(), |
165 | features: Vec::new(), | 175 | features: Vec::new(), |
166 | }), | 176 | }), |
177 | runnables: RunnablesConfig::default(), | ||
167 | 178 | ||
168 | inlay_hints: InlayHintsConfig { | 179 | inlay_hints: InlayHintsConfig { |
169 | type_hints: true, | 180 | type_hints: true, |
@@ -220,6 +231,10 @@ impl Config { | |||
220 | load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, | 231 | load_out_dirs_from_check: data.cargo_loadOutDirsFromCheck, |
221 | target: data.cargo_target.clone(), | 232 | target: data.cargo_target.clone(), |
222 | }; | 233 | }; |
234 | self.runnables = RunnablesConfig { | ||
235 | override_cargo: data.runnables_overrideCargo, | ||
236 | cargo_extra_args: data.runnables_cargoExtraArgs, | ||
237 | }; | ||
223 | 238 | ||
224 | self.proc_macro_srv = if data.procMacro_enable { | 239 | self.proc_macro_srv = if data.procMacro_enable { |
225 | std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()])) | 240 | std::env::current_exe().ok().map(|path| (path, vec!["proc-macro".into()])) |
@@ -474,6 +489,9 @@ config_data! { | |||
474 | notifications_cargoTomlNotFound: bool = true, | 489 | notifications_cargoTomlNotFound: bool = true, |
475 | procMacro_enable: bool = false, | 490 | procMacro_enable: bool = false, |
476 | 491 | ||
492 | runnables_overrideCargo: Option<String> = None, | ||
493 | runnables_cargoExtraArgs: Vec<String> = Vec::new(), | ||
494 | |||
477 | rustfmt_extraArgs: Vec<String> = Vec::new(), | 495 | rustfmt_extraArgs: Vec<String> = Vec::new(), |
478 | rustfmt_overrideCommand: Option<Vec<String>> = None, | 496 | rustfmt_overrideCommand: Option<Vec<String>> = None, |
479 | 497 | ||
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs index 891fdb96d..9c8815e29 100644 --- a/crates/rust-analyzer/src/dispatch.rs +++ b/crates/rust-analyzer/src/dispatch.rs | |||
@@ -1,5 +1,5 @@ | |||
1 | //! A visitor for downcasting arbitrary request (JSON) into a specific type. | 1 | //! A visitor for downcasting arbitrary request (JSON) into a specific type. |
2 | use std::panic; | 2 | use std::{fmt, panic}; |
3 | 3 | ||
4 | use serde::{de::DeserializeOwned, Serialize}; | 4 | use serde::{de::DeserializeOwned, Serialize}; |
5 | 5 | ||
@@ -23,7 +23,7 @@ impl<'a> RequestDispatcher<'a> { | |||
23 | ) -> Result<&mut Self> | 23 | ) -> Result<&mut Self> |
24 | where | 24 | where |
25 | R: lsp_types::request::Request + 'static, | 25 | R: lsp_types::request::Request + 'static, |
26 | R::Params: DeserializeOwned + panic::UnwindSafe + 'static, | 26 | R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug + 'static, |
27 | R::Result: Serialize + 'static, | 27 | R::Result: Serialize + 'static, |
28 | { | 28 | { |
29 | let (id, params) = match self.parse::<R>() { | 29 | let (id, params) = match self.parse::<R>() { |
@@ -34,6 +34,7 @@ impl<'a> RequestDispatcher<'a> { | |||
34 | }; | 34 | }; |
35 | let world = panic::AssertUnwindSafe(&mut *self.global_state); | 35 | let world = panic::AssertUnwindSafe(&mut *self.global_state); |
36 | let response = panic::catch_unwind(move || { | 36 | let response = panic::catch_unwind(move || { |
37 | stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params)); | ||
37 | let result = f(world.0, params); | 38 | let result = f(world.0, params); |
38 | result_to_response::<R>(id, result) | 39 | result_to_response::<R>(id, result) |
39 | }) | 40 | }) |
@@ -49,7 +50,7 @@ impl<'a> RequestDispatcher<'a> { | |||
49 | ) -> Result<&mut Self> | 50 | ) -> Result<&mut Self> |
50 | where | 51 | where |
51 | R: lsp_types::request::Request + 'static, | 52 | R: lsp_types::request::Request + 'static, |
52 | R::Params: DeserializeOwned + Send + 'static, | 53 | R::Params: DeserializeOwned + Send + fmt::Debug + 'static, |
53 | R::Result: Serialize + 'static, | 54 | R::Result: Serialize + 'static, |
54 | { | 55 | { |
55 | let (id, params) = match self.parse::<R>() { | 56 | let (id, params) = match self.parse::<R>() { |
@@ -61,7 +62,10 @@ impl<'a> RequestDispatcher<'a> { | |||
61 | 62 | ||
62 | self.global_state.task_pool.handle.spawn({ | 63 | self.global_state.task_pool.handle.spawn({ |
63 | let world = self.global_state.snapshot(); | 64 | let world = self.global_state.snapshot(); |
65 | |||
64 | move || { | 66 | move || { |
67 | let _ctx = | ||
68 | stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params)); | ||
65 | let result = f(world, params); | 69 | let result = f(world, params); |
66 | Task::Response(result_to_response::<R>(id, result)) | 70 | Task::Response(result_to_response::<R>(id, result)) |
67 | } | 71 | } |
@@ -156,6 +160,7 @@ impl<'a> NotificationDispatcher<'a> { | |||
156 | return Ok(self); | 160 | return Ok(self); |
157 | } | 161 | } |
158 | }; | 162 | }; |
163 | stdx::panic_context::enter(format!("notification: {}", N::METHOD)); | ||
159 | f(self.global_state, params)?; | 164 | f(self.global_state, params)?; |
160 | Ok(self) | 165 | Ok(self) |
161 | } | 166 | } |
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 96313aaec..dafab6a6a 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs | |||
@@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant}; | |||
8 | use base_db::{CrateId, VfsPath}; | 8 | use base_db::{CrateId, VfsPath}; |
9 | use crossbeam_channel::{unbounded, Receiver, Sender}; | 9 | use crossbeam_channel::{unbounded, Receiver, Sender}; |
10 | use flycheck::FlycheckHandle; | 10 | use flycheck::FlycheckHandle; |
11 | use ide::{Analysis, AnalysisChange, AnalysisHost, FileId}; | 11 | use ide::{Analysis, AnalysisHost, Change, FileId}; |
12 | use lsp_types::{SemanticTokens, Url}; | 12 | use lsp_types::{SemanticTokens, Url}; |
13 | use parking_lot::{Mutex, RwLock}; | 13 | use parking_lot::{Mutex, RwLock}; |
14 | use project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; | 14 | use project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target}; |
@@ -139,7 +139,7 @@ impl GlobalState { | |||
139 | let mut has_fs_changes = false; | 139 | let mut has_fs_changes = false; |
140 | 140 | ||
141 | let change = { | 141 | let change = { |
142 | let mut change = AnalysisChange::new(); | 142 | let mut change = Change::new(); |
143 | let (vfs, line_endings_map) = &mut *self.vfs.write(); | 143 | let (vfs, line_endings_map) = &mut *self.vfs.write(); |
144 | let changed_files = vfs.take_changes(); | 144 | let changed_files = vfs.take_changes(); |
145 | if changed_files.is_empty() { | 145 | if changed_files.is_empty() { |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 7ac1a30f6..e970abb7c 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -38,10 +38,22 @@ use crate::{ | |||
38 | to_proto, LspError, Result, | 38 | to_proto, LspError, Result, |
39 | }; | 39 | }; |
40 | 40 | ||
41 | pub(crate) fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result<String> { | 41 | pub(crate) fn handle_analyzer_status( |
42 | snap: GlobalStateSnapshot, | ||
43 | params: lsp_ext::AnalyzerStatusParams, | ||
44 | ) -> Result<String> { | ||
42 | let _p = profile::span("handle_analyzer_status"); | 45 | let _p = profile::span("handle_analyzer_status"); |
43 | 46 | ||
44 | let mut buf = String::new(); | 47 | let mut buf = String::new(); |
48 | |||
49 | let mut file_id = None; | ||
50 | if let Some(tdi) = params.text_document { | ||
51 | match from_proto::file_id(&snap, &tdi.uri) { | ||
52 | Ok(it) => file_id = Some(it), | ||
53 | Err(_) => format_to!(buf, "file {} not found in vfs", tdi.uri), | ||
54 | } | ||
55 | } | ||
56 | |||
45 | if snap.workspaces.is_empty() { | 57 | if snap.workspaces.is_empty() { |
46 | buf.push_str("no workspaces\n") | 58 | buf.push_str("no workspaces\n") |
47 | } else { | 59 | } else { |
@@ -52,7 +64,10 @@ pub(crate) fn handle_analyzer_status(snap: GlobalStateSnapshot, _: ()) -> Result | |||
52 | } | 64 | } |
53 | buf.push_str("\nanalysis:\n"); | 65 | buf.push_str("\nanalysis:\n"); |
54 | buf.push_str( | 66 | buf.push_str( |
55 | &snap.analysis.status().unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()), | 67 | &snap |
68 | .analysis | ||
69 | .status(file_id) | ||
70 | .unwrap_or_else(|_| "Analysis retrieval was cancelled".to_owned()), | ||
56 | ); | 71 | ); |
57 | format_to!(buf, "\n\nrequests:\n"); | 72 | format_to!(buf, "\n\nrequests:\n"); |
58 | let requests = snap.latest_requests.read(); | 73 | let requests = snap.latest_requests.read(); |
@@ -476,6 +491,7 @@ pub(crate) fn handle_runnables( | |||
476 | } | 491 | } |
477 | 492 | ||
478 | // Add `cargo check` and `cargo test` for all targets of the whole package | 493 | // Add `cargo check` and `cargo test` for all targets of the whole package |
494 | let config = &snap.config.runnables; | ||
479 | match cargo_spec { | 495 | match cargo_spec { |
480 | Some(spec) => { | 496 | Some(spec) => { |
481 | for &cmd in ["check", "test"].iter() { | 497 | for &cmd in ["check", "test"].iter() { |
@@ -485,12 +501,14 @@ pub(crate) fn handle_runnables( | |||
485 | kind: lsp_ext::RunnableKind::Cargo, | 501 | kind: lsp_ext::RunnableKind::Cargo, |
486 | args: lsp_ext::CargoRunnable { | 502 | args: lsp_ext::CargoRunnable { |
487 | workspace_root: Some(spec.workspace_root.clone().into()), | 503 | workspace_root: Some(spec.workspace_root.clone().into()), |
504 | override_cargo: config.override_cargo.clone(), | ||
488 | cargo_args: vec![ | 505 | cargo_args: vec![ |
489 | cmd.to_string(), | 506 | cmd.to_string(), |
490 | "--package".to_string(), | 507 | "--package".to_string(), |
491 | spec.package.clone(), | 508 | spec.package.clone(), |
492 | "--all-targets".to_string(), | 509 | "--all-targets".to_string(), |
493 | ], | 510 | ], |
511 | cargo_extra_args: config.cargo_extra_args.clone(), | ||
494 | executable_args: Vec::new(), | 512 | executable_args: Vec::new(), |
495 | expect_test: None, | 513 | expect_test: None, |
496 | }, | 514 | }, |
@@ -504,7 +522,9 @@ pub(crate) fn handle_runnables( | |||
504 | kind: lsp_ext::RunnableKind::Cargo, | 522 | kind: lsp_ext::RunnableKind::Cargo, |
505 | args: lsp_ext::CargoRunnable { | 523 | args: lsp_ext::CargoRunnable { |
506 | workspace_root: None, | 524 | workspace_root: None, |
525 | override_cargo: config.override_cargo.clone(), | ||
507 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], | 526 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], |
527 | cargo_extra_args: config.cargo_extra_args.clone(), | ||
508 | executable_args: Vec::new(), | 528 | executable_args: Vec::new(), |
509 | expect_test: None, | 529 | expect_test: None, |
510 | }, | 530 | }, |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index e1a28b1b4..fee0bb69c 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -11,11 +11,17 @@ use serde::{Deserialize, Serialize}; | |||
11 | pub enum AnalyzerStatus {} | 11 | pub enum AnalyzerStatus {} |
12 | 12 | ||
13 | impl Request for AnalyzerStatus { | 13 | impl Request for AnalyzerStatus { |
14 | type Params = (); | 14 | type Params = AnalyzerStatusParams; |
15 | type Result = String; | 15 | type Result = String; |
16 | const METHOD: &'static str = "rust-analyzer/analyzerStatus"; | 16 | const METHOD: &'static str = "rust-analyzer/analyzerStatus"; |
17 | } | 17 | } |
18 | 18 | ||
19 | #[derive(Deserialize, Serialize, Debug)] | ||
20 | #[serde(rename_all = "camelCase")] | ||
21 | pub struct AnalyzerStatusParams { | ||
22 | pub text_document: Option<TextDocumentIdentifier>, | ||
23 | } | ||
24 | |||
19 | pub enum MemoryUsage {} | 25 | pub enum MemoryUsage {} |
20 | 26 | ||
21 | impl Request for MemoryUsage { | 27 | impl Request for MemoryUsage { |
@@ -165,10 +171,14 @@ pub enum RunnableKind { | |||
165 | #[derive(Deserialize, Serialize, Debug)] | 171 | #[derive(Deserialize, Serialize, Debug)] |
166 | #[serde(rename_all = "camelCase")] | 172 | #[serde(rename_all = "camelCase")] |
167 | pub struct CargoRunnable { | 173 | pub struct CargoRunnable { |
174 | // command to be executed instead of cargo | ||
175 | pub override_cargo: Option<String>, | ||
168 | #[serde(skip_serializing_if = "Option::is_none")] | 176 | #[serde(skip_serializing_if = "Option::is_none")] |
169 | pub workspace_root: Option<PathBuf>, | 177 | pub workspace_root: Option<PathBuf>, |
170 | // command, --package and --lib stuff | 178 | // command, --package and --lib stuff |
171 | pub cargo_args: Vec<String>, | 179 | pub cargo_args: Vec<String>, |
180 | // user-specified additional cargo args, like `--release`. | ||
181 | pub cargo_extra_args: Vec<String>, | ||
172 | // stuff after -- | 182 | // stuff after -- |
173 | pub executable_args: Vec<String>, | 183 | pub executable_args: Vec<String>, |
174 | #[serde(skip_serializing_if = "Option::is_none")] | 184 | #[serde(skip_serializing_if = "Option::is_none")] |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 06ab9d508..c2d0ac791 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -189,19 +189,16 @@ impl GlobalState { | |||
189 | } | 189 | } |
190 | lsp_server::Message::Response(resp) => self.complete_request(resp), | 190 | lsp_server::Message::Response(resp) => self.complete_request(resp), |
191 | }, | 191 | }, |
192 | Event::Task(task) => { | 192 | Event::Task(task) => match task { |
193 | match task { | 193 | Task::Response(response) => self.respond(response), |
194 | Task::Response(response) => self.respond(response), | 194 | Task::Diagnostics(diagnostics_per_file) => { |
195 | Task::Diagnostics(diagnostics_per_file) => { | 195 | for (file_id, diagnostics) in diagnostics_per_file { |
196 | for (file_id, diagnostics) in diagnostics_per_file { | 196 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) |
197 | self.diagnostics.set_native_diagnostics(file_id, diagnostics) | ||
198 | } | ||
199 | } | 197 | } |
200 | Task::Workspaces(workspaces) => self.switch_workspaces(workspaces), | ||
201 | Task::Unit => (), | ||
202 | } | 198 | } |
203 | self.analysis_host.maybe_collect_garbage(); | 199 | Task::Workspaces(workspaces) => self.switch_workspaces(workspaces), |
204 | } | 200 | Task::Unit => (), |
201 | }, | ||
205 | Event::Vfs(mut task) => { | 202 | Event::Vfs(mut task) => { |
206 | let _p = profile::span("GlobalState::handle_event/vfs"); | 203 | let _p = profile::span("GlobalState::handle_event/vfs"); |
207 | loop { | 204 | loop { |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index de0dbcad4..f7215f129 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -3,7 +3,7 @@ use std::{mem, sync::Arc}; | |||
3 | 3 | ||
4 | use base_db::{CrateGraph, SourceRoot, VfsPath}; | 4 | use base_db::{CrateGraph, SourceRoot, VfsPath}; |
5 | use flycheck::{FlycheckConfig, FlycheckHandle}; | 5 | use flycheck::{FlycheckConfig, FlycheckHandle}; |
6 | use ide::AnalysisChange; | 6 | use ide::Change; |
7 | use project_model::{ProcMacroClient, ProjectWorkspace}; | 7 | use project_model::{ProcMacroClient, ProjectWorkspace}; |
8 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; | 8 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; |
9 | 9 | ||
@@ -171,7 +171,7 @@ impl GlobalState { | |||
171 | ); | 171 | ); |
172 | } | 172 | } |
173 | 173 | ||
174 | let mut change = AnalysisChange::new(); | 174 | let mut change = Change::new(); |
175 | 175 | ||
176 | let project_folders = ProjectFolders::new(&workspaces); | 176 | let project_folders = ProjectFolders::new(&workspaces); |
177 | 177 | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 59e780b7d..aeacde0f7 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -740,6 +740,7 @@ pub(crate) fn runnable( | |||
740 | file_id: FileId, | 740 | file_id: FileId, |
741 | runnable: Runnable, | 741 | runnable: Runnable, |
742 | ) -> Result<lsp_ext::Runnable> { | 742 | ) -> Result<lsp_ext::Runnable> { |
743 | let config = &snap.config.runnables; | ||
743 | let spec = CargoTargetSpec::for_file(snap, file_id)?; | 744 | let spec = CargoTargetSpec::for_file(snap, file_id)?; |
744 | let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); | 745 | let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); |
745 | let target = spec.as_ref().map(|s| s.target.clone()); | 746 | let target = spec.as_ref().map(|s| s.target.clone()); |
@@ -754,7 +755,9 @@ pub(crate) fn runnable( | |||
754 | kind: lsp_ext::RunnableKind::Cargo, | 755 | kind: lsp_ext::RunnableKind::Cargo, |
755 | args: lsp_ext::CargoRunnable { | 756 | args: lsp_ext::CargoRunnable { |
756 | workspace_root: workspace_root.map(|it| it.into()), | 757 | workspace_root: workspace_root.map(|it| it.into()), |
758 | override_cargo: config.override_cargo.clone(), | ||
757 | cargo_args, | 759 | cargo_args, |
760 | cargo_extra_args: config.cargo_extra_args.clone(), | ||
758 | executable_args, | 761 | executable_args, |
759 | expect_test: None, | 762 | expect_test: None, |
760 | }, | 763 | }, |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/main.rs b/crates/rust-analyzer/tests/rust-analyzer/main.rs index 06726f957..e51eb2626 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/main.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/main.rs | |||
@@ -107,6 +107,8 @@ fn main() {} | |||
107 | "args": { | 107 | "args": { |
108 | "cargoArgs": ["test", "--package", "foo", "--test", "spam"], | 108 | "cargoArgs": ["test", "--package", "foo", "--test", "spam"], |
109 | "executableArgs": ["test_eggs", "--exact", "--nocapture"], | 109 | "executableArgs": ["test_eggs", "--exact", "--nocapture"], |
110 | "cargoExtraArgs": [], | ||
111 | "overrideCargo": null, | ||
110 | "workspaceRoot": server.path().join("foo") | 112 | "workspaceRoot": server.path().join("foo") |
111 | }, | 113 | }, |
112 | "kind": "cargo", | 114 | "kind": "cargo", |
@@ -127,6 +129,8 @@ fn main() {} | |||
127 | "args": { | 129 | "args": { |
128 | "cargoArgs": ["check", "--package", "foo", "--all-targets"], | 130 | "cargoArgs": ["check", "--package", "foo", "--all-targets"], |
129 | "executableArgs": [], | 131 | "executableArgs": [], |
132 | "cargoExtraArgs": [], | ||
133 | "overrideCargo": null, | ||
130 | "workspaceRoot": server.path().join("foo") | 134 | "workspaceRoot": server.path().join("foo") |
131 | }, | 135 | }, |
132 | "kind": "cargo", | 136 | "kind": "cargo", |
@@ -136,6 +140,8 @@ fn main() {} | |||
136 | "args": { | 140 | "args": { |
137 | "cargoArgs": ["test", "--package", "foo", "--all-targets"], | 141 | "cargoArgs": ["test", "--package", "foo", "--all-targets"], |
138 | "executableArgs": [], | 142 | "executableArgs": [], |
143 | "cargoExtraArgs": [], | ||
144 | "overrideCargo": null, | ||
139 | "workspaceRoot": server.path().join("foo") | 145 | "workspaceRoot": server.path().join("foo") |
140 | }, | 146 | }, |
141 | "kind": "cargo", | 147 | "kind": "cargo", |
diff --git a/crates/ssr/src/resolving.rs b/crates/ssr/src/resolving.rs index 5d2cbec47..347cc4aad 100644 --- a/crates/ssr/src/resolving.rs +++ b/crates/ssr/src/resolving.rs | |||
@@ -205,7 +205,7 @@ impl<'db> ResolutionScope<'db> { | |||
205 | 205 | ||
206 | /// Returns the function in which SSR was invoked, if any. | 206 | /// Returns the function in which SSR was invoked, if any. |
207 | pub(crate) fn current_function(&self) -> Option<SyntaxNode> { | 207 | pub(crate) fn current_function(&self) -> Option<SyntaxNode> { |
208 | self.node.ancestors().find(|node| node.kind() == SyntaxKind::FN).map(|node| node.clone()) | 208 | self.node.ancestors().find(|node| node.kind() == SyntaxKind::FN) |
209 | } | 209 | } |
210 | 210 | ||
211 | fn resolve_path(&self, path: &ast::Path) -> Option<hir::PathResolution> { | 211 | fn resolve_path(&self, path: &ast::Path) -> Option<hir::PathResolution> { |
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index 273b0f55b..011935cad 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -5,6 +5,7 @@ use std::{ | |||
5 | }; | 5 | }; |
6 | 6 | ||
7 | mod macros; | 7 | mod macros; |
8 | pub mod panic_context; | ||
8 | 9 | ||
9 | #[inline(always)] | 10 | #[inline(always)] |
10 | pub fn is_ci() -> bool { | 11 | pub fn is_ci() -> bool { |
diff --git a/crates/stdx/src/panic_context.rs b/crates/stdx/src/panic_context.rs new file mode 100644 index 000000000..fd232e0cc --- /dev/null +++ b/crates/stdx/src/panic_context.rs | |||
@@ -0,0 +1,49 @@ | |||
1 | //! A micro-crate to enhance panic messages with context info. | ||
2 | //! | ||
3 | //! FIXME: upstream to https://github.com/kriomant/panic-context ? | ||
4 | |||
5 | use std::{cell::RefCell, panic, sync::Once}; | ||
6 | |||
7 | pub fn enter(context: String) -> impl Drop { | ||
8 | static ONCE: Once = Once::new(); | ||
9 | ONCE.call_once(PanicContext::init); | ||
10 | |||
11 | with_ctx(|ctx| ctx.push(context)); | ||
12 | PanicContext { _priv: () } | ||
13 | } | ||
14 | |||
15 | #[must_use] | ||
16 | struct PanicContext { | ||
17 | _priv: (), | ||
18 | } | ||
19 | |||
20 | impl PanicContext { | ||
21 | fn init() { | ||
22 | let default_hook = panic::take_hook(); | ||
23 | let hook = move |panic_info: &panic::PanicInfo<'_>| { | ||
24 | with_ctx(|ctx| { | ||
25 | if !ctx.is_empty() { | ||
26 | eprintln!("Panic context:"); | ||
27 | for frame in ctx.iter() { | ||
28 | eprintln!("> {}\n", frame) | ||
29 | } | ||
30 | } | ||
31 | default_hook(panic_info) | ||
32 | }) | ||
33 | }; | ||
34 | panic::set_hook(Box::new(hook)) | ||
35 | } | ||
36 | } | ||
37 | |||
38 | impl Drop for PanicContext { | ||
39 | fn drop(&mut self) { | ||
40 | with_ctx(|ctx| assert!(ctx.pop().is_some())) | ||
41 | } | ||
42 | } | ||
43 | |||
44 | fn with_ctx(f: impl FnOnce(&mut Vec<String>)) { | ||
45 | thread_local! { | ||
46 | static CTX: RefCell<Vec<String>> = RefCell::new(Vec::new()); | ||
47 | } | ||
48 | CTX.with(|ctx| f(&mut *ctx.borrow_mut())) | ||
49 | } | ||
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index af61bb658..0b15f10e9 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "673.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "681.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index dda0a0319..77233ab31 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs | |||
@@ -159,7 +159,7 @@ impl ast::AssocItemList { | |||
159 | let whitespace = | 159 | let whitespace = |
160 | last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?; | 160 | last_token_before_curly.clone().into_token().and_then(ast::Whitespace::cast)?; |
161 | let text = whitespace.syntax().text(); | 161 | let text = whitespace.syntax().text(); |
162 | let newline = text.rfind("\n")?; | 162 | let newline = text.rfind('\n')?; |
163 | let keep = tokens::WsBuilder::new(&text[newline..]); | 163 | let keep = tokens::WsBuilder::new(&text[newline..]); |
164 | Some(self.replace_children( | 164 | Some(self.replace_children( |
165 | first_token_after_items..=last_token_before_curly, | 165 | first_token_after_items..=last_token_before_curly, |
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs index fa3be1016..f1202113b 100644 --- a/crates/syntax/src/parsing/lexer.rs +++ b/crates/syntax/src/parsing/lexer.rs | |||
@@ -120,10 +120,10 @@ fn rustc_token_kind_to_syntax_kind( | |||
120 | 120 | ||
121 | let syntax_kind = { | 121 | let syntax_kind = { |
122 | match rustc_token_kind { | 122 | match rustc_token_kind { |
123 | rustc_lexer::TokenKind::LineComment => COMMENT, | 123 | rustc_lexer::TokenKind::LineComment { doc_style: _ } => COMMENT, |
124 | 124 | ||
125 | rustc_lexer::TokenKind::BlockComment { terminated: true } => COMMENT, | 125 | rustc_lexer::TokenKind::BlockComment { doc_style: _, terminated: true } => COMMENT, |
126 | rustc_lexer::TokenKind::BlockComment { terminated: false } => { | 126 | rustc_lexer::TokenKind::BlockComment { doc_style: _, terminated: false } => { |
127 | return ( | 127 | return ( |
128 | COMMENT, | 128 | COMMENT, |
129 | Some("Missing trailing `*/` symbols to terminate the block comment"), | 129 | Some("Missing trailing `*/` symbols to terminate the block comment"), |
@@ -164,7 +164,7 @@ fn rustc_token_kind_to_syntax_kind( | |||
164 | rustc_lexer::TokenKind::Colon => T![:], | 164 | rustc_lexer::TokenKind::Colon => T![:], |
165 | rustc_lexer::TokenKind::Dollar => T![$], | 165 | rustc_lexer::TokenKind::Dollar => T![$], |
166 | rustc_lexer::TokenKind::Eq => T![=], | 166 | rustc_lexer::TokenKind::Eq => T![=], |
167 | rustc_lexer::TokenKind::Not => T![!], | 167 | rustc_lexer::TokenKind::Bang => T![!], |
168 | rustc_lexer::TokenKind::Lt => T![<], | 168 | rustc_lexer::TokenKind::Lt => T![<], |
169 | rustc_lexer::TokenKind::Gt => T![>], | 169 | rustc_lexer::TokenKind::Gt => T![>], |
170 | rustc_lexer::TokenKind::Minus => T![-], | 170 | rustc_lexer::TokenKind::Minus => T![-], |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 2e3133449..f1160bb1c 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -390,7 +390,14 @@ rust-analyzer supports only one `kind`, `"cargo"`. The `args` for `"cargo"` look | |||
390 | 390 | ||
391 | **Method:** `rust-analyzer/analyzerStatus` | 391 | **Method:** `rust-analyzer/analyzerStatus` |
392 | 392 | ||
393 | **Request:** `null` | 393 | **Request:** |
394 | |||
395 | ```typescript | ||
396 | interface AnalyzerStatusParams { | ||
397 | /// If specified, show dependencies of the current file. | ||
398 | textDocument?: TextDocumentIdentifier; | ||
399 | } | ||
400 | ``` | ||
394 | 401 | ||
395 | **Response:** `string` | 402 | **Response:** `string` |
396 | 403 | ||
diff --git a/docs/dev/style.md b/docs/dev/style.md index bcd86fd3f..fb407afcd 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -197,7 +197,7 @@ fn frobnicate(walrus: Option<Walrus>) { | |||
197 | } | 197 | } |
198 | ``` | 198 | ``` |
199 | 199 | ||
200 | Avoid preconditions that spawn function boundaries: | 200 | Avoid preconditions that span across function boundaries: |
201 | 201 | ||
202 | 202 | ||
203 | ```rust | 203 | ```rust |
@@ -218,9 +218,8 @@ fn foo() { | |||
218 | } | 218 | } |
219 | 219 | ||
220 | // Not as good | 220 | // Not as good |
221 | fn is_string_literal(s: &str) -> Option<&str> { | 221 | fn is_string_literal(s: &str) -> bool { |
222 | s.starts_with('"') && s.ends_with('"') | 222 | s.starts_with('"') && s.ends_with('"') |
223 | Some() | ||
224 | } | 223 | } |
225 | 224 | ||
226 | fn foo() { | 225 | fn foo() { |
@@ -231,8 +230,8 @@ fn foo() { | |||
231 | } | 230 | } |
232 | ``` | 231 | ``` |
233 | 232 | ||
234 | In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and utilized in `foo`. | 233 | In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and used in `foo`. |
235 | In the "Good" version, precondition check and usage are checked in the same block, and then encoded in the types. | 234 | In the "Good" version, the precondition check and usage are checked in the same block, and then encoded in the types. |
236 | 235 | ||
237 | # Early Returns | 236 | # Early Returns |
238 | 237 | ||
@@ -372,3 +371,13 @@ After you are happy with the state of the code, please use [interactive rebase]( | |||
372 | 371 | ||
373 | Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors). | 372 | Avoid @mentioning people in commit messages and pull request descriptions(they are added to commit message by bors). |
374 | Such messages create a lot of duplicate notification traffic during rebases. | 373 | Such messages create a lot of duplicate notification traffic during rebases. |
374 | |||
375 | # Clippy | ||
376 | |||
377 | We don't enforce Clippy. | ||
378 | A number of default lints have high false positive rate. | ||
379 | Selectively patching false-positives with `allow(clippy)` is considered worse than not using Clippy at all. | ||
380 | There's `cargo xtask lint` command which runs a subset of low-FPR lints. | ||
381 | Careful tweaking of `xtask lint` is welcome. | ||
382 | See also [rust-lang/clippy#5537](https://github.com/rust-lang/rust-clippy/issues/5537). | ||
383 | Of course, applying Clippy suggestions is welcome as long as they indeed improve the code. | ||
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 7d85b36cb..c1a778852 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc | |||
@@ -113,8 +113,8 @@ Note that installing via `xtask install` does not work for VS Code Remote, inste | |||
113 | 113 | ||
114 | Here are some useful self-diagnostic commands: | 114 | Here are some useful self-diagnostic commands: |
115 | 115 | ||
116 | * **Rust Analyzer: Show RA Version** shows the version of `rust-analyzer` binary | 116 | * **Rust Analyzer: Show RA Version** shows the version of `rust-analyzer` binary. |
117 | * **Rust Analyzer: Status** prints some statistics about the server, like the few latest LSP requests | 117 | * **Rust Analyzer: Status** prints some statistics about the server, and dependency information for the current file. |
118 | * To enable server-side logging, run with `env RA_LOG=info` and see `Output > Rust Analyzer Language Server` in VS Code's panel. | 118 | * To enable server-side logging, run with `env RA_LOG=info` and see `Output > Rust Analyzer Language Server` in VS Code's panel. |
119 | * To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Rust Analyzer Language Server Trace` in the panel. | 119 | * To log all LSP requests, add `"rust-analyzer.trace.server": "verbose"` to the settings and look for `Rust Analyzer Language Server Trace` in the panel. |
120 | * To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open `Output > Rust Analyzer Client` in the panel. | 120 | * To enable client-side logging, add `"rust-analyzer.trace.extension": true` to the settings and open `Output > Rust Analyzer Client` in the panel. |
diff --git a/editors/code/package.json b/editors/code/package.json index bdd8a0c29..cc2ac3bd2 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -651,6 +651,22 @@ | |||
651 | ], | 651 | ], |
652 | "default": "full", | 652 | "default": "full", |
653 | "description": "The strategy to use when inserting new imports or merging imports." | 653 | "description": "The strategy to use when inserting new imports or merging imports." |
654 | }, | ||
655 | "rust-analyzer.runnables.overrideCargo": { | ||
656 | "type": [ | ||
657 | "null", | ||
658 | "string" | ||
659 | ], | ||
660 | "default": null, | ||
661 | "description": "Command to be executed instead of 'cargo' for runnables." | ||
662 | }, | ||
663 | "rust-analyzer.runnables.cargoExtraArgs": { | ||
664 | "type": "array", | ||
665 | "items": { | ||
666 | "type": "string" | ||
667 | }, | ||
668 | "default": [], | ||
669 | "description": "Additional arguments to be passed to cargo for runnables such as tests or binaries.\nFor example, it may be '--release'" | ||
654 | } | 670 | } |
655 | } | 671 | } |
656 | }, | 672 | }, |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index e9581a9b5..1a90f1b7d 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -21,7 +21,12 @@ export function analyzerStatus(ctx: Ctx): Cmd { | |||
21 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { | 21 | provideTextDocumentContent(_uri: vscode.Uri): vscode.ProviderResult<string> { |
22 | if (!vscode.window.activeTextEditor) return ''; | 22 | if (!vscode.window.activeTextEditor) return ''; |
23 | 23 | ||
24 | return ctx.client.sendRequest(ra.analyzerStatus); | 24 | const params: ra.AnalyzerStatusParams = {}; |
25 | const doc = ctx.activeRustEditor?.document; | ||
26 | if (doc != null) { | ||
27 | params.textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(doc); | ||
28 | } | ||
29 | return ctx.client.sendRequest(ra.analyzerStatus, params); | ||
25 | } | 30 | } |
26 | 31 | ||
27 | get onDidChange(): vscode.Event<vscode.Uri> { | 32 | get onDidChange(): vscode.Event<vscode.Uri> { |
@@ -94,7 +99,7 @@ export function matchingBrace(ctx: Ctx): Cmd { | |||
94 | if (!editor || !client) return; | 99 | if (!editor || !client) return; |
95 | 100 | ||
96 | const response = await client.sendRequest(ra.matchingBrace, { | 101 | const response = await client.sendRequest(ra.matchingBrace, { |
97 | textDocument: { uri: editor.document.uri.toString() }, | 102 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
98 | positions: editor.selections.map(s => | 103 | positions: editor.selections.map(s => |
99 | client.code2ProtocolConverter.asPosition(s.active), | 104 | client.code2ProtocolConverter.asPosition(s.active), |
100 | ), | 105 | ), |
@@ -118,7 +123,7 @@ export function joinLines(ctx: Ctx): Cmd { | |||
118 | 123 | ||
119 | const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, { | 124 | const items: lc.TextEdit[] = await client.sendRequest(ra.joinLines, { |
120 | ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), | 125 | ranges: editor.selections.map((it) => client.code2ProtocolConverter.asRange(it)), |
121 | textDocument: { uri: editor.document.uri.toString() }, | 126 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
122 | }); | 127 | }); |
123 | editor.edit((builder) => { | 128 | editor.edit((builder) => { |
124 | client.protocol2CodeConverter.asTextEdits(items).forEach((edit: any) => { | 129 | client.protocol2CodeConverter.asTextEdits(items).forEach((edit: any) => { |
@@ -136,7 +141,7 @@ export function onEnter(ctx: Ctx): Cmd { | |||
136 | if (!editor || !client) return false; | 141 | if (!editor || !client) return false; |
137 | 142 | ||
138 | const lcEdits = await client.sendRequest(ra.onEnter, { | 143 | const lcEdits = await client.sendRequest(ra.onEnter, { |
139 | textDocument: { uri: editor.document.uri.toString() }, | 144 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
140 | position: client.code2ProtocolConverter.asPosition( | 145 | position: client.code2ProtocolConverter.asPosition( |
141 | editor.selection.active, | 146 | editor.selection.active, |
142 | ), | 147 | ), |
@@ -165,7 +170,7 @@ export function parentModule(ctx: Ctx): Cmd { | |||
165 | if (!editor || !client) return; | 170 | if (!editor || !client) return; |
166 | 171 | ||
167 | const response = await client.sendRequest(ra.parentModule, { | 172 | const response = await client.sendRequest(ra.parentModule, { |
168 | textDocument: { uri: editor.document.uri.toString() }, | 173 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
169 | position: client.code2ProtocolConverter.asPosition( | 174 | position: client.code2ProtocolConverter.asPosition( |
170 | editor.selection.active, | 175 | editor.selection.active, |
171 | ), | 176 | ), |
@@ -191,7 +196,7 @@ export function ssr(ctx: Ctx): Cmd { | |||
191 | 196 | ||
192 | const position = editor.selection.active; | 197 | const position = editor.selection.active; |
193 | const selections = editor.selections; | 198 | const selections = editor.selections; |
194 | const textDocument = { uri: editor.document.uri.toString() }; | 199 | const textDocument = ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document); |
195 | 200 | ||
196 | const options: vscode.InputBoxOptions = { | 201 | const options: vscode.InputBoxOptions = { |
197 | value: "() ==>> ()", | 202 | value: "() ==>> ()", |
@@ -339,7 +344,7 @@ export function expandMacro(ctx: Ctx): Cmd { | |||
339 | const position = editor.selection.active; | 344 | const position = editor.selection.active; |
340 | 345 | ||
341 | const expanded = await client.sendRequest(ra.expandMacro, { | 346 | const expanded = await client.sendRequest(ra.expandMacro, { |
342 | textDocument: { uri: editor.document.uri.toString() }, | 347 | textDocument: ctx.client.code2ProtocolConverter.asTextDocumentIdentifier(editor.document), |
343 | position, | 348 | position, |
344 | }); | 349 | }); |
345 | 350 | ||
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index d167041c4..f286b68a6 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -4,7 +4,10 @@ | |||
4 | 4 | ||
5 | import * as lc from "vscode-languageclient"; | 5 | import * as lc from "vscode-languageclient"; |
6 | 6 | ||
7 | export const analyzerStatus = new lc.RequestType0<string, void>("rust-analyzer/analyzerStatus"); | 7 | export interface AnalyzerStatusParams { |
8 | textDocument?: lc.TextDocumentIdentifier; | ||
9 | } | ||
10 | export const analyzerStatus = new lc.RequestType<AnalyzerStatusParams, string, void>("rust-analyzer/analyzerStatus"); | ||
8 | export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); | 11 | export const memoryUsage = new lc.RequestType0<string, void>("rust-analyzer/memoryUsage"); |
9 | 12 | ||
10 | export type Status = "loading" | "ready" | "invalid" | "needsReload"; | 13 | export type Status = "loading" | "ready" | "invalid" | "needsReload"; |
@@ -66,8 +69,10 @@ export interface Runnable { | |||
66 | args: { | 69 | args: { |
67 | workspaceRoot?: string; | 70 | workspaceRoot?: string; |
68 | cargoArgs: string[]; | 71 | cargoArgs: string[]; |
72 | cargoExtraArgs: string[]; | ||
69 | executableArgs: string[]; | 73 | executableArgs: string[]; |
70 | expectTest?: boolean; | 74 | expectTest?: boolean; |
75 | overrideCargo?: string; | ||
71 | }; | 76 | }; |
72 | } | 77 | } |
73 | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables"); | 78 | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables"); |
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index de68f27ae..459b7f250 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts | |||
@@ -129,6 +129,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise | |||
129 | } | 129 | } |
130 | 130 | ||
131 | const args = [...runnable.args.cargoArgs]; // should be a copy! | 131 | const args = [...runnable.args.cargoArgs]; // should be a copy! |
132 | args.push(...runnable.args.cargoExtraArgs); // Append user-specified cargo options. | ||
132 | if (runnable.args.executableArgs.length > 0) { | 133 | if (runnable.args.executableArgs.length > 0) { |
133 | args.push('--', ...runnable.args.executableArgs); | 134 | args.push('--', ...runnable.args.executableArgs); |
134 | } | 135 | } |
@@ -139,6 +140,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise | |||
139 | args: args.slice(1), | 140 | args: args.slice(1), |
140 | cwd: runnable.args.workspaceRoot || ".", | 141 | cwd: runnable.args.workspaceRoot || ".", |
141 | env: prepareEnv(runnable, config.runnableEnv), | 142 | env: prepareEnv(runnable, config.runnableEnv), |
143 | overrideCargo: runnable.args.overrideCargo, | ||
142 | }; | 144 | }; |
143 | 145 | ||
144 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() | 146 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() |
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 14abbd5b7..a3ff15102 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts | |||
@@ -13,6 +13,7 @@ export interface CargoTaskDefinition extends vscode.TaskDefinition { | |||
13 | args?: string[]; | 13 | args?: string[]; |
14 | cwd?: string; | 14 | cwd?: string; |
15 | env?: { [key: string]: string }; | 15 | env?: { [key: string]: string }; |
16 | overrideCargo?: string; | ||
16 | } | 17 | } |
17 | 18 | ||
18 | class CargoTaskProvider implements vscode.TaskProvider { | 19 | class CargoTaskProvider implements vscode.TaskProvider { |
@@ -98,7 +99,14 @@ export async function buildCargoTask( | |||
98 | } | 99 | } |
99 | 100 | ||
100 | if (!exec) { | 101 | if (!exec) { |
101 | exec = new vscode.ShellExecution(toolchain.cargoPath(), args, definition); | 102 | // Check whether we must use a user-defined substitute for cargo. |
103 | const cargoCommand = definition.overrideCargo ? definition.overrideCargo : toolchain.cargoPath(); | ||
104 | |||
105 | // Prepare the whole command as one line. It is required if user has provided override command which contains spaces, | ||
106 | // for example "wrapper cargo". Without manual preparation the overridden command will be quoted and fail to execute. | ||
107 | const fullCommand = [cargoCommand, ...args].join(" "); | ||
108 | |||
109 | exec = new vscode.ShellExecution(fullCommand, definition); | ||
102 | } | 110 | } |
103 | 111 | ||
104 | return new vscode.Task( | 112 | return new vscode.Task( |
diff --git a/editors/code/tests/unit/runnable_env.test.ts b/editors/code/tests/unit/runnable_env.test.ts index f2f53e91a..c5600cf64 100644 --- a/editors/code/tests/unit/runnable_env.test.ts +++ b/editors/code/tests/unit/runnable_env.test.ts | |||
@@ -9,7 +9,8 @@ function makeRunnable(label: string): ra.Runnable { | |||
9 | kind: "cargo", | 9 | kind: "cargo", |
10 | args: { | 10 | args: { |
11 | cargoArgs: [], | 11 | cargoArgs: [], |
12 | executableArgs: [] | 12 | executableArgs: [], |
13 | cargoExtraArgs: [] | ||
13 | } | 14 | } |
14 | }; | 15 | }; |
15 | } | 16 | } |
diff --git a/xtask/src/codegen/gen_feature_docs.rs b/xtask/src/codegen/gen_feature_docs.rs index 3f0013e82..341e67c73 100644 --- a/xtask/src/codegen/gen_feature_docs.rs +++ b/xtask/src/codegen/gen_feature_docs.rs | |||
@@ -38,7 +38,9 @@ impl Feature { | |||
38 | 38 | ||
39 | for block in comment_blocks { | 39 | for block in comment_blocks { |
40 | let id = block.id; | 40 | let id = block.id; |
41 | assert!(is_valid_feature_name(&id), "invalid feature name: {:?}", id); | 41 | if let Err(msg) = is_valid_feature_name(&id) { |
42 | panic!("invalid feature name: {:?}:\n {}", id, msg) | ||
43 | } | ||
42 | let doc = block.contents.join("\n"); | 44 | let doc = block.contents.join("\n"); |
43 | let location = Location::new(path.clone(), block.line); | 45 | let location = Location::new(path.clone(), block.line); |
44 | acc.push(Feature { id, location, doc }) | 46 | acc.push(Feature { id, location, doc }) |
@@ -49,7 +51,7 @@ impl Feature { | |||
49 | } | 51 | } |
50 | } | 52 | } |
51 | 53 | ||
52 | fn is_valid_feature_name(feature: &str) -> bool { | 54 | fn is_valid_feature_name(feature: &str) -> Result<(), String> { |
53 | 'word: for word in feature.split_whitespace() { | 55 | 'word: for word in feature.split_whitespace() { |
54 | for &short in ["to", "and"].iter() { | 56 | for &short in ["to", "and"].iter() { |
55 | if word == short { | 57 | if word == short { |
@@ -58,14 +60,14 @@ fn is_valid_feature_name(feature: &str) -> bool { | |||
58 | } | 60 | } |
59 | for &short in ["To", "And"].iter() { | 61 | for &short in ["To", "And"].iter() { |
60 | if word == short { | 62 | if word == short { |
61 | return false; | 63 | return Err(format!("Don't capitalize {:?}", word)); |
62 | } | 64 | } |
63 | } | 65 | } |
64 | if !word.starts_with(char::is_uppercase) { | 66 | if !word.starts_with(char::is_uppercase) { |
65 | return false; | 67 | return Err(format!("Capitalize {:?}", word)); |
66 | } | 68 | } |
67 | } | 69 | } |
68 | true | 70 | Ok(()) |
69 | } | 71 | } |
70 | 72 | ||
71 | impl fmt::Display for Feature { | 73 | impl fmt::Display for Feature { |