diff options
75 files changed, 1802 insertions, 489 deletions
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 02a3b6228..00f299ff1 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml | |||
@@ -20,25 +20,14 @@ jobs: | |||
20 | name: Audit Rust vulnerabilities | 20 | name: Audit Rust vulnerabilities |
21 | runs-on: ubuntu-latest | 21 | runs-on: ubuntu-latest |
22 | steps: | 22 | steps: |
23 | - name: Install Rust toolchain | ||
24 | uses: actions-rs/toolchain@v1 | ||
25 | with: | ||
26 | toolchain: stable | ||
27 | profile: minimal | ||
28 | override: true | ||
29 | |||
30 | - name: Checkout repository | 23 | - name: Checkout repository |
31 | uses: actions/checkout@v2 | 24 | uses: actions/checkout@v2 |
32 | 25 | ||
33 | - run: sudo chown -R $(whoami):$(id -ng) ~/.cargo/ | 26 | - uses: actions-rs/[email protected] |
34 | |||
35 | - name: Cache cargo | ||
36 | uses: actions/cache@v1 | ||
37 | with: | 27 | with: |
38 | path: ~/.cargo/ | 28 | crate: cargo-audit |
39 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} | 29 | use-tool-cache: true |
40 | 30 | ||
41 | - run: cargo install cargo-audit | ||
42 | - run: cargo audit | 31 | - run: cargo audit |
43 | 32 | ||
44 | rust: | 33 | rust: |
diff --git a/Cargo.lock b/Cargo.lock index 975c1aef8..01fa64e6f 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -114,17 +114,18 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" | |||
114 | [[package]] | 114 | [[package]] |
115 | name = "chalk-derive" | 115 | name = "chalk-derive" |
116 | version = "0.1.0" | 116 | version = "0.1.0" |
117 | source = "git+https://github.com/rust-lang/chalk.git?rev=039fc904a05f8cb3d0c682c9a57a63dda7a35356#039fc904a05f8cb3d0c682c9a57a63dda7a35356" | 117 | source = "git+https://github.com/rust-lang/chalk.git?rev=6222e416b96892b2a86bc08de7dbc9826ff1acea#6222e416b96892b2a86bc08de7dbc9826ff1acea" |
118 | dependencies = [ | 118 | dependencies = [ |
119 | "proc-macro2", | 119 | "proc-macro2", |
120 | "quote", | 120 | "quote", |
121 | "syn", | 121 | "syn", |
122 | "synstructure", | ||
122 | ] | 123 | ] |
123 | 124 | ||
124 | [[package]] | 125 | [[package]] |
125 | name = "chalk-engine" | 126 | name = "chalk-engine" |
126 | version = "0.9.0" | 127 | version = "0.9.0" |
127 | source = "git+https://github.com/rust-lang/chalk.git?rev=039fc904a05f8cb3d0c682c9a57a63dda7a35356#039fc904a05f8cb3d0c682c9a57a63dda7a35356" | 128 | source = "git+https://github.com/rust-lang/chalk.git?rev=6222e416b96892b2a86bc08de7dbc9826ff1acea#6222e416b96892b2a86bc08de7dbc9826ff1acea" |
128 | dependencies = [ | 129 | dependencies = [ |
129 | "chalk-macros", | 130 | "chalk-macros", |
130 | "rustc-hash", | 131 | "rustc-hash", |
@@ -133,7 +134,7 @@ dependencies = [ | |||
133 | [[package]] | 134 | [[package]] |
134 | name = "chalk-ir" | 135 | name = "chalk-ir" |
135 | version = "0.1.0" | 136 | version = "0.1.0" |
136 | source = "git+https://github.com/rust-lang/chalk.git?rev=039fc904a05f8cb3d0c682c9a57a63dda7a35356#039fc904a05f8cb3d0c682c9a57a63dda7a35356" | 137 | source = "git+https://github.com/rust-lang/chalk.git?rev=6222e416b96892b2a86bc08de7dbc9826ff1acea#6222e416b96892b2a86bc08de7dbc9826ff1acea" |
137 | dependencies = [ | 138 | dependencies = [ |
138 | "chalk-derive", | 139 | "chalk-derive", |
139 | "chalk-engine", | 140 | "chalk-engine", |
@@ -143,7 +144,7 @@ dependencies = [ | |||
143 | [[package]] | 144 | [[package]] |
144 | name = "chalk-macros" | 145 | name = "chalk-macros" |
145 | version = "0.1.1" | 146 | version = "0.1.1" |
146 | source = "git+https://github.com/rust-lang/chalk.git?rev=039fc904a05f8cb3d0c682c9a57a63dda7a35356#039fc904a05f8cb3d0c682c9a57a63dda7a35356" | 147 | source = "git+https://github.com/rust-lang/chalk.git?rev=6222e416b96892b2a86bc08de7dbc9826ff1acea#6222e416b96892b2a86bc08de7dbc9826ff1acea" |
147 | dependencies = [ | 148 | dependencies = [ |
148 | "lazy_static", | 149 | "lazy_static", |
149 | ] | 150 | ] |
@@ -151,7 +152,7 @@ dependencies = [ | |||
151 | [[package]] | 152 | [[package]] |
152 | name = "chalk-rust-ir" | 153 | name = "chalk-rust-ir" |
153 | version = "0.1.0" | 154 | version = "0.1.0" |
154 | source = "git+https://github.com/rust-lang/chalk.git?rev=039fc904a05f8cb3d0c682c9a57a63dda7a35356#039fc904a05f8cb3d0c682c9a57a63dda7a35356" | 155 | source = "git+https://github.com/rust-lang/chalk.git?rev=6222e416b96892b2a86bc08de7dbc9826ff1acea#6222e416b96892b2a86bc08de7dbc9826ff1acea" |
155 | dependencies = [ | 156 | dependencies = [ |
156 | "chalk-derive", | 157 | "chalk-derive", |
157 | "chalk-engine", | 158 | "chalk-engine", |
@@ -162,7 +163,7 @@ dependencies = [ | |||
162 | [[package]] | 163 | [[package]] |
163 | name = "chalk-solve" | 164 | name = "chalk-solve" |
164 | version = "0.1.0" | 165 | version = "0.1.0" |
165 | source = "git+https://github.com/rust-lang/chalk.git?rev=039fc904a05f8cb3d0c682c9a57a63dda7a35356#039fc904a05f8cb3d0c682c9a57a63dda7a35356" | 166 | source = "git+https://github.com/rust-lang/chalk.git?rev=6222e416b96892b2a86bc08de7dbc9826ff1acea#6222e416b96892b2a86bc08de7dbc9826ff1acea" |
166 | dependencies = [ | 167 | dependencies = [ |
167 | "chalk-derive", | 168 | "chalk-derive", |
168 | "chalk-engine", | 169 | "chalk-engine", |
@@ -495,9 +496,9 @@ dependencies = [ | |||
495 | 496 | ||
496 | [[package]] | 497 | [[package]] |
497 | name = "insta" | 498 | name = "insta" |
498 | version = "0.15.0" | 499 | version = "0.16.0" |
499 | source = "registry+https://github.com/rust-lang/crates.io-index" | 500 | source = "registry+https://github.com/rust-lang/crates.io-index" |
500 | checksum = "8de3f029212a3fe78a6090f1f2b993877ca245a9ded863f3fcbd6eae084fc1ed" | 501 | checksum = "8386e795fb3927131ea4cede203c529a333652eb6dc4ff29616b832b27e9b096" |
501 | dependencies = [ | 502 | dependencies = [ |
502 | "console", | 503 | "console", |
503 | "difference", | 504 | "difference", |
@@ -1013,6 +1014,7 @@ dependencies = [ | |||
1013 | "chalk-solve", | 1014 | "chalk-solve", |
1014 | "ena", | 1015 | "ena", |
1015 | "insta", | 1016 | "insta", |
1017 | "itertools", | ||
1016 | "log", | 1018 | "log", |
1017 | "ra_arena", | 1019 | "ra_arena", |
1018 | "ra_db", | 1020 | "ra_db", |
@@ -1565,6 +1567,18 @@ dependencies = [ | |||
1565 | ] | 1567 | ] |
1566 | 1568 | ||
1567 | [[package]] | 1569 | [[package]] |
1570 | name = "synstructure" | ||
1571 | version = "0.12.3" | ||
1572 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
1573 | checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" | ||
1574 | dependencies = [ | ||
1575 | "proc-macro2", | ||
1576 | "quote", | ||
1577 | "syn", | ||
1578 | "unicode-xid", | ||
1579 | ] | ||
1580 | |||
1581 | [[package]] | ||
1568 | name = "tempfile" | 1582 | name = "tempfile" |
1569 | version = "3.1.0" | 1583 | version = "3.1.0" |
1570 | source = "registry+https://github.com/rust-lang/crates.io-index" | 1584 | source = "registry+https://github.com/rust-lang/crates.io-index" |
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 | ||
80 | fn bar(arg: &str, baz: Baz) { | 80 | fn 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 | ||
182 | impl Trait<u32> for () { | 182 | impl 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] |
610 | fn doctest_reorder_fields() { | ||
611 | check( | ||
612 | "reorder_fields", | ||
613 | r#####" | ||
614 | struct Foo {foo: i32, bar: i32}; | ||
615 | const test: Foo = <|>Foo {bar: 0, foo: 1} | ||
616 | "#####, | ||
617 | r#####" | ||
618 | struct Foo {foo: i32, bar: i32}; | ||
619 | const test: Foo = Foo {foo: 1, bar: 0} | ||
620 | "#####, | ||
621 | ) | ||
622 | } | ||
623 | |||
624 | #[test] | ||
610 | fn doctest_replace_if_let_with_match() { | 625 | fn 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 | ||
227 | fn bar() { | 227 | fn bar() { |
228 | <|>unimplemented!() | 228 | <|>todo!() |
229 | } | 229 | } |
230 | ", | 230 | ", |
231 | ) | 231 | ) |
@@ -252,7 +252,7 @@ impl Foo { | |||
252 | } | 252 | } |
253 | 253 | ||
254 | fn bar() { | 254 | fn bar() { |
255 | <|>unimplemented!() | 255 | <|>todo!() |
256 | } | 256 | } |
257 | ", | 257 | ", |
258 | ) | 258 | ) |
@@ -276,7 +276,7 @@ fn foo1() { | |||
276 | } | 276 | } |
277 | 277 | ||
278 | fn bar() { | 278 | fn bar() { |
279 | <|>unimplemented!() | 279 | <|>todo!() |
280 | } | 280 | } |
281 | 281 | ||
282 | fn foo2() {} | 282 | fn 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" |
317 | struct Baz; | 317 | struct Baz; |
318 | fn baz() -> Baz { unimplemented!() } | 318 | fn baz() -> Baz { todo!() } |
319 | fn foo() { | 319 | fn foo() { |
320 | bar<|>(baz()); | 320 | bar<|>(baz()); |
321 | } | 321 | } |
322 | ", | 322 | ", |
323 | r" | 323 | r" |
324 | struct Baz; | 324 | struct Baz; |
325 | fn baz() -> Baz { unimplemented!() } | 325 | fn baz() -> Baz { todo!() } |
326 | fn foo() { | 326 | fn foo() { |
327 | bar(baz()); | 327 | bar(baz()); |
328 | } | 328 | } |
329 | 329 | ||
330 | fn bar(baz: Baz) { | 330 | fn bar(baz: Baz) { |
331 | <|>unimplemented!() | 331 | <|>todo!() |
332 | } | 332 | } |
333 | ", | 333 | ", |
334 | ); | 334 | ); |
@@ -361,7 +361,7 @@ impl Baz { | |||
361 | } | 361 | } |
362 | 362 | ||
363 | fn bar(baz: Baz) { | 363 | fn bar(baz: Baz) { |
364 | <|>unimplemented!() | 364 | <|>todo!() |
365 | } | 365 | } |
366 | ", | 366 | ", |
367 | ) | 367 | ) |
@@ -382,7 +382,7 @@ fn foo() { | |||
382 | } | 382 | } |
383 | 383 | ||
384 | fn bar(arg: &str) { | 384 | fn bar(arg: &str) { |
385 | <|>unimplemented!() | 385 | <|>todo!() |
386 | } | 386 | } |
387 | "#, | 387 | "#, |
388 | ) | 388 | ) |
@@ -403,7 +403,7 @@ fn foo() { | |||
403 | } | 403 | } |
404 | 404 | ||
405 | fn bar(arg: char) { | 405 | fn bar(arg: char) { |
406 | <|>unimplemented!() | 406 | <|>todo!() |
407 | } | 407 | } |
408 | "#, | 408 | "#, |
409 | ) | 409 | ) |
@@ -424,7 +424,7 @@ fn foo() { | |||
424 | } | 424 | } |
425 | 425 | ||
426 | fn bar(arg: i32) { | 426 | fn bar(arg: i32) { |
427 | <|>unimplemented!() | 427 | <|>todo!() |
428 | } | 428 | } |
429 | ", | 429 | ", |
430 | ) | 430 | ) |
@@ -445,7 +445,7 @@ fn foo() { | |||
445 | } | 445 | } |
446 | 446 | ||
447 | fn bar(arg: u8) { | 447 | fn bar(arg: u8) { |
448 | <|>unimplemented!() | 448 | <|>todo!() |
449 | } | 449 | } |
450 | ", | 450 | ", |
451 | ) | 451 | ) |
@@ -470,7 +470,7 @@ fn foo() { | |||
470 | } | 470 | } |
471 | 471 | ||
472 | fn bar(x: u8) { | 472 | fn bar(x: u8) { |
473 | <|>unimplemented!() | 473 | <|>todo!() |
474 | } | 474 | } |
475 | ", | 475 | ", |
476 | ) | 476 | ) |
@@ -493,7 +493,7 @@ fn foo() { | |||
493 | } | 493 | } |
494 | 494 | ||
495 | fn bar(worble: ()) { | 495 | fn bar(worble: ()) { |
496 | <|>unimplemented!() | 496 | <|>todo!() |
497 | } | 497 | } |
498 | ", | 498 | ", |
499 | ) | 499 | ) |
@@ -506,7 +506,7 @@ fn bar(worble: ()) { | |||
506 | r" | 506 | r" |
507 | trait Foo {} | 507 | trait Foo {} |
508 | fn foo() -> impl Foo { | 508 | fn foo() -> impl Foo { |
509 | unimplemented!() | 509 | todo!() |
510 | } | 510 | } |
511 | fn baz() { | 511 | fn baz() { |
512 | <|>bar(foo()) | 512 | <|>bar(foo()) |
@@ -515,14 +515,14 @@ fn baz() { | |||
515 | r" | 515 | r" |
516 | trait Foo {} | 516 | trait Foo {} |
517 | fn foo() -> impl Foo { | 517 | fn foo() -> impl Foo { |
518 | unimplemented!() | 518 | todo!() |
519 | } | 519 | } |
520 | fn baz() { | 520 | fn baz() { |
521 | bar(foo()) | 521 | bar(foo()) |
522 | } | 522 | } |
523 | 523 | ||
524 | fn bar(foo: impl Foo) { | 524 | fn 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 | ||
582 | fn bar<T>(t: T) { | 582 | fn bar<T>(t: T) { |
583 | <|>unimplemented!() | 583 | <|>todo!() |
584 | } | 584 | } |
585 | ", | 585 | ", |
586 | ) | 586 | ) |
@@ -611,7 +611,7 @@ fn foo() { | |||
611 | } | 611 | } |
612 | 612 | ||
613 | fn bar(arg: fn() -> Baz) { | 613 | fn bar(arg: fn() -> Baz) { |
614 | <|>unimplemented!() | 614 | <|>todo!() |
615 | } | 615 | } |
616 | ", | 616 | ", |
617 | ) | 617 | ) |
@@ -636,7 +636,7 @@ fn foo() { | |||
636 | } | 636 | } |
637 | 637 | ||
638 | fn bar(closure: impl Fn(i64) -> i64) { | 638 | fn 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 | ||
659 | fn bar(baz: ()) { | 659 | fn bar(baz: ()) { |
660 | <|>unimplemented!() | 660 | <|>todo!() |
661 | } | 661 | } |
662 | ", | 662 | ", |
663 | ) | 663 | ) |
@@ -682,7 +682,7 @@ fn foo() { | |||
682 | } | 682 | } |
683 | 683 | ||
684 | fn bar(baz_1: Baz, baz_2: Baz) { | 684 | fn 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 | ||
709 | fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) { | 709 | fn 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 | ||
166 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | 166 | fn 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 | ||
251 | impl Foo for S { | 251 | impl 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 { <|> }", | |||
268 | trait Foo { fn foo(&self); } | 268 | trait Foo { fn foo(&self); } |
269 | struct S; | 269 | struct S; |
270 | impl Foo for S { | 270 | impl 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 { <|> }", | |||
285 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | 285 | trait Foo<T> { fn foo(&self, t: T) -> &T; } |
286 | struct S; | 286 | struct S; |
287 | impl Foo<u32> for S { | 287 | impl 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 { <|> }", | |||
302 | trait Foo<T> { fn foo(&self, t: T) -> &T; } | 302 | trait Foo<T> { fn foo(&self, t: T) -> &T; } |
303 | struct S; | 303 | struct S; |
304 | impl<U> Foo<U> for S { | 304 | impl<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 {}<|>", | |||
319 | trait Foo { fn foo(&self); } | 319 | trait Foo { fn foo(&self); } |
320 | struct S; | 320 | struct S; |
321 | impl Foo for S { | 321 | impl 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 | } |
343 | struct S; | 343 | struct S; |
344 | impl foo::Foo for S { | 344 | impl 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 | } |
366 | struct S; | 366 | struct S; |
367 | impl foo::Foo for S { | 367 | impl 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 | } |
389 | struct S; | 389 | struct S; |
390 | impl foo::Foo<u32> for S { | 390 | impl 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 { | |||
414 | struct Param; | 414 | struct Param; |
415 | struct S; | 415 | struct S; |
416 | impl foo::Foo<Param> for S { | 416 | impl 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 | } |
440 | struct S; | 440 | struct S; |
441 | impl foo::Foo for S { | 441 | impl 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 | } |
465 | struct S; | 465 | struct S; |
466 | impl foo::Foo for S { | 466 | impl 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 | } |
488 | struct S; | 488 | struct S; |
489 | impl foo::Foo for S { | 489 | impl 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 { | |||
544 | struct S; | 544 | struct S; |
545 | impl Foo for S { | 545 | impl 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 @@ | |||
1 | use std::iter::successors; | 1 | use std::iter::successors; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use 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" | ||
265 | use foo::bar::baz; | ||
266 | use foo::<|>{ | ||
267 | FooBar, | ||
268 | }; | ||
269 | ", | ||
270 | r" | ||
271 | use foo::{<|> | ||
272 | FooBar, | ||
273 | bar::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 @@ | |||
1 | use std::collections::HashMap; | ||
2 | |||
3 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; | ||
4 | use itertools::Itertools; | ||
5 | use ra_ide_db::RootDatabase; | ||
6 | use ra_syntax::{ | ||
7 | algo, | ||
8 | ast::{self, Path, RecordLit, RecordPat}, | ||
9 | match_ast, AstNode, SyntaxKind, | ||
10 | SyntaxKind::*, | ||
11 | SyntaxNode, | ||
12 | }; | ||
13 | |||
14 | use 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 | // | ||
34 | pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> { | ||
35 | reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx)) | ||
36 | } | ||
37 | |||
38 | fn 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 | |||
61 | fn 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 | |||
69 | fn 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 | |||
80 | fn 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 | |||
85 | fn 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 | |||
92 | fn 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 | |||
99 | fn 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)] | ||
111 | mod 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" | |||
13 | jod-thread = "0.1.1" | 13 | jod-thread = "0.1.1" |
14 | 14 | ||
15 | [dev-dependencies] | 15 | [dev-dependencies] |
16 | insta = "0.15.0" | 16 | insta = "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 | }; |
28 | use ra_db::{CrateId, Edition, FileId}; | 28 | use ra_db::{CrateId, CrateName, Edition, FileId}; |
29 | use ra_prof::profile; | 29 | use ra_prof::profile; |
30 | use ra_syntax::{ | 30 | use 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" } | |||
28 | tt = { path = "../ra_tt", package = "ra_tt" } | 28 | tt = { path = "../ra_tt", package = "ra_tt" } |
29 | 29 | ||
30 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | insta = "0.15.0" | 31 | insta = "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 | ||
5 | use either::Either; | 5 | use either::Either; |
6 | use hir_expand::{ | 6 | use 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; | |||
13 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; | 12 | use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner}; |
14 | 13 | ||
15 | use crate::{ | 14 | use 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 | ||
126 | impl VariantData { | 125 | impl 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 | ||
194 | fn lower_struct( | 195 | fn 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; | |||
9 | use either::Either; | 9 | use either::Either; |
10 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; | 10 | use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, AstId, HirFileId, InFile, MacroDefId}; |
11 | use ra_arena::{map::ArenaMap, Arena}; | 11 | use ra_arena::{map::ArenaMap, Arena}; |
12 | use ra_cfg::CfgOptions; | ||
13 | use ra_db::CrateId; | ||
12 | use ra_prof::profile; | 14 | use ra_prof::profile; |
13 | use ra_syntax::{ast, AstNode, AstPtr}; | 15 | use ra_syntax::{ast, AstNode, AstPtr}; |
14 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
15 | 17 | ||
16 | use crate::{ | 18 | use 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. | ||
32 | pub(crate) struct CfgExpander { | ||
33 | cfg_options: CfgOptions, | ||
34 | hygiene: Hygiene, | ||
35 | } | ||
36 | |||
27 | pub(crate) struct Expander { | 37 | pub(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 | ||
46 | impl 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 | |||
36 | impl Expander { | 66 | impl 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 | ||
145 | pub type ExprPtr = Either<AstPtr<ast::Expr>, AstPtr<ast::RecordField>>; | 190 | pub type ExprPtr = AstPtr<ast::Expr>; |
146 | pub type ExprSource = InFile<ExprPtr>; | 191 | pub type ExprSource = InFile<ExprPtr>; |
147 | 192 | ||
148 | pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; | 193 | pub 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 | ||
4 | use either::Either; | 4 | use either::Either; |
5 | |||
6 | use hir_expand::{ | 5 | use 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 | }; |
19 | use test_utils::tested_by; | 17 | use test_utils::tested_by; |
20 | 18 | ||
21 | use super::{ExprSource, PatSource}; | ||
22 | use crate::{ | 19 | use 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 | ||
35 | use super::{ExprSource, PatSource}; | ||
36 | use ast::AstChildren; | ||
37 | |||
40 | pub(super) fn lower( | 38 | pub(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 | ||
711 | impl From<ast::BinOp> for BinaryOp { | 701 | impl 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 | }; |
10 | use ra_prof::profile; | 10 | use ra_prof::profile; |
11 | use ra_syntax::ast::{ | 11 | use 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 | ||
15 | use crate::{ | 16 | use 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 | ||
111 | impl TypeAliasData { | 117 | impl 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 | ||
269 | fn collect_impl_items_in_macros( | 290 | fn 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 | ||
320 | fn collect_impl_items( | 341 | fn 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 { | |||
374 | pub enum Pat { | 374 | pub 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::{ | |||
14 | use ra_db::CrateId; | 14 | use ra_db::CrateId; |
15 | use ra_syntax::ast; | 15 | use ra_syntax::ast; |
16 | 16 | ||
17 | use crate::{type_ref::TypeRef, InFile}; | 17 | use 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)] |
20 | pub struct ModPath { | 23 | pub 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)] | ||
122 | pub 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 | }; |
12 | use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner}; | 12 | use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner, TypeBoundsOwner}; |
13 | 13 | ||
14 | use super::AssociatedTypeBinding; | ||
14 | use crate::{ | 15 | use 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 | ||
19 | pub(super) use lower_use::lower_use_tree; | 20 | pub(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 | ||
86 | impl 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 | |||
86 | impl AsName for tt::Ident { | 95 | impl 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"] | |||
8 | doctest = false | 8 | doctest = false |
9 | 9 | ||
10 | [dependencies] | 10 | [dependencies] |
11 | itertools = "0.9.0" | ||
11 | arrayvec = "0.5.1" | 12 | arrayvec = "0.5.1" |
12 | smallvec = "1.2.0" | 13 | smallvec = "1.2.0" |
13 | ena = "0.13.1" | 14 | ena = "0.13.1" |
@@ -26,9 +27,9 @@ test_utils = { path = "../test_utils" } | |||
26 | 27 | ||
27 | scoped-tls = "1" | 28 | scoped-tls = "1" |
28 | 29 | ||
29 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } | 30 | chalk-solve = { git = "https://github.com/rust-lang/chalk.git", rev = "6222e416b96892b2a86bc08de7dbc9826ff1acea" } |
30 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } | 31 | chalk-rust-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "6222e416b96892b2a86bc08de7dbc9826ff1acea" } |
31 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "039fc904a05f8cb3d0c682c9a57a63dda7a35356" } | 32 | chalk-ir = { git = "https://github.com/rust-lang/chalk.git", rev = "6222e416b96892b2a86bc08de7dbc9826ff1acea" } |
32 | 33 | ||
33 | [dev-dependencies] | 34 | [dev-dependencies] |
34 | insta = "0.15.0" | 35 | insta = "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 { | |||
644 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { | 674 | fn 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}; | |||
11 | use ra_prof::profile; | 11 | use ra_prof::profile; |
12 | 12 | ||
13 | use crate::{ | 13 | use 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 @@ | |||
8 | use std::iter; | 8 | use std::iter; |
9 | use std::sync::Arc; | 9 | use std::sync::Arc; |
10 | 10 | ||
11 | use smallvec::SmallVec; | ||
12 | |||
11 | use hir_def::{ | 13 | use 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)] |
46 | pub struct CrateImplDefs { | 46 | pub 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 | ||
51 | impl CrateImplDefs { | 51 | impl 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 @@ | |||
1 | use super::{infer, infer_with_mismatches}; | ||
2 | use insta::assert_snapshot; | 1 | use insta::assert_snapshot; |
3 | use test_utils::covers; | 2 | use test_utils::covers; |
4 | 3 | ||
4 | use super::{infer, infer_with_mismatches}; | ||
5 | |||
5 | #[test] | 6 | #[test] |
6 | fn infer_pattern() { | 7 | fn 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] |
1806 | fn unselected_projection_on_trait_self() { | 1806 | fn 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] |
1847 | fn unselected_projection_on_trait_self() { | ||
1848 | let t = type_at( | ||
1849 | r#" | ||
1850 | //- /main.rs | ||
1851 | trait Trait { | ||
1852 | type Item; | ||
1853 | |||
1854 | fn f(&self) -> Self::Item { loop {} } | ||
1855 | } | ||
1856 | |||
1857 | struct S; | ||
1858 | impl Trait for S { | ||
1859 | type Item = u32; | ||
1860 | } | ||
1861 | |||
1862 | fn test() { | ||
1863 | S.f()<|>; | ||
1864 | } | ||
1865 | "#, | ||
1866 | ); | ||
1867 | assert_eq!(t, "u32"); | ||
1868 | } | ||
1869 | |||
1870 | #[test] | ||
1847 | fn trait_impl_self_ty() { | 1871 | fn 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] |
1951 | fn inline_assoc_type_bounds_1() { | ||
1952 | let t = type_at( | ||
1953 | r#" | ||
1954 | //- /main.rs | ||
1955 | trait Iterator { | ||
1956 | type Item; | ||
1957 | } | ||
1958 | trait OtherTrait<T> { | ||
1959 | fn foo(&self) -> T; | ||
1960 | } | ||
1961 | |||
1962 | // workaround for Chalk assoc type normalization problems | ||
1963 | pub struct S<T>; | ||
1964 | impl<T: Iterator> Iterator for S<T> { | ||
1965 | type Item = <T as Iterator>::Item; | ||
1966 | } | ||
1967 | |||
1968 | fn 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] | ||
1978 | fn inline_assoc_type_bounds_2() { | ||
1979 | let t = type_at( | ||
1980 | r#" | ||
1981 | //- /main.rs | ||
1982 | trait Iterator { | ||
1983 | type Item; | ||
1984 | } | ||
1985 | |||
1986 | fn 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] | ||
1927 | fn unify_impl_trait() { | 1998 | fn 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] |
2097 | fn associated_type_bound() { | ||
2098 | let t = type_at( | ||
2099 | r#" | ||
2100 | //- /main.rs | ||
2101 | pub trait Trait { | ||
2102 | type Item: OtherTrait<u32>; | ||
2103 | } | ||
2104 | pub trait OtherTrait<T> { | ||
2105 | fn foo(&self) -> T; | ||
2106 | } | ||
2107 | |||
2108 | // this is just a workaround for chalk#234 | ||
2109 | pub struct S<T>; | ||
2110 | impl<T: Trait> Trait for S<T> { | ||
2111 | type Item = <T as Trait>::Item; | ||
2112 | } | ||
2113 | |||
2114 | fn 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] | ||
2026 | fn dyn_trait_through_chalk() { | 2124 | fn 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}; | |||
7 | use ra_prof::profile; | 7 | use ra_prof::profile; |
8 | use rustc_hash::FxHashSet; | 8 | use rustc_hash::FxHashSet; |
9 | 9 | ||
10 | use crate::{db::HirDatabase, DebruijnIndex}; | 10 | use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex}; |
11 | 11 | ||
12 | use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk}; | 12 | use 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 | ||
17 | use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; | 17 | use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation}; |
18 | use crate::{ | 18 | use 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 | ||
23 | pub(super) mod tls; | 23 | pub(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 | ||
186 | impl chalk_ir::interner::HasInterner for Interner { | 231 | impl 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 | ||
605 | fn make_binders<T>(value: T, num_vars: usize) -> chalk_ir::Binders<T> { | 652 | fn 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 | ||
612 | fn convert_where_clauses( | 659 | fn convert_where_clauses( |
@@ -626,6 +673,55 @@ fn convert_where_clauses( | |||
626 | result | 673 | result |
627 | } | 674 | } |
628 | 675 | ||
676 | fn 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 | |||
629 | impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | 725 | impl<'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 | ||
698 | pub(crate) fn associated_ty_data_query( | 803 | pub(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 @@ | |||
2 | use std::fmt; | 2 | use std::fmt; |
3 | 3 | ||
4 | use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName}; | 4 | use chalk_ir::{AliasTy, Goal, Goals, Lifetime, Parameter, ProgramClauseImplication, TypeName}; |
5 | use itertools::Itertools; | ||
5 | 6 | ||
6 | use super::{from_chalk, Interner}; | 7 | use super::{from_chalk, Interner}; |
7 | use crate::{db::HirDatabase, CallableDef, TypeCtor}; | 8 | use crate::{db::HirDatabase, CallableDef, TypeCtor}; |
8 | use hir_def::{AdtId, AssocContainerId, Lookup, TypeAliasId}; | 9 | use hir_def::{AdtId, AssocContainerId, DefWithBodyId, Lookup, TypeAliasId}; |
9 | 10 | ||
10 | pub use unsafe_tls::{set_current_program, with_current_program}; | 11 | pub 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 {}", ¶ms[0], trait_data.name,)?; |
117 | fmt, | 138 | if params.len() > 1 { |
118 | "<{:?} as {}<{:?}>>::{}", | 139 | write!( |
119 | ¶ms[0], | 140 | fmt, |
120 | trait_data.name, | 141 | "<{}>", |
121 | ¶ms[1..], | 142 | ¶ms[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" } | |||
35 | hir = { path = "../ra_hir", package = "ra_hir" } | 35 | hir = { path = "../ra_hir", package = "ra_hir" } |
36 | 36 | ||
37 | [dev-dependencies] | 37 | [dev-dependencies] |
38 | insta = "0.15.0" | 38 | insta = "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#" |
565 | struct TS { x: u32, y: i32 } | 575 | struct TS { x: u32, y: i32 } |
566 | fn main() { | 576 | fn 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#" |
601 | enum E { | 610 | enum 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 @@ | |||
2 | use crate::completion::{CompletionContext, Completions}; | 2 | use crate::completion::{CompletionContext, Completions}; |
3 | 3 | ||
4 | pub(super) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 4 | pub(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 @@ | |||
3 | use crate::completion::{CompletionContext, Completions}; | 3 | use crate::completion::{CompletionContext, Completions}; |
4 | 4 | ||
5 | pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { | 5 | pub(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 | ||
256 | fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { | 256 | fn 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 | ||
1091 | struct Param {} | 1093 | struct Param {} |
@@ -1093,6 +1095,8 @@ struct Param {} | |||
1093 | fn different_order(param: &Param) {} | 1095 | fn different_order(param: &Param) {} |
1094 | fn different_order_mut(param: &mut Param) {} | 1096 | fn different_order_mut(param: &mut Param) {} |
1095 | 1097 | ||
1098 | fn twiddle(twiddle: bool) {} | ||
1099 | |||
1096 | fn main() { | 1100 | fn 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(¶m_begin); | 1117 | different_order(¶m_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 | ||
284 | fn name_ref_or_index(p: &mut Parser) { | 284 | fn 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 | ||
294 | fn error_block(p: &mut Parser, message: &str) { | 291 | fn 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 | // } | ||
218 | fn 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 _ = (); } |
235 | fn placeholder_pat(p: &mut Parser) -> CompletedMarker { | 231 | fn 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::{ | |||
18 | pub use self::{ | 18 | pub 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 | ||
4 | use std::fmt; | ||
5 | |||
4 | use itertools::Itertools; | 6 | use itertools::Itertools; |
5 | use ra_parser::SyntaxKind; | 7 | use ra_parser::SyntaxKind; |
6 | 8 | ||
@@ -187,6 +189,64 @@ impl ast::StructDef { | |||
187 | } | 189 | } |
188 | } | 190 | } |
189 | 191 | ||
192 | impl 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 | |||
222 | pub enum NameOrNameRef { | ||
223 | Name(ast::Name), | ||
224 | NameRef(ast::NameRef), | ||
225 | } | ||
226 | |||
227 | impl 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 | |||
236 | impl 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 | |||
190 | impl ast::EnumVariant { | 250 | impl 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 | } |
1808 | impl ast::AttrsOwner for RecordFieldPat {} | 1808 | impl ast::AttrsOwner for RecordFieldPat {} |
1809 | impl ast::NameOwner for RecordFieldPat {} | ||
1810 | impl RecordFieldPat { | 1809 | impl 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 { | |||
100 | pub fn expr_unimplemented() -> ast::Expr { | 100 | pub fn expr_unimplemented() -> ast::Expr { |
101 | expr_from_text("unimplemented!()") | 101 | expr_from_text("unimplemented!()") |
102 | } | 102 | } |
103 | pub fn expr_todo() -> ast::Expr { | ||
104 | expr_from_text("todo!()") | ||
105 | } | ||
103 | pub fn expr_path(path: ast::Path) -> ast::Expr { | 106 | pub 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)] | ||
23 | macro_rules! eprintln { | ||
24 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | ||
25 | } | ||
26 | |||
22 | mod syntax_node; | 27 | mod syntax_node; |
23 | mod syntax_error; | 28 | mod syntax_error; |
24 | mod parsing; | 29 | mod 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 @@ | |||
1 | SOURCE_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" | ||
49 | error [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 @@ | |||
1 | fn 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 | "\ | ||
223 | ra-cli-diagnostics | ||
224 | |||
225 | USAGE: | ||
226 | rust-analyzer diagnostics [FLAGS] [PATH] | ||
227 | |||
228 | FLAGS: | ||
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 | |||
233 | ARGS: | ||
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 @@ | |||
3 | mod load_cargo; | 3 | mod load_cargo; |
4 | mod analysis_stats; | 4 | mod analysis_stats; |
5 | mod analysis_bench; | 5 | mod analysis_bench; |
6 | mod diagnostics; | ||
6 | mod progress_report; | 7 | mod progress_report; |
7 | 8 | ||
8 | use std::io::Read; | 9 | use std::io::Read; |
@@ -12,6 +13,10 @@ use ra_ide::{file_structure, Analysis}; | |||
12 | use ra_prof::profile; | 13 | use ra_prof::profile; |
13 | use ra_syntax::{AstNode, SourceFile}; | 14 | use ra_syntax::{AstNode, SourceFile}; |
14 | 15 | ||
16 | pub use analysis_bench::{analysis_bench, BenchWhat, Position}; | ||
17 | pub use analysis_stats::analysis_stats; | ||
18 | pub use diagnostics::diagnostics; | ||
19 | |||
15 | #[derive(Clone, Copy)] | 20 | #[derive(Clone, Copy)] |
16 | pub enum Verbosity { | 21 | pub enum Verbosity { |
17 | Spammy, | 22 | Spammy, |
@@ -60,9 +65,6 @@ pub fn highlight(rainbow: bool) -> Result<()> { | |||
60 | Ok(()) | 65 | Ok(()) |
61 | } | 66 | } |
62 | 67 | ||
63 | pub use analysis_bench::{analysis_bench, BenchWhat, Position}; | ||
64 | pub use analysis_stats::analysis_stats; | ||
65 | |||
66 | fn file() -> Result<SourceFile> { | 68 | fn 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 | |||
4 | use anyhow::anyhow; | ||
5 | use ra_db::SourceDatabaseExt; | ||
6 | use ra_ide::Severity; | ||
7 | use std::{collections::HashSet, path::Path}; | ||
8 | |||
9 | use crate::cli::{load_cargo::load_cargo, Result}; | ||
10 | use hir::Semantics; | ||
11 | |||
12 | pub 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 | ||
398 | const REWRITE: bool = true; | 398 | const 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 |
diff --git a/docs/user/assists.md b/docs/user/assists.md index 754131f6f..6483ba4f3 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -77,7 +77,7 @@ fn foo() { | |||
77 | } | 77 | } |
78 | 78 | ||
79 | fn bar(arg: &str, baz: Baz) { | 79 | fn bar(arg: &str, baz: Baz) { |
80 | unimplemented!() | 80 | todo!() |
81 | } | 81 | } |
82 | 82 | ||
83 | ``` | 83 | ``` |
@@ -175,7 +175,7 @@ trait Trait<T> { | |||
175 | } | 175 | } |
176 | 176 | ||
177 | impl Trait<u32> for () { | 177 | impl Trait<u32> for () { |
178 | fn foo(&self) -> u32 { unimplemented!() } | 178 | fn foo(&self) -> u32 { todo!() } |
179 | 179 | ||
180 | } | 180 | } |
181 | ``` | 181 | ``` |
@@ -582,6 +582,21 @@ impl Walrus { | |||
582 | } | 582 | } |
583 | ``` | 583 | ``` |
584 | 584 | ||
585 | ## `reorder_fields` | ||
586 | |||
587 | Reorder the fields of record literals and record patterns in the same order as in | ||
588 | the definition. | ||
589 | |||
590 | ```rust | ||
591 | // BEFORE | ||
592 | struct Foo {foo: i32, bar: i32}; | ||
593 | const test: Foo = ┃Foo {bar: 0, foo: 1} | ||
594 | |||
595 | // AFTER | ||
596 | struct Foo {foo: i32, bar: i32}; | ||
597 | const test: Foo = Foo {foo: 1, bar: 0} | ||
598 | ``` | ||
599 | |||
585 | ## `replace_if_let_with_match` | 600 | ## `replace_if_let_with_match` |
586 | 601 | ||
587 | Replaces `if let` with an else branch with a `match` expression. | 602 | Replaces `if let` with an else branch with a `match` expression. |
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index eb4f299a1..e11cffd68 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json | |||
@@ -109,9 +109,9 @@ | |||
109 | } | 109 | } |
110 | }, | 110 | }, |
111 | "@types/vscode": { | 111 | "@types/vscode": { |
112 | "version": "1.43.0", | 112 | "version": "1.44.0", |
113 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.43.0.tgz", | 113 | "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.44.0.tgz", |
114 | "integrity": "sha512-kIaR9qzd80rJOxePKpCB/mdy00mz8Apt2QA5Y6rdrKFn13QNFNeP3Hzmsf37Bwh/3cS7QjtAeGSK7wSqAU0sYQ==", | 114 | "integrity": "sha512-WJZtZlinE3meRdH+I7wTsIhpz/GLhqEQwmPGeh4s1irWLwMzCeTV8WZ+pgPTwrDXoafVUWwo1LiZ9HJVHFlJSQ==", |
115 | "dev": true | 115 | "dev": true |
116 | }, | 116 | }, |
117 | "@typescript-eslint/eslint-plugin": { | 117 | "@typescript-eslint/eslint-plugin": { |
@@ -1776,32 +1776,32 @@ | |||
1776 | } | 1776 | } |
1777 | }, | 1777 | }, |
1778 | "vscode-jsonrpc": { | 1778 | "vscode-jsonrpc": { |
1779 | "version": "5.0.1", | 1779 | "version": "5.1.0-next.1", |
1780 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz", | 1780 | "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-5.1.0-next.1.tgz", |
1781 | "integrity": "sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==" | 1781 | "integrity": "sha512-mwLDojZkbmpizSJSmp690oa9FB9jig18SIDGZeBCvFc2/LYSRvMm/WwWtMBJuJ1MfFh7rZXfQige4Uje5Z9NzA==" |
1782 | }, | 1782 | }, |
1783 | "vscode-languageclient": { | 1783 | "vscode-languageclient": { |
1784 | "version": "6.1.3", | 1784 | "version": "7.0.0-next.1", |
1785 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-6.1.3.tgz", | 1785 | "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0-next.1.tgz", |
1786 | "integrity": "sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==", | 1786 | "integrity": "sha512-JrjCUhLpQZxQ5VpWpilOHDMhVsn0fdN5jBh1uFNhSr5c2loJvRdr9Km2EuSQOFfOQsBKx0+xvY8PbsypNEcJ6w==", |
1787 | "requires": { | 1787 | "requires": { |
1788 | "semver": "^6.3.0", | 1788 | "semver": "^6.3.0", |
1789 | "vscode-languageserver-protocol": "^3.15.3" | 1789 | "vscode-languageserver-protocol": "3.16.0-next.2" |
1790 | } | 1790 | } |
1791 | }, | 1791 | }, |
1792 | "vscode-languageserver-protocol": { | 1792 | "vscode-languageserver-protocol": { |
1793 | "version": "3.15.3", | 1793 | "version": "3.16.0-next.2", |
1794 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.3.tgz", | 1794 | "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0-next.2.tgz", |
1795 | "integrity": "sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==", | 1795 | "integrity": "sha512-atmkGT/W6tF0cx4SaWFYtFs2UeSeC28RPiap9myv2YZTaTCFvTBEPNWrU5QRKfkyM0tbgtGo6T3UCQ8tkDpjzA==", |
1796 | "requires": { | 1796 | "requires": { |
1797 | "vscode-jsonrpc": "^5.0.1", | 1797 | "vscode-jsonrpc": "5.1.0-next.1", |
1798 | "vscode-languageserver-types": "3.15.1" | 1798 | "vscode-languageserver-types": "3.16.0-next.1" |
1799 | } | 1799 | } |
1800 | }, | 1800 | }, |
1801 | "vscode-languageserver-types": { | 1801 | "vscode-languageserver-types": { |
1802 | "version": "3.15.1", | 1802 | "version": "3.16.0-next.1", |
1803 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", | 1803 | "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.1.tgz", |
1804 | "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" | 1804 | "integrity": "sha512-tZFUSbyjUcrh+qQf13ALX4QDdOfDX0cVaBFgy7ktJ0VwS7AW/yRKgGPSxVqqP9OCMNPdqP57O5q47w2pEwfaUg==" |
1805 | }, | 1805 | }, |
1806 | "which": { | 1806 | "which": { |
1807 | "version": "1.3.1", | 1807 | "version": "1.3.1", |
diff --git a/editors/code/package.json b/editors/code/package.json index 0bf7b6ae6..33bd66b40 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -34,14 +34,14 @@ | |||
34 | "dependencies": { | 34 | "dependencies": { |
35 | "jsonc-parser": "^2.2.1", | 35 | "jsonc-parser": "^2.2.1", |
36 | "node-fetch": "^2.6.0", | 36 | "node-fetch": "^2.6.0", |
37 | "vscode-languageclient": "6.1.3" | 37 | "vscode-languageclient": "7.0.0-next.1" |
38 | }, | 38 | }, |
39 | "devDependencies": { | 39 | "devDependencies": { |
40 | "@rollup/plugin-commonjs": "^11.0.2", | 40 | "@rollup/plugin-commonjs": "^11.0.2", |
41 | "@rollup/plugin-node-resolve": "^7.1.1", | 41 | "@rollup/plugin-node-resolve": "^7.1.1", |
42 | "@types/node": "^12.12.34", | 42 | "@types/node": "^12.12.34", |
43 | "@types/node-fetch": "^2.5.5", | 43 | "@types/node-fetch": "^2.5.5", |
44 | "@types/vscode": "^1.43.0", | 44 | "@types/vscode": "^1.44.0", |
45 | "@typescript-eslint/eslint-plugin": "^2.27.0", | 45 | "@typescript-eslint/eslint-plugin": "^2.27.0", |
46 | "@typescript-eslint/parser": "^2.27.0", | 46 | "@typescript-eslint/parser": "^2.27.0", |
47 | "eslint": "^6.8.0", | 47 | "eslint": "^6.8.0", |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 21c1c9f23..35a05131c 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -66,23 +66,44 @@ export class Config { | |||
66 | return vscode.workspace.getConfiguration(this.rootSection); | 66 | return vscode.workspace.getConfiguration(this.rootSection); |
67 | } | 67 | } |
68 | 68 | ||
69 | get serverPath() { return this.cfg.get<null | string>("serverPath")!; } | 69 | /** |
70 | get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; } | 70 | * Beware that postfix `!` operator erases both `null` and `undefined`. |
71 | get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; } | 71 | * This is why the following doesn't work as expected: |
72 | get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; } | 72 | * |
73 | * ```ts | ||
74 | * const nullableNum = vscode | ||
75 | * .workspace | ||
76 | * .getConfiguration | ||
77 | * .getConfiguration("rust-analyer") | ||
78 | * .get<number | null>(path)!; | ||
79 | * | ||
80 | * // What happens is that type of `nullableNum` is `number` but not `null | number`: | ||
81 | * const fullFledgedNum: number = nullableNum; | ||
82 | * ``` | ||
83 | * So this getter handles this quirk by not requiring the caller to use postfix `!` | ||
84 | */ | ||
85 | private get<T>(path: string): T { | ||
86 | return this.cfg.get<T>(path)!; | ||
87 | } | ||
88 | |||
89 | get serverPath() { return this.get<null | string>("serverPath"); } | ||
90 | get channel() { return this.get<UpdatesChannel>("updates.channel"); } | ||
91 | get askBeforeDownload() { return this.get<boolean>("updates.askBeforeDownload"); } | ||
92 | get traceExtension() { return this.get<boolean>("trace.extension"); } | ||
93 | |||
73 | 94 | ||
74 | get inlayHints() { | 95 | get inlayHints() { |
75 | return { | 96 | return { |
76 | typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, | 97 | typeHints: this.get<boolean>("inlayHints.typeHints"), |
77 | parameterHints: this.cfg.get<boolean>("inlayHints.parameterHints")!, | 98 | parameterHints: this.get<boolean>("inlayHints.parameterHints"), |
78 | chainingHints: this.cfg.get<boolean>("inlayHints.chainingHints")!, | 99 | chainingHints: this.get<boolean>("inlayHints.chainingHints"), |
79 | maxLength: this.cfg.get<null | number>("inlayHints.maxLength")!, | 100 | maxLength: this.get<null | number>("inlayHints.maxLength"), |
80 | }; | 101 | }; |
81 | } | 102 | } |
82 | 103 | ||
83 | get checkOnSave() { | 104 | get checkOnSave() { |
84 | return { | 105 | return { |
85 | command: this.cfg.get<string>("checkOnSave.command")!, | 106 | command: this.get<string>("checkOnSave.command"), |
86 | }; | 107 | }; |
87 | } | 108 | } |
88 | } | 109 | } |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 6a8bd942e..da74f03d2 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -229,13 +229,13 @@ interface InlaysDecorations { | |||
229 | } | 229 | } |
230 | 230 | ||
231 | interface RustSourceFile { | 231 | interface RustSourceFile { |
232 | /* | 232 | /** |
233 | * Source of the token to cancel in-flight inlay hints request if any. | 233 | * Source of the token to cancel in-flight inlay hints request if any. |
234 | */ | 234 | */ |
235 | inlaysRequest: null | vscode.CancellationTokenSource; | 235 | inlaysRequest: null | vscode.CancellationTokenSource; |
236 | /** | 236 | /** |
237 | * Last applied decorations. | 237 | * Last applied decorations. |
238 | */ | 238 | */ |
239 | cachedDecorations: null | InlaysDecorations; | 239 | cachedDecorations: null | InlaysDecorations; |
240 | 240 | ||
241 | document: RustDocument; | 241 | document: RustDocument; |
diff --git a/xtask/src/ast_src.rs b/xtask/src/ast_src.rs index 69cba9168..9c02f7c6f 100644 --- a/xtask/src/ast_src.rs +++ b/xtask/src/ast_src.rs | |||
@@ -511,7 +511,7 @@ pub(crate) const AST_SRC: AstSrc = AstSrc { | |||
511 | T![..], | 511 | T![..], |
512 | T!['}'] | 512 | T!['}'] |
513 | } | 513 | } |
514 | struct RecordFieldPat: AttrsOwner, NameOwner { T![:], Pat } | 514 | struct RecordFieldPat: AttrsOwner { NameRef, T![:], Pat } |
515 | 515 | ||
516 | struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] } | 516 | struct TupleStructPat { Path, T!['('], args: [Pat], T![')'] } |
517 | struct TuplePat { T!['('], args: [Pat], T![')'] } | 517 | struct TuplePat { T!['('], args: [Pat], T![')'] } |
diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs index 80911a68e..101ae19bd 100644 --- a/xtask/tests/tidy-tests/main.rs +++ b/xtask/tests/tidy-tests/main.rs | |||
@@ -20,7 +20,17 @@ fn rust_files_are_tidy() { | |||
20 | } | 20 | } |
21 | 21 | ||
22 | fn check_todo(path: &Path, text: &str) { | 22 | fn check_todo(path: &Path, text: &str) { |
23 | if path.ends_with("tests/cli.rs") { | 23 | let whitelist = &[ |
24 | // This file itself is whitelisted since this test itself contains matches. | ||
25 | "tests/cli.rs", | ||
26 | // Some of our assists generate `todo!()` so those files are whitelisted. | ||
27 | "doc_tests/generated.rs", | ||
28 | "handlers/add_missing_impl_members.rs", | ||
29 | "handlers/add_function.rs", | ||
30 | // To support generating `todo!()` in assists, we have `expr_todo()` in ast::make. | ||
31 | "ast/make.rs", | ||
32 | ]; | ||
33 | if whitelist.iter().any(|p| path.ends_with(p)) { | ||
24 | return; | 34 | return; |
25 | } | 35 | } |
26 | if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { | 36 | if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { |