aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorFlorian Diebold <[email protected]>2019-09-22 19:01:12 +0100
committerFlorian Diebold <[email protected]>2019-09-22 19:02:32 +0100
commit18bf278c258f44ed68f67f84993f20f7c27c63d0 (patch)
tree3c07347cee581c61107950b1ed9daed191027f5b /crates
parent468e1d14c1a4b3d646207ae919082dc17753cd31 (diff)
Handle associated type shorthand (`T::Item`)
This is only allowed for generic parameters (including `Self` in traits), and special care needs to be taken to not run into cycles while resolving it, because we use the where clauses of the generic parameter to find candidates for the trait containing the associated type, but the where clauses may themselves contain instances of short-hand associated types. In some cases this is even fine, e.g. we might have `T: Trait<U::Item>, U: Iterator`. If there is a cycle, we'll currently panic, which isn't great, but better than overflowing the stack...
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/raw_string.rs740
-rw-r--r--crates/ra_hir/src/db.rs7
-rw-r--r--crates/ra_hir/src/generics.rs11
-rw-r--r--crates/ra_hir/src/resolve.rs7
-rw-r--r--crates/ra_hir/src/ty.rs4
-rw-r--r--crates/ra_hir/src/ty/lower.rs97
-rw-r--r--crates/ra_hir/src/ty/tests.rs64
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 @@
1use hir::db::HirDatabase; 1use hir::db::HirDatabase;
2use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit}; 2use ra_syntax::{ast::AstNode, ast::Literal, TextRange, TextUnit};
3 3
4use crate::{Assist, AssistCtx, AssistId}; 4use crate::{Assist, AssistCtx, AssistId};
5 5
6pub(crate) fn make_raw_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 6pub(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
18fn find_usual_string_range(s: &str) -> Option<TextRange> { 18fn 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
25pub(crate) fn make_usual_string(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 25pub(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
45pub(crate) fn add_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 45pub(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
58pub(crate) fn remove_hash(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 58pub(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)]
85mod test { 85mod 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)]
30pub struct GenericParams { 30pub 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
349impl Resolver { 356impl 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;
23pub(crate) use infer::{infer_query, InferTy, InferenceResult}; 23pub(crate) use infer::{infer_query, InferTy, InferenceResult};
24pub use lower::CallableDef; 24pub use lower::CallableDef;
25pub(crate) use lower::{ 25pub(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};
29pub(crate) use traits::{InEnvironment, Obligation, ProjectionPredicate, TraitEnvironment}; 29pub(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<()>`.
558pub(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
494pub(crate) fn trait_env( 573pub(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]
4083fn unselected_projection_in_trait_env() { 4083fn 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]
4105fn unselected_projection_in_trait_env_cycle() { 4105fn unselected_projection_in_trait_env_2() {
4106 let t = type_at(
4107 r#"
4108//- /main.rs
4109trait Trait<T> {
4110 type Item;
4111}
4112
4113trait Trait2 {
4114 fn foo(&self) -> u32;
4115}
4116
4117fn 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]
4131fn 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]
4155fn unselected_projection_in_trait_env_cycle_2() {
4156 let t = type_at(
4157 r#"
4158//- /main.rs
4159trait Trait<T> {
4160 type Item;
4161}
4162
4163fn 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
4124fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String { 4172fn 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();