aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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();