aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs19
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs54
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs34
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs33
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs224
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_flycheck/Cargo.toml2
-rw-r--r--crates/ra_hir/src/code_model.rs6
-rw-r--r--crates/ra_hir/src/source_analyzer.rs36
-rw-r--r--crates/ra_hir_def/Cargo.toml2
-rw-r--r--crates/ra_hir_def/src/adt.rs20
-rw-r--r--crates/ra_hir_def/src/attr.rs1
-rw-r--r--crates/ra_hir_def/src/body.rs66
-rw-r--r--crates/ra_hir_def/src/body/lower.rs80
-rw-r--r--crates/ra_hir_def/src/data.rs63
-rw-r--r--crates/ra_hir_def/src/expr.rs6
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs14
-rw-r--r--crates/ra_hir_def/src/nameres/tests/incremental.rs6
-rw-r--r--crates/ra_hir_def/src/path.rs26
-rw-r--r--crates/ra_hir_def/src/path/lower.rs21
-rw-r--r--crates/ra_hir_def/src/type_ref.rs12
-rw-r--r--crates/ra_hir_expand/src/name.rs9
-rw-r--r--crates/ra_hir_ty/Cargo.toml9
-rw-r--r--crates/ra_hir_ty/src/_match.rs350
-rw-r--r--crates/ra_hir_ty/src/db.rs9
-rw-r--r--crates/ra_hir_ty/src/expr.rs68
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs6
-rw-r--r--crates/ra_hir_ty/src/lower.rs42
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs45
-rw-r--r--crates/ra_hir_ty/src/tests.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs100
-rw-r--r--crates/ra_hir_ty/src/traits.rs21
-rw-r--r--crates/ra_hir_ty/src/traits/chalk.rs162
-rw-r--r--crates/ra_hir_ty/src/traits/chalk/tls.rs42
-rw-r--r--crates/ra_ide/Cargo.toml2
-rw-r--r--crates/ra_ide/src/call_info.rs21
-rw-r--r--crates/ra_ide/src/completion/complete_pattern.rs4
-rw-r--r--crates/ra_ide/src/completion/complete_record.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs9
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs14
-rw-r--r--crates/ra_ide/src/inlay_hints.rs17
-rw-r--r--crates/ra_ide_db/src/defs.rs2
-rw-r--r--crates/ra_parser/src/grammar.rs11
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs37
-rw-r--r--crates/ra_parser/src/grammar/patterns.rs50
-rw-r--r--crates/ra_syntax/src/ast.rs4
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs60
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/ra_syntax/src/ast/make.rs3
-rw-r--r--crates/ra_syntax/src/lib.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast49
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast14
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast26
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast14
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast14
-rw-r--r--crates/rust-analyzer/src/bin/args.rs39
-rw-r--r--crates/rust-analyzer/src/bin/main.rs4
-rw-r--r--crates/rust-analyzer/src/cli.rs8
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs12
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs74
-rw-r--r--crates/test_utils/src/lib.rs2
66 files changed, 1693 insertions, 429 deletions
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index 64444ee3a..b39e60870 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -78,7 +78,7 @@ fn foo() {
78} 78}
79 79
80fn bar(arg: &str, baz: Baz) { 80fn bar(arg: &str, baz: Baz) {
81 unimplemented!() 81 todo!()
82} 82}
83 83
84"#####, 84"#####,
@@ -180,7 +180,7 @@ trait Trait<T> {
180} 180}
181 181
182impl Trait<u32> for () { 182impl Trait<u32> for () {
183 fn foo(&self) -> u32 { unimplemented!() } 183 fn foo(&self) -> u32 { todo!() }
184 184
185} 185}
186"#####, 186"#####,
@@ -607,6 +607,21 @@ impl Walrus {
607} 607}
608 608
609#[test] 609#[test]
610fn doctest_reorder_fields() {
611 check(
612 "reorder_fields",
613 r#####"
614struct Foo {foo: i32, bar: i32};
615const test: Foo = <|>Foo {bar: 0, foo: 1}
616"#####,
617 r#####"
618struct Foo {foo: i32, bar: i32};
619const test: Foo = Foo {foo: 1, bar: 0}
620"#####,
621 )
622}
623
624#[test]
610fn doctest_replace_if_let_with_match() { 625fn doctest_replace_if_let_with_match() {
611 check( 626 check(
612 "replace_if_let_with_match", 627 "replace_if_let_with_match",
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index 488bae08f..ad4ab66ed 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -29,7 +29,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
29// } 29// }
30// 30//
31// fn bar(arg: &str, baz: Baz) { 31// fn bar(arg: &str, baz: Baz) {
32// unimplemented!() 32// todo!()
33// } 33// }
34// 34//
35// ``` 35// ```
@@ -80,7 +80,7 @@ impl FunctionBuilder {
80 Some(Self { append_fn_at, fn_name, type_params, params }) 80 Some(Self { append_fn_at, fn_name, type_params, params })
81 } 81 }
82 fn render(self) -> Option<FunctionTemplate> { 82 fn render(self) -> Option<FunctionTemplate> {
83 let placeholder_expr = ast::make::expr_unimplemented(); 83 let placeholder_expr = ast::make::expr_todo();
84 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); 84 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr));
85 let fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); 85 let fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body);
86 let fn_def = ast::make::add_newlines(2, fn_def); 86 let fn_def = ast::make::add_newlines(2, fn_def);
@@ -225,7 +225,7 @@ fn foo() {
225} 225}
226 226
227fn bar() { 227fn bar() {
228 <|>unimplemented!() 228 <|>todo!()
229} 229}
230", 230",
231 ) 231 )
@@ -252,7 +252,7 @@ impl Foo {
252} 252}
253 253
254fn bar() { 254fn bar() {
255 <|>unimplemented!() 255 <|>todo!()
256} 256}
257", 257",
258 ) 258 )
@@ -276,7 +276,7 @@ fn foo1() {
276} 276}
277 277
278fn bar() { 278fn bar() {
279 <|>unimplemented!() 279 <|>todo!()
280} 280}
281 281
282fn foo2() {} 282fn foo2() {}
@@ -302,7 +302,7 @@ mod baz {
302 } 302 }
303 303
304 fn bar() { 304 fn bar() {
305 <|>unimplemented!() 305 <|>todo!()
306 } 306 }
307} 307}
308", 308",
@@ -315,20 +315,20 @@ mod baz {
315 add_function, 315 add_function,
316 r" 316 r"
317struct Baz; 317struct Baz;
318fn baz() -> Baz { unimplemented!() } 318fn baz() -> Baz { todo!() }
319fn foo() { 319fn foo() {
320 bar<|>(baz()); 320 bar<|>(baz());
321} 321}
322", 322",
323 r" 323 r"
324struct Baz; 324struct Baz;
325fn baz() -> Baz { unimplemented!() } 325fn baz() -> Baz { todo!() }
326fn foo() { 326fn foo() {
327 bar(baz()); 327 bar(baz());
328} 328}
329 329
330fn bar(baz: Baz) { 330fn bar(baz: Baz) {
331 <|>unimplemented!() 331 <|>todo!()
332} 332}
333", 333",
334 ); 334 );
@@ -361,7 +361,7 @@ impl Baz {
361} 361}
362 362
363fn bar(baz: Baz) { 363fn bar(baz: Baz) {
364 <|>unimplemented!() 364 <|>todo!()
365} 365}
366", 366",
367 ) 367 )
@@ -382,7 +382,7 @@ fn foo() {
382} 382}
383 383
384fn bar(arg: &str) { 384fn bar(arg: &str) {
385 <|>unimplemented!() 385 <|>todo!()
386} 386}
387"#, 387"#,
388 ) 388 )
@@ -403,7 +403,7 @@ fn foo() {
403} 403}
404 404
405fn bar(arg: char) { 405fn bar(arg: char) {
406 <|>unimplemented!() 406 <|>todo!()
407} 407}
408"#, 408"#,
409 ) 409 )
@@ -424,7 +424,7 @@ fn foo() {
424} 424}
425 425
426fn bar(arg: i32) { 426fn bar(arg: i32) {
427 <|>unimplemented!() 427 <|>todo!()
428} 428}
429", 429",
430 ) 430 )
@@ -445,7 +445,7 @@ fn foo() {
445} 445}
446 446
447fn bar(arg: u8) { 447fn bar(arg: u8) {
448 <|>unimplemented!() 448 <|>todo!()
449} 449}
450", 450",
451 ) 451 )
@@ -470,7 +470,7 @@ fn foo() {
470} 470}
471 471
472fn bar(x: u8) { 472fn bar(x: u8) {
473 <|>unimplemented!() 473 <|>todo!()
474} 474}
475", 475",
476 ) 476 )
@@ -493,7 +493,7 @@ fn foo() {
493} 493}
494 494
495fn bar(worble: ()) { 495fn bar(worble: ()) {
496 <|>unimplemented!() 496 <|>todo!()
497} 497}
498", 498",
499 ) 499 )
@@ -506,7 +506,7 @@ fn bar(worble: ()) {
506 r" 506 r"
507trait Foo {} 507trait Foo {}
508fn foo() -> impl Foo { 508fn foo() -> impl Foo {
509 unimplemented!() 509 todo!()
510} 510}
511fn baz() { 511fn baz() {
512 <|>bar(foo()) 512 <|>bar(foo())
@@ -515,14 +515,14 @@ fn baz() {
515 r" 515 r"
516trait Foo {} 516trait Foo {}
517fn foo() -> impl Foo { 517fn foo() -> impl Foo {
518 unimplemented!() 518 todo!()
519} 519}
520fn baz() { 520fn baz() {
521 bar(foo()) 521 bar(foo())
522} 522}
523 523
524fn bar(foo: impl Foo) { 524fn bar(foo: impl Foo) {
525 <|>unimplemented!() 525 <|>todo!()
526} 526}
527", 527",
528 ) 528 )
@@ -556,7 +556,7 @@ mod Foo {
556 } 556 }
557 557
558 fn bar(baz: super::Baz::Bof) { 558 fn bar(baz: super::Baz::Bof) {
559 <|>unimplemented!() 559 <|>todo!()
560 } 560 }
561} 561}
562", 562",
@@ -580,7 +580,7 @@ fn foo<T>(t: T) {
580} 580}
581 581
582fn bar<T>(t: T) { 582fn bar<T>(t: T) {
583 <|>unimplemented!() 583 <|>todo!()
584} 584}
585", 585",
586 ) 586 )
@@ -611,7 +611,7 @@ fn foo() {
611} 611}
612 612
613fn bar(arg: fn() -> Baz) { 613fn bar(arg: fn() -> Baz) {
614 <|>unimplemented!() 614 <|>todo!()
615} 615}
616", 616",
617 ) 617 )
@@ -636,7 +636,7 @@ fn foo() {
636} 636}
637 637
638fn bar(closure: impl Fn(i64) -> i64) { 638fn bar(closure: impl Fn(i64) -> i64) {
639 <|>unimplemented!() 639 <|>todo!()
640} 640}
641", 641",
642 ) 642 )
@@ -657,7 +657,7 @@ fn foo() {
657} 657}
658 658
659fn bar(baz: ()) { 659fn bar(baz: ()) {
660 <|>unimplemented!() 660 <|>todo!()
661} 661}
662", 662",
663 ) 663 )
@@ -682,7 +682,7 @@ fn foo() {
682} 682}
683 683
684fn bar(baz_1: Baz, baz_2: Baz) { 684fn bar(baz_1: Baz, baz_2: Baz) {
685 <|>unimplemented!() 685 <|>todo!()
686} 686}
687", 687",
688 ) 688 )
@@ -707,7 +707,7 @@ fn foo() {
707} 707}
708 708
709fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { 709fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
710 <|>unimplemented!() 710 <|>todo!()
711} 711}
712"#, 712"#,
713 ) 713 )
@@ -779,7 +779,7 @@ impl Foo {
779 self.bar(); 779 self.bar();
780 } 780 }
781 fn bar(&self) { 781 fn bar(&self) {
782 unimplemented!(); 782 todo!();
783 } 783 }
784} 784}
785 ", 785 ",
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 722f207e2..2d6d44980 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -40,7 +40,7 @@ enum AddMissingImplMembersMode {
40// } 40// }
41// 41//
42// impl Trait<u32> for () { 42// impl Trait<u32> for () {
43// fn foo(&self) -> u32 { unimplemented!() } 43// fn foo(&self) -> u32 { todo!() }
44// 44//
45// } 45// }
46// ``` 46// ```
@@ -165,7 +165,7 @@ fn add_missing_impl_members_inner(
165 165
166fn add_body(fn_def: ast::FnDef) -> ast::FnDef { 166fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
167 if fn_def.body().is_none() { 167 if fn_def.body().is_none() {
168 fn_def.with_body(make::block_from_expr(make::expr_unimplemented())) 168 fn_def.with_body(make::block_from_expr(make::expr_todo()))
169 } else { 169 } else {
170 fn_def 170 fn_def
171 } 171 }
@@ -215,8 +215,8 @@ impl Foo for S {
215 fn bar(&self) {} 215 fn bar(&self) {}
216 <|>type Output; 216 <|>type Output;
217 const CONST: usize = 42; 217 const CONST: usize = 42;
218 fn foo(&self) { unimplemented!() } 218 fn foo(&self) { todo!() }
219 fn baz(&self) { unimplemented!() } 219 fn baz(&self) { todo!() }
220 220
221}", 221}",
222 ); 222 );
@@ -250,7 +250,7 @@ struct S;
250 250
251impl Foo for S { 251impl Foo for S {
252 fn bar(&self) {} 252 fn bar(&self) {}
253 <|>fn foo(&self) { unimplemented!() } 253 <|>fn foo(&self) { todo!() }
254 254
255}", 255}",
256 ); 256 );
@@ -268,7 +268,7 @@ impl Foo for S { <|> }",
268trait Foo { fn foo(&self); } 268trait Foo { fn foo(&self); }
269struct S; 269struct S;
270impl Foo for S { 270impl Foo for S {
271 <|>fn foo(&self) { unimplemented!() } 271 <|>fn foo(&self) { todo!() }
272}", 272}",
273 ); 273 );
274 } 274 }
@@ -285,7 +285,7 @@ impl Foo<u32> for S { <|> }",
285trait Foo<T> { fn foo(&self, t: T) -> &T; } 285trait Foo<T> { fn foo(&self, t: T) -> &T; }
286struct S; 286struct S;
287impl Foo<u32> for S { 287impl Foo<u32> for S {
288 <|>fn foo(&self, t: u32) -> &u32 { unimplemented!() } 288 <|>fn foo(&self, t: u32) -> &u32 { todo!() }
289}", 289}",
290 ); 290 );
291 } 291 }
@@ -302,7 +302,7 @@ impl<U> Foo<U> for S { <|> }",
302trait Foo<T> { fn foo(&self, t: T) -> &T; } 302trait Foo<T> { fn foo(&self, t: T) -> &T; }
303struct S; 303struct S;
304impl<U> Foo<U> for S { 304impl<U> Foo<U> for S {
305 <|>fn foo(&self, t: U) -> &U { unimplemented!() } 305 <|>fn foo(&self, t: U) -> &U { todo!() }
306}", 306}",
307 ); 307 );
308 } 308 }
@@ -319,7 +319,7 @@ impl Foo for S {}<|>",
319trait Foo { fn foo(&self); } 319trait Foo { fn foo(&self); }
320struct S; 320struct S;
321impl Foo for S { 321impl Foo for S {
322 <|>fn foo(&self) { unimplemented!() } 322 <|>fn foo(&self) { todo!() }
323}", 323}",
324 ) 324 )
325 } 325 }
@@ -342,7 +342,7 @@ mod foo {
342} 342}
343struct S; 343struct S;
344impl foo::Foo for S { 344impl foo::Foo for S {
345 <|>fn foo(&self, bar: foo::Bar) { unimplemented!() } 345 <|>fn foo(&self, bar: foo::Bar) { todo!() }
346}", 346}",
347 ); 347 );
348 } 348 }
@@ -365,7 +365,7 @@ mod foo {
365} 365}
366struct S; 366struct S;
367impl foo::Foo for S { 367impl foo::Foo for S {
368 <|>fn foo(&self, bar: foo::Bar<u32>) { unimplemented!() } 368 <|>fn foo(&self, bar: foo::Bar<u32>) { todo!() }
369}", 369}",
370 ); 370 );
371 } 371 }
@@ -388,7 +388,7 @@ mod foo {
388} 388}
389struct S; 389struct S;
390impl foo::Foo<u32> for S { 390impl foo::Foo<u32> for S {
391 <|>fn foo(&self, bar: foo::Bar<u32>) { unimplemented!() } 391 <|>fn foo(&self, bar: foo::Bar<u32>) { todo!() }
392}", 392}",
393 ); 393 );
394 } 394 }
@@ -414,7 +414,7 @@ mod foo {
414struct Param; 414struct Param;
415struct S; 415struct S;
416impl foo::Foo<Param> for S { 416impl foo::Foo<Param> for S {
417 <|>fn foo(&self, bar: Param) { unimplemented!() } 417 <|>fn foo(&self, bar: Param) { todo!() }
418}", 418}",
419 ); 419 );
420 } 420 }
@@ -439,7 +439,7 @@ mod foo {
439} 439}
440struct S; 440struct S;
441impl foo::Foo for S { 441impl foo::Foo for S {
442 <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { unimplemented!() } 442 <|>fn foo(&self, bar: foo::Bar<u32>::Assoc) { todo!() }
443}", 443}",
444 ); 444 );
445 } 445 }
@@ -464,7 +464,7 @@ mod foo {
464} 464}
465struct S; 465struct S;
466impl foo::Foo for S { 466impl foo::Foo for S {
467 <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { unimplemented!() } 467 <|>fn foo(&self, bar: foo::Bar<foo::Baz>) { todo!() }
468}", 468}",
469 ); 469 );
470 } 470 }
@@ -487,7 +487,7 @@ mod foo {
487} 487}
488struct S; 488struct S;
489impl foo::Foo for S { 489impl foo::Foo for S {
490 <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { unimplemented!() } 490 <|>fn foo(&self, bar: dyn Fn(u32) -> i32) { todo!() }
491}", 491}",
492 ); 492 );
493 } 493 }
@@ -544,7 +544,7 @@ trait Foo {
544struct S; 544struct S;
545impl Foo for S { 545impl Foo for S {
546 <|>type Output; 546 <|>type Output;
547 fn foo(&self) { unimplemented!() } 547 fn foo(&self) { todo!() }
548}"#, 548}"#,
549 ) 549 )
550 } 550 }
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 0958f52f1..ef0ce0586 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -1,7 +1,7 @@
1use std::iter::successors; 1use std::iter::successors;
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 algo::{neighbor, SyntaxRewriter}, 4 algo::{neighbor, skip_trivia_token, SyntaxRewriter},
5 ast::{self, edit::AstNodeEdit, make}, 5 ast::{self, edit::AstNodeEdit, make},
6 AstNode, Direction, InsertPosition, SyntaxElement, T, 6 AstNode, Direction, InsertPosition, SyntaxElement, T,
7}; 7};
@@ -72,9 +72,18 @@ fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTre
72 let lhs = old.split_prefix(&lhs_prefix); 72 let lhs = old.split_prefix(&lhs_prefix);
73 let rhs = new.split_prefix(&rhs_prefix); 73 let rhs = new.split_prefix(&rhs_prefix);
74 74
75 let should_insert_comma = lhs
76 .use_tree_list()?
77 .r_curly_token()
78 .and_then(|it| skip_trivia_token(it.prev_token()?, Direction::Prev))
79 .map(|it| it.kind() != T![,])
80 .unwrap_or(true);
81
75 let mut to_insert: Vec<SyntaxElement> = Vec::new(); 82 let mut to_insert: Vec<SyntaxElement> = Vec::new();
76 to_insert.push(make::token(T![,]).into()); 83 if should_insert_comma {
77 to_insert.push(make::tokens::single_space().into()); 84 to_insert.push(make::token(T![,]).into());
85 to_insert.push(make::tokens::single_space().into());
86 }
78 to_insert.extend( 87 to_insert.extend(
79 rhs.use_tree_list()? 88 rhs.use_tree_list()?
80 .syntax() 89 .syntax()
@@ -247,4 +256,22 @@ use {
247", 256",
248 ); 257 );
249 } 258 }
259
260 #[test]
261 fn test_double_comma() {
262 check_assist(
263 merge_imports,
264 r"
265use foo::bar::baz;
266use foo::<|>{
267 FooBar,
268};
269",
270 r"
271use foo::{<|>
272 FooBar,
273bar::baz};
274",
275 )
276 }
250} 277}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
new file mode 100644
index 000000000..5cbb98d73
--- /dev/null
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -0,0 +1,224 @@
1use std::collections::HashMap;
2
3use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
4use itertools::Itertools;
5use ra_ide_db::RootDatabase;
6use ra_syntax::{
7 algo,
8 ast::{self, Path, RecordLit, RecordPat},
9 match_ast, AstNode, SyntaxKind,
10 SyntaxKind::*,
11 SyntaxNode,
12};
13
14use crate::{
15 assist_ctx::{Assist, AssistCtx},
16 AssistId,
17};
18
19// Assist: reorder_fields
20//
21// Reorder the fields of record literals and record patterns in the same order as in
22// the definition.
23//
24// ```
25// struct Foo {foo: i32, bar: i32};
26// const test: Foo = <|>Foo {bar: 0, foo: 1}
27// ```
28// ->
29// ```
30// struct Foo {foo: i32, bar: i32};
31// const test: Foo = Foo {foo: 1, bar: 0}
32// ```
33//
34pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> {
35 reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx))
36}
37
38fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
39 let record = ctx.find_node_at_offset::<R>()?;
40 let path = record.syntax().children().find_map(Path::cast)?;
41
42 let ranks = compute_fields_ranks(&path, &ctx)?;
43
44 let fields = get_fields(&record.syntax());
45 let sorted_fields = sorted_by_rank(&fields, |node| {
46 *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())
47 });
48
49 if sorted_fields == fields {
50 return None;
51 }
52
53 ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", |edit| {
54 for (old, new) in fields.iter().zip(&sorted_fields) {
55 algo::diff(old, new).into_text_edit(edit.text_edit_builder());
56 }
57 edit.target(record.syntax().text_range())
58 })
59}
60
61fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
62 match node.kind() {
63 RECORD_LIT => vec![RECORD_FIELD],
64 RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
65 _ => vec![],
66 }
67}
68
69fn get_field_name(node: &SyntaxNode) -> String {
70 let res = match_ast! {
71 match node {
72 ast::RecordField(field) => field.field_name().map(|it| it.to_string()),
73 ast::RecordFieldPat(field) => field.field_name().map(|it| it.to_string()),
74 _ => None,
75 }
76 };
77 res.unwrap_or_default()
78}
79
80fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
81 let kinds = get_fields_kind(record);
82 record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect()
83}
84
85fn sorted_by_rank(
86 fields: &[SyntaxNode],
87 get_rank: impl Fn(&SyntaxNode) -> usize,
88) -> Vec<SyntaxNode> {
89 fields.iter().cloned().sorted_by_key(get_rank).collect()
90}
91
92fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<Struct> {
93 match sema.resolve_path(path) {
94 Some(PathResolution::Def(ModuleDef::Adt(Adt::Struct(s)))) => Some(s),
95 _ => None,
96 }
97}
98
99fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String, usize>> {
100 Some(
101 struct_definition(path, ctx.sema)?
102 .fields(ctx.db)
103 .iter()
104 .enumerate()
105 .map(|(idx, field)| (field.name(ctx.db).to_string(), idx))
106 .collect(),
107 )
108}
109
110#[cfg(test)]
111mod tests {
112 use crate::helpers::{check_assist, check_assist_not_applicable};
113
114 use super::*;
115
116 #[test]
117 fn not_applicable_if_sorted() {
118 check_assist_not_applicable(
119 reorder_fields,
120 r#"
121 struct Foo {
122 foo: i32,
123 bar: i32,
124 }
125
126 const test: Foo = <|>Foo { foo: 0, bar: 0 };
127 "#,
128 )
129 }
130
131 #[test]
132 fn trivial_empty_fields() {
133 check_assist_not_applicable(
134 reorder_fields,
135 r#"
136 struct Foo {};
137 const test: Foo = <|>Foo {}
138 "#,
139 )
140 }
141
142 #[test]
143 fn reorder_struct_fields() {
144 check_assist(
145 reorder_fields,
146 r#"
147 struct Foo {foo: i32, bar: i32};
148 const test: Foo = <|>Foo {bar: 0, foo: 1}
149 "#,
150 r#"
151 struct Foo {foo: i32, bar: i32};
152 const test: Foo = <|>Foo {foo: 1, bar: 0}
153 "#,
154 )
155 }
156
157 #[test]
158 fn reorder_struct_pattern() {
159 check_assist(
160 reorder_fields,
161 r#"
162 struct Foo { foo: i64, bar: i64, baz: i64 }
163
164 fn f(f: Foo) -> {
165 match f {
166 <|>Foo { baz: 0, ref mut bar, .. } => (),
167 _ => ()
168 }
169 }
170 "#,
171 r#"
172 struct Foo { foo: i64, bar: i64, baz: i64 }
173
174 fn f(f: Foo) -> {
175 match f {
176 <|>Foo { ref mut bar, baz: 0, .. } => (),
177 _ => ()
178 }
179 }
180 "#,
181 )
182 }
183
184 #[test]
185 fn reorder_with_extra_field() {
186 check_assist(
187 reorder_fields,
188 r#"
189 struct Foo {
190 foo: String,
191 bar: String,
192 }
193
194 impl Foo {
195 fn new() -> Foo {
196 let foo = String::new();
197 <|>Foo {
198 bar: foo.clone(),
199 extra: "Extra field",
200 foo,
201 }
202 }
203 }
204 "#,
205 r#"
206 struct Foo {
207 foo: String,
208 bar: String,
209 }
210
211 impl Foo {
212 fn new() -> Foo {
213 let foo = String::new();
214 <|>Foo {
215 foo,
216 bar: foo.clone(),
217 extra: "Extra field",
218 }
219 }
220 }
221 "#,
222 )
223 }
224}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 5ba5254fd..a00136da1 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -129,6 +129,7 @@ mod handlers {
129 mod replace_unwrap_with_match; 129 mod replace_unwrap_with_match;
130 mod split_import; 130 mod split_import;
131 mod add_from_impl_for_enum; 131 mod add_from_impl_for_enum;
132 mod reorder_fields;
132 133
133 pub(crate) fn all() -> &'static [AssistHandler] { 134 pub(crate) fn all() -> &'static [AssistHandler] {
134 &[ 135 &[
@@ -170,6 +171,7 @@ mod handlers {
170 // These are manually sorted for better priorities 171 // These are manually sorted for better priorities
171 add_missing_impl_members::add_missing_impl_members, 172 add_missing_impl_members::add_missing_impl_members,
172 add_missing_impl_members::add_missing_default_members, 173 add_missing_impl_members::add_missing_default_members,
174 reorder_fields::reorder_fields,
173 ] 175 ]
174 } 176 }
175} 177}
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml
index c9a9ddc12..76e5cada4 100644
--- a/crates/ra_flycheck/Cargo.toml
+++ b/crates/ra_flycheck/Cargo.toml
@@ -13,4 +13,4 @@ serde_json = "1.0.48"
13jod-thread = "0.1.1" 13jod-thread = "0.1.1"
14 14
15[dev-dependencies] 15[dev-dependencies]
16insta = "0.15.0" 16insta = "0.16.0"
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 9baebf643..3801fce23 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -25,7 +25,7 @@ use hir_ty::{
25 autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, 25 autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
26 Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, 26 Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
27}; 27};
28use ra_db::{CrateId, Edition, FileId}; 28use ra_db::{CrateId, CrateName, Edition, FileId};
29use ra_prof::profile; 29use ra_prof::profile;
30use ra_syntax::{ 30use ra_syntax::{
31 ast::{self, AttrsOwner, NameOwner}, 31 ast::{self, AttrsOwner, NameOwner},
@@ -91,6 +91,10 @@ impl Crate {
91 db.crate_graph()[self.id].edition 91 db.crate_graph()[self.id].edition
92 } 92 }
93 93
94 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateName> {
95 db.crate_graph()[self.id].display_name.as_ref().cloned()
96 }
97
94 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 98 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
95 db.crate_graph().iter().map(|id| Crate { id }).collect() 99 db.crate_graph().iter().map(|id| Crate { id }).collect()
96 } 100 }
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 226fb4534..58ae6ce41 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -139,7 +139,7 @@ impl SourceAnalyzer {
139 &self, 139 &self,
140 db: &dyn HirDatabase, 140 db: &dyn HirDatabase,
141 field: &ast::FieldExpr, 141 field: &ast::FieldExpr,
142 ) -> Option<crate::StructField> { 142 ) -> Option<StructField> {
143 let expr_id = self.expr_id(db, &field.clone().into())?; 143 let expr_id = self.expr_id(db, &field.clone().into())?;
144 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) 144 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
145 } 145 }
@@ -148,21 +148,19 @@ impl SourceAnalyzer {
148 &self, 148 &self,
149 db: &dyn HirDatabase, 149 db: &dyn HirDatabase,
150 field: &ast::RecordField, 150 field: &ast::RecordField,
151 ) -> Option<(crate::StructField, Option<Local>)> { 151 ) -> Option<(StructField, Option<Local>)> {
152 let (expr_id, local) = match field.expr() { 152 let expr = field.expr()?;
153 Some(it) => (self.expr_id(db, &it)?, None), 153 let expr_id = self.expr_id(db, &expr)?;
154 None => { 154 let local = if field.name_ref().is_some() {
155 let src = InFile { file_id: self.file_id, value: field }; 155 None
156 let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?; 156 } else {
157 let local_name = field.name_ref()?.as_name(); 157 let local_name = field.field_name()?.as_name();
158 let path = ModPath::from_segments(PathKind::Plain, once(local_name)); 158 let path = ModPath::from_segments(PathKind::Plain, once(local_name));
159 let local = match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { 159 match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) {
160 Some(ValueNs::LocalBinding(pat_id)) => { 160 Some(ValueNs::LocalBinding(pat_id)) => {
161 Some(Local { pat_id, parent: self.resolver.body_owner()? }) 161 Some(Local { pat_id, parent: self.resolver.body_owner()? })
162 } 162 }
163 _ => None, 163 _ => None,
164 };
165 (expr_id, local)
166 } 164 }
167 }; 165 };
168 let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?; 166 let struct_field = self.infer.as_ref()?.record_field_resolution(expr_id)?;
@@ -319,8 +317,7 @@ fn scope_for_offset(
319 if source.file_id != offset.file_id { 317 if source.file_id != offset.file_id {
320 return None; 318 return None;
321 } 319 }
322 let syntax_node_ptr = 320 let syntax_node_ptr = source.value.syntax_node_ptr();
323 source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
324 Some((syntax_node_ptr, scope)) 321 Some((syntax_node_ptr, scope))
325 }) 322 })
326 // find containing scope 323 // find containing scope
@@ -399,8 +396,7 @@ fn adjust(
399 if source.file_id != file_id { 396 if source.file_id != file_id {
400 return None; 397 return None;
401 } 398 }
402 let syntax_node_ptr = 399 let syntax_node_ptr = source.value.syntax_node_ptr();
403 source.value.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr());
404 Some((syntax_node_ptr, scope)) 400 Some((syntax_node_ptr, scope))
405 }) 401 })
406 .map(|(ptr, scope)| (ptr.range(), scope)) 402 .map(|(ptr, scope)| (ptr.range(), scope))
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index 56e791e3e..b85358308 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -28,4 +28,4 @@ ra_cfg = { path = "../ra_cfg" }
28tt = { path = "../ra_tt", package = "ra_tt" } 28tt = { path = "../ra_tt", package = "ra_tt" }
29 29
30[dev-dependencies] 30[dev-dependencies]
31insta = "0.15.0" 31insta = "0.16.0"
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs
index be4b0accb..7c0d93691 100644
--- a/crates/ra_hir_def/src/adt.rs
+++ b/crates/ra_hir_def/src/adt.rs
@@ -4,7 +4,6 @@ use std::sync::Arc;
4 4
5use either::Either; 5use either::Either;
6use hir_expand::{ 6use hir_expand::{
7 hygiene::Hygiene,
8 name::{AsName, Name}, 7 name::{AsName, Name},
9 InFile, 8 InFile,
10}; 9};
@@ -13,7 +12,7 @@ use ra_prof::profile;
13use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; 12use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
14 13
15use crate::{ 14use crate::{
16 attr::Attrs, db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace, 15 body::CfgExpander, db::DefDatabase, src::HasChildSource, src::HasSource, trace::Trace,
17 type_ref::TypeRef, visibility::RawVisibility, EnumId, HasModule, LocalEnumVariantId, 16 type_ref::TypeRef, visibility::RawVisibility, EnumId, HasModule, LocalEnumVariantId,
18 LocalStructFieldId, Lookup, ModuleId, StructId, UnionId, VariantId, 17 LocalStructFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
19}; 18};
@@ -125,8 +124,9 @@ fn lower_enum(
125 124
126impl VariantData { 125impl VariantData {
127 fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self { 126 fn new(db: &dyn DefDatabase, flavor: InFile<ast::StructKind>, module_id: ModuleId) -> Self {
127 let mut expander = CfgExpander::new(db, flavor.file_id, module_id.krate);
128 let mut trace = Trace::new_for_arena(); 128 let mut trace = Trace::new_for_arena();
129 match lower_struct(db, &mut trace, &flavor, module_id) { 129 match lower_struct(db, &mut expander, &mut trace, &flavor) {
130 StructKind::Tuple => VariantData::Tuple(trace.into_arena()), 130 StructKind::Tuple => VariantData::Tuple(trace.into_arena()),
131 StructKind::Record => VariantData::Record(trace.into_arena()), 131 StructKind::Record => VariantData::Record(trace.into_arena()),
132 StructKind::Unit => VariantData::Unit, 132 StructKind::Unit => VariantData::Unit,
@@ -178,8 +178,9 @@ impl HasChildSource for VariantId {
178 it.lookup(db).container.module(db), 178 it.lookup(db).container.module(db),
179 ), 179 ),
180 }; 180 };
181 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
181 let mut trace = Trace::new_for_map(); 182 let mut trace = Trace::new_for_map();
182 lower_struct(db, &mut trace, &src, module_id); 183 lower_struct(db, &mut expander, &mut trace, &src);
183 src.with_value(trace.into_map()) 184 src.with_value(trace.into_map())
184 } 185 }
185} 186}
@@ -193,16 +194,15 @@ pub enum StructKind {
193 194
194fn lower_struct( 195fn lower_struct(
195 db: &dyn DefDatabase, 196 db: &dyn DefDatabase,
197 expander: &mut CfgExpander,
196 trace: &mut Trace<StructFieldData, Either<ast::TupleFieldDef, ast::RecordFieldDef>>, 198 trace: &mut Trace<StructFieldData, Either<ast::TupleFieldDef, ast::RecordFieldDef>>,
197 ast: &InFile<ast::StructKind>, 199 ast: &InFile<ast::StructKind>,
198 module_id: ModuleId,
199) -> StructKind { 200) -> StructKind {
200 let crate_graph = db.crate_graph();
201 match &ast.value { 201 match &ast.value {
202 ast::StructKind::Tuple(fl) => { 202 ast::StructKind::Tuple(fl) => {
203 for (i, fd) in fl.fields().enumerate() { 203 for (i, fd) in fl.fields().enumerate() {
204 let attrs = Attrs::new(&fd, &Hygiene::new(db.upcast(), ast.file_id)); 204 let attrs = expander.parse_attrs(&fd);
205 if !attrs.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options) { 205 if !expander.is_cfg_enabled(&attrs) {
206 continue; 206 continue;
207 } 207 }
208 208
@@ -219,8 +219,8 @@ fn lower_struct(
219 } 219 }
220 ast::StructKind::Record(fl) => { 220 ast::StructKind::Record(fl) => {
221 for fd in fl.fields() { 221 for fd in fl.fields() {
222 let attrs = Attrs::new(&fd, &Hygiene::new(db.upcast(), ast.file_id)); 222 let attrs = expander.parse_attrs(&fd);
223 if !attrs.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options) { 223 if !expander.is_cfg_enabled(&attrs) {
224 continue; 224 continue;
225 } 225 }
226 226
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 7b0c506b1..2f2e3e5ba 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -93,6 +93,7 @@ impl Attrs {
93 } 93 }
94 94
95 pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool { 95 pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
96 // FIXME: handle cfg_attr :-)
96 self.by_key("cfg").tt_values().all(|tt| cfg_options.is_cfg_enabled(tt) != Some(false)) 97 self.by_key("cfg").tt_values().all(|tt| cfg_options.is_cfg_enabled(tt) != Some(false))
97 } 98 }
98} 99}
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index e09996c6f..eafaf48c1 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -9,11 +9,14 @@ use drop_bomb::DropBomb;
9use either::Either; 9use either::Either;
10use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; 10use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId};
11use ra_arena::{map::ArenaMap, Arena}; 11use ra_arena::{map::ArenaMap, Arena};
12use ra_cfg::CfgOptions;
13use ra_db::CrateId;
12use ra_prof::profile; 14use ra_prof::profile;
13use ra_syntax::{ast, AstNode, AstPtr}; 15use ra_syntax::{ast, AstNode, AstPtr};
14use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
15 17
16use crate::{ 18use crate::{
19 attr::Attrs,
17 db::DefDatabase, 20 db::DefDatabase,
18 expr::{Expr, ExprId, Pat, PatId}, 21 expr::{Expr, ExprId, Pat, PatId},
19 item_scope::BuiltinShadowMode, 22 item_scope::BuiltinShadowMode,
@@ -24,25 +27,59 @@ use crate::{
24 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, 27 AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId,
25}; 28};
26 29
30/// A subser of Exander that only deals with cfg attributes. We only need it to
31/// avoid cyclic queries in crate def map during enum processing.
32pub(crate) struct CfgExpander {
33 cfg_options: CfgOptions,
34 hygiene: Hygiene,
35}
36
27pub(crate) struct Expander { 37pub(crate) struct Expander {
38 cfg_expander: CfgExpander,
28 crate_def_map: Arc<CrateDefMap>, 39 crate_def_map: Arc<CrateDefMap>,
29 current_file_id: HirFileId, 40 current_file_id: HirFileId,
30 hygiene: Hygiene,
31 ast_id_map: Arc<AstIdMap>, 41 ast_id_map: Arc<AstIdMap>,
32 module: ModuleId, 42 module: ModuleId,
33 recursive_limit: usize, 43 recursive_limit: usize,
34} 44}
35 45
46impl CfgExpander {
47 pub(crate) fn new(
48 db: &dyn DefDatabase,
49 current_file_id: HirFileId,
50 krate: CrateId,
51 ) -> CfgExpander {
52 let hygiene = Hygiene::new(db.upcast(), current_file_id);
53 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
54 CfgExpander { cfg_options, hygiene }
55 }
56
57 pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
58 Attrs::new(owner, &self.hygiene)
59 }
60
61 pub(crate) fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
62 attrs.is_cfg_enabled(&self.cfg_options)
63 }
64}
65
36impl Expander { 66impl Expander {
37 pub(crate) fn new( 67 pub(crate) fn new(
38 db: &dyn DefDatabase, 68 db: &dyn DefDatabase,
39 current_file_id: HirFileId, 69 current_file_id: HirFileId,
40 module: ModuleId, 70 module: ModuleId,
41 ) -> Expander { 71 ) -> Expander {
72 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate);
42 let crate_def_map = db.crate_def_map(module.krate); 73 let crate_def_map = db.crate_def_map(module.krate);
43 let hygiene = Hygiene::new(db.upcast(), current_file_id);
44 let ast_id_map = db.ast_id_map(current_file_id); 74 let ast_id_map = db.ast_id_map(current_file_id);
45 Expander { crate_def_map, current_file_id, hygiene, ast_id_map, module, recursive_limit: 0 } 75 Expander {
76 cfg_expander,
77 crate_def_map,
78 current_file_id,
79 ast_id_map,
80 module,
81 recursive_limit: 0,
82 }
46 } 83 }
47 84
48 pub(crate) fn enter_expand<T: ast::AstNode>( 85 pub(crate) fn enter_expand<T: ast::AstNode>(
@@ -75,7 +112,7 @@ impl Expander {
75 ast_id_map: mem::take(&mut self.ast_id_map), 112 ast_id_map: mem::take(&mut self.ast_id_map),
76 bomb: DropBomb::new("expansion mark dropped"), 113 bomb: DropBomb::new("expansion mark dropped"),
77 }; 114 };
78 self.hygiene = Hygiene::new(db.upcast(), file_id); 115 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id);
79 self.current_file_id = file_id; 116 self.current_file_id = file_id;
80 self.ast_id_map = db.ast_id_map(file_id); 117 self.ast_id_map = db.ast_id_map(file_id);
81 self.recursive_limit += 1; 118 self.recursive_limit += 1;
@@ -91,7 +128,7 @@ impl Expander {
91 } 128 }
92 129
93 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { 130 pub(crate) fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) {
94 self.hygiene = Hygiene::new(db.upcast(), mark.file_id); 131 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id);
95 self.current_file_id = mark.file_id; 132 self.current_file_id = mark.file_id;
96 self.ast_id_map = mem::take(&mut mark.ast_id_map); 133 self.ast_id_map = mem::take(&mut mark.ast_id_map);
97 self.recursive_limit -= 1; 134 self.recursive_limit -= 1;
@@ -102,8 +139,16 @@ impl Expander {
102 InFile { file_id: self.current_file_id, value } 139 InFile { file_id: self.current_file_id, value }
103 } 140 }
104 141
142 pub(crate) fn parse_attrs(&self, owner: &dyn ast::AttrsOwner) -> Attrs {
143 self.cfg_expander.parse_attrs(owner)
144 }
145
146 pub(crate) fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
147 self.cfg_expander.is_cfg_enabled(attrs)
148 }
149
105 fn parse_path(&mut self, path: ast::Path) -> Option<Path> { 150 fn parse_path(&mut self, path: ast::Path) -> Option<Path> {
106 Path::from_src(path, &self.hygiene) 151 Path::from_src(path, &self.cfg_expander.hygiene)
107 } 152 }
108 153
109 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { 154 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> {
@@ -142,7 +187,7 @@ pub struct Body {
142 pub item_scope: ItemScope, 187 pub item_scope: ItemScope,
143} 188}
144 189
145pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>; 190pub type ExprPtr = AstPtr<ast::Expr>;
146pub type ExprSource = InFile<ExprPtr>; 191pub type ExprSource = InFile<ExprPtr>;
147 192
148pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; 193pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>;
@@ -240,7 +285,7 @@ impl BodySourceMap {
240 } 285 }
241 286
242 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> { 287 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
243 let src = node.map(|it| Either::Left(AstPtr::new(it))); 288 let src = node.map(|it| AstPtr::new(it));
244 self.expr_map.get(&src).cloned() 289 self.expr_map.get(&src).cloned()
245 } 290 }
246 291
@@ -249,11 +294,6 @@ impl BodySourceMap {
249 self.expansions.get(&src).cloned() 294 self.expansions.get(&src).cloned()
250 } 295 }
251 296
252 pub fn field_init_shorthand_expr(&self, node: InFile<&ast::RecordField>) -> Option<ExprId> {
253 let src = node.map(|it| Either::Right(AstPtr::new(it)));
254 self.expr_map.get(&src).cloned()
255 }
256
257 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> { 297 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
258 self.pat_map_back[pat].clone() 298 self.pat_map_back[pat].clone()
259 } 299 }
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 9d6ee095e..79abe55ce 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -2,9 +2,7 @@
2//! representation. 2//! representation.
3 3
4use either::Either; 4use either::Either;
5
6use hir_expand::{ 5use hir_expand::{
7 hygiene::Hygiene,
8 name::{name, AsName, Name}, 6 name::{name, AsName, Name},
9 MacroDefId, MacroDefKind, 7 MacroDefId, MacroDefKind,
10}; 8};
@@ -18,10 +16,8 @@ use ra_syntax::{
18}; 16};
19use test_utils::tested_by; 17use test_utils::tested_by;
20 18
21use super::{ExprSource, PatSource};
22use crate::{ 19use crate::{
23 adt::StructKind, 20 adt::StructKind,
24 attr::Attrs,
25 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax}, 21 body::{Body, BodySourceMap, Expander, PatPtr, SyntheticSyntax},
26 builtin_type::{BuiltinFloat, BuiltinInt}, 22 builtin_type::{BuiltinFloat, BuiltinInt},
27 db::DefDatabase, 23 db::DefDatabase,
@@ -31,12 +27,14 @@ use crate::{
31 }, 27 },
32 item_scope::BuiltinShadowMode, 28 item_scope::BuiltinShadowMode,
33 path::GenericArgs, 29 path::GenericArgs,
34 path::Path,
35 type_ref::{Mutability, TypeRef}, 30 type_ref::{Mutability, TypeRef},
36 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, HasModule, Intern, 31 AdtId, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, ModuleDefId,
37 ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, 32 StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
38}; 33};
39 34
35use super::{ExprSource, PatSource};
36use ast::AstChildren;
37
40pub(super) fn lower( 38pub(super) fn lower(
41 db: &dyn DefDatabase, 39 db: &dyn DefDatabase,
42 def: DefWithBodyId, 40 def: DefWithBodyId,
@@ -104,7 +102,6 @@ impl ExprCollector<'_> {
104 } 102 }
105 103
106 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId { 104 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
107 let ptr = Either::Left(ptr);
108 let src = self.expander.to_source(ptr); 105 let src = self.expander.to_source(ptr);
109 let id = self.make_expr(expr, Ok(src.clone())); 106 let id = self.make_expr(expr, Ok(src.clone()));
110 self.source_map.expr_map.insert(src, id); 107 self.source_map.expr_map.insert(src, id);
@@ -115,13 +112,6 @@ impl ExprCollector<'_> {
115 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId { 112 fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
116 self.make_expr(expr, Err(SyntheticSyntax)) 113 self.make_expr(expr, Err(SyntheticSyntax))
117 } 114 }
118 fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId {
119 let ptr = Either::Right(ptr);
120 let src = self.expander.to_source(ptr);
121 let id = self.make_expr(expr, Ok(src.clone()));
122 self.source_map.expr_map.insert(src, id);
123 id
124 }
125 fn empty_block(&mut self) -> ExprId { 115 fn empty_block(&mut self) -> ExprId {
126 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None }) 116 self.alloc_expr_desugared(Expr::Block { statements: Vec::new(), tail: None })
127 } 117 }
@@ -291,7 +281,7 @@ impl ExprCollector<'_> {
291 ast::Expr::ParenExpr(e) => { 281 ast::Expr::ParenExpr(e) => {
292 let inner = self.collect_expr_opt(e.expr()); 282 let inner = self.collect_expr_opt(e.expr());
293 // make the paren expr point to the inner expression as well 283 // make the paren expr point to the inner expression as well
294 let src = self.expander.to_source(Either::Left(syntax_ptr)); 284 let src = self.expander.to_source(syntax_ptr);
295 self.source_map.expr_map.insert(src, inner); 285 self.source_map.expr_map.insert(src, inner);
296 inner 286 inner
297 } 287 }
@@ -300,7 +290,6 @@ impl ExprCollector<'_> {
300 self.alloc_expr(Expr::Return { expr }, syntax_ptr) 290 self.alloc_expr(Expr::Return { expr }, syntax_ptr)
301 } 291 }
302 ast::Expr::RecordLit(e) => { 292 ast::Expr::RecordLit(e) => {
303 let crate_graph = self.db.crate_graph();
304 let path = e.path().and_then(|path| self.expander.parse_path(path)); 293 let path = e.path().and_then(|path| self.expander.parse_path(path));
305 let mut field_ptrs = Vec::new(); 294 let mut field_ptrs = Vec::new();
306 let record_lit = if let Some(nfl) = e.record_field_list() { 295 let record_lit = if let Some(nfl) = e.record_field_list() {
@@ -308,31 +297,17 @@ impl ExprCollector<'_> {
308 .fields() 297 .fields()
309 .inspect(|field| field_ptrs.push(AstPtr::new(field))) 298 .inspect(|field| field_ptrs.push(AstPtr::new(field)))
310 .filter_map(|field| { 299 .filter_map(|field| {
311 let module_id = ContainerId::DefWithBodyId(self.def).module(self.db); 300 let attrs = self.expander.parse_attrs(&field);
312 let attrs = Attrs::new( 301 if !self.expander.is_cfg_enabled(&attrs) {
313 &field,
314 &Hygiene::new(self.db.upcast(), self.expander.current_file_id),
315 );
316
317 if !attrs.is_cfg_enabled(&crate_graph[module_id.krate].cfg_options) {
318 return None; 302 return None;
319 } 303 }
304 let name = field.field_name()?.as_name();
320 305
321 Some(RecordLitField { 306 Some(RecordLitField {
322 name: field 307 name,
323 .name_ref() 308 expr: match field.expr() {
324 .map(|nr| nr.as_name()) 309 Some(e) => self.collect_expr(e),
325 .unwrap_or_else(Name::missing), 310 None => self.missing_expr(),
326 expr: if let Some(e) = field.expr() {
327 self.collect_expr(e)
328 } else if let Some(nr) = field.name_ref() {
329 // field shorthand
330 self.alloc_expr_field_shorthand(
331 Expr::Path(Path::from_name_ref(&nr)),
332 AstPtr::new(&field),
333 )
334 } else {
335 self.missing_expr()
336 }, 311 },
337 }) 312 })
338 }) 313 })
@@ -624,8 +599,8 @@ impl ExprCollector<'_> {
624 } 599 }
625 ast::Pat::TupleStructPat(p) => { 600 ast::Pat::TupleStructPat(p) => {
626 let path = p.path().and_then(|path| self.expander.parse_path(path)); 601 let path = p.path().and_then(|path| self.expander.parse_path(path));
627 let args = p.args().map(|p| self.collect_pat(p)).collect(); 602 let (args, ellipsis) = self.collect_tuple_pat(p.args());
628 Pat::TupleStruct { path, args } 603 Pat::TupleStruct { path, args, ellipsis }
629 } 604 }
630 ast::Pat::RefPat(p) => { 605 ast::Pat::RefPat(p) => {
631 let pat = self.collect_pat_opt(p.pat()); 606 let pat = self.collect_pat_opt(p.pat());
@@ -642,10 +617,10 @@ impl ExprCollector<'_> {
642 } 617 }
643 ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()), 618 ast::Pat::ParenPat(p) => return self.collect_pat_opt(p.pat()),
644 ast::Pat::TuplePat(p) => { 619 ast::Pat::TuplePat(p) => {
645 let args = p.args().map(|p| self.collect_pat(p)).collect(); 620 let (args, ellipsis) = self.collect_tuple_pat(p.args());
646 Pat::Tuple(args) 621 Pat::Tuple { args, ellipsis }
647 } 622 }
648 ast::Pat::PlaceholderPat(_) | ast::Pat::DotDotPat(_) => Pat::Wild, 623 ast::Pat::PlaceholderPat(_) => Pat::Wild,
649 ast::Pat::RecordPat(p) => { 624 ast::Pat::RecordPat(p) => {
650 let path = p.path().and_then(|path| self.expander.parse_path(path)); 625 let path = p.path().and_then(|path| self.expander.parse_path(path));
651 let record_field_pat_list = 626 let record_field_pat_list =
@@ -663,7 +638,7 @@ impl ExprCollector<'_> {
663 let iter = record_field_pat_list.record_field_pats().filter_map(|f| { 638 let iter = record_field_pat_list.record_field_pats().filter_map(|f| {
664 let ast_pat = f.pat()?; 639 let ast_pat = f.pat()?;
665 let pat = self.collect_pat(ast_pat); 640 let pat = self.collect_pat(ast_pat);
666 let name = f.name()?.as_name(); 641 let name = f.field_name()?.as_name();
667 Some(RecordFieldPat { name, pat }) 642 Some(RecordFieldPat { name, pat })
668 }); 643 });
669 fields.extend(iter); 644 fields.extend(iter);
@@ -691,7 +666,9 @@ impl ExprCollector<'_> {
691 Pat::Missing 666 Pat::Missing
692 } 667 }
693 } 668 }
694 669 ast::Pat::DotDotPat(_) => unreachable!(
670 "`DotDotPat` requires special handling and should not be mapped to a Pat."
671 ),
695 // FIXME: implement 672 // FIXME: implement
696 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing, 673 ast::Pat::BoxPat(_) | ast::Pat::RangePat(_) | ast::Pat::MacroPat(_) => Pat::Missing,
697 }; 674 };
@@ -706,6 +683,19 @@ impl ExprCollector<'_> {
706 self.missing_pat() 683 self.missing_pat()
707 } 684 }
708 } 685 }
686
687 fn collect_tuple_pat(&mut self, args: AstChildren<ast::Pat>) -> (Vec<PatId>, Option<usize>) {
688 // Find the location of the `..`, if there is one. Note that we do not
689 // consider the possiblity of there being multiple `..` here.
690 let ellipsis = args.clone().position(|p| matches!(p, ast::Pat::DotDotPat(_)));
691 // We want to skip the `..` pattern here, since we account for it above.
692 let args = args
693 .filter(|p| !matches!(p, ast::Pat::DotDotPat(_)))
694 .map(|p| self.collect_pat(p))
695 .collect();
696
697 (args, ellipsis)
698 }
709} 699}
710 700
711impl From<ast::BinOp> for BinaryOp { 701impl From<ast::BinOp> for BinaryOp {
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index b8fbf0ed4..ccb682f9a 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -9,18 +9,19 @@ use hir_expand::{
9}; 9};
10use ra_prof::profile; 10use ra_prof::profile;
11use ra_syntax::ast::{ 11use ra_syntax::ast::{
12 self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, VisibilityOwner, 12 self, AstNode, ImplItem, ModuleItemOwner, NameOwner, TypeAscriptionOwner, TypeBoundsOwner,
13 VisibilityOwner,
13}; 14};
14 15
15use crate::{ 16use crate::{
16 attr::Attrs, 17 attr::Attrs,
17 db::DefDatabase, 18 db::DefDatabase,
18 path::{path, GenericArgs, Path}, 19 path::{path, AssociatedTypeBinding, GenericArgs, Path},
19 src::HasSource, 20 src::HasSource,
20 type_ref::{Mutability, TypeBound, TypeRef}, 21 type_ref::{Mutability, TypeBound, TypeRef},
21 visibility::RawVisibility, 22 visibility::RawVisibility,
22 AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule, 23 AssocContainerId, AssocItemId, ConstId, ConstLoc, Expander, FunctionId, FunctionLoc, HasModule,
23 ImplId, Intern, Lookup, ModuleId, StaticId, TraitId, TypeAliasId, TypeAliasLoc, 24 ImplId, Intern, Lookup, StaticId, TraitId, TypeAliasId, TypeAliasLoc,
24}; 25};
25 26
26#[derive(Debug, Clone, PartialEq, Eq)] 27#[derive(Debug, Clone, PartialEq, Eq)]
@@ -95,7 +96,11 @@ fn desugar_future_path(orig: TypeRef) -> Path {
95 let path = path![std::future::Future]; 96 let path = path![std::future::Future];
96 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); 97 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
97 let mut last = GenericArgs::empty(); 98 let mut last = GenericArgs::empty();
98 last.bindings.push((name![Output], orig)); 99 last.bindings.push(AssociatedTypeBinding {
100 name: name![Output],
101 type_ref: Some(orig),
102 bounds: Vec::new(),
103 });
99 generic_args.push(Some(Arc::new(last))); 104 generic_args.push(Some(Arc::new(last)));
100 105
101 Path::from_known_path(path, generic_args) 106 Path::from_known_path(path, generic_args)
@@ -106,6 +111,7 @@ pub struct TypeAliasData {
106 pub name: Name, 111 pub name: Name,
107 pub type_ref: Option<TypeRef>, 112 pub type_ref: Option<TypeRef>,
108 pub visibility: RawVisibility, 113 pub visibility: RawVisibility,
114 pub bounds: Vec<TypeBound>,
109} 115}
110 116
111impl TypeAliasData { 117impl TypeAliasData {
@@ -118,9 +124,17 @@ impl TypeAliasData {
118 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name()); 124 let name = node.value.name().map_or_else(Name::missing, |n| n.as_name());
119 let type_ref = node.value.type_ref().map(TypeRef::from_ast); 125 let type_ref = node.value.type_ref().map(TypeRef::from_ast);
120 let vis_default = RawVisibility::default_for_container(loc.container); 126 let vis_default = RawVisibility::default_for_container(loc.container);
121 let visibility = 127 let visibility = RawVisibility::from_ast_with_default(
122 RawVisibility::from_ast_with_default(db, vis_default, node.map(|n| n.visibility())); 128 db,
123 Arc::new(TypeAliasData { name, type_ref, visibility }) 129 vis_default,
130 node.as_ref().map(|n| n.visibility()),
131 );
132 let bounds = if let Some(bound_list) = node.value.type_bound_list() {
133 bound_list.bounds().map(TypeBound::from_ast).collect()
134 } else {
135 Vec::new()
136 };
137 Arc::new(TypeAliasData { name, type_ref, visibility, bounds })
124 } 138 }
125} 139}
126 140
@@ -218,10 +232,17 @@ impl ImplData {
218 let mut items = Vec::new(); 232 let mut items = Vec::new();
219 233
220 if let Some(item_list) = src.value.item_list() { 234 if let Some(item_list) = src.value.item_list() {
221 items.extend(collect_impl_items(db, item_list.impl_items(), src.file_id, id)); 235 let mut expander = Expander::new(db, impl_loc.ast_id.file_id, module_id);
236 items.extend(collect_impl_items(
237 db,
238 &mut expander,
239 item_list.impl_items(),
240 src.file_id,
241 id,
242 ));
222 items.extend(collect_impl_items_in_macros( 243 items.extend(collect_impl_items_in_macros(
223 db, 244 db,
224 module_id, 245 &mut expander,
225 &src.with_value(item_list), 246 &src.with_value(item_list),
226 id, 247 id,
227 )); 248 ));
@@ -268,18 +289,17 @@ impl ConstData {
268 289
269fn collect_impl_items_in_macros( 290fn collect_impl_items_in_macros(
270 db: &dyn DefDatabase, 291 db: &dyn DefDatabase,
271 module_id: ModuleId, 292 expander: &mut Expander,
272 impl_def: &InFile<ast::ItemList>, 293 impl_def: &InFile<ast::ItemList>,
273 id: ImplId, 294 id: ImplId,
274) -> Vec<AssocItemId> { 295) -> Vec<AssocItemId> {
275 let mut expander = Expander::new(db, impl_def.file_id, module_id);
276 let mut res = Vec::new(); 296 let mut res = Vec::new();
277 297
278 // We set a limit to protect against infinite recursion 298 // We set a limit to protect against infinite recursion
279 let limit = 100; 299 let limit = 100;
280 300
281 for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) { 301 for m in impl_def.value.syntax().children().filter_map(ast::MacroCall::cast) {
282 res.extend(collect_impl_items_in_macro(db, &mut expander, m, id, limit)) 302 res.extend(collect_impl_items_in_macro(db, expander, m, id, limit))
283 } 303 }
284 304
285 res 305 res
@@ -300,6 +320,7 @@ fn collect_impl_items_in_macro(
300 let items: InFile<ast::MacroItems> = expander.to_source(items); 320 let items: InFile<ast::MacroItems> = expander.to_source(items);
301 let mut res = collect_impl_items( 321 let mut res = collect_impl_items(
302 db, 322 db,
323 expander,
303 items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())), 324 items.value.items().filter_map(|it| ImplItem::cast(it.syntax().clone())),
304 items.file_id, 325 items.file_id,
305 id, 326 id,
@@ -319,32 +340,26 @@ fn collect_impl_items_in_macro(
319 340
320fn collect_impl_items( 341fn collect_impl_items(
321 db: &dyn DefDatabase, 342 db: &dyn DefDatabase,
343 expander: &mut Expander,
322 impl_items: impl Iterator<Item = ImplItem>, 344 impl_items: impl Iterator<Item = ImplItem>,
323 file_id: crate::HirFileId, 345 file_id: crate::HirFileId,
324 id: ImplId, 346 id: ImplId,
325) -> Vec<AssocItemId> { 347) -> Vec<AssocItemId> {
326 let items = db.ast_id_map(file_id); 348 let items = db.ast_id_map(file_id);
327 let crate_graph = db.crate_graph();
328 let module_id = id.lookup(db).container.module(db);
329 349
330 impl_items 350 impl_items
331 .filter_map(|item_node| match item_node { 351 .filter_map(|item_node| match item_node {
332 ast::ImplItem::FnDef(it) => { 352 ast::ImplItem::FnDef(it) => {
353 let attrs = expander.parse_attrs(&it);
354 if !expander.is_cfg_enabled(&attrs) {
355 return None;
356 }
333 let def = FunctionLoc { 357 let def = FunctionLoc {
334 container: AssocContainerId::ImplId(id), 358 container: AssocContainerId::ImplId(id),
335 ast_id: AstId::new(file_id, items.ast_id(&it)), 359 ast_id: AstId::new(file_id, items.ast_id(&it)),
336 } 360 }
337 .intern(db); 361 .intern(db);
338 362 Some(def.into())
339 if !db
340 .function_data(def)
341 .attrs
342 .is_cfg_enabled(&crate_graph[module_id.krate].cfg_options)
343 {
344 None
345 } else {
346 Some(def.into())
347 }
348 } 363 }
349 ast::ImplItem::ConstDef(it) => { 364 ast::ImplItem::ConstDef(it) => {
350 let def = ConstLoc { 365 let def = ConstLoc {
diff --git a/crates/ra_hir_def/src/expr.rs b/crates/ra_hir_def/src/expr.rs
index e11bdf3ec..a0cdad529 100644
--- a/crates/ra_hir_def/src/expr.rs
+++ b/crates/ra_hir_def/src/expr.rs
@@ -374,7 +374,7 @@ pub struct RecordFieldPat {
374pub enum Pat { 374pub enum Pat {
375 Missing, 375 Missing,
376 Wild, 376 Wild,
377 Tuple(Vec<PatId>), 377 Tuple { args: Vec<PatId>, ellipsis: Option<usize> },
378 Or(Vec<PatId>), 378 Or(Vec<PatId>),
379 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool }, 379 Record { path: Option<Path>, args: Vec<RecordFieldPat>, ellipsis: bool },
380 Range { start: ExprId, end: ExprId }, 380 Range { start: ExprId, end: ExprId },
@@ -382,7 +382,7 @@ pub enum Pat {
382 Path(Path), 382 Path(Path),
383 Lit(ExprId), 383 Lit(ExprId),
384 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> }, 384 Bind { mode: BindingAnnotation, name: Name, subpat: Option<PatId> },
385 TupleStruct { path: Option<Path>, args: Vec<PatId> }, 385 TupleStruct { path: Option<Path>, args: Vec<PatId>, ellipsis: Option<usize> },
386 Ref { pat: PatId, mutability: Mutability }, 386 Ref { pat: PatId, mutability: Mutability },
387} 387}
388 388
@@ -393,7 +393,7 @@ impl Pat {
393 Pat::Bind { subpat, .. } => { 393 Pat::Bind { subpat, .. } => {
394 subpat.iter().copied().for_each(f); 394 subpat.iter().copied().for_each(f);
395 } 395 }
396 Pat::Or(args) | Pat::Tuple(args) | Pat::TupleStruct { args, .. } => { 396 Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
397 args.iter().copied().for_each(f); 397 args.iter().copied().for_each(f);
398 } 398 }
399 Pat::Ref { pat, .. } => f(*pat), 399 Pat::Ref { pat, .. } => f(*pat),
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 8fe3f8617..98c74fe25 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -462,6 +462,14 @@ impl DefCollector<'_> {
462 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { 462 Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
463 tested_by!(glob_enum); 463 tested_by!(glob_enum);
464 // glob import from enum => just import all the variants 464 // glob import from enum => just import all the variants
465
466 // XXX: urgh, so this works by accident! Here, we look at
467 // the enum data, and, in theory, this might require us to
468 // look back at the crate_def_map, creating a cycle. For
469 // example, `enum E { crate::some_macro!(); }`. Luckely, the
470 // only kind of macro that is allowed inside enum is a
471 // `cfg_macro`, and we don't need to run name resolution for
472 // it, but this is sheer luck!
465 let enum_data = self.db.enum_data(e); 473 let enum_data = self.db.enum_data(e);
466 let resolutions = enum_data 474 let resolutions = enum_data
467 .variants 475 .variants
@@ -977,11 +985,7 @@ impl ModCollector<'_, '_> {
977 } 985 }
978 986
979 fn is_cfg_enabled(&self, attrs: &Attrs) -> bool { 987 fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
980 // FIXME: handle cfg_attr :-) 988 attrs.is_cfg_enabled(self.def_collector.cfg_options)
981 attrs
982 .by_key("cfg")
983 .tt_values()
984 .all(|tt| self.def_collector.cfg_options.is_cfg_enabled(tt) != Some(false))
985 } 989 }
986} 990}
987 991
diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs
index 496fc6b08..87165ac33 100644
--- a/crates/ra_hir_def/src/nameres/tests/incremental.rs
+++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs
@@ -32,6 +32,9 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
32 32
33 use crate::foo::bar::Baz; 33 use crate::foo::bar::Baz;
34 34
35 enum E { A, B }
36 use E::*;
37
35 fn foo() -> i32 { 38 fn foo() -> i32 {
36 1 + 1 39 1 + 1
37 } 40 }
@@ -46,6 +49,9 @@ fn typing_inside_a_function_should_not_invalidate_def_map() {
46 49
47 use crate::foo::bar::Baz; 50 use crate::foo::bar::Baz;
48 51
52 enum E { A, B }
53 use E::*;
54
49 fn foo() -> i32 { 92 } 55 fn foo() -> i32 { 92 }
50 ", 56 ",
51 ); 57 );
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index 904080341..162b3c8c7 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -14,7 +14,10 @@ use hir_expand::{
14use ra_db::CrateId; 14use ra_db::CrateId;
15use ra_syntax::ast; 15use ra_syntax::ast;
16 16
17use crate::{type_ref::TypeRef, InFile}; 17use crate::{
18 type_ref::{TypeBound, TypeRef},
19 InFile,
20};
18 21
19#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 22#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct ModPath { 23pub struct ModPath {
@@ -111,7 +114,21 @@ pub struct GenericArgs {
111 /// is left out. 114 /// is left out.
112 pub has_self_type: bool, 115 pub has_self_type: bool,
113 /// Associated type bindings like in `Iterator<Item = T>`. 116 /// Associated type bindings like in `Iterator<Item = T>`.
114 pub bindings: Vec<(Name, TypeRef)>, 117 pub bindings: Vec<AssociatedTypeBinding>,
118}
119
120/// An associated type binding like in `Iterator<Item = T>`.
121#[derive(Debug, Clone, PartialEq, Eq, Hash)]
122pub struct AssociatedTypeBinding {
123 /// The name of the associated type.
124 pub name: Name,
125 /// The type bound to this associated type (in `Item = T`, this would be the
126 /// `T`). This can be `None` if there are bounds instead.
127 pub type_ref: Option<TypeRef>,
128 /// Bounds for the associated type, like in `Iterator<Item:
129 /// SomeOtherTrait>`. (This is the unstable `associated_type_bounds`
130 /// feature.)
131 pub bounds: Vec<TypeBound>,
115} 132}
116 133
117/// A single generic argument. 134/// A single generic argument.
@@ -134,11 +151,6 @@ impl Path {
134 lower::lower_path(path, hygiene) 151 lower::lower_path(path, hygiene)
135 } 152 }
136 153
137 /// Converts an `ast::NameRef` into a single-identifier `Path`.
138 pub(crate) fn from_name_ref(name_ref: &ast::NameRef) -> Path {
139 Path { type_anchor: None, mod_path: name_ref.as_name().into(), generic_args: vec![None] }
140 }
141
142 /// Converts a known mod path to `Path`. 154 /// Converts a known mod path to `Path`.
143 pub(crate) fn from_known_path( 155 pub(crate) fn from_known_path(
144 path: ModPath, 156 path: ModPath,
diff --git a/crates/ra_hir_def/src/path/lower.rs b/crates/ra_hir_def/src/path/lower.rs
index 0f806d6fb..9ec2e0dcd 100644
--- a/crates/ra_hir_def/src/path/lower.rs
+++ b/crates/ra_hir_def/src/path/lower.rs
@@ -9,11 +9,12 @@ use hir_expand::{
9 hygiene::Hygiene, 9 hygiene::Hygiene,
10 name::{name, AsName}, 10 name::{name, AsName},
11}; 11};
12use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner}; 12use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner, TypeBoundsOwner};
13 13
14use super::AssociatedTypeBinding;
14use crate::{ 15use crate::{
15 path::{GenericArg, GenericArgs, ModPath, Path, PathKind}, 16 path::{GenericArg, GenericArgs, ModPath, Path, PathKind},
16 type_ref::TypeRef, 17 type_ref::{TypeBound, TypeRef},
17}; 18};
18 19
19pub(super) use lower_use::lower_use_tree; 20pub(super) use lower_use::lower_use_tree;
@@ -136,10 +137,16 @@ pub(super) fn lower_generic_args(node: ast::TypeArgList) -> Option<GenericArgs>
136 // lifetimes ignored for now 137 // lifetimes ignored for now
137 let mut bindings = Vec::new(); 138 let mut bindings = Vec::new();
138 for assoc_type_arg in node.assoc_type_args() { 139 for assoc_type_arg in node.assoc_type_args() {
140 let assoc_type_arg: ast::AssocTypeArg = assoc_type_arg;
139 if let Some(name_ref) = assoc_type_arg.name_ref() { 141 if let Some(name_ref) = assoc_type_arg.name_ref() {
140 let name = name_ref.as_name(); 142 let name = name_ref.as_name();
141 let type_ref = TypeRef::from_ast_opt(assoc_type_arg.type_ref()); 143 let type_ref = assoc_type_arg.type_ref().map(TypeRef::from_ast);
142 bindings.push((name, type_ref)); 144 let bounds = if let Some(l) = assoc_type_arg.type_bound_list() {
145 l.bounds().map(TypeBound::from_ast).collect()
146 } else {
147 Vec::new()
148 };
149 bindings.push(AssociatedTypeBinding { name, type_ref, bounds });
143 } 150 }
144 } 151 }
145 if args.is_empty() && bindings.is_empty() { 152 if args.is_empty() && bindings.is_empty() {
@@ -168,7 +175,11 @@ fn lower_generic_args_from_fn_path(
168 } 175 }
169 if let Some(ret_type) = ret_type { 176 if let Some(ret_type) = ret_type {
170 let type_ref = TypeRef::from_ast_opt(ret_type.type_ref()); 177 let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
171 bindings.push((name![Output], type_ref)) 178 bindings.push(AssociatedTypeBinding {
179 name: name![Output],
180 type_ref: Some(type_ref),
181 bounds: Vec::new(),
182 });
172 } 183 }
173 if args.is_empty() && bindings.is_empty() { 184 if args.is_empty() && bindings.is_empty() {
174 None 185 None
diff --git a/crates/ra_hir_def/src/type_ref.rs b/crates/ra_hir_def/src/type_ref.rs
index ea29c4176..f308c6bdf 100644
--- a/crates/ra_hir_def/src/type_ref.rs
+++ b/crates/ra_hir_def/src/type_ref.rs
@@ -163,8 +163,16 @@ impl TypeRef {
163 let crate::path::GenericArg::Type(type_ref) = arg; 163 let crate::path::GenericArg::Type(type_ref) = arg;
164 go(type_ref, f); 164 go(type_ref, f);
165 } 165 }
166 for (_, type_ref) in &args_and_bindings.bindings { 166 for binding in &args_and_bindings.bindings {
167 go(type_ref, f); 167 if let Some(type_ref) = &binding.type_ref {
168 go(type_ref, f);
169 }
170 for bound in &binding.bounds {
171 match bound {
172 TypeBound::Path(path) => go_path(path, f),
173 TypeBound::Error => (),
174 }
175 }
168 } 176 }
169 } 177 }
170 } 178 }
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index 25cc1e9fc..fecce224e 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -83,6 +83,15 @@ impl AsName for ast::Name {
83 } 83 }
84} 84}
85 85
86impl AsName for ast::NameOrNameRef {
87 fn as_name(&self) -> Name {
88 match self {
89 ast::NameOrNameRef::Name(it) => it.as_name(),
90 ast::NameOrNameRef::NameRef(it) => it.as_name(),
91 }
92 }
93}
94
86impl AsName for tt::Ident { 95impl AsName for tt::Ident {
87 fn as_name(&self) -> Name { 96 fn as_name(&self) -> Name {
88 Name::resolve(&self.text) 97 Name::resolve(&self.text)
diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml
index 59efc1c31..e891d733f 100644
--- a/crates/ra_hir_ty/Cargo.toml
+++ b/crates/ra_hir_ty/Cargo.toml
@@ -8,6 +8,7 @@ authors = ["rust-analyzer developers"]
8doctest = false 8doctest = false
9 9
10[dependencies] 10[dependencies]
11itertools = "0.9.0"
11arrayvec = "0.5.1" 12arrayvec = "0.5.1"
12smallvec = "1.2.0" 13smallvec = "1.2.0"
13ena = "0.13.1" 14ena = "0.13.1"
@@ -26,9 +27,9 @@ test_utils = { path = "../test_utils" }
26 27
27scoped-tls = "1" 28scoped-tls = "1"
28 29
29chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } 30chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "6222e416b96892b2a86bc08de7dbc9826ff1acea" }
30chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } 31chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "6222e416b96892b2a86bc08de7dbc9826ff1acea" }
31chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } 32chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "6222e416b96892b2a86bc08de7dbc9826ff1acea" }
32 33
33[dev-dependencies] 34[dev-dependencies]
34insta = "0.15.0" 35insta = "0.16.0"
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 9e9a9d047..a64be9848 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -289,7 +289,7 @@ impl PatStack {
289 Self::from_slice(&self.0[1..]) 289 Self::from_slice(&self.0[1..])
290 } 290 }
291 291
292 fn replace_head_with(&self, pat_ids: &[PatId]) -> PatStack { 292 fn replace_head_with<T: Into<PatIdOrWild> + Copy>(&self, pat_ids: &[T]) -> PatStack {
293 let mut patterns: PatStackInner = smallvec![]; 293 let mut patterns: PatStackInner = smallvec![];
294 for pat in pat_ids { 294 for pat in pat_ids {
295 patterns.push((*pat).into()); 295 patterns.push((*pat).into());
@@ -320,12 +320,14 @@ impl PatStack {
320 constructor: &Constructor, 320 constructor: &Constructor,
321 ) -> MatchCheckResult<Option<PatStack>> { 321 ) -> MatchCheckResult<Option<PatStack>> {
322 let result = match (self.head().as_pat(cx), constructor) { 322 let result = match (self.head().as_pat(cx), constructor) {
323 (Pat::Tuple(ref pat_ids), Constructor::Tuple { arity }) => { 323 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
324 debug_assert_eq!( 324 if ellipsis.is_some() {
325 pat_ids.len(), 325 // If there are ellipsis here, we should add the correct number of
326 *arity, 326 // Pat::Wild patterns to `pat_ids`. We should be able to use the
327 "we type check before calling this code, so we should never hit this case", 327 // constructors arity for this, but at the time of writing we aren't
328 ); 328 // correctly calculating this arity when ellipsis are present.
329 return Err(MatchCheckErr::NotImplemented);
330 }
329 331
330 Some(self.replace_head_with(pat_ids)) 332 Some(self.replace_head_with(pat_ids))
331 } 333 }
@@ -351,19 +353,47 @@ impl PatStack {
351 Some(self.to_tail()) 353 Some(self.to_tail())
352 } 354 }
353 } 355 }
354 (Pat::TupleStruct { args: ref pat_ids, .. }, Constructor::Enum(enum_constructor)) => { 356 (
357 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
358 Constructor::Enum(enum_constructor),
359 ) => {
355 let pat_id = self.head().as_id().expect("we know this isn't a wild"); 360 let pat_id = self.head().as_id().expect("we know this isn't a wild");
356 if !enum_variant_matches(cx, pat_id, *enum_constructor) { 361 if !enum_variant_matches(cx, pat_id, *enum_constructor) {
357 None 362 None
358 } else { 363 } else {
359 // If the enum variant matches, then we need to confirm 364 let constructor_arity = constructor.arity(cx)?;
360 // that the number of patterns aligns with the expected 365 if let Some(ellipsis_position) = ellipsis {
361 // number of patterns for that enum variant. 366 // If there are ellipsis in the pattern, the ellipsis must take the place
362 if pat_ids.len() != constructor.arity(cx)? { 367 // of at least one sub-pattern, so `pat_ids` should be smaller than the
363 return Err(MatchCheckErr::MalformedMatchArm); 368 // constructor arity.
369 if pat_ids.len() < constructor_arity {
370 let mut new_patterns: Vec<PatIdOrWild> = vec![];
371
372 for pat_id in &pat_ids[0..ellipsis_position] {
373 new_patterns.push((*pat_id).into());
374 }
375
376 for _ in 0..(constructor_arity - pat_ids.len()) {
377 new_patterns.push(PatIdOrWild::Wild);
378 }
379
380 for pat_id in &pat_ids[ellipsis_position..pat_ids.len()] {
381 new_patterns.push((*pat_id).into());
382 }
383
384 Some(self.replace_head_with(&new_patterns))
385 } else {
386 return Err(MatchCheckErr::MalformedMatchArm);
387 }
388 } else {
389 // If there is no ellipsis in the tuple pattern, the number
390 // of patterns must equal the constructor arity.
391 if pat_ids.len() == constructor_arity {
392 Some(self.replace_head_with(pat_ids))
393 } else {
394 return Err(MatchCheckErr::MalformedMatchArm);
395 }
364 } 396 }
365
366 Some(self.replace_head_with(pat_ids))
367 } 397 }
368 } 398 }
369 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), 399 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented),
@@ -644,7 +674,11 @@ impl Constructor {
644fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { 674fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
645 let res = match pat.as_pat(cx) { 675 let res = match pat.as_pat(cx) {
646 Pat::Wild => None, 676 Pat::Wild => None,
647 Pat::Tuple(pats) => Some(Constructor::Tuple { arity: pats.len() }), 677 // FIXME somehow create the Tuple constructor with the proper arity. If there are
678 // ellipsis, the arity is not equal to the number of patterns.
679 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => {
680 Some(Constructor::Tuple { arity: pats.len() })
681 }
648 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { 682 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
649 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), 683 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
650 _ => return Err(MatchCheckErr::NotImplemented), 684 _ => return Err(MatchCheckErr::NotImplemented),
@@ -973,6 +1007,47 @@ mod tests {
973 } 1007 }
974 1008
975 #[test] 1009 #[test]
1010 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1011 let content = r"
1012 fn test_fn() {
1013 match (false, true, false) {
1014 (false, ..) => {},
1015 (true, ..) => {},
1016 }
1017 }
1018 ";
1019
1020 check_no_diagnostic(content);
1021 }
1022
1023 #[test]
1024 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1025 let content = r"
1026 fn test_fn() {
1027 match (false, true, false) {
1028 (.., false) => {},
1029 (.., true) => {},
1030 }
1031 }
1032 ";
1033
1034 check_no_diagnostic(content);
1035 }
1036
1037 #[test]
1038 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1039 let content = r"
1040 fn test_fn() {
1041 match (false, true, false) {
1042 (..) => {},
1043 }
1044 }
1045 ";
1046
1047 check_no_diagnostic(content);
1048 }
1049
1050 #[test]
976 fn tuple_of_tuple_and_bools_no_arms() { 1051 fn tuple_of_tuple_and_bools_no_arms() {
977 let content = r" 1052 let content = r"
978 fn test_fn() { 1053 fn test_fn() {
@@ -1315,8 +1390,9 @@ mod tests {
1315 } 1390 }
1316 "; 1391 ";
1317 1392
1318 // Match arms with the incorrect type are filtered out. 1393 // Match statements with arms that don't match the
1319 check_diagnostic(content); 1394 // expression pattern do not fire this diagnostic.
1395 check_no_diagnostic(content);
1320 } 1396 }
1321 1397
1322 #[test] 1398 #[test]
@@ -1330,8 +1406,9 @@ mod tests {
1330 } 1406 }
1331 "; 1407 ";
1332 1408
1333 // Match arms with the incorrect type are filtered out. 1409 // Match statements with arms that don't match the
1334 check_diagnostic(content); 1410 // expression pattern do not fire this diagnostic.
1411 check_no_diagnostic(content);
1335 } 1412 }
1336 1413
1337 #[test] 1414 #[test]
@@ -1344,8 +1421,9 @@ mod tests {
1344 } 1421 }
1345 "; 1422 ";
1346 1423
1347 // Match arms with the incorrect type are filtered out. 1424 // Match statements with arms that don't match the
1348 check_diagnostic(content); 1425 // expression pattern do not fire this diagnostic.
1426 check_no_diagnostic(content);
1349 } 1427 }
1350 1428
1351 #[test] 1429 #[test]
@@ -1383,6 +1461,163 @@ mod tests {
1383 // we don't create a diagnostic). 1461 // we don't create a diagnostic).
1384 check_no_diagnostic(content); 1462 check_no_diagnostic(content);
1385 } 1463 }
1464
1465 #[test]
1466 fn expr_diverges() {
1467 let content = r"
1468 enum Either {
1469 A,
1470 B,
1471 }
1472 fn test_fn() {
1473 match loop {} {
1474 Either::A => (),
1475 Either::B => (),
1476 }
1477 }
1478 ";
1479
1480 check_no_diagnostic(content);
1481 }
1482
1483 #[test]
1484 fn expr_loop_with_break() {
1485 let content = r"
1486 enum Either {
1487 A,
1488 B,
1489 }
1490 fn test_fn() {
1491 match loop { break Foo::A } {
1492 Either::A => (),
1493 Either::B => (),
1494 }
1495 }
1496 ";
1497
1498 check_no_diagnostic(content);
1499 }
1500
1501 #[test]
1502 fn expr_partially_diverges() {
1503 let content = r"
1504 enum Either<T> {
1505 A(T),
1506 B,
1507 }
1508 fn foo() -> Either<!> {
1509 Either::B
1510 }
1511 fn test_fn() -> u32 {
1512 match foo() {
1513 Either::A(val) => val,
1514 Either::B => 0,
1515 }
1516 }
1517 ";
1518
1519 check_no_diagnostic(content);
1520 }
1521
1522 #[test]
1523 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1524 let content = r"
1525 enum Either {
1526 A(bool, bool, bool, bool),
1527 B,
1528 }
1529 fn test_fn() {
1530 match Either::B {
1531 Either::A(true, .., true) => {},
1532 Either::A(true, .., false) => {},
1533 Either::A(false, .., true) => {},
1534 Either::A(false, .., false) => {},
1535 Either::B => {},
1536 }
1537 }
1538 ";
1539
1540 check_no_diagnostic(content);
1541 }
1542
1543 #[test]
1544 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1545 let content = r"
1546 enum Either {
1547 A(bool, bool, bool, bool),
1548 B,
1549 }
1550 fn test_fn() {
1551 match Either::B {
1552 Either::A(true, .., true) => {},
1553 Either::A(true, .., false) => {},
1554 Either::A(.., true) => {},
1555 Either::A(.., false) => {},
1556 Either::B => {},
1557 }
1558 }
1559 ";
1560
1561 check_no_diagnostic(content);
1562 }
1563
1564 #[test]
1565 fn enum_tuple_partial_ellipsis_missing_arm() {
1566 let content = r"
1567 enum Either {
1568 A(bool, bool, bool, bool),
1569 B,
1570 }
1571 fn test_fn() {
1572 match Either::B {
1573 Either::A(true, .., true) => {},
1574 Either::A(true, .., false) => {},
1575 Either::A(false, .., false) => {},
1576 Either::B => {},
1577 }
1578 }
1579 ";
1580
1581 check_diagnostic(content);
1582 }
1583
1584 #[test]
1585 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1586 let content = r"
1587 enum Either {
1588 A(bool, bool, bool, bool),
1589 B,
1590 }
1591 fn test_fn() {
1592 match Either::B {
1593 Either::A(true, .., true) => {},
1594 Either::A(true, .., false) => {},
1595 Either::A(.., true) => {},
1596 Either::B => {},
1597 }
1598 }
1599 ";
1600
1601 check_diagnostic(content);
1602 }
1603
1604 #[test]
1605 fn enum_tuple_ellipsis_no_diagnostic() {
1606 let content = r"
1607 enum Either {
1608 A(bool, bool, bool, bool),
1609 B,
1610 }
1611 fn test_fn() {
1612 match Either::B {
1613 Either::A(..) => {},
1614 Either::B => {},
1615 }
1616 }
1617 ";
1618
1619 check_no_diagnostic(content);
1620 }
1386} 1621}
1387 1622
1388#[cfg(test)] 1623#[cfg(test)]
@@ -1452,4 +1687,75 @@ mod false_negatives {
1452 // We do not currently handle patterns with internal `or`s. 1687 // We do not currently handle patterns with internal `or`s.
1453 check_no_diagnostic(content); 1688 check_no_diagnostic(content);
1454 } 1689 }
1690
1691 #[test]
1692 fn expr_diverges_missing_arm() {
1693 let content = r"
1694 enum Either {
1695 A,
1696 B,
1697 }
1698 fn test_fn() {
1699 match loop {} {
1700 Either::A => (),
1701 }
1702 }
1703 ";
1704
1705 // This is a false negative.
1706 // Even though the match expression diverges, rustc fails
1707 // to compile here since `Either::B` is missing.
1708 check_no_diagnostic(content);
1709 }
1710
1711 #[test]
1712 fn expr_loop_missing_arm() {
1713 let content = r"
1714 enum Either {
1715 A,
1716 B,
1717 }
1718 fn test_fn() {
1719 match loop { break Foo::A } {
1720 Either::A => (),
1721 }
1722 }
1723 ";
1724
1725 // This is a false negative.
1726 // We currently infer the type of `loop { break Foo::A }` to `!`, which
1727 // causes us to skip the diagnostic since `Either::A` doesn't type check
1728 // with `!`.
1729 check_no_diagnostic(content);
1730 }
1731
1732 #[test]
1733 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1734 let content = r"
1735 fn test_fn() {
1736 match (false, true, false) {
1737 (false, ..) => {},
1738 }
1739 }
1740 ";
1741
1742 // This is a false negative.
1743 // We don't currently handle tuple patterns with ellipsis.
1744 check_no_diagnostic(content);
1745 }
1746
1747 #[test]
1748 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1749 let content = r"
1750 fn test_fn() {
1751 match (false, true, false) {
1752 (.., false) => {},
1753 }
1754 }
1755 ";
1756
1757 // This is a false negative.
1758 // We don't currently handle tuple patterns with ellipsis.
1759 check_no_diagnostic(content);
1760 }
1455} 1761}
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 1462b053f..33da16b48 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -11,7 +11,7 @@ use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
11use ra_prof::profile; 11use ra_prof::profile;
12 12
13use crate::{ 13use crate::{
14 method_resolution::CrateImplDefs, 14 method_resolution::{CrateImplDefs, TyFingerprint},
15 traits::{chalk, AssocTyValue, Impl}, 15 traits::{chalk, AssocTyValue, Impl},
16 Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty, 16 Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty,
17 TyDefId, TypeCtor, ValueTyDefId, 17 TyDefId, TypeCtor, ValueTyDefId,
@@ -65,7 +65,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
65 fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>; 65 fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>;
66 66
67 #[salsa::invoke(crate::traits::impls_for_trait_query)] 67 #[salsa::invoke(crate::traits::impls_for_trait_query)]
68 fn impls_for_trait(&self, krate: CrateId, trait_: TraitId) -> Arc<[ImplId]>; 68 fn impls_for_trait(
69 &self,
70 krate: CrateId,
71 trait_: TraitId,
72 self_ty_fp: Option<TyFingerprint>,
73 ) -> Arc<[ImplId]>;
69 74
70 // Interned IDs for Chalk integration 75 // Interned IDs for Chalk integration
71 #[salsa::interned] 76 #[salsa::interned]
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index 827b687de..21abbcf1e 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -89,21 +89,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
89 let (_, source_map) = db.body_with_source_map(self.func.into()); 89 let (_, source_map) = db.body_with_source_map(self.func.into());
90 90
91 if let Ok(source_ptr) = source_map.expr_syntax(id) { 91 if let Ok(source_ptr) = source_map.expr_syntax(id) {
92 if let Some(expr) = source_ptr.value.as_ref().left() { 92 let root = source_ptr.file_syntax(db.upcast());
93 let root = source_ptr.file_syntax(db.upcast()); 93 if let ast::Expr::RecordLit(record_lit) = &source_ptr.value.to_node(&root) {
94 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) { 94 if let Some(field_list) = record_lit.record_field_list() {
95 if let Some(field_list) = record_lit.record_field_list() { 95 let variant_data = variant_data(db.upcast(), variant_def);
96 let variant_data = variant_data(db.upcast(), variant_def); 96 let missed_fields = missed_fields
97 let missed_fields = missed_fields 97 .into_iter()
98 .into_iter() 98 .map(|idx| variant_data.fields()[idx].name.clone())
99 .map(|idx| variant_data.fields()[idx].name.clone()) 99 .collect();
100 .collect(); 100 self.sink.push(MissingFields {
101 self.sink.push(MissingFields { 101 file: source_ptr.file_id,
102 file: source_ptr.file_id, 102 field_list: AstPtr::new(&field_list),
103 field_list: AstPtr::new(&field_list), 103 missed_fields,
104 missed_fields, 104 })
105 })
106 }
107 } 105 }
108 } 106 }
109 } 107 }
@@ -163,12 +161,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
163 161
164 let mut seen = Matrix::empty(); 162 let mut seen = Matrix::empty();
165 for pat in pats { 163 for pat in pats {
166 // We skip any patterns whose type we cannot resolve.
167 //
168 // This could lead to false positives in this diagnostic, so
169 // it might be better to skip the entire diagnostic if we either
170 // cannot resolve a match arm or determine that the match arm has
171 // the wrong type.
172 if let Some(pat_ty) = infer.type_of_pat.get(pat) { 164 if let Some(pat_ty) = infer.type_of_pat.get(pat) {
173 // We only include patterns whose type matches the type 165 // We only include patterns whose type matches the type
174 // of the match expression. If we had a InvalidMatchArmPattern 166 // of the match expression. If we had a InvalidMatchArmPattern
@@ -191,8 +183,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
191 // to the matrix here. 183 // to the matrix here.
192 let v = PatStack::from_pattern(pat); 184 let v = PatStack::from_pattern(pat);
193 seen.push(&cx, v); 185 seen.push(&cx, v);
186 continue;
194 } 187 }
195 } 188 }
189
190 // If we can't resolve the type of a pattern, or the pattern type doesn't
191 // fit the match expression, we skip this diagnostic. Skipping the entire
192 // diagnostic rather than just not including this match arm is preferred
193 // to avoid the chance of false positives.
194 return;
196 } 195 }
197 196
198 match is_useful(&cx, &seen, &PatStack::from_wild()) { 197 match is_useful(&cx, &seen, &PatStack::from_wild()) {
@@ -205,18 +204,16 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
205 } 204 }
206 205
207 if let Ok(source_ptr) = source_map.expr_syntax(id) { 206 if let Ok(source_ptr) = source_map.expr_syntax(id) {
208 if let Some(expr) = source_ptr.value.as_ref().left() { 207 let root = source_ptr.file_syntax(db.upcast());
209 let root = source_ptr.file_syntax(db.upcast()); 208 if let ast::Expr::MatchExpr(match_expr) = &source_ptr.value.to_node(&root) {
210 if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) { 209 if let (Some(match_expr), Some(arms)) =
211 if let (Some(match_expr), Some(arms)) = 210 (match_expr.expr(), match_expr.match_arm_list())
212 (match_expr.expr(), match_expr.match_arm_list()) 211 {
213 { 212 self.sink.push(MissingMatchArms {
214 self.sink.push(MissingMatchArms { 213 file: source_ptr.file_id,
215 file: source_ptr.file_id, 214 match_expr: AstPtr::new(&match_expr),
216 match_expr: AstPtr::new(&match_expr), 215 arms: AstPtr::new(&arms),
217 arms: AstPtr::new(&arms), 216 })
218 })
219 }
220 } 217 }
221 } 218 }
222 } 219 }
@@ -247,9 +244,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
247 let (_, source_map) = db.body_with_source_map(self.func.into()); 244 let (_, source_map) = db.body_with_source_map(self.func.into());
248 245
249 if let Ok(source_ptr) = source_map.expr_syntax(id) { 246 if let Ok(source_ptr) = source_map.expr_syntax(id) {
250 if let Some(expr) = source_ptr.value.left() { 247 self.sink
251 self.sink.push(MissingOkInTailExpr { file: source_ptr.file_id, expr }); 248 .push(MissingOkInTailExpr { file: source_ptr.file_id, expr: source_ptr.value });
252 }
253 } 249 }
254 } 250 }
255 } 251 }
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 078476f76..8ec4d4ace 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -85,7 +85,7 @@ impl<'a> InferenceContext<'a> {
85 let body = Arc::clone(&self.body); // avoid borrow checker problem 85 let body = Arc::clone(&self.body); // avoid borrow checker problem
86 86
87 let is_non_ref_pat = match &body[pat] { 87 let is_non_ref_pat = match &body[pat] {
88 Pat::Tuple(..) 88 Pat::Tuple { .. }
89 | Pat::Or(..) 89 | Pat::Or(..)
90 | Pat::TupleStruct { .. } 90 | Pat::TupleStruct { .. }
91 | Pat::Record { .. } 91 | Pat::Record { .. }
@@ -116,7 +116,7 @@ impl<'a> InferenceContext<'a> {
116 let expected = expected; 116 let expected = expected;
117 117
118 let ty = match &body[pat] { 118 let ty = match &body[pat] {
119 Pat::Tuple(ref args) => { 119 Pat::Tuple { ref args, .. } => {
120 let expectations = match expected.as_tuple() { 120 let expectations = match expected.as_tuple() {
121 Some(parameters) => &*parameters.0, 121 Some(parameters) => &*parameters.0,
122 _ => &[], 122 _ => &[],
@@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> {
155 let subty = self.infer_pat(*pat, expectation, default_bm); 155 let subty = self.infer_pat(*pat, expectation, default_bm);
156 Ty::apply_one(TypeCtor::Ref(*mutability), subty) 156 Ty::apply_one(TypeCtor::Ref(*mutability), subty)
157 } 157 }
158 Pat::TupleStruct { path: p, args: subpats } => { 158 Pat::TupleStruct { path: p, args: subpats, .. } => {
159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) 159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
160 } 160 }
161 Pat::Record { path: p, args: fields, ellipsis: _ } => { 161 Pat::Record { path: p, args: fields, ellipsis: _ } => {
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 6c7bbc448..cc1ac8e3e 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -8,6 +8,8 @@
8use std::iter; 8use std::iter;
9use std::sync::Arc; 9use std::sync::Arc;
10 10
11use smallvec::SmallVec;
12
11use hir_def::{ 13use hir_def::{
12 adt::StructKind, 14 adt::StructKind,
13 builtin_type::BuiltinType, 15 builtin_type::BuiltinType,
@@ -360,13 +362,23 @@ impl Ty {
360 }, 362 },
361 Some(TypeNs::GenericParam(param_id)) => { 363 Some(TypeNs::GenericParam(param_id)) => {
362 let predicates = ctx.db.generic_predicates_for_param(param_id); 364 let predicates = ctx.db.generic_predicates_for_param(param_id);
363 predicates 365 let mut traits_: Vec<_> = predicates
364 .iter() 366 .iter()
365 .filter_map(|pred| match &pred.value { 367 .filter_map(|pred| match &pred.value {
366 GenericPredicate::Implemented(tr) => Some(tr.trait_), 368 GenericPredicate::Implemented(tr) => Some(tr.trait_),
367 _ => None, 369 _ => None,
368 }) 370 })
369 .collect() 371 .collect();
372 // Handle `Self::Type` referring to own associated type in trait definitions
373 if let GenericDefId::TraitId(trait_id) = param_id.parent {
374 let generics = generics(ctx.db.upcast(), trait_id.into());
375 if generics.params.types[param_id.local_id].provenance
376 == TypeParamProvenance::TraitSelf
377 {
378 traits_.push(trait_id);
379 }
380 }
381 traits_
370 } 382 }
371 _ => return Ty::Unknown, 383 _ => return Ty::Unknown,
372 }; 384 };
@@ -596,21 +608,35 @@ fn assoc_type_bindings_from_type_bound<'a>(
596 .into_iter() 608 .into_iter()
597 .flat_map(|segment| segment.args_and_bindings.into_iter()) 609 .flat_map(|segment| segment.args_and_bindings.into_iter())
598 .flat_map(|args_and_bindings| args_and_bindings.bindings.iter()) 610 .flat_map(|args_and_bindings| args_and_bindings.bindings.iter())
599 .map(move |(name, type_ref)| { 611 .flat_map(move |binding| {
600 let associated_ty = associated_type_by_name_including_super_traits( 612 let associated_ty = associated_type_by_name_including_super_traits(
601 ctx.db.upcast(), 613 ctx.db.upcast(),
602 trait_ref.trait_, 614 trait_ref.trait_,
603 &name, 615 &binding.name,
604 ); 616 );
605 let associated_ty = match associated_ty { 617 let associated_ty = match associated_ty {
606 None => return GenericPredicate::Error, 618 None => return SmallVec::<[GenericPredicate; 1]>::new(),
607 Some(t) => t, 619 Some(t) => t,
608 }; 620 };
609 let projection_ty = 621 let projection_ty =
610 ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() }; 622 ProjectionTy { associated_ty, parameters: trait_ref.substs.clone() };
611 let ty = Ty::from_hir(ctx, type_ref); 623 let mut preds = SmallVec::with_capacity(
612 let projection_predicate = ProjectionPredicate { projection_ty, ty }; 624 binding.type_ref.as_ref().map_or(0, |_| 1) + binding.bounds.len(),
613 GenericPredicate::Projection(projection_predicate) 625 );
626 if let Some(type_ref) = &binding.type_ref {
627 let ty = Ty::from_hir(ctx, type_ref);
628 let projection_predicate =
629 ProjectionPredicate { projection_ty: projection_ty.clone(), ty };
630 preds.push(GenericPredicate::Projection(projection_predicate));
631 }
632 for bound in &binding.bounds {
633 preds.extend(GenericPredicate::from_type_bound(
634 ctx,
635 bound,
636 Ty::Projection(projection_ty.clone()),
637 ));
638 }
639 preds
614 }) 640 })
615} 641}
616 642
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index 74a0bc7db..657284fd0 100644
--- a/crates/ra_hir_ty/src/method_resolution.rs
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -34,7 +34,7 @@ impl TyFingerprint {
34 /// Creates a TyFingerprint for looking up an impl. Only certain types can 34 /// Creates a TyFingerprint for looking up an impl. Only certain types can
35 /// have impls: if we have some `struct S`, we can have an `impl S`, but not 35 /// have impls: if we have some `struct S`, we can have an `impl S`, but not
36 /// `impl &S`. Hence, this will return `None` for reference types and such. 36 /// `impl &S`. Hence, this will return `None` for reference types and such.
37 fn for_impl(ty: &Ty) -> Option<TyFingerprint> { 37 pub(crate) fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
38 match ty { 38 match ty {
39 Ty::Apply(a_ty) => Some(TyFingerprint::Apply(a_ty.ctor)), 39 Ty::Apply(a_ty) => Some(TyFingerprint::Apply(a_ty.ctor)),
40 _ => None, 40 _ => None,
@@ -45,7 +45,7 @@ impl TyFingerprint {
45#[derive(Debug, PartialEq, Eq)] 45#[derive(Debug, PartialEq, Eq)]
46pub struct CrateImplDefs { 46pub struct CrateImplDefs {
47 impls: FxHashMap<TyFingerprint, Vec<ImplId>>, 47 impls: FxHashMap<TyFingerprint, Vec<ImplId>>,
48 impls_by_trait: FxHashMap<TraitId, Vec<ImplId>>, 48 impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>,
49} 49}
50 50
51impl CrateImplDefs { 51impl CrateImplDefs {
@@ -59,7 +59,14 @@ impl CrateImplDefs {
59 for impl_id in module_data.scope.impls() { 59 for impl_id in module_data.scope.impls() {
60 match db.impl_trait(impl_id) { 60 match db.impl_trait(impl_id) {
61 Some(tr) => { 61 Some(tr) => {
62 res.impls_by_trait.entry(tr.value.trait_).or_default().push(impl_id); 62 let self_ty = db.impl_self_ty(impl_id);
63 let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
64 res.impls_by_trait
65 .entry(tr.value.trait_)
66 .or_default()
67 .entry(self_ty_fp)
68 .or_default()
69 .push(impl_id);
63 } 70 }
64 None => { 71 None => {
65 let self_ty = db.impl_self_ty(impl_id); 72 let self_ty = db.impl_self_ty(impl_id);
@@ -79,11 +86,39 @@ impl CrateImplDefs {
79 } 86 }
80 87
81 pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ { 88 pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ {
82 self.impls_by_trait.get(&tr).into_iter().flatten().copied() 89 self.impls_by_trait
90 .get(&tr)
91 .into_iter()
92 .flat_map(|m| m.values().flat_map(|v| v.iter().copied()))
93 }
94
95 pub fn lookup_impl_defs_for_trait_and_ty(
96 &self,
97 tr: TraitId,
98 fp: TyFingerprint,
99 ) -> impl Iterator<Item = ImplId> + '_ {
100 self.impls_by_trait
101 .get(&tr)
102 .and_then(|m| m.get(&Some(fp)))
103 .into_iter()
104 .flatten()
105 .copied()
106 .chain(
107 self.impls_by_trait
108 .get(&tr)
109 .and_then(|m| m.get(&None))
110 .into_iter()
111 .flatten()
112 .copied(),
113 )
83 } 114 }
84 115
85 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a { 116 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a {
86 self.impls.values().chain(self.impls_by_trait.values()).flatten().copied() 117 self.impls
118 .values()
119 .chain(self.impls_by_trait.values().flat_map(|m| m.values()))
120 .flatten()
121 .copied()
87 } 122 }
88} 123}
89 124
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 54e31602f..81fc0f63a 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -82,9 +82,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
82 82
83 for (expr, ty) in inference_result.type_of_expr.iter() { 83 for (expr, ty) in inference_result.type_of_expr.iter() {
84 let syntax_ptr = match body_source_map.expr_syntax(expr) { 84 let syntax_ptr = match body_source_map.expr_syntax(expr) {
85 Ok(sp) => { 85 Ok(sp) => sp.map(|ast| ast.syntax_node_ptr()),
86 sp.map(|ast| ast.either(|it| it.syntax_node_ptr(), |it| it.syntax_node_ptr()))
87 }
88 Err(SyntheticSyntax) => continue, 86 Err(SyntheticSyntax) => continue,
89 }; 87 };
90 types.push((syntax_ptr.clone(), ty)); 88 types.push((syntax_ptr.clone(), ty));
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 6e5d2247c..07cbc521a 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -1,7 +1,8 @@
1use super::{infer, infer_with_mismatches};
2use insta::assert_snapshot; 1use insta::assert_snapshot;
3use test_utils::covers; 2use test_utils::covers;
4 3
4use super::{infer, infer_with_mismatches};
5
5#[test] 6#[test]
6fn infer_pattern() { 7fn infer_pattern() {
7 assert_snapshot!( 8 assert_snapshot!(
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index 3402e0cb5..d69115a2f 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -451,8 +451,7 @@ pub mod str {
451"#, 451"#,
452 ); 452 );
453 453
454 // should be Option<char>, but currently not because of Chalk ambiguity problem 454 assert_eq!("(Option<char>, Option<char>)", super::type_at_pos(&db, pos));
455 assert_eq!("(Option<{unknown}>, Option<{unknown}>)", super::type_at_pos(&db, pos));
456} 455}
457 456
458#[test] 457#[test]
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 22ae6ca90..b3a2fc439 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -1803,7 +1803,7 @@ fn test<T, U>() where T::Item: Trait2, T: Trait<U::Item>, U: Trait<()> {
1803} 1803}
1804 1804
1805#[test] 1805#[test]
1806fn unselected_projection_on_trait_self() { 1806fn unselected_projection_on_impl_self() {
1807 assert_snapshot!(infer( 1807 assert_snapshot!(infer(
1808 r#" 1808 r#"
1809//- /main.rs 1809//- /main.rs
@@ -1844,6 +1844,30 @@ impl Trait for S2 {
1844} 1844}
1845 1845
1846#[test] 1846#[test]
1847fn unselected_projection_on_trait_self() {
1848 let t = type_at(
1849 r#"
1850//- /main.rs
1851trait Trait {
1852 type Item;
1853
1854 fn f(&self) -> Self::Item { loop {} }
1855}
1856
1857struct S;
1858impl Trait for S {
1859 type Item = u32;
1860}
1861
1862fn test() {
1863 S.f()<|>;
1864}
1865"#,
1866 );
1867 assert_eq!(t, "u32");
1868}
1869
1870#[test]
1847fn trait_impl_self_ty() { 1871fn trait_impl_self_ty() {
1848 let t = type_at( 1872 let t = type_at(
1849 r#" 1873 r#"
@@ -1924,6 +1948,53 @@ fn test<T, U>() where T: Trait<U::Item>, U: Trait<T::Item> {
1924} 1948}
1925 1949
1926#[test] 1950#[test]
1951fn inline_assoc_type_bounds_1() {
1952 let t = type_at(
1953 r#"
1954//- /main.rs
1955trait Iterator {
1956 type Item;
1957}
1958trait OtherTrait<T> {
1959 fn foo(&self) -> T;
1960}
1961
1962// workaround for Chalk assoc type normalization problems
1963pub struct S<T>;
1964impl<T: Iterator> Iterator for S<T> {
1965 type Item = <T as Iterator>::Item;
1966}
1967
1968fn test<I: Iterator<Item: OtherTrait<u32>>>() {
1969 let x: <S<I> as Iterator>::Item;
1970 x.foo()<|>;
1971}
1972"#,
1973 );
1974 assert_eq!(t, "u32");
1975}
1976
1977#[test]
1978fn inline_assoc_type_bounds_2() {
1979 let t = type_at(
1980 r#"
1981//- /main.rs
1982trait Iterator {
1983 type Item;
1984}
1985
1986fn test<I: Iterator<Item: Iterator<Item = u32>>>() {
1987 let x: <<I as Iterator>::Item as Iterator>::Item;
1988 x<|>;
1989}
1990"#,
1991 );
1992 // assert_eq!(t, "u32");
1993 // doesn't currently work, Chalk #234
1994 assert_eq!(t, "{unknown}");
1995}
1996
1997#[test]
1927fn unify_impl_trait() { 1998fn unify_impl_trait() {
1928 assert_snapshot!( 1999 assert_snapshot!(
1929 infer_with_mismatches(r#" 2000 infer_with_mismatches(r#"
@@ -2023,6 +2094,33 @@ fn main() {
2023} 2094}
2024 2095
2025#[test] 2096#[test]
2097fn associated_type_bound() {
2098 let t = type_at(
2099 r#"
2100//- /main.rs
2101pub trait Trait {
2102 type Item: OtherTrait<u32>;
2103}
2104pub trait OtherTrait<T> {
2105 fn foo(&self) -> T;
2106}
2107
2108// this is just a workaround for chalk#234
2109pub struct S<T>;
2110impl<T: Trait> Trait for S<T> {
2111 type Item = <T as Trait>::Item;
2112}
2113
2114fn test<T: Trait>() {
2115 let y: <S<T> as Trait>::Item = no_matter;
2116 y.foo()<|>;
2117}
2118"#,
2119 );
2120 assert_eq!(t, "u32");
2121}
2122
2123#[test]
2026fn dyn_trait_through_chalk() { 2124fn dyn_trait_through_chalk() {
2027 let t = type_at( 2125 let t = type_at(
2028 r#" 2126 r#"
diff --git a/crates/ra_hir_ty/src/traits.rs b/crates/ra_hir_ty/src/traits.rs
index 21e233379..44fbdb197 100644
--- a/crates/ra_hir_ty/src/traits.rs
+++ b/crates/ra_hir_ty/src/traits.rs
@@ -7,7 +7,7 @@ use ra_db::{impl_intern_key, salsa, CrateId};
7use ra_prof::profile; 7use ra_prof::profile;
8use rustc_hash::FxHashSet; 8use rustc_hash::FxHashSet;
9 9
10use crate::{db::HirDatabase, DebruijnIndex}; 10use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex};
11 11
12use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; 12use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
13 13
@@ -40,7 +40,12 @@ pub(crate) fn impls_for_trait_query(
40 db: &dyn HirDatabase, 40 db: &dyn HirDatabase,
41 krate: CrateId, 41 krate: CrateId,
42 trait_: TraitId, 42 trait_: TraitId,
43 self_ty_fp: Option<TyFingerprint>,
43) -> Arc<[ImplId]> { 44) -> Arc<[ImplId]> {
45 // FIXME: We could be a lot smarter here - because of the orphan rules and
46 // the fact that the trait and the self type need to be in the dependency
47 // tree of a crate somewhere for an impl to exist, we could skip looking in
48 // a lot of crates completely
44 let mut impls = FxHashSet::default(); 49 let mut impls = FxHashSet::default();
45 // We call the query recursively here. On the one hand, this means we can 50 // We call the query recursively here. On the one hand, this means we can
46 // reuse results from queries for different crates; on the other hand, this 51 // reuse results from queries for different crates; on the other hand, this
@@ -48,10 +53,13 @@ pub(crate) fn impls_for_trait_query(
48 // ones the user is editing), so this may actually be a waste of memory. I'm 53 // ones the user is editing), so this may actually be a waste of memory. I'm
49 // doing it like this mainly for simplicity for now. 54 // doing it like this mainly for simplicity for now.
50 for dep in &db.crate_graph()[krate].dependencies { 55 for dep in &db.crate_graph()[krate].dependencies {
51 impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter()); 56 impls.extend(db.impls_for_trait(dep.crate_id, trait_, self_ty_fp).iter());
52 } 57 }
53 let crate_impl_defs = db.impls_in_crate(krate); 58 let crate_impl_defs = db.impls_in_crate(krate);
54 impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_)); 59 match self_ty_fp {
60 Some(fp) => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp)),
61 None => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_)),
62 }
55 impls.into_iter().collect() 63 impls.into_iter().collect()
56} 64}
57 65
@@ -186,13 +194,16 @@ fn solve(
186 } 194 }
187 remaining > 0 195 remaining > 0
188 }; 196 };
189 let mut solve = || solver.solve_limited(&context, goal, should_continue); 197 let mut solve = || {
198 let solution = solver.solve_limited(&context, goal, should_continue);
199 log::debug!("solve({:?}) => {:?}", goal, solution);
200 solution
201 };
190 // don't set the TLS for Chalk unless Chalk debugging is active, to make 202 // don't set the TLS for Chalk unless Chalk debugging is active, to make
191 // extra sure we only use it for debugging 203 // extra sure we only use it for debugging
192 let solution = 204 let solution =
193 if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() }; 205 if is_chalk_debug() { chalk::tls::set_current_program(db, solve) } else { solve() };
194 206
195 log::debug!("solve({:?}) => {:?}", goal, solution);
196 solution 207 solution
197} 208}
198 209
diff --git a/crates/ra_hir_ty/src/traits/chalk.rs b/crates/ra_hir_ty/src/traits/chalk.rs
index c5f1b5232..55eb0ffcb 100644
--- a/crates/ra_hir_ty/src/traits/chalk.rs
+++ b/crates/ra_hir_ty/src/traits/chalk.rs
@@ -16,8 +16,8 @@ use ra_db::{
16 16
17use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; 17use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
18use crate::{ 18use crate::{
19 db::HirDatabase, display::HirDisplay, utils::generics, ApplicationTy, GenericPredicate, 19 db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
20 ProjectionTy, Substs, TraitRef, Ty, TypeCtor, 20 ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
21}; 21};
22 22
23pub(super) mod tls; 23pub(super) mod tls;
@@ -32,6 +32,9 @@ impl chalk_ir::interner::Interner for Interner {
32 type InternedGoal = Arc<GoalData<Self>>; 32 type InternedGoal = Arc<GoalData<Self>>;
33 type InternedGoals = Vec<Goal<Self>>; 33 type InternedGoals = Vec<Goal<Self>>;
34 type InternedSubstitution = Vec<Parameter<Self>>; 34 type InternedSubstitution = Vec<Parameter<Self>>;
35 type InternedProgramClause = chalk_ir::ProgramClauseData<Self>;
36 type InternedProgramClauses = Vec<chalk_ir::ProgramClause<Self>>;
37 type InternedQuantifiedWhereClauses = Vec<chalk_ir::QuantifiedWhereClause<Self>>;
35 type Identifier = TypeAliasId; 38 type Identifier = TypeAliasId;
36 type DefId = InternId; 39 type DefId = InternId;
37 40
@@ -181,6 +184,48 @@ impl chalk_ir::interner::Interner for Interner {
181 ) -> &'a [Parameter<Self>] { 184 ) -> &'a [Parameter<Self>] {
182 substitution 185 substitution
183 } 186 }
187
188 fn intern_program_clause(
189 &self,
190 data: chalk_ir::ProgramClauseData<Self>,
191 ) -> chalk_ir::ProgramClauseData<Self> {
192 data
193 }
194
195 fn program_clause_data<'a>(
196 &self,
197 clause: &'a chalk_ir::ProgramClauseData<Self>,
198 ) -> &'a chalk_ir::ProgramClauseData<Self> {
199 clause
200 }
201
202 fn intern_program_clauses(
203 &self,
204 data: impl IntoIterator<Item = chalk_ir::ProgramClause<Self>>,
205 ) -> Vec<chalk_ir::ProgramClause<Self>> {
206 data.into_iter().collect()
207 }
208
209 fn program_clauses_data<'a>(
210 &self,
211 clauses: &'a Vec<chalk_ir::ProgramClause<Self>>,
212 ) -> &'a [chalk_ir::ProgramClause<Self>] {
213 clauses
214 }
215
216 fn intern_quantified_where_clauses(
217 &self,
218 data: impl IntoIterator<Item = chalk_ir::QuantifiedWhereClause<Self>>,
219 ) -> Self::InternedQuantifiedWhereClauses {
220 data.into_iter().collect()
221 }
222
223 fn quantified_where_clauses_data<'a>(
224 &self,
225 clauses: &'a Self::InternedQuantifiedWhereClauses,
226 ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
227 clauses
228 }
184} 229}
185 230
186impl chalk_ir::interner::HasInterner for Interner { 231impl chalk_ir::interner::HasInterner for Interner {
@@ -238,12 +283,10 @@ impl ToChalk for Ty {
238 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner), 283 Ty::Bound(idx) => chalk_ir::TyData::BoundVar(idx).intern(&Interner),
239 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"), 284 Ty::Infer(_infer_ty) => panic!("uncanonicalized infer ty"),
240 Ty::Dyn(predicates) => { 285 Ty::Dyn(predicates) => {
241 let where_clauses = predicates 286 let where_clauses = chalk_ir::QuantifiedWhereClauses::from(
242 .iter() 287 &Interner,
243 .filter(|p| !p.is_error()) 288 predicates.iter().filter(|p| !p.is_error()).cloned().map(|p| p.to_chalk(db)),
244 .cloned() 289 );
245 .map(|p| p.to_chalk(db))
246 .collect();
247 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) }; 290 let bounded_ty = chalk_ir::DynTy { bounds: make_binders(where_clauses, 1) };
248 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner) 291 chalk_ir::TyData::Dyn(bounded_ty).intern(&Interner)
249 } 292 }
@@ -281,8 +324,12 @@ impl ToChalk for Ty {
281 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown, 324 chalk_ir::TyData::InferenceVar(_iv) => Ty::Unknown,
282 chalk_ir::TyData::Dyn(where_clauses) => { 325 chalk_ir::TyData::Dyn(where_clauses) => {
283 assert_eq!(where_clauses.bounds.binders.len(), 1); 326 assert_eq!(where_clauses.bounds.binders.len(), 1);
284 let predicates = 327 let predicates = where_clauses
285 where_clauses.bounds.value.into_iter().map(|c| from_chalk(db, c)).collect(); 328 .bounds
329 .skip_binders()
330 .iter(&Interner)
331 .map(|c| from_chalk(db, c.clone()))
332 .collect();
286 Ty::Dyn(predicates) 333 Ty::Dyn(predicates)
287 } 334 }
288 } 335 }
@@ -426,7 +473,7 @@ impl ToChalk for GenericPredicate {
426 ) -> GenericPredicate { 473 ) -> GenericPredicate {
427 // we don't produce any where clauses with binders and can't currently deal with them 474 // we don't produce any where clauses with binders and can't currently deal with them
428 match where_clause 475 match where_clause
429 .value 476 .skip_binders()
430 .shifted_out(&Interner) 477 .shifted_out(&Interner)
431 .expect("unexpected bound vars in where clause") 478 .expect("unexpected bound vars in where clause")
432 { 479 {
@@ -521,7 +568,7 @@ impl ToChalk for Arc<super::TraitEnvironment> {
521 pred.clone().to_chalk(db).cast(&Interner); 568 pred.clone().to_chalk(db).cast(&Interner);
522 clauses.push(program_clause.into_from_env_clause(&Interner)); 569 clauses.push(program_clause.into_from_env_clause(&Interner));
523 } 570 }
524 chalk_ir::Environment::new().add_clauses(clauses) 571 chalk_ir::Environment::new(&Interner).add_clauses(&Interner, clauses)
525 } 572 }
526 573
527 fn from_chalk( 574 fn from_chalk(
@@ -603,10 +650,10 @@ impl ToChalk for builtin::BuiltinImplAssocTyValueData {
603} 650}
604 651
605fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> { 652fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> {
606 chalk_ir::Binders { 653 chalk_ir::Binders::new(
654 std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars).collect(),
607 value, 655 value,
608 binders: std::iter::repeat(chalk_ir::ParameterKind::Ty(())).take(num_vars).collect(), 656 )
609 }
610} 657}
611 658
612fn convert_where_clauses( 659fn convert_where_clauses(
@@ -626,6 +673,55 @@ fn convert_where_clauses(
626 result 673 result
627} 674}
628 675
676fn generic_predicate_to_inline_bound(
677 db: &dyn HirDatabase,
678 pred: &GenericPredicate,
679 self_ty: &Ty,
680) -> Option<chalk_rust_ir::InlineBound<Interner>> {
681 // An InlineBound is like a GenericPredicate, except the self type is left out.
682 // We don't have a special type for this, but Chalk does.
683 match pred {
684 GenericPredicate::Implemented(trait_ref) => {
685 if &trait_ref.substs[0] != self_ty {
686 // we can only convert predicates back to type bounds if they
687 // have the expected self type
688 return None;
689 }
690 let args_no_self = trait_ref.substs[1..]
691 .iter()
692 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
693 .collect();
694 let trait_bound =
695 chalk_rust_ir::TraitBound { trait_id: trait_ref.trait_.to_chalk(db), args_no_self };
696 Some(chalk_rust_ir::InlineBound::TraitBound(trait_bound))
697 }
698 GenericPredicate::Projection(proj) => {
699 if &proj.projection_ty.parameters[0] != self_ty {
700 return None;
701 }
702 let trait_ = match proj.projection_ty.associated_ty.lookup(db.upcast()).container {
703 AssocContainerId::TraitId(t) => t,
704 _ => panic!("associated type not in trait"),
705 };
706 let args_no_self = proj.projection_ty.parameters[1..]
707 .iter()
708 .map(|ty| ty.clone().to_chalk(db).cast(&Interner))
709 .collect();
710 let alias_eq_bound = chalk_rust_ir::AliasEqBound {
711 value: proj.ty.clone().to_chalk(db),
712 trait_bound: chalk_rust_ir::TraitBound {
713 trait_id: trait_.to_chalk(db),
714 args_no_self,
715 },
716 associated_ty_id: proj.projection_ty.associated_ty.to_chalk(db),
717 parameters: Vec::new(), // FIXME we don't support generic associated types yet
718 };
719 Some(chalk_rust_ir::InlineBound::AliasEqBound(alias_eq_bound))
720 }
721 GenericPredicate::Error => None,
722 }
723}
724
629impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { 725impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
630 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> { 726 fn associated_ty_data(&self, id: AssocTypeId) -> Arc<AssociatedTyDatum> {
631 self.db.associated_ty_data(id) 727 self.db.associated_ty_data(id)
@@ -647,19 +743,22 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
647 debug!("impls_for_trait {:?}", trait_id); 743 debug!("impls_for_trait {:?}", trait_id);
648 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id); 744 let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
649 745
746 let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
747
748 let self_ty_fp = TyFingerprint::for_impl(&ty);
749
650 // Note: Since we're using impls_for_trait, only impls where the trait 750 // Note: Since we're using impls_for_trait, only impls where the trait
651 // can be resolved should ever reach Chalk. `impl_datum` relies on that 751 // can be resolved should ever reach Chalk. `impl_datum` relies on that
652 // and will panic if the trait can't be resolved. 752 // and will panic if the trait can't be resolved.
653 let mut result: Vec<_> = self 753 let mut result: Vec<_> = self
654 .db 754 .db
655 .impls_for_trait(self.krate, trait_) 755 .impls_for_trait(self.krate, trait_, self_ty_fp)
656 .iter() 756 .iter()
657 .copied() 757 .copied()
658 .map(Impl::ImplDef) 758 .map(Impl::ImplDef)
659 .map(|impl_| impl_.to_chalk(self.db)) 759 .map(|impl_| impl_.to_chalk(self.db))
660 .collect(); 760 .collect();
661 761
662 let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
663 let arg: Option<Ty> = 762 let arg: Option<Ty> =
664 parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone())); 763 parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone()));
665 764
@@ -693,6 +792,12 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
693 fn interner(&self) -> &Interner { 792 fn interner(&self) -> &Interner {
694 &Interner 793 &Interner
695 } 794 }
795 fn well_known_trait_id(
796 &self,
797 _well_known_trait: chalk_rust_ir::WellKnownTrait,
798 ) -> chalk_ir::TraitId<Interner> {
799 unimplemented!()
800 }
696} 801}
697 802
698pub(crate) fn associated_ty_data_query( 803pub(crate) fn associated_ty_data_query(
@@ -705,12 +810,25 @@ pub(crate) fn associated_ty_data_query(
705 AssocContainerId::TraitId(t) => t, 810 AssocContainerId::TraitId(t) => t,
706 _ => panic!("associated type not in trait"), 811 _ => panic!("associated type not in trait"),
707 }; 812 };
813
814 // Lower bounds -- we could/should maybe move this to a separate query in `lower`
815 let type_alias_data = db.type_alias_data(type_alias);
708 let generic_params = generics(db.upcast(), type_alias.into()); 816 let generic_params = generics(db.upcast(), type_alias.into());
709 let bound_data = chalk_rust_ir::AssociatedTyDatumBound { 817 let bound_vars = Substs::bound_vars(&generic_params);
710 // FIXME add bounds and where clauses 818 let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db.upcast());
711 bounds: vec![], 819 let ctx = crate::TyLoweringContext::new(db, &resolver)
712 where_clauses: vec![], 820 .with_type_param_mode(crate::lower::TypeParamLoweringMode::Variable);
713 }; 821 let self_ty = Ty::Bound(crate::BoundVar::new(crate::DebruijnIndex::INNERMOST, 0));
822 let bounds = type_alias_data
823 .bounds
824 .iter()
825 .flat_map(|bound| GenericPredicate::from_type_bound(&ctx, bound, self_ty.clone()))
826 .filter_map(|pred| generic_predicate_to_inline_bound(db, &pred, &self_ty))
827 .map(|bound| make_binders(bound.shifted_in(&Interner), 0))
828 .collect();
829
830 let where_clauses = convert_where_clauses(db, type_alias.into(), &bound_vars);
831 let bound_data = chalk_rust_ir::AssociatedTyDatumBound { bounds, where_clauses };
714 let datum = AssociatedTyDatum { 832 let datum = AssociatedTyDatum {
715 trait_id: trait_.to_chalk(db), 833 trait_id: trait_.to_chalk(db),
716 id, 834 id,
diff --git a/crates/ra_hir_ty/src/traits/chalk/tls.rs b/crates/ra_hir_ty/src/traits/chalk/tls.rs
index d9bbb54a5..fa8e4d1ad 100644
--- a/crates/ra_hir_ty/src/traits/chalk/tls.rs
+++ b/crates/ra_hir_ty/src/traits/chalk/tls.rs
@@ -2,10 +2,11 @@
2use std::fmt; 2use std::fmt;
3 3
4use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName}; 4use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName};
5use itertools::Itertools;
5 6
6use super::{from_chalk, Interner}; 7use super::{from_chalk, Interner};
7use crate::{db::HirDatabase, CallableDef, TypeCtor}; 8use crate::{db::HirDatabase, CallableDef, TypeCtor};
8use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; 9use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId};
9 10
10pub use unsafe_tls::{set_current_program, with_current_program}; 11pub use unsafe_tls::{set_current_program, with_current_program};
11 12
@@ -69,7 +70,27 @@ impl DebugContext<'_> {
69 write!(f, "{}::{}", trait_name, name)?; 70 write!(f, "{}::{}", trait_name, name)?;
70 } 71 }
71 TypeCtor::Closure { def, expr } => { 72 TypeCtor::Closure { def, expr } => {
72 write!(f, "{{closure {:?} in {:?}}}", expr.into_raw(), def)?; 73 write!(f, "{{closure {:?} in ", expr.into_raw())?;
74 match def {
75 DefWithBodyId::FunctionId(func) => {
76 write!(f, "fn {}", self.0.function_data(func).name)?
77 }
78 DefWithBodyId::StaticId(s) => {
79 if let Some(name) = self.0.static_data(s).name.as_ref() {
80 write!(f, "body of static {}", name)?;
81 } else {
82 write!(f, "body of unnamed static {:?}", s)?;
83 }
84 }
85 DefWithBodyId::ConstId(c) => {
86 if let Some(name) = self.0.const_data(c).name.as_ref() {
87 write!(f, "body of const {}", name)?;
88 } else {
89 write!(f, "body of unnamed const {:?}", c)?;
90 }
91 }
92 };
93 write!(f, "}}")?;
73 } 94 }
74 } 95 }
75 Ok(()) 96 Ok(())
@@ -113,14 +134,15 @@ impl DebugContext<'_> {
113 }; 134 };
114 let trait_data = self.0.trait_data(trait_); 135 let trait_data = self.0.trait_data(trait_);
115 let params = alias.substitution.parameters(&Interner); 136 let params = alias.substitution.parameters(&Interner);
116 write!( 137 write!(fmt, "<{:?} as {}", &params[0], trait_data.name,)?;
117 fmt, 138 if params.len() > 1 {
118 "<{:?} as {}<{:?}>>::{}", 139 write!(
119 &params[0], 140 fmt,
120 trait_data.name, 141 "<{}>",
121 &params[1..], 142 &params[1..].iter().format_with(", ", |x, f| f(&format_args!("{:?}", x))),
122 type_alias_data.name 143 )?;
123 ) 144 }
145 write!(fmt, ">::{}", type_alias_data.name)
124 } 146 }
125 147
126 pub fn debug_ty( 148 pub fn debug_ty(
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index b4a29b81b..05c940605 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -35,4 +35,4 @@ ra_assists = { path = "../ra_assists" }
35hir = { path = "../ra_hir", package = "ra_hir" } 35hir = { path = "../ra_hir", package = "ra_hir" }
36 36
37[dev-dependencies] 37[dev-dependencies]
38insta = "0.15.0" 38insta = "0.16.0"
diff --git a/crates/ra_ide/src/call_info.rs b/crates/ra_ide/src/call_info.rs
index ca57eceff..f95b6baf3 100644
--- a/crates/ra_ide/src/call_info.rs
+++ b/crates/ra_ide/src/call_info.rs
@@ -208,9 +208,20 @@ mod tests {
208 } 208 }
209 } 209 }
210 210
211 fn call_info(text: &str) -> CallInfo { 211 fn call_info_helper(text: &str) -> Option<CallInfo> {
212 let (analysis, position) = single_file_with_position(text); 212 let (analysis, position) = single_file_with_position(text);
213 analysis.call_info(position).unwrap().unwrap() 213 analysis.call_info(position).unwrap()
214 }
215
216 fn call_info(text: &str) -> CallInfo {
217 let info = call_info_helper(text);
218 assert!(info.is_some());
219 info.unwrap()
220 }
221
222 fn no_call_info(text: &str) {
223 let info = call_info_helper(text);
224 assert!(info.is_none());
214 } 225 }
215 226
216 #[test] 227 #[test]
@@ -558,9 +569,8 @@ fn main() {
558 } 569 }
559 570
560 #[test] 571 #[test]
561 #[should_panic]
562 fn cant_call_named_structs() { 572 fn cant_call_named_structs() {
563 let _ = call_info( 573 no_call_info(
564 r#" 574 r#"
565struct TS { x: u32, y: i32 } 575struct TS { x: u32, y: i32 }
566fn main() { 576fn main() {
@@ -594,9 +604,8 @@ fn main() {
594 } 604 }
595 605
596 #[test] 606 #[test]
597 #[should_panic]
598 fn cant_call_enum_records() { 607 fn cant_call_enum_records() {
599 let _ = call_info( 608 no_call_info(
600 r#" 609 r#"
601enum E { 610enum E {
602 /// A Variant 611 /// A Variant
diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs
index 1b7d3122f..a8b4ce114 100644
--- a/crates/ra_ide/src/completion/complete_pattern.rs
+++ b/crates/ra_ide/src/completion/complete_pattern.rs
@@ -7,6 +7,10 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
7 if !ctx.is_pat_binding_or_const { 7 if !ctx.is_pat_binding_or_const {
8 return; 8 return;
9 } 9 }
10 if ctx.record_pat_syntax.is_some() {
11 return;
12 }
13
10 // FIXME: ideally, we should look at the type we are matching against and 14 // FIXME: ideally, we should look at the type we are matching against and
11 // suggest variants + auto-imports 15 // suggest variants + auto-imports
12 ctx.scope().process_all_names(&mut |name, res| { 16 ctx.scope().process_all_names(&mut |name, res| {
diff --git a/crates/ra_ide/src/completion/complete_record.rs b/crates/ra_ide/src/completion/complete_record.rs
index f46bcee5c..83a553155 100644
--- a/crates/ra_ide/src/completion/complete_record.rs
+++ b/crates/ra_ide/src/completion/complete_record.rs
@@ -2,7 +2,7 @@
2use crate::completion::{CompletionContext, Completions}; 2use crate::completion::{CompletionContext, Completions};
3 3
4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 4pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
5 let missing_fields = match (ctx.record_lit_pat.as_ref(), ctx.record_lit_syntax.as_ref()) { 5 let missing_fields = match (ctx.record_pat_syntax.as_ref(), ctx.record_lit_syntax.as_ref()) {
6 (None, None) => return None, 6 (None, None) => return None,
7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"), 7 (Some(_), Some(_)) => unreachable!("A record cannot be both a literal and a pattern"),
8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat), 8 (Some(record_pat), _) => ctx.sema.record_pattern_missing_fields(record_pat),
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index efde9bf73..2d8e0776c 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -3,7 +3,14 @@
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4 4
5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 5pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
6 if !(ctx.is_trivial_path && !ctx.is_pat_binding_or_const) { 6 if !ctx.is_trivial_path {
7 return;
8 }
9
10 if ctx.is_pat_binding_or_const
11 || ctx.record_lit_syntax.is_some()
12 || ctx.record_pat_syntax.is_some()
13 {
7 return; 14 return;
8 } 15 }
9 16
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 6637afaf7..8b3401595 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -30,7 +30,7 @@ pub(crate) struct CompletionContext<'a> {
30 pub(super) function_syntax: Option<ast::FnDef>, 30 pub(super) function_syntax: Option<ast::FnDef>,
31 pub(super) use_item_syntax: Option<ast::UseItem>, 31 pub(super) use_item_syntax: Option<ast::UseItem>,
32 pub(super) record_lit_syntax: Option<ast::RecordLit>, 32 pub(super) record_lit_syntax: Option<ast::RecordLit>,
33 pub(super) record_lit_pat: Option<ast::RecordPat>, 33 pub(super) record_pat_syntax: Option<ast::RecordPat>,
34 pub(super) impl_def: Option<ast::ImplDef>, 34 pub(super) impl_def: Option<ast::ImplDef>,
35 pub(super) is_param: bool, 35 pub(super) is_param: bool,
36 /// If a name-binding or reference to a const in a pattern. 36 /// If a name-binding or reference to a const in a pattern.
@@ -93,7 +93,7 @@ impl<'a> CompletionContext<'a> {
93 function_syntax: None, 93 function_syntax: None,
94 use_item_syntax: None, 94 use_item_syntax: None,
95 record_lit_syntax: None, 95 record_lit_syntax: None,
96 record_lit_pat: None, 96 record_pat_syntax: None,
97 impl_def: None, 97 impl_def: None,
98 is_param: false, 98 is_param: false,
99 is_pat_binding_or_const: false, 99 is_pat_binding_or_const: false,
@@ -182,6 +182,11 @@ impl<'a> CompletionContext<'a> {
182 self.is_param = true; 182 self.is_param = true;
183 return; 183 return;
184 } 184 }
185 // FIXME: remove this (V) duplication and make the check more precise
186 if name_ref.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
187 self.record_pat_syntax =
188 self.sema.find_node_at_offset_with_macros(&original_file, offset);
189 }
185 self.classify_name_ref(original_file, name_ref, offset); 190 self.classify_name_ref(original_file, name_ref, offset);
186 } 191 }
187 192
@@ -211,8 +216,9 @@ impl<'a> CompletionContext<'a> {
211 self.is_param = true; 216 self.is_param = true;
212 return; 217 return;
213 } 218 }
219 // FIXME: remove this (^) duplication and make the check more precise
214 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { 220 if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() {
215 self.record_lit_pat = 221 self.record_pat_syntax =
216 self.sema.find_node_at_offset_with_macros(&original_file, offset); 222 self.sema.find_node_at_offset_with_macros(&original_file, offset);
217 } 223 }
218 } 224 }
@@ -227,7 +233,7 @@ impl<'a> CompletionContext<'a> {
227 self.name_ref_syntax = 233 self.name_ref_syntax =
228 find_node_at_offset(&original_file, name_ref.syntax().text_range().start()); 234 find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
229 let name_range = name_ref.syntax().text_range(); 235 let name_range = name_ref.syntax().text_range();
230 if name_ref.syntax().parent().and_then(ast::RecordField::cast).is_some() { 236 if ast::RecordField::for_field_name(&name_ref).is_some() {
231 self.record_lit_syntax = 237 self.record_lit_syntax =
232 self.sema.find_node_at_offset_with_macros(&original_file, offset); 238 self.sema.find_node_at_offset_with_macros(&original_file, offset);
233 } 239 }
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index da9f55a69..45b9f7802 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -235,7 +235,10 @@ fn should_show_param_hint(
235 param_name: &str, 235 param_name: &str,
236 argument: &ast::Expr, 236 argument: &ast::Expr,
237) -> bool { 237) -> bool {
238 if param_name.is_empty() || is_argument_similar_to_param(argument, param_name) { 238 if param_name.is_empty()
239 || is_argument_similar_to_param(argument, param_name)
240 || Some(param_name) == fn_signature.name.as_ref().map(String::as_str)
241 {
239 return false; 242 return false;
240 } 243 }
241 244
@@ -247,10 +250,7 @@ fn should_show_param_hint(
247 250
248 // avoid displaying hints for common functions like map, filter, etc. 251 // avoid displaying hints for common functions like map, filter, etc.
249 // or other obvious words used in std 252 // or other obvious words used in std
250 if parameters_len == 1 && is_obvious_param(param_name) { 253 parameters_len != 1 || !is_obvious_param(param_name)
251 return false;
252 }
253 true
254} 254}
255 255
256fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { 256fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool {
@@ -1086,6 +1086,8 @@ impl Test {
1086 } 1086 }
1087 1087
1088 fn no_hints_expected(&self, _: i32, test_var: i32) {} 1088 fn no_hints_expected(&self, _: i32, test_var: i32) {}
1089
1090 fn frob(&self, frob: bool) {}
1089} 1091}
1090 1092
1091struct Param {} 1093struct Param {}
@@ -1093,6 +1095,8 @@ struct Param {}
1093fn different_order(param: &Param) {} 1095fn different_order(param: &Param) {}
1094fn different_order_mut(param: &mut Param) {} 1096fn different_order_mut(param: &mut Param) {}
1095 1097
1098fn twiddle(twiddle: bool) {}
1099
1096fn main() { 1100fn main() {
1097 let container: TestVarContainer = TestVarContainer { test_var: 42 }; 1101 let container: TestVarContainer = TestVarContainer { test_var: 42 };
1098 let test: Test = Test {}; 1102 let test: Test = Test {};
@@ -1105,6 +1109,9 @@ fn main() {
1105 let test_var: i32 = 55; 1109 let test_var: i32 = 55;
1106 test_processed.no_hints_expected(22, test_var); 1110 test_processed.no_hints_expected(22, test_var);
1107 test_processed.no_hints_expected(33, container.test_var); 1111 test_processed.no_hints_expected(33, container.test_var);
1112 test_processed.frob(false);
1113
1114 twiddle(true);
1108 1115
1109 let param_begin: Param = Param {}; 1116 let param_begin: Param = Param {};
1110 different_order(&param_begin); 1117 different_order(&param_begin);
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index e9934844e..49a8c74fb 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -216,7 +216,7 @@ pub fn classify_name_ref(
216 } 216 }
217 } 217 }
218 218
219 if let Some(record_field) = ast::RecordField::cast(parent.clone()) { 219 if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
220 tested_by!(goto_def_for_record_fields; force); 220 tested_by!(goto_def_for_record_fields; force);
221 tested_by!(goto_def_for_field_init_shorthand; force); 221 tested_by!(goto_def_for_field_init_shorthand; force);
222 if let Some((field, local)) = sema.resolve_record_field(&record_field) { 222 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs
index d0530955e..c2a6e82e9 100644
--- a/crates/ra_parser/src/grammar.rs
+++ b/crates/ra_parser/src/grammar.rs
@@ -282,13 +282,10 @@ fn name_ref(p: &mut Parser) {
282} 282}
283 283
284fn name_ref_or_index(p: &mut Parser) { 284fn name_ref_or_index(p: &mut Parser) {
285 if p.at(IDENT) || p.at(INT_NUMBER) { 285 assert!(p.at(IDENT) || p.at(INT_NUMBER));
286 let m = p.start(); 286 let m = p.start();
287 p.bump_any(); 287 p.bump_any();
288 m.complete(p, NAME_REF); 288 m.complete(p, NAME_REF);
289 } else {
290 p.err_and_bump("expected identifier");
291 }
292} 289}
293 290
294fn error_block(p: &mut Parser, message: &str) { 291fn error_block(p: &mut Parser, message: &str) {
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index a1bd53063..cb30b25a8 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -619,26 +619,39 @@ pub(crate) fn record_field_list(p: &mut Parser) {
619 let m = p.start(); 619 let m = p.start();
620 p.bump(T!['{']); 620 p.bump(T!['{']);
621 while !p.at(EOF) && !p.at(T!['}']) { 621 while !p.at(EOF) && !p.at(T!['}']) {
622 let m = p.start();
623 // test record_literal_field_with_attr
624 // fn main() {
625 // S { #[cfg(test)] field: 1 }
626 // }
627 attributes::outer_attributes(p);
628
622 match p.current() { 629 match p.current() {
623 // test record_literal_field_with_attr 630 IDENT | INT_NUMBER => {
624 // fn main() { 631 // test_err record_literal_before_ellipsis_recovery
625 // S { #[cfg(test)] field: 1 } 632 // fn main() {
626 // } 633 // S { field ..S::default() }
627 IDENT | INT_NUMBER | T![#] => { 634 // }
628 let m = p.start(); 635 if p.nth_at(1, T![:]) || p.nth_at(1, T![..]) {
629 attributes::outer_attributes(p); 636 name_ref_or_index(p);
630 name_ref_or_index(p); 637 p.expect(T![:]);
631 if p.eat(T![:]) {
632 expr(p);
633 } 638 }
639 expr(p);
634 m.complete(p, RECORD_FIELD); 640 m.complete(p, RECORD_FIELD);
635 } 641 }
636 T![.] if p.at(T![..]) => { 642 T![.] if p.at(T![..]) => {
643 m.abandon(p);
637 p.bump(T![..]); 644 p.bump(T![..]);
638 expr(p); 645 expr(p);
639 } 646 }
640 T!['{'] => error_block(p, "expected a field"), 647 T!['{'] => {
641 _ => p.err_and_bump("expected identifier"), 648 error_block(p, "expected a field");
649 m.abandon(p);
650 }
651 _ => {
652 p.err_and_bump("expected identifier");
653 m.abandon(p);
654 }
642 } 655 }
643 if !p.at(T!['}']) { 656 if !p.at(T!['}']) {
644 p.expect(T![,]); 657 p.expect(T![,]);
diff --git a/crates/ra_parser/src/grammar/patterns.rs b/crates/ra_parser/src/grammar/patterns.rs
index 936d27575..68fb2fc73 100644
--- a/crates/ra_parser/src/grammar/patterns.rs
+++ b/crates/ra_parser/src/grammar/patterns.rs
@@ -192,14 +192,30 @@ fn record_field_pat_list(p: &mut Parser) {
192 match p.current() { 192 match p.current() {
193 // A trailing `..` is *not* treated as a DOT_DOT_PAT. 193 // A trailing `..` is *not* treated as a DOT_DOT_PAT.
194 T![.] if p.at(T![..]) => p.bump(T![..]), 194 T![.] if p.at(T![..]) => p.bump(T![..]),
195
196 IDENT | INT_NUMBER if p.nth(1) == T![:] => record_field_pat(p),
197 T!['{'] => error_block(p, "expected ident"), 195 T!['{'] => error_block(p, "expected ident"),
198 T![box] => { 196
199 box_pat(p); 197 c => {
200 } 198 let m = p.start();
201 _ => { 199 match c {
202 bind_pat(p, false); 200 // test record_field_pat
201 // fn foo() {
202 // let S { 0: 1 } = ();
203 // let S { x: 1 } = ();
204 // }
205 IDENT | INT_NUMBER if p.nth(1) == T![:] => {
206 name_ref_or_index(p);
207 p.bump(T![:]);
208 pattern(p);
209 }
210 T![box] => {
211 // FIXME: not all box patterns should be allowed
212 box_pat(p);
213 }
214 _ => {
215 bind_pat(p, false);
216 }
217 }
218 m.complete(p, RECORD_FIELD_PAT);
203 } 219 }
204 } 220 }
205 if !p.at(T!['}']) { 221 if !p.at(T!['}']) {
@@ -210,26 +226,6 @@ fn record_field_pat_list(p: &mut Parser) {
210 m.complete(p, RECORD_FIELD_PAT_LIST); 226 m.complete(p, RECORD_FIELD_PAT_LIST);
211} 227}
212 228
213// test record_field_pat
214// fn foo() {
215// let S { 0: 1 } = ();
216// let S { x: 1 } = ();
217// }
218fn record_field_pat(p: &mut Parser) {
219 assert!(p.at(IDENT) || p.at(INT_NUMBER));
220 assert!(p.nth(1) == T![:]);
221
222 let m = p.start();
223
224 if !p.eat(INT_NUMBER) {
225 name(p)
226 }
227
228 p.bump_any();
229 pattern(p);
230 m.complete(p, RECORD_FIELD_PAT);
231}
232
233// test placeholder_pat 229// test placeholder_pat
234// fn main() { let _ = (); } 230// fn main() { let _ = (); }
235fn placeholder_pat(p: &mut Parser) -> CompletedMarker { 231fn placeholder_pat(p: &mut Parser) -> CompletedMarker {
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 99c6b7219..7fca5661e 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -18,8 +18,8 @@ use crate::{
18pub use self::{ 18pub use self::{
19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp}, 19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
20 extensions::{ 20 extensions::{
21 AttrKind, FieldKind, PathSegmentKind, SelfParamKind, SlicePatComponents, StructKind, 21 AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents,
22 TypeBoundKind, VisibilityKind, 22 StructKind, TypeBoundKind, VisibilityKind,
23 }, 23 },
24 generated::{nodes::*, tokens::*}, 24 generated::{nodes::*, tokens::*},
25 tokens::*, 25 tokens::*,
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs
index 76b7655e6..f2ea5088e 100644
--- a/crates/ra_syntax/src/ast/extensions.rs
+++ b/crates/ra_syntax/src/ast/extensions.rs
@@ -1,6 +1,8 @@
1//! Various extension methods to ast Nodes, which are hard to code-generate. 1//! Various extension methods to ast Nodes, which are hard to code-generate.
2//! Extensions for various expressions live in a sibling `expr_extensions` module. 2//! Extensions for various expressions live in a sibling `expr_extensions` module.
3 3
4use std::fmt;
5
4use itertools::Itertools; 6use itertools::Itertools;
5use ra_parser::SyntaxKind; 7use ra_parser::SyntaxKind;
6 8
@@ -187,6 +189,64 @@ impl ast::StructDef {
187 } 189 }
188} 190}
189 191
192impl ast::RecordField {
193 pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordField> {
194 let candidate =
195 field_name.syntax().parent().and_then(ast::RecordField::cast).or_else(|| {
196 field_name.syntax().ancestors().nth(4).and_then(ast::RecordField::cast)
197 })?;
198 if candidate.field_name().as_ref() == Some(field_name) {
199 Some(candidate)
200 } else {
201 None
202 }
203 }
204
205 /// Deals with field init shorthand
206 pub fn field_name(&self) -> Option<ast::NameRef> {
207 if let Some(name_ref) = self.name_ref() {
208 return Some(name_ref);
209 }
210 if let Some(ast::Expr::PathExpr(expr)) = self.expr() {
211 let path = expr.path()?;
212 let segment = path.segment()?;
213 let name_ref = segment.name_ref()?;
214 if path.qualifier().is_none() {
215 return Some(name_ref);
216 }
217 }
218 None
219 }
220}
221
222pub enum NameOrNameRef {
223 Name(ast::Name),
224 NameRef(ast::NameRef),
225}
226
227impl fmt::Display for NameOrNameRef {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 match self {
230 NameOrNameRef::Name(it) => fmt::Display::fmt(it, f),
231 NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f),
232 }
233 }
234}
235
236impl ast::RecordFieldPat {
237 /// Deals with field init shorthand
238 pub fn field_name(&self) -> Option<NameOrNameRef> {
239 if let Some(name_ref) = self.name_ref() {
240 return Some(NameOrNameRef::NameRef(name_ref));
241 }
242 if let Some(ast::Pat::BindPat(pat)) = self.pat() {
243 let name = pat.name()?;
244 return Some(NameOrNameRef::Name(name));
245 }
246 None
247 }
248}
249
190impl ast::EnumVariant { 250impl ast::EnumVariant {
191 pub fn parent_enum(&self) -> ast::EnumDef { 251 pub fn parent_enum(&self) -> ast::EnumDef {
192 self.syntax() 252 self.syntax()
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index f1098755b..188f0df96 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -1806,8 +1806,8 @@ impl AstNode for RecordFieldPat {
1806 fn syntax(&self) -> &SyntaxNode { &self.syntax } 1806 fn syntax(&self) -> &SyntaxNode { &self.syntax }
1807} 1807}
1808impl ast::AttrsOwner for RecordFieldPat {} 1808impl ast::AttrsOwner for RecordFieldPat {}
1809impl ast::NameOwner for RecordFieldPat {}
1810impl RecordFieldPat { 1809impl RecordFieldPat {
1810 pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
1811 pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) } 1811 pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
1812 pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) } 1812 pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
1813} 1813}
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index f39559e9e..0f4a50be4 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -100,6 +100,9 @@ pub fn expr_empty_block() -> ast::Expr {
100pub fn expr_unimplemented() -> ast::Expr { 100pub fn expr_unimplemented() -> ast::Expr {
101 expr_from_text("unimplemented!()") 101 expr_from_text("unimplemented!()")
102} 102}
103pub fn expr_todo() -> ast::Expr {
104 expr_from_text("todo!()")
105}
103pub fn expr_path(path: ast::Path) -> ast::Expr { 106pub fn expr_path(path: ast::Path) -> ast::Expr {
104 expr_from_text(&path.to_string()) 107 expr_from_text(&path.to_string())
105} 108}
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index f0e16dc2b..a796e78b1 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -19,6 +19,11 @@
19//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256> 19//! [RFC]: <https://github.com/rust-lang/rfcs/pull/2256>
20//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md> 20//! [Swift]: <https://github.com/apple/swift/blob/13d593df6f359d0cb2fc81cfaac273297c539455/lib/Syntax/README.md>
21 21
22#[allow(unused)]
23macro_rules! eprintln {
24 ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
25}
26
22mod syntax_node; 27mod syntax_node;
23mod syntax_error; 28mod syntax_error;
24mod parsing; 29mod parsing;
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast
new file mode 100644
index 000000000..75043c9c0
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rast
@@ -0,0 +1,49 @@
1SOURCE_FILE@[0; 45)
2 FN_DEF@[0; 44)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK_EXPR@[10; 44)
12 BLOCK@[10; 44)
13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 16) "\n "
15 RECORD_LIT@[16; 42)
16 PATH@[16; 17)
17 PATH_SEGMENT@[16; 17)
18 NAME_REF@[16; 17)
19 IDENT@[16; 17) "S"
20 WHITESPACE@[17; 18) " "
21 RECORD_FIELD_LIST@[18; 42)
22 L_CURLY@[18; 19) "{"
23 WHITESPACE@[19; 20) " "
24 RECORD_FIELD@[20; 40)
25 NAME_REF@[20; 25)
26 IDENT@[20; 25) "field"
27 WHITESPACE@[25; 26) " "
28 RANGE_EXPR@[26; 40)
29 DOT2@[26; 28) ".."
30 CALL_EXPR@[28; 40)
31 PATH_EXPR@[28; 38)
32 PATH@[28; 38)
33 PATH@[28; 29)
34 PATH_SEGMENT@[28; 29)
35 NAME_REF@[28; 29)
36 IDENT@[28; 29) "S"
37 COLON2@[29; 31) "::"
38 PATH_SEGMENT@[31; 38)
39 NAME_REF@[31; 38)
40 IDENT@[31; 38) "default"
41 ARG_LIST@[38; 40)
42 L_PAREN@[38; 39) "("
43 R_PAREN@[39; 40) ")"
44 WHITESPACE@[40; 41) " "
45 R_CURLY@[41; 42) "}"
46 WHITESPACE@[42; 43) "\n"
47 R_CURLY@[43; 44) "}"
48 WHITESPACE@[44; 45) "\n"
49error [25; 25): expected COLON
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs
new file mode 100644
index 000000000..a4e5b2f69
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/err/0014_record_literal_before_ellipsis_recovery.rs
@@ -0,0 +1,3 @@
1fn main() {
2 S { field ..S::default() }
3}
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast b/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast
index f4206858b..89a611799 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0061_record_lit.rast
@@ -35,8 +35,11 @@ SOURCE_FILE@[0; 112)
35 L_CURLY@[27; 28) "{" 35 L_CURLY@[27; 28) "{"
36 WHITESPACE@[28; 29) " " 36 WHITESPACE@[28; 29) " "
37 RECORD_FIELD@[29; 30) 37 RECORD_FIELD@[29; 30)
38 NAME_REF@[29; 30) 38 PATH_EXPR@[29; 30)
39 IDENT@[29; 30) "x" 39 PATH@[29; 30)
40 PATH_SEGMENT@[29; 30)
41 NAME_REF@[29; 30)
42 IDENT@[29; 30) "x"
40 COMMA@[30; 31) "," 43 COMMA@[30; 31) ","
41 WHITESPACE@[31; 32) " " 44 WHITESPACE@[31; 32) " "
42 RECORD_FIELD@[32; 37) 45 RECORD_FIELD@[32; 37)
@@ -62,8 +65,11 @@ SOURCE_FILE@[0; 112)
62 L_CURLY@[48; 49) "{" 65 L_CURLY@[48; 49) "{"
63 WHITESPACE@[49; 50) " " 66 WHITESPACE@[49; 50) " "
64 RECORD_FIELD@[50; 51) 67 RECORD_FIELD@[50; 51)
65 NAME_REF@[50; 51) 68 PATH_EXPR@[50; 51)
66 IDENT@[50; 51) "x" 69 PATH@[50; 51)
70 PATH_SEGMENT@[50; 51)
71 NAME_REF@[50; 51)
72 IDENT@[50; 51) "x"
67 COMMA@[51; 52) "," 73 COMMA@[51; 52) ","
68 WHITESPACE@[52; 53) " " 74 WHITESPACE@[52; 53) " "
69 RECORD_FIELD@[53; 58) 75 RECORD_FIELD@[53; 58)
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast b/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast
index c2614543c..fcd099de9 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0102_record_field_pat_list.rast
@@ -44,18 +44,20 @@ SOURCE_FILE@[0; 119)
44 RECORD_FIELD_PAT_LIST@[40; 56) 44 RECORD_FIELD_PAT_LIST@[40; 56)
45 L_CURLY@[40; 41) "{" 45 L_CURLY@[40; 41) "{"
46 WHITESPACE@[41; 42) " " 46 WHITESPACE@[41; 42) " "
47 BIND_PAT@[42; 43) 47 RECORD_FIELD_PAT@[42; 43)
48 NAME@[42; 43) 48 BIND_PAT@[42; 43)
49 IDENT@[42; 43) "f" 49 NAME@[42; 43)
50 IDENT@[42; 43) "f"
50 COMMA@[43; 44) "," 51 COMMA@[43; 44) ","
51 WHITESPACE@[44; 45) " " 52 WHITESPACE@[44; 45) " "
52 BIND_PAT@[45; 54) 53 RECORD_FIELD_PAT@[45; 54)
53 REF_KW@[45; 48) "ref" 54 BIND_PAT@[45; 54)
54 WHITESPACE@[48; 49) " " 55 REF_KW@[45; 48) "ref"
55 MUT_KW@[49; 52) "mut" 56 WHITESPACE@[48; 49) " "
56 WHITESPACE@[52; 53) " " 57 MUT_KW@[49; 52) "mut"
57 NAME@[53; 54) 58 WHITESPACE@[52; 53) " "
58 IDENT@[53; 54) "g" 59 NAME@[53; 54)
60 IDENT@[53; 54) "g"
59 WHITESPACE@[54; 55) " " 61 WHITESPACE@[54; 55) " "
60 R_CURLY@[55; 56) "}" 62 R_CURLY@[55; 56) "}"
61 WHITESPACE@[56; 57) " " 63 WHITESPACE@[56; 57) " "
@@ -79,7 +81,7 @@ SOURCE_FILE@[0; 119)
79 L_CURLY@[73; 74) "{" 81 L_CURLY@[73; 74) "{"
80 WHITESPACE@[74; 75) " " 82 WHITESPACE@[74; 75) " "
81 RECORD_FIELD_PAT@[75; 79) 83 RECORD_FIELD_PAT@[75; 79)
82 NAME@[75; 76) 84 NAME_REF@[75; 76)
83 IDENT@[75; 76) "h" 85 IDENT@[75; 76) "h"
84 COLON@[76; 77) ":" 86 COLON@[76; 77) ":"
85 WHITESPACE@[77; 78) " " 87 WHITESPACE@[77; 78) " "
@@ -110,7 +112,7 @@ SOURCE_FILE@[0; 119)
110 L_CURLY@[101; 102) "{" 112 L_CURLY@[101; 102) "{"
111 WHITESPACE@[102; 103) " " 113 WHITESPACE@[102; 103) " "
112 RECORD_FIELD_PAT@[103; 107) 114 RECORD_FIELD_PAT@[103; 107)
113 NAME@[103; 104) 115 NAME_REF@[103; 104)
114 IDENT@[103; 104) "h" 116 IDENT@[103; 104) "h"
115 COLON@[104; 105) ":" 117 COLON@[104; 105) ":"
116 WHITESPACE@[105; 106) " " 118 WHITESPACE@[105; 106) " "
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast
index f75673070..1d245f8f3 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0143_box_pat.rast
@@ -44,16 +44,17 @@ SOURCE_FILE@[0; 118)
44 RECORD_FIELD_PAT_LIST@[50; 81) 44 RECORD_FIELD_PAT_LIST@[50; 81)
45 L_CURLY@[50; 51) "{" 45 L_CURLY@[50; 51) "{"
46 WHITESPACE@[51; 52) " " 46 WHITESPACE@[51; 52) " "
47 BOX_PAT@[52; 57) 47 RECORD_FIELD_PAT@[52; 57)
48 BOX_KW@[52; 55) "box" 48 BOX_PAT@[52; 57)
49 WHITESPACE@[55; 56) " " 49 BOX_KW@[52; 55) "box"
50 BIND_PAT@[56; 57) 50 WHITESPACE@[55; 56) " "
51 NAME@[56; 57) 51 BIND_PAT@[56; 57)
52 IDENT@[56; 57) "i" 52 NAME@[56; 57)
53 IDENT@[56; 57) "i"
53 COMMA@[57; 58) "," 54 COMMA@[57; 58) ","
54 WHITESPACE@[58; 59) " " 55 WHITESPACE@[58; 59) " "
55 RECORD_FIELD_PAT@[59; 79) 56 RECORD_FIELD_PAT@[59; 79)
56 NAME@[59; 60) 57 NAME_REF@[59; 60)
57 IDENT@[59; 60) "j" 58 IDENT@[59; 60) "j"
58 COLON@[60; 61) ":" 59 COLON@[60; 61) ":"
59 WHITESPACE@[61; 62) " " 60 WHITESPACE@[61; 62) " "
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast b/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast
index 0d786f597..cac2ffdcf 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0145_record_field_pat.rast
@@ -25,7 +25,8 @@ SOURCE_FILE@[0; 63)
25 L_CURLY@[21; 22) "{" 25 L_CURLY@[21; 22) "{"
26 WHITESPACE@[22; 23) " " 26 WHITESPACE@[22; 23) " "
27 RECORD_FIELD_PAT@[23; 27) 27 RECORD_FIELD_PAT@[23; 27)
28 INT_NUMBER@[23; 24) "0" 28 NAME_REF@[23; 24)
29 INT_NUMBER@[23; 24) "0"
29 COLON@[24; 25) ":" 30 COLON@[24; 25) ":"
30 WHITESPACE@[25; 26) " " 31 WHITESPACE@[25; 26) " "
31 LITERAL_PAT@[26; 27) 32 LITERAL_PAT@[26; 27)
@@ -54,7 +55,7 @@ SOURCE_FILE@[0; 63)
54 L_CURLY@[46; 47) "{" 55 L_CURLY@[46; 47) "{"
55 WHITESPACE@[47; 48) " " 56 WHITESPACE@[47; 48) " "
56 RECORD_FIELD_PAT@[48; 52) 57 RECORD_FIELD_PAT@[48; 52)
57 NAME@[48; 49) 58 NAME_REF@[48; 49)
58 IDENT@[48; 49) "x" 59 IDENT@[48; 49) "x"
59 COLON@[49; 50) ":" 60 COLON@[49; 50) ":"
60 WHITESPACE@[50; 51) " " 61 WHITESPACE@[50; 51) " "
diff --git a/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast
index 9b5954ebd..d0623ba90 100644
--- a/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast
+++ b/crates/ra_syntax/test_data/parser/ok/0063_trait_fn_patterns.rast
@@ -68,14 +68,16 @@ SOURCE_FILE@[0; 170)
68 RECORD_FIELD_PAT_LIST@[59; 67) 68 RECORD_FIELD_PAT_LIST@[59; 67)
69 L_CURLY@[59; 60) "{" 69 L_CURLY@[59; 60) "{"
70 WHITESPACE@[60; 61) " " 70 WHITESPACE@[60; 61) " "
71 BIND_PAT@[61; 62) 71 RECORD_FIELD_PAT@[61; 62)
72 NAME@[61; 62) 72 BIND_PAT@[61; 62)
73 IDENT@[61; 62) "a" 73 NAME@[61; 62)
74 IDENT@[61; 62) "a"
74 COMMA@[62; 63) "," 75 COMMA@[62; 63) ","
75 WHITESPACE@[63; 64) " " 76 WHITESPACE@[63; 64) " "
76 BIND_PAT@[64; 65) 77 RECORD_FIELD_PAT@[64; 65)
77 NAME@[64; 65) 78 BIND_PAT@[64; 65)
78 IDENT@[64; 65) "b" 79 NAME@[64; 65)
80 IDENT@[64; 65) "b"
79 WHITESPACE@[65; 66) " " 81 WHITESPACE@[65; 66) " "
80 R_CURLY@[66; 67) "}" 82 R_CURLY@[66; 67) "}"
81 COLON@[67; 68) ":" 83 COLON@[67; 68) ":"
diff --git a/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast
index b30030de3..5e96b695b 100644
--- a/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast
+++ b/crates/ra_syntax/test_data/parser/ok/0064_impl_fn_params.rast
@@ -71,14 +71,16 @@ SOURCE_FILE@[0; 137)
71 RECORD_FIELD_PAT_LIST@[58; 66) 71 RECORD_FIELD_PAT_LIST@[58; 66)
72 L_CURLY@[58; 59) "{" 72 L_CURLY@[58; 59) "{"
73 WHITESPACE@[59; 60) " " 73 WHITESPACE@[59; 60) " "
74 BIND_PAT@[60; 61) 74 RECORD_FIELD_PAT@[60; 61)
75 NAME@[60; 61) 75 BIND_PAT@[60; 61)
76 IDENT@[60; 61) "a" 76 NAME@[60; 61)
77 IDENT@[60; 61) "a"
77 COMMA@[61; 62) "," 78 COMMA@[61; 62) ","
78 WHITESPACE@[62; 63) " " 79 WHITESPACE@[62; 63) " "
79 BIND_PAT@[63; 64) 80 RECORD_FIELD_PAT@[63; 64)
80 NAME@[63; 64) 81 BIND_PAT@[63; 64)
81 IDENT@[63; 64) "b" 82 NAME@[63; 64)
83 IDENT@[63; 64) "b"
82 WHITESPACE@[64; 65) " " 84 WHITESPACE@[64; 65) " "
83 R_CURLY@[65; 66) "}" 85 R_CURLY@[65; 66) "}"
84 COLON@[66; 67) ":" 86 COLON@[66; 67) ":"
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 3cf394bb4..f5981588a 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -35,6 +35,13 @@ pub(crate) enum Command {
35 what: BenchWhat, 35 what: BenchWhat,
36 load_output_dirs: bool, 36 load_output_dirs: bool,
37 }, 37 },
38 Diagnostics {
39 path: PathBuf,
40 load_output_dirs: bool,
41 /// Include files which are not modules. In rust-analyzer
42 /// this would include the parser test files.
43 all: bool,
44 },
38 RunServer, 45 RunServer,
39 Version, 46 Version,
40} 47}
@@ -209,6 +216,38 @@ ARGS:
209 let load_output_dirs = matches.contains("--load-output-dirs"); 216 let load_output_dirs = matches.contains("--load-output-dirs");
210 Command::Bench { path, what, load_output_dirs } 217 Command::Bench { path, what, load_output_dirs }
211 } 218 }
219 "diagnostics" => {
220 if matches.contains(["-h", "--help"]) {
221 eprintln!(
222 "\
223ra-cli-diagnostics
224
225USAGE:
226 rust-analyzer diagnostics [FLAGS] [PATH]
227
228FLAGS:
229 -h, --help Prints help information
230 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
231 --all Include all files rather than only modules
232
233ARGS:
234 <PATH>"
235 );
236 return Ok(Err(HelpPrinted));
237 }
238
239 let load_output_dirs = matches.contains("--load-output-dirs");
240 let all = matches.contains("--all");
241 let path = {
242 let mut trailing = matches.free()?;
243 if trailing.len() != 1 {
244 bail!("Invalid flags");
245 }
246 trailing.pop().unwrap().into()
247 };
248
249 Command::Diagnostics { path, load_output_dirs, all }
250 }
212 _ => { 251 _ => {
213 eprintln!( 252 eprintln!(
214 "\ 253 "\
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 608f4f67b..7cfc44f01 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -39,6 +39,10 @@ fn main() -> Result<()> {
39 cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)? 39 cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)?
40 } 40 }
41 41
42 args::Command::Diagnostics { path, load_output_dirs, all } => {
43 cli::diagnostics(path.as_ref(), load_output_dirs, all)?
44 }
45
42 args::Command::RunServer => run_server()?, 46 args::Command::RunServer => run_server()?,
43 args::Command::Version => println!("rust-analyzer {}", env!("REV")), 47 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
44 } 48 }
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index c9738d101..a865a7c7e 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -3,6 +3,7 @@
3mod load_cargo; 3mod load_cargo;
4mod analysis_stats; 4mod analysis_stats;
5mod analysis_bench; 5mod analysis_bench;
6mod diagnostics;
6mod progress_report; 7mod progress_report;
7 8
8use std::io::Read; 9use std::io::Read;
@@ -12,6 +13,10 @@ use ra_ide::{file_structure, Analysis};
12use ra_prof::profile; 13use ra_prof::profile;
13use ra_syntax::{AstNode, SourceFile}; 14use ra_syntax::{AstNode, SourceFile};
14 15
16pub use analysis_bench::{analysis_bench, BenchWhat, Position};
17pub use analysis_stats::analysis_stats;
18pub use diagnostics::diagnostics;
19
15#[derive(Clone, Copy)] 20#[derive(Clone, Copy)]
16pub enum Verbosity { 21pub enum Verbosity {
17 Spammy, 22 Spammy,
@@ -60,9 +65,6 @@ pub fn highlight(rainbow: bool) -> Result<()> {
60 Ok(()) 65 Ok(())
61} 66}
62 67
63pub use analysis_bench::{analysis_bench, BenchWhat, Position};
64pub use analysis_stats::analysis_stats;
65
66fn file() -> Result<SourceFile> { 68fn file() -> Result<SourceFile> {
67 let text = read_stdin()?; 69 let text = read_stdin()?;
68 Ok(SourceFile::parse(&text).tree()) 70 Ok(SourceFile::parse(&text).tree())
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 75cf2dae5..e9ee0b888 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -163,10 +163,7 @@ pub fn analysis_stats(
163 if let Ok(src) = src { 163 if let Ok(src) = src {
164 let original_file = src.file_id.original_file(db); 164 let original_file = src.file_id.original_file(db);
165 let line_index = host.analysis().file_line_index(original_file).unwrap(); 165 let line_index = host.analysis().file_line_index(original_file).unwrap();
166 let text_range = src.value.either( 166 let text_range = src.value.syntax_node_ptr().range();
167 |it| it.syntax_node_ptr().range(),
168 |it| it.syntax_node_ptr().range(),
169 );
170 let (start, end) = ( 167 let (start, end) = (
171 line_index.line_col(text_range.start()), 168 line_index.line_col(text_range.start()),
172 line_index.line_col(text_range.end()), 169 line_index.line_col(text_range.end()),
@@ -192,12 +189,7 @@ pub fn analysis_stats(
192 // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly 189 // FIXME: it might be nice to have a function (on Analysis?) that goes from Source<T> -> (LineCol, LineCol) directly
193 // But also, we should just turn the type mismatches into diagnostics and provide these 190 // But also, we should just turn the type mismatches into diagnostics and provide these
194 let root = db.parse_or_expand(src.file_id).unwrap(); 191 let root = db.parse_or_expand(src.file_id).unwrap();
195 let node = src.map(|e| { 192 let node = src.map(|e| e.to_node(&root).syntax().clone());
196 e.either(
197 |p| p.to_node(&root).syntax().clone(),
198 |p| p.to_node(&root).syntax().clone(),
199 )
200 });
201 let original_range = original_range(db, node.as_ref()); 193 let original_range = original_range(db, node.as_ref());
202 let path = db.file_relative_path(original_range.file_id); 194 let path = db.file_relative_path(original_range.file_id);
203 let line_index = 195 let line_index =
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs
new file mode 100644
index 000000000..92664b415
--- /dev/null
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -0,0 +1,74 @@
1//! Analyze all modules in a project for diagnostics. Exits with a non-zero status
2//! code if any errors are found.
3
4use anyhow::anyhow;
5use ra_db::SourceDatabaseExt;
6use ra_ide::Severity;
7use std::{collections::HashSet, path::Path};
8
9use crate::cli::{load_cargo::load_cargo, Result};
10use hir::Semantics;
11
12pub fn diagnostics(path: &Path, load_output_dirs: bool, all: bool) -> Result<()> {
13 let (host, roots) = load_cargo(path, load_output_dirs)?;
14 let db = host.raw_database();
15 let analysis = host.analysis();
16 let semantics = Semantics::new(db);
17 let members = roots
18 .into_iter()
19 .filter_map(|(source_root_id, project_root)| {
20 // filter out dependencies
21 if project_root.is_member() {
22 Some(source_root_id)
23 } else {
24 None
25 }
26 })
27 .collect::<HashSet<_>>();
28
29 let mut found_error = false;
30 let mut visited_files = HashSet::new();
31 for source_root_id in members {
32 for file_id in db.source_root(source_root_id).walk() {
33 // Filter out files which are not actually modules (unless `--all` flag is
34 // passed). In the rust-analyzer repository this filters out the parser test files.
35 if semantics.to_module_def(file_id).is_some() || all {
36 if !visited_files.contains(&file_id) {
37 let crate_name = if let Some(module) = semantics.to_module_def(file_id) {
38 if let Some(name) = module.krate().display_name(db) {
39 format!("{}", name)
40 } else {
41 String::from("unknown")
42 }
43 } else {
44 String::from("unknown")
45 };
46 println!(
47 "processing crate: {}, module: {}",
48 crate_name,
49 db.file_relative_path(file_id)
50 );
51 for diagnostic in analysis.diagnostics(file_id).unwrap() {
52 if matches!(diagnostic.severity, Severity::Error) {
53 found_error = true;
54 }
55
56 println!("{:?}", diagnostic);
57 }
58
59 visited_files.insert(file_id);
60 }
61 }
62 }
63 }
64
65 println!();
66 println!("diagnostic scan complete");
67
68 if found_error {
69 println!();
70 Err(anyhow!("diagnostic error detected"))
71 } else {
72 Ok(())
73 }
74}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 3b60c55f3..4164bfd5e 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -395,7 +395,7 @@ pub fn skip_slow_tests() -> bool {
395 should_skip 395 should_skip
396} 396}
397 397
398const REWRITE: bool = true; 398const REWRITE: bool = false;
399 399
400/// Asserts that `expected` and `actual` strings are equal. If they differ only 400/// Asserts that `expected` and `actual` strings are equal. If they differ only
401/// in trailing or leading whitespace the test won't fail and 401/// in trailing or leading whitespace the test won't fail and