diff options
-rw-r--r-- | crates/ra_assists/src/raw_string.rs | 740 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/generics.rs | 11 | ||||
-rw-r--r-- | crates/ra_hir/src/resolve.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 4 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/lower.rs | 97 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 64 |
7 files changed, 538 insertions, 392 deletions
diff --git a/crates/ra_assists/src/raw_string.rs b/crates/ra_assists/src/raw_string.rs index e00267060..965a64c98 100644 --- a/crates/ra_assists/src/raw_string.rs +++ b/crates/ra_assists/src/raw_string.rs | |||
@@ -1,370 +1,370 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; | 2 | use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; |
3 | 3 | ||
4 | use crate::{Assist, AssistCtx, AssistId}; | 4 | use crate::{Assist, AssistCtx, AssistId}; |
5 | 5 | ||
6 | pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 6 | pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
7 | let literal = ctx.node_at_offset::<Literal>()?; | 7 | let literal = ctx.node_at_offset::<Literal>()?; |
8 | if literal.token().kind() != ra_syntax::SyntaxKind::STRING { | 8 | if literal.token().kind() != ra_syntax::SyntaxKind::STRING { |
9 | return None; | 9 | return None; |
10 | } | 10 | } |
11 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { | 11 | ctx.add_action(AssistId("make_raw_string"), "make raw string", |edit| { |
12 | edit.target(literal.syntax().text_range()); | 12 | edit.target(literal.syntax().text_range()); |
13 | edit.insert(literal.syntax().text_range().start(), "r"); | 13 | edit.insert(literal.syntax().text_range().start(), "r"); |
14 | }); | 14 | }); |
15 | ctx.build() | 15 | ctx.build() |
16 | } | 16 | } |
17 | 17 | ||
18 | fn find_usual_string_range(s: &str) -> Option<TextRange> { | 18 | fn find_usual_string_range(s: &str) -> Option<TextRange> { |
19 | Some(TextRange::from_to( | 19 | Some(TextRange::from_to( |
20 | TextUnit::from(s.find('"')? as u32), | 20 | TextUnit::from(s.find('"')? as u32), |
21 | TextUnit::from(s.rfind('"')? as u32), | 21 | TextUnit::from(s.rfind('"')? as u32), |
22 | )) | 22 | )) |
23 | } | 23 | } |
24 | 24 | ||
25 | pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 25 | pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
26 | let literal = ctx.node_at_offset::<Literal>()?; | 26 | let literal = ctx.node_at_offset::<Literal>()?; |
27 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { | 27 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { |
28 | return None; | 28 | return None; |
29 | } | 29 | } |
30 | let token = literal.token(); | 30 | let token = literal.token(); |
31 | let text = token.text().as_str(); | 31 | let text = token.text().as_str(); |
32 | let usual_string_range = find_usual_string_range(text)?; | 32 | let usual_string_range = find_usual_string_range(text)?; |
33 | ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { | 33 | ctx.add_action(AssistId("make_usual_string"), "make usual string", |edit| { |
34 | edit.target(literal.syntax().text_range()); | 34 | edit.target(literal.syntax().text_range()); |
35 | // parse inside string to escape `"` | 35 | // parse inside string to escape `"` |
36 | let start_of_inside = usual_string_range.start().to_usize() + 1; | 36 | let start_of_inside = usual_string_range.start().to_usize() + 1; |
37 | let end_of_inside = usual_string_range.end().to_usize(); | 37 | let end_of_inside = usual_string_range.end().to_usize(); |
38 | let inside_str = &text[start_of_inside..end_of_inside]; | 38 | let inside_str = &text[start_of_inside..end_of_inside]; |
39 | let escaped = inside_str.escape_default().to_string(); | 39 | let escaped = inside_str.escape_default().to_string(); |
40 | edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped)); | 40 | edit.replace(literal.syntax().text_range(), format!("\"{}\"", escaped)); |
41 | }); | 41 | }); |
42 | ctx.build() | 42 | ctx.build() |
43 | } | 43 | } |
44 | 44 | ||
45 | pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 45 | pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
46 | let literal = ctx.node_at_offset::<Literal>()?; | 46 | let literal = ctx.node_at_offset::<Literal>()?; |
47 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { | 47 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { |
48 | return None; | 48 | return None; |
49 | } | 49 | } |
50 | ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { | 50 | ctx.add_action(AssistId("add_hash"), "add hash to raw string", |edit| { |
51 | edit.target(literal.syntax().text_range()); | 51 | edit.target(literal.syntax().text_range()); |
52 | edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#"); | 52 | edit.insert(literal.syntax().text_range().start() + TextUnit::of_char('r'), "#"); |
53 | edit.insert(literal.syntax().text_range().end(), "#"); | 53 | edit.insert(literal.syntax().text_range().end(), "#"); |
54 | }); | 54 | }); |
55 | ctx.build() | 55 | ctx.build() |
56 | } | 56 | } |
57 | 57 | ||
58 | pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 58 | pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { |
59 | let literal = ctx.node_at_offset::<Literal>()?; | 59 | let literal = ctx.node_at_offset::<Literal>()?; |
60 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { | 60 | if literal.token().kind() != ra_syntax::SyntaxKind::RAW_STRING { |
61 | return None; | 61 | return None; |
62 | } | 62 | } |
63 | let token = literal.token(); | 63 | let token = literal.token(); |
64 | let text = token.text().as_str(); | 64 | let text = token.text().as_str(); |
65 | if text.starts_with("r\"") { | 65 | if text.starts_with("r\"") { |
66 | // no hash to remove | 66 | // no hash to remove |
67 | return None; | 67 | return None; |
68 | } | 68 | } |
69 | ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { | 69 | ctx.add_action(AssistId("remove_hash"), "remove hash from raw string", |edit| { |
70 | edit.target(literal.syntax().text_range()); | 70 | edit.target(literal.syntax().text_range()); |
71 | let result = &text[2..text.len() - 1]; | 71 | let result = &text[2..text.len() - 1]; |
72 | let result = if result.starts_with("\"") { | 72 | let result = if result.starts_with("\"") { |
73 | // no more hash, escape | 73 | // no more hash, escape |
74 | let internal_str = &result[1..result.len() - 1]; | 74 | let internal_str = &result[1..result.len() - 1]; |
75 | format!("\"{}\"", internal_str.escape_default().to_string()) | 75 | format!("\"{}\"", internal_str.escape_default().to_string()) |
76 | } else { | 76 | } else { |
77 | result.to_owned() | 77 | result.to_owned() |
78 | }; | 78 | }; |
79 | edit.replace(literal.syntax().text_range(), format!("r{}", result)); | 79 | edit.replace(literal.syntax().text_range(), format!("r{}", result)); |
80 | }); | 80 | }); |
81 | ctx.build() | 81 | ctx.build() |
82 | } | 82 | } |
83 | 83 | ||
84 | #[cfg(test)] | 84 | #[cfg(test)] |
85 | mod test { | 85 | mod test { |
86 | use super::*; | 86 | use super::*; |
87 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; | 87 | use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; |
88 | 88 | ||
89 | #[test] | 89 | #[test] |
90 | fn make_raw_string_target() { | 90 | fn make_raw_string_target() { |
91 | check_assist_target( | 91 | check_assist_target( |
92 | make_raw_string, | 92 | make_raw_string, |
93 | r#" | 93 | r#" |
94 | fn f() { | 94 | fn f() { |
95 | let s = <|>"random string"; | 95 | let s = <|>"random string"; |
96 | } | 96 | } |
97 | "#, | 97 | "#, |
98 | r#""random string""#, | 98 | r#""random string""#, |
99 | ); | 99 | ); |
100 | } | 100 | } |
101 | 101 | ||
102 | #[test] | 102 | #[test] |
103 | fn make_raw_string_works() { | 103 | fn make_raw_string_works() { |
104 | check_assist( | 104 | check_assist( |
105 | make_raw_string, | 105 | make_raw_string, |
106 | r#" | 106 | r#" |
107 | fn f() { | 107 | fn f() { |
108 | let s = <|>"random string"; | 108 | let s = <|>"random string"; |
109 | } | 109 | } |
110 | "#, | 110 | "#, |
111 | r#" | 111 | r#" |
112 | fn f() { | 112 | fn f() { |
113 | let s = <|>r"random string"; | 113 | let s = <|>r"random string"; |
114 | } | 114 | } |
115 | "#, | 115 | "#, |
116 | ) | 116 | ) |
117 | } | 117 | } |
118 | 118 | ||
119 | #[test] | 119 | #[test] |
120 | fn make_raw_string_with_escaped_works() { | 120 | fn make_raw_string_with_escaped_works() { |
121 | check_assist( | 121 | check_assist( |
122 | make_raw_string, | 122 | make_raw_string, |
123 | r#" | 123 | r#" |
124 | fn f() { | 124 | fn f() { |
125 | let s = <|>"random\nstring"; | 125 | let s = <|>"random\nstring"; |
126 | } | 126 | } |
127 | "#, | 127 | "#, |
128 | r#" | 128 | r#" |
129 | fn f() { | 129 | fn f() { |
130 | let s = <|>r"random\nstring"; | 130 | let s = <|>r"random\nstring"; |
131 | } | 131 | } |
132 | "#, | 132 | "#, |
133 | ) | 133 | ) |
134 | } | 134 | } |
135 | 135 | ||
136 | #[test] | 136 | #[test] |
137 | fn make_raw_string_not_works() { | 137 | fn make_raw_string_not_works() { |
138 | check_assist_not_applicable( | 138 | check_assist_not_applicable( |
139 | make_raw_string, | 139 | make_raw_string, |
140 | r#" | 140 | r#" |
141 | fn f() { | 141 | fn f() { |
142 | let s = <|>r"random string"; | 142 | let s = <|>r"random string"; |
143 | } | 143 | } |
144 | "#, | 144 | "#, |
145 | ); | 145 | ); |
146 | } | 146 | } |
147 | 147 | ||
148 | #[test] | 148 | #[test] |
149 | fn add_hash_target() { | 149 | fn add_hash_target() { |
150 | check_assist_target( | 150 | check_assist_target( |
151 | add_hash, | 151 | add_hash, |
152 | r#" | 152 | r#" |
153 | fn f() { | 153 | fn f() { |
154 | let s = <|>r"random string"; | 154 | let s = <|>r"random string"; |
155 | } | 155 | } |
156 | "#, | 156 | "#, |
157 | r#"r"random string""#, | 157 | r#"r"random string""#, |
158 | ); | 158 | ); |
159 | } | 159 | } |
160 | 160 | ||
161 | #[test] | 161 | #[test] |
162 | fn add_hash_works() { | 162 | fn add_hash_works() { |
163 | check_assist( | 163 | check_assist( |
164 | add_hash, | 164 | add_hash, |
165 | r#" | 165 | r#" |
166 | fn f() { | 166 | fn f() { |
167 | let s = <|>r"random string"; | 167 | let s = <|>r"random string"; |
168 | } | 168 | } |
169 | "#, | 169 | "#, |
170 | r##" | 170 | r##" |
171 | fn f() { | 171 | fn f() { |
172 | let s = <|>r#"random string"#; | 172 | let s = <|>r#"random string"#; |
173 | } | 173 | } |
174 | "##, | 174 | "##, |
175 | ) | 175 | ) |
176 | } | 176 | } |
177 | 177 | ||
178 | #[test] | 178 | #[test] |
179 | fn add_more_hash_works() { | 179 | fn add_more_hash_works() { |
180 | check_assist( | 180 | check_assist( |
181 | add_hash, | 181 | add_hash, |
182 | r##" | 182 | r##" |
183 | fn f() { | 183 | fn f() { |
184 | let s = <|>r#"random"string"#; | 184 | let s = <|>r#"random"string"#; |
185 | } | 185 | } |
186 | "##, | 186 | "##, |
187 | r###" | 187 | r###" |
188 | fn f() { | 188 | fn f() { |
189 | let s = <|>r##"random"string"##; | 189 | let s = <|>r##"random"string"##; |
190 | } | 190 | } |
191 | "###, | 191 | "###, |
192 | ) | 192 | ) |
193 | } | 193 | } |
194 | 194 | ||
195 | #[test] | 195 | #[test] |
196 | fn add_hash_not_works() { | 196 | fn add_hash_not_works() { |
197 | check_assist_not_applicable( | 197 | check_assist_not_applicable( |
198 | add_hash, | 198 | add_hash, |
199 | r#" | 199 | r#" |
200 | fn f() { | 200 | fn f() { |
201 | let s = <|>"random string"; | 201 | let s = <|>"random string"; |
202 | } | 202 | } |
203 | "#, | 203 | "#, |
204 | ); | 204 | ); |
205 | } | 205 | } |
206 | 206 | ||
207 | #[test] | 207 | #[test] |
208 | fn remove_hash_target() { | 208 | fn remove_hash_target() { |
209 | check_assist_target( | 209 | check_assist_target( |
210 | remove_hash, | 210 | remove_hash, |
211 | r##" | 211 | r##" |
212 | fn f() { | 212 | fn f() { |
213 | let s = <|>r#"random string"#; | 213 | let s = <|>r#"random string"#; |
214 | } | 214 | } |
215 | "##, | 215 | "##, |
216 | r##"r#"random string"#"##, | 216 | r##"r#"random string"#"##, |
217 | ); | 217 | ); |
218 | } | 218 | } |
219 | 219 | ||
220 | #[test] | 220 | #[test] |
221 | fn remove_hash_works() { | 221 | fn remove_hash_works() { |
222 | check_assist( | 222 | check_assist( |
223 | remove_hash, | 223 | remove_hash, |
224 | r##" | 224 | r##" |
225 | fn f() { | 225 | fn f() { |
226 | let s = <|>r#"random string"#; | 226 | let s = <|>r#"random string"#; |
227 | } | 227 | } |
228 | "##, | 228 | "##, |
229 | r#" | 229 | r#" |
230 | fn f() { | 230 | fn f() { |
231 | let s = <|>r"random string"; | 231 | let s = <|>r"random string"; |
232 | } | 232 | } |
233 | "#, | 233 | "#, |
234 | ) | 234 | ) |
235 | } | 235 | } |
236 | 236 | ||
237 | #[test] | 237 | #[test] |
238 | fn remove_hash_with_quote_works() { | 238 | fn remove_hash_with_quote_works() { |
239 | check_assist( | 239 | check_assist( |
240 | remove_hash, | 240 | remove_hash, |
241 | r##" | 241 | r##" |
242 | fn f() { | 242 | fn f() { |
243 | let s = <|>r#"random"str"ing"#; | 243 | let s = <|>r#"random"str"ing"#; |
244 | } | 244 | } |
245 | "##, | 245 | "##, |
246 | r#" | 246 | r#" |
247 | fn f() { | 247 | fn f() { |
248 | let s = <|>r"random\"str\"ing"; | 248 | let s = <|>r"random\"str\"ing"; |
249 | } | 249 | } |
250 | "#, | 250 | "#, |
251 | ) | 251 | ) |
252 | } | 252 | } |
253 | 253 | ||
254 | #[test] | 254 | #[test] |
255 | fn remove_more_hash_works() { | 255 | fn remove_more_hash_works() { |
256 | check_assist( | 256 | check_assist( |
257 | remove_hash, | 257 | remove_hash, |
258 | r###" | 258 | r###" |
259 | fn f() { | 259 | fn f() { |
260 | let s = <|>r##"random string"##; | 260 | let s = <|>r##"random string"##; |
261 | } | 261 | } |
262 | "###, | 262 | "###, |
263 | r##" | 263 | r##" |
264 | fn f() { | 264 | fn f() { |
265 | let s = <|>r#"random string"#; | 265 | let s = <|>r#"random string"#; |
266 | } | 266 | } |
267 | "##, | 267 | "##, |
268 | ) | 268 | ) |
269 | } | 269 | } |
270 | 270 | ||
271 | #[test] | 271 | #[test] |
272 | fn remove_hash_not_works() { | 272 | fn remove_hash_not_works() { |
273 | check_assist_not_applicable( | 273 | check_assist_not_applicable( |
274 | remove_hash, | 274 | remove_hash, |
275 | r#" | 275 | r#" |
276 | fn f() { | 276 | fn f() { |
277 | let s = <|>"random string"; | 277 | let s = <|>"random string"; |
278 | } | 278 | } |
279 | "#, | 279 | "#, |
280 | ); | 280 | ); |
281 | } | 281 | } |
282 | 282 | ||
283 | #[test] | 283 | #[test] |
284 | fn remove_hash_no_hash_not_works() { | 284 | fn remove_hash_no_hash_not_works() { |
285 | check_assist_not_applicable( | 285 | check_assist_not_applicable( |
286 | remove_hash, | 286 | remove_hash, |
287 | r#" | 287 | r#" |
288 | fn f() { | 288 | fn f() { |
289 | let s = <|>r"random string"; | 289 | let s = <|>r"random string"; |
290 | } | 290 | } |
291 | "#, | 291 | "#, |
292 | ); | 292 | ); |
293 | } | 293 | } |
294 | 294 | ||
295 | #[test] | 295 | #[test] |
296 | fn make_usual_string_target() { | 296 | fn make_usual_string_target() { |
297 | check_assist_target( | 297 | check_assist_target( |
298 | make_usual_string, | 298 | make_usual_string, |
299 | r##" | 299 | r##" |
300 | fn f() { | 300 | fn f() { |
301 | let s = <|>r#"random string"#; | 301 | let s = <|>r#"random string"#; |
302 | } | 302 | } |
303 | "##, | 303 | "##, |
304 | r##"r#"random string"#"##, | 304 | r##"r#"random string"#"##, |
305 | ); | 305 | ); |
306 | } | 306 | } |
307 | 307 | ||
308 | #[test] | 308 | #[test] |
309 | fn make_usual_string_works() { | 309 | fn make_usual_string_works() { |
310 | check_assist( | 310 | check_assist( |
311 | make_usual_string, | 311 | make_usual_string, |
312 | r##" | 312 | r##" |
313 | fn f() { | 313 | fn f() { |
314 | let s = <|>r#"random string"#; | 314 | let s = <|>r#"random string"#; |
315 | } | 315 | } |
316 | "##, | 316 | "##, |
317 | r#" | 317 | r#" |
318 | fn f() { | 318 | fn f() { |
319 | let s = <|>"random string"; | 319 | let s = <|>"random string"; |
320 | } | 320 | } |
321 | "#, | 321 | "#, |
322 | ) | 322 | ) |
323 | } | 323 | } |
324 | 324 | ||
325 | #[test] | 325 | #[test] |
326 | fn make_usual_string_with_quote_works() { | 326 | fn make_usual_string_with_quote_works() { |
327 | check_assist( | 327 | check_assist( |
328 | make_usual_string, | 328 | make_usual_string, |
329 | r##" | 329 | r##" |
330 | fn f() { | 330 | fn f() { |
331 | let s = <|>r#"random"str"ing"#; | 331 | let s = <|>r#"random"str"ing"#; |
332 | } | 332 | } |
333 | "##, | 333 | "##, |
334 | r#" | 334 | r#" |
335 | fn f() { | 335 | fn f() { |
336 | let s = <|>"random\"str\"ing"; | 336 | let s = <|>"random\"str\"ing"; |
337 | } | 337 | } |
338 | "#, | 338 | "#, |
339 | ) | 339 | ) |
340 | } | 340 | } |
341 | 341 | ||
342 | #[test] | 342 | #[test] |
343 | fn make_usual_string_more_hash_works() { | 343 | fn make_usual_string_more_hash_works() { |
344 | check_assist( | 344 | check_assist( |
345 | make_usual_string, | 345 | make_usual_string, |
346 | r###" | 346 | r###" |
347 | fn f() { | 347 | fn f() { |
348 | let s = <|>r##"random string"##; | 348 | let s = <|>r##"random string"##; |
349 | } | 349 | } |
350 | "###, | 350 | "###, |
351 | r##" | 351 | r##" |
352 | fn f() { | 352 | fn f() { |
353 | let s = <|>"random string"; | 353 | let s = <|>"random string"; |
354 | } | 354 | } |
355 | "##, | 355 | "##, |
356 | ) | 356 | ) |
357 | } | 357 | } |
358 | 358 | ||
359 | #[test] | 359 | #[test] |
360 | fn make_usual_string_not_works() { | 360 | fn make_usual_string_not_works() { |
361 | check_assist_not_applicable( | 361 | check_assist_not_applicable( |
362 | make_usual_string, | 362 | make_usual_string, |
363 | r#" | 363 | r#" |
364 | fn f() { | 364 | fn f() { |
365 | let s = <|>"random string"; | 365 | let s = <|>"random string"; |
366 | } | 366 | } |
367 | "#, | 367 | "#, |
368 | ); | 368 | ); |
369 | } | 369 | } |
370 | } | 370 | } |
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index f7f124904..05259dcbb 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs | |||
@@ -164,6 +164,13 @@ pub trait HirDatabase: DefDatabase + AstDatabase { | |||
164 | #[salsa::invoke(crate::ty::callable_item_sig)] | 164 | #[salsa::invoke(crate::ty::callable_item_sig)] |
165 | fn callable_item_signature(&self, def: CallableDef) -> FnSig; | 165 | fn callable_item_signature(&self, def: CallableDef) -> FnSig; |
166 | 166 | ||
167 | #[salsa::invoke(crate::ty::generic_predicates_for_param_query)] | ||
168 | fn generic_predicates_for_param( | ||
169 | &self, | ||
170 | def: GenericDef, | ||
171 | param_idx: u32, | ||
172 | ) -> Arc<[GenericPredicate]>; | ||
173 | |||
167 | #[salsa::invoke(crate::ty::generic_predicates_query)] | 174 | #[salsa::invoke(crate::ty::generic_predicates_query)] |
168 | fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>; | 175 | fn generic_predicates(&self, def: GenericDef) -> Arc<[GenericPredicate]>; |
169 | 176 | ||
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 77fb76bfc..ccb777492 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs | |||
@@ -26,8 +26,9 @@ pub struct GenericParam { | |||
26 | } | 26 | } |
27 | 27 | ||
28 | /// Data about the generic parameters of a function, struct, impl, etc. | 28 | /// Data about the generic parameters of a function, struct, impl, etc. |
29 | #[derive(Clone, PartialEq, Eq, Debug, Default)] | 29 | #[derive(Clone, PartialEq, Eq, Debug)] |
30 | pub struct GenericParams { | 30 | pub struct GenericParams { |
31 | pub(crate) def: GenericDef, | ||
31 | pub(crate) parent_params: Option<Arc<GenericParams>>, | 32 | pub(crate) parent_params: Option<Arc<GenericParams>>, |
32 | pub(crate) params: Vec<GenericParam>, | 33 | pub(crate) params: Vec<GenericParam>, |
33 | pub(crate) where_predicates: Vec<WherePredicate>, | 34 | pub(crate) where_predicates: Vec<WherePredicate>, |
@@ -69,7 +70,6 @@ impl GenericParams { | |||
69 | db: &(impl DefDatabase + AstDatabase), | 70 | db: &(impl DefDatabase + AstDatabase), |
70 | def: GenericDef, | 71 | def: GenericDef, |
71 | ) -> Arc<GenericParams> { | 72 | ) -> Arc<GenericParams> { |
72 | let mut generics = GenericParams::default(); | ||
73 | let parent = match def { | 73 | let parent = match def { |
74 | GenericDef::Function(it) => it.container(db).map(GenericDef::from), | 74 | GenericDef::Function(it) => it.container(db).map(GenericDef::from), |
75 | GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), | 75 | GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), |
@@ -77,7 +77,12 @@ impl GenericParams { | |||
77 | GenericDef::Adt(_) | GenericDef::Trait(_) => None, | 77 | GenericDef::Adt(_) | GenericDef::Trait(_) => None, |
78 | GenericDef::ImplBlock(_) => None, | 78 | GenericDef::ImplBlock(_) => None, |
79 | }; | 79 | }; |
80 | generics.parent_params = parent.map(|p| db.generic_params(p)); | 80 | let mut generics = GenericParams { |
81 | def, | ||
82 | params: Vec::new(), | ||
83 | parent_params: parent.map(|p| db.generic_params(p)), | ||
84 | where_predicates: Vec::new(), | ||
85 | }; | ||
81 | let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; | 86 | let start = generics.parent_params.as_ref().map(|p| p.params.len()).unwrap_or(0) as u32; |
82 | // FIXME: add `: Sized` bound for everything except for `Self` in traits | 87 | // FIXME: add `: Sized` bound for everything except for `Self` in traits |
83 | match def { | 88 | match def { |
diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 254d1a964..39f8e1d8a 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs | |||
@@ -344,6 +344,13 @@ impl Resolver { | |||
344 | }) | 344 | }) |
345 | .flat_map(|params| params.where_predicates.iter()) | 345 | .flat_map(|params| params.where_predicates.iter()) |
346 | } | 346 | } |
347 | |||
348 | pub(crate) fn generic_def(&self) -> Option<crate::generics::GenericDef> { | ||
349 | self.scopes.iter().find_map(|scope| match scope { | ||
350 | Scope::GenericParams(params) => Some(params.def), | ||
351 | _ => None, | ||
352 | }) | ||
353 | } | ||
347 | } | 354 | } |
348 | 355 | ||
349 | impl Resolver { | 356 | impl Resolver { |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index a223e120a..36bfb10ce 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -23,8 +23,8 @@ pub(crate) use autoderef::autoderef; | |||
23 | pub(crate) use infer::{infer_query, InferTy, InferenceResult}; | 23 | pub(crate) use infer::{infer_query, InferTy, InferenceResult}; |
24 | pub use lower::CallableDef; | 24 | pub use lower::CallableDef; |
25 | pub(crate) use lower::{ | 25 | pub(crate) use lower::{ |
26 | callable_item_sig, generic_defaults_query, generic_predicates_query, type_for_def, | 26 | callable_item_sig, generic_defaults_query, generic_predicates_for_param_query, |
27 | type_for_field, TypableDef, | 27 | generic_predicates_query, type_for_def, type_for_field, TypableDef, |
28 | }; | 28 | }; |
29 | pub(crate) use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; | 29 | pub(crate) use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; |
30 | 30 | ||
diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index a83842b0f..8d71abc95 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs | |||
@@ -86,6 +86,35 @@ impl Ty { | |||
86 | } | 86 | } |
87 | } | 87 | } |
88 | 88 | ||
89 | /// This is only for `generic_predicates_for_param`, where we can't just | ||
90 | /// lower the self types of the predicates since that could lead to cycles. | ||
91 | /// So we just check here if the `type_ref` resolves to a generic param, and which. | ||
92 | fn from_hir_only_param( | ||
93 | db: &impl HirDatabase, | ||
94 | resolver: &Resolver, | ||
95 | type_ref: &TypeRef, | ||
96 | ) -> Option<u32> { | ||
97 | let path = match type_ref { | ||
98 | TypeRef::Path(path) => path, | ||
99 | _ => return None, | ||
100 | }; | ||
101 | if let crate::PathKind::Type(_) = &path.kind { | ||
102 | return None; | ||
103 | } | ||
104 | if path.segments.len() > 1 { | ||
105 | return None; | ||
106 | } | ||
107 | let resolution = match resolver.resolve_path_in_type_ns(db, path) { | ||
108 | Some((it, None)) => it, | ||
109 | _ => return None, | ||
110 | }; | ||
111 | if let TypeNs::GenericParam(idx) = resolution { | ||
112 | Some(idx) | ||
113 | } else { | ||
114 | None | ||
115 | } | ||
116 | } | ||
117 | |||
89 | pub(crate) fn from_type_relative_path( | 118 | pub(crate) fn from_type_relative_path( |
90 | db: &impl HirDatabase, | 119 | db: &impl HirDatabase, |
91 | resolver: &Resolver, | 120 | resolver: &Resolver, |
@@ -189,11 +218,37 @@ impl Ty { | |||
189 | } | 218 | } |
190 | 219 | ||
191 | fn select_associated_type( | 220 | fn select_associated_type( |
192 | _db: &impl HirDatabase, | 221 | db: &impl HirDatabase, |
193 | _resolver: &Resolver, | 222 | resolver: &Resolver, |
194 | _self_ty: Ty, | 223 | self_ty: Ty, |
195 | _segment: &PathSegment, | 224 | segment: &PathSegment, |
196 | ) -> Ty { | 225 | ) -> Ty { |
226 | let param_idx = match self_ty { | ||
227 | Ty::Param { idx, .. } => idx, | ||
228 | _ => return Ty::Unknown, // Error: Ambiguous associated type | ||
229 | }; | ||
230 | let def = match resolver.generic_def() { | ||
231 | Some(def) => def, | ||
232 | None => return Ty::Unknown, // this can't actually happen | ||
233 | }; | ||
234 | let predicates = db.generic_predicates_for_param(def, param_idx); | ||
235 | let traits_from_env = predicates.iter().filter_map(|pred| match pred { | ||
236 | GenericPredicate::Implemented(tr) if tr.self_ty() == &self_ty => Some(tr.trait_), | ||
237 | _ => None, | ||
238 | }); | ||
239 | let traits = traits_from_env.flat_map(|t| t.all_super_traits(db)); | ||
240 | for t in traits { | ||
241 | if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) { | ||
242 | let generics = t.generic_params(db); | ||
243 | let mut substs = Vec::new(); | ||
244 | substs.push(self_ty.clone()); | ||
245 | substs.extend( | ||
246 | iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1), | ||
247 | ); | ||
248 | // FIXME handle type parameters on the segment | ||
249 | return Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() }); | ||
250 | } | ||
251 | } | ||
197 | Ty::Unknown | 252 | Ty::Unknown |
198 | } | 253 | } |
199 | 254 | ||
@@ -269,9 +324,10 @@ pub(super) fn substs_from_path_segment( | |||
269 | add_self_param: bool, | 324 | add_self_param: bool, |
270 | ) -> Substs { | 325 | ) -> Substs { |
271 | let mut substs = Vec::new(); | 326 | let mut substs = Vec::new(); |
272 | let def_generics = def_generic.map(|def| def.generic_params(db)).unwrap_or_default(); | 327 | let def_generics = def_generic.map(|def| def.generic_params(db)); |
273 | 328 | ||
274 | let parent_param_count = def_generics.count_parent_params(); | 329 | let (parent_param_count, param_count) = |
330 | def_generics.map_or((0, 0), |g| (g.count_parent_params(), g.params.len())); | ||
275 | substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count)); | 331 | substs.extend(iter::repeat(Ty::Unknown).take(parent_param_count)); |
276 | if add_self_param { | 332 | if add_self_param { |
277 | // FIXME this add_self_param argument is kind of a hack: Traits have the | 333 | // FIXME this add_self_param argument is kind of a hack: Traits have the |
@@ -283,7 +339,7 @@ pub(super) fn substs_from_path_segment( | |||
283 | if let Some(generic_args) = &segment.args_and_bindings { | 339 | if let Some(generic_args) = &segment.args_and_bindings { |
284 | // if args are provided, it should be all of them, but we can't rely on that | 340 | // if args are provided, it should be all of them, but we can't rely on that |
285 | let self_param_correction = if add_self_param { 1 } else { 0 }; | 341 | let self_param_correction = if add_self_param { 1 } else { 0 }; |
286 | let param_count = def_generics.params.len() - self_param_correction; | 342 | let param_count = param_count - self_param_correction; |
287 | for arg in generic_args.args.iter().take(param_count) { | 343 | for arg in generic_args.args.iter().take(param_count) { |
288 | match arg { | 344 | match arg { |
289 | GenericArg::Type(type_ref) => { | 345 | GenericArg::Type(type_ref) => { |
@@ -295,10 +351,10 @@ pub(super) fn substs_from_path_segment( | |||
295 | } | 351 | } |
296 | // add placeholders for args that were not provided | 352 | // add placeholders for args that were not provided |
297 | let supplied_params = substs.len(); | 353 | let supplied_params = substs.len(); |
298 | for _ in supplied_params..def_generics.count_params_including_parent() { | 354 | for _ in supplied_params..parent_param_count + param_count { |
299 | substs.push(Ty::Unknown); | 355 | substs.push(Ty::Unknown); |
300 | } | 356 | } |
301 | assert_eq!(substs.len(), def_generics.count_params_including_parent()); | 357 | assert_eq!(substs.len(), parent_param_count + param_count); |
302 | 358 | ||
303 | // handle defaults | 359 | // handle defaults |
304 | if let Some(def_generic) = def_generic { | 360 | if let Some(def_generic) = def_generic { |
@@ -491,6 +547,29 @@ pub(crate) fn type_for_field(db: &impl HirDatabase, field: StructField) -> Ty { | |||
491 | Ty::from_hir(db, &resolver, type_ref) | 547 | Ty::from_hir(db, &resolver, type_ref) |
492 | } | 548 | } |
493 | 549 | ||
550 | /// This query exists only to be used when resolving short-hand associated types | ||
551 | /// like `T::Item`. | ||
552 | /// | ||
553 | /// See the analogous query in rustc and its comment: | ||
554 | /// https://github.com/rust-lang/rust/blob/9150f844e2624eb013ec78ca08c1d416e6644026/src/librustc_typeck/astconv.rs#L46 | ||
555 | /// This is a query mostly to handle cycles somewhat gracefully; e.g. the | ||
556 | /// following bounds are disallowed: `T: Foo<U::Item>, U: Foo<T::Item>`, but | ||
557 | /// these are fine: `T: Foo<U::Item>, U: Foo<()>`. | ||
558 | pub(crate) fn generic_predicates_for_param_query( | ||
559 | db: &impl HirDatabase, | ||
560 | def: GenericDef, | ||
561 | param_idx: u32, | ||
562 | ) -> Arc<[GenericPredicate]> { | ||
563 | let resolver = def.resolver(db); | ||
564 | let predicates = resolver | ||
565 | .where_predicates_in_scope() | ||
566 | // we have to filter out all other predicates *first*, before attempting to lower them | ||
567 | .filter(|pred| Ty::from_hir_only_param(db, &resolver, &pred.type_ref) == Some(param_idx)) | ||
568 | .flat_map(|pred| GenericPredicate::from_where_predicate(db, &resolver, pred)) | ||
569 | .collect::<Vec<_>>(); | ||
570 | predicates.into() | ||
571 | } | ||
572 | |||
494 | pub(crate) fn trait_env( | 573 | pub(crate) fn trait_env( |
495 | db: &impl HirDatabase, | 574 | db: &impl HirDatabase, |
496 | resolver: &Resolver, | 575 | resolver: &Resolver, |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 3b0a99460..3ac1fbdd5 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -2740,17 +2740,17 @@ fn test() { | |||
2740 | [202; 203) 't': T | 2740 | [202; 203) 't': T |
2741 | [221; 223) '{}': () | 2741 | [221; 223) '{}': () |
2742 | [234; 300) '{ ...(S); }': () | 2742 | [234; 300) '{ ...(S); }': () |
2743 | [244; 245) 'x': {unknown} | 2743 | [244; 245) 'x': u32 |
2744 | [248; 252) 'foo1': fn foo1<S>(T) -> {unknown} | 2744 | [248; 252) 'foo1': fn foo1<S>(T) -> <T as Iterable>::Item |
2745 | [248; 255) 'foo1(S)': {unknown} | 2745 | [248; 255) 'foo1(S)': u32 |
2746 | [253; 254) 'S': S | 2746 | [253; 254) 'S': S |
2747 | [265; 266) 'y': u32 | 2747 | [265; 266) 'y': u32 |
2748 | [269; 273) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item | 2748 | [269; 273) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item |
2749 | [269; 276) 'foo2(S)': u32 | 2749 | [269; 276) 'foo2(S)': u32 |
2750 | [274; 275) 'S': S | 2750 | [274; 275) 'S': S |
2751 | [286; 287) 'z': {unknown} | 2751 | [286; 287) 'z': u32 |
2752 | [290; 294) 'foo3': fn foo3<S>(T) -> {unknown} | 2752 | [290; 294) 'foo3': fn foo3<S>(T) -> <T as Iterable>::Item |
2753 | [290; 297) 'foo3(S)': {unknown} | 2753 | [290; 297) 'foo3(S)': u32 |
2754 | [295; 296) 'S': S | 2754 | [295; 296) 'S': S |
2755 | "### | 2755 | "### |
2756 | ); | 2756 | ); |
@@ -4080,7 +4080,7 @@ fn test<F: FnOnce(u32) -> u64>(f: F) { | |||
4080 | } | 4080 | } |
4081 | 4081 | ||
4082 | #[test] | 4082 | #[test] |
4083 | fn unselected_projection_in_trait_env() { | 4083 | fn unselected_projection_in_trait_env_1() { |
4084 | let t = type_at( | 4084 | let t = type_at( |
4085 | r#" | 4085 | r#" |
4086 | //- /main.rs | 4086 | //- /main.rs |
@@ -4102,7 +4102,33 @@ fn test<T: Trait>() where T::Item: Trait2 { | |||
4102 | } | 4102 | } |
4103 | 4103 | ||
4104 | #[test] | 4104 | #[test] |
4105 | fn unselected_projection_in_trait_env_cycle() { | 4105 | fn unselected_projection_in_trait_env_2() { |
4106 | let t = type_at( | ||
4107 | r#" | ||
4108 | //- /main.rs | ||
4109 | trait Trait<T> { | ||
4110 | type Item; | ||
4111 | } | ||
4112 | |||
4113 | trait Trait2 { | ||
4114 | fn foo(&self) -> u32; | ||
4115 | } | ||
4116 | |||
4117 | fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> { | ||
4118 | let x: T::Item = no_matter; | ||
4119 | x.foo()<|>; | ||
4120 | } | ||
4121 | "#, | ||
4122 | ); | ||
4123 | assert_eq!(t, "u32"); | ||
4124 | } | ||
4125 | |||
4126 | #[test] | ||
4127 | // FIXME this is currently a Salsa panic; it would be nicer if it just returned | ||
4128 | // in Unknown, and we should be able to do that once Salsa allows us to handle | ||
4129 | // the cycle. But at least it doesn't overflow for now. | ||
4130 | #[should_panic] | ||
4131 | fn unselected_projection_in_trait_env_cycle_1() { | ||
4106 | let t = type_at( | 4132 | let t = type_at( |
4107 | r#" | 4133 | r#" |
4108 | //- /main.rs | 4134 | //- /main.rs |
@@ -4121,6 +4147,28 @@ fn test<T: Trait>() where T: Trait2<T::Item> { | |||
4121 | assert_eq!(t, "{unknown}"); | 4147 | assert_eq!(t, "{unknown}"); |
4122 | } | 4148 | } |
4123 | 4149 | ||
4150 | #[test] | ||
4151 | // FIXME this is currently a Salsa panic; it would be nicer if it just returned | ||
4152 | // in Unknown, and we should be able to do that once Salsa allows us to handle | ||
4153 | // the cycle. But at least it doesn't overflow for now. | ||
4154 | #[should_panic] | ||
4155 | fn unselected_projection_in_trait_env_cycle_2() { | ||
4156 | let t = type_at( | ||
4157 | r#" | ||
4158 | //- /main.rs | ||
4159 | trait Trait<T> { | ||
4160 | type Item; | ||
4161 | } | ||
4162 | |||
4163 | fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> { | ||
4164 | let x: T::Item = no_matter<|>; | ||
4165 | } | ||
4166 | "#, | ||
4167 | ); | ||
4168 | // this is a legitimate cycle | ||
4169 | assert_eq!(t, "{unknown}"); | ||
4170 | } | ||
4171 | |||
4124 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { | 4172 | fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { |
4125 | let file = db.parse(pos.file_id).ok().unwrap(); | 4173 | let file = db.parse(pos.file_id).ok().unwrap(); |
4126 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); | 4174 | let expr = algo::find_node_at_offset::<ast::Expr>(file.syntax(), pos.offset).unwrap(); |