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, |