diff options
-rw-r--r-- | crates/ra_hir/src/expr.rs | 103 | ||||
-rw-r--r-- | crates/ra_hir/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 20 | ||||
-rw-r--r-- | crates/ra_ide_api/src/inlay_hints.rs | 36 | ||||
-rw-r--r-- | crates/ra_parser/src/grammar/type_args.rs | 7 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt | 55 | ||||
-rw-r--r-- | docs/dev/architecture.md | 6 |
8 files changed, 175 insertions, 54 deletions
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 4dcea19a9..f33676655 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -11,17 +11,16 @@ use ra_syntax::{ | |||
11 | }, | 11 | }, |
12 | AstNode, AstPtr, SyntaxNodePtr, | 12 | AstNode, AstPtr, SyntaxNodePtr, |
13 | }; | 13 | }; |
14 | use test_utils::tested_by; | ||
14 | 15 | ||
15 | use crate::{ | 16 | use crate::{ |
16 | name::{AsName, SELF_PARAM}, | 17 | name::{AsName, SELF_PARAM}, |
18 | path::GenericArgs, | ||
19 | ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy}, | ||
17 | type_ref::{Mutability, TypeRef}, | 20 | type_ref::{Mutability, TypeRef}, |
18 | DefWithBody, Either, HasSource, HirDatabase, HirFileId, MacroCallLoc, MacroFileKind, Name, | 21 | DefWithBody, Either, HasSource, HirDatabase, HirFileId, MacroCallLoc, MacroFileKind, Name, |
19 | Path, Resolver, | 22 | Path, Resolver, |
20 | }; | 23 | }; |
21 | use crate::{ | ||
22 | path::GenericArgs, | ||
23 | ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy}, | ||
24 | }; | ||
25 | 24 | ||
26 | pub use self::scope::ExprScopes; | 25 | pub use self::scope::ExprScopes; |
27 | 26 | ||
@@ -558,37 +557,40 @@ where | |||
558 | let syntax_ptr = SyntaxNodePtr::new(expr.syntax()); | 557 | let syntax_ptr = SyntaxNodePtr::new(expr.syntax()); |
559 | match expr.kind() { | 558 | match expr.kind() { |
560 | ast::ExprKind::IfExpr(e) => { | 559 | ast::ExprKind::IfExpr(e) => { |
561 | if let Some(pat) = e.condition().and_then(|c| c.pat()) { | 560 | let then_branch = self.collect_block_opt(e.then_branch()); |
562 | // if let -- desugar to match | 561 | |
563 | let pat = self.collect_pat(pat); | 562 | let else_branch = e.else_branch().map(|b| match b { |
564 | let match_expr = | 563 | ast::ElseBranch::Block(it) => self.collect_block(it), |
565 | self.collect_expr_opt(e.condition().expect("checked above").expr()); | 564 | ast::ElseBranch::IfExpr(elif) => { |
566 | let then_branch = self.collect_block_opt(e.then_branch()); | 565 | let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap(); |
567 | let else_branch = e | 566 | self.collect_expr(expr) |
568 | .else_branch() | 567 | } |
569 | .map(|b| match b { | 568 | }); |
570 | ast::ElseBranch::Block(it) => self.collect_block(it), | 569 | |
571 | ast::ElseBranch::IfExpr(elif) => self.collect_expr(elif.into()), | 570 | let condition = match e.condition() { |
572 | }) | 571 | None => self.exprs.alloc(Expr::Missing), |
573 | .unwrap_or_else(|| self.empty_block()); | 572 | Some(condition) => match condition.pat() { |
574 | let placeholder_pat = self.pats.alloc(Pat::Missing); | 573 | None => self.collect_expr_opt(condition.expr()), |
575 | let arms = vec![ | 574 | // if let -- desugar to match |
576 | MatchArm { pats: vec![pat], expr: then_branch, guard: None }, | 575 | Some(pat) => { |
577 | MatchArm { pats: vec![placeholder_pat], expr: else_branch, guard: None }, | 576 | let pat = self.collect_pat(pat); |
578 | ]; | 577 | let match_expr = self.collect_expr_opt(condition.expr()); |
579 | self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr) | 578 | let placeholder_pat = self.pats.alloc(Pat::Missing); |
580 | } else { | 579 | let arms = vec![ |
581 | let condition = self.collect_expr_opt(e.condition().and_then(|c| c.expr())); | 580 | MatchArm { pats: vec![pat], expr: then_branch, guard: None }, |
582 | let then_branch = self.collect_block_opt(e.then_branch()); | 581 | MatchArm { |
583 | let else_branch = e.else_branch().map(|b| match b { | 582 | pats: vec![placeholder_pat], |
584 | ast::ElseBranch::Block(it) => self.collect_block(it), | 583 | expr: else_branch.unwrap_or_else(|| self.empty_block()), |
585 | ast::ElseBranch::IfExpr(elif) => { | 584 | guard: None, |
586 | let expr: ast::Expr = ast::Expr::cast(elif.syntax().clone()).unwrap(); | 585 | }, |
587 | self.collect_expr(expr) | 586 | ]; |
587 | return self | ||
588 | .alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr); | ||
588 | } | 589 | } |
589 | }); | 590 | }, |
590 | self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr) | 591 | }; |
591 | } | 592 | |
593 | self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr) | ||
592 | } | 594 | } |
593 | ast::ExprKind::TryBlockExpr(e) => { | 595 | ast::ExprKind::TryBlockExpr(e) => { |
594 | let body = self.collect_block_opt(e.try_body()); | 596 | let body = self.collect_block_opt(e.try_body()); |
@@ -600,17 +602,30 @@ where | |||
600 | self.alloc_expr(Expr::Loop { body }, syntax_ptr) | 602 | self.alloc_expr(Expr::Loop { body }, syntax_ptr) |
601 | } | 603 | } |
602 | ast::ExprKind::WhileExpr(e) => { | 604 | ast::ExprKind::WhileExpr(e) => { |
603 | let condition = if let Some(condition) = e.condition() { | ||
604 | if condition.pat().is_none() { | ||
605 | self.collect_expr_opt(condition.expr()) | ||
606 | } else { | ||
607 | // FIXME handle while let | ||
608 | return self.alloc_expr(Expr::Missing, syntax_ptr); | ||
609 | } | ||
610 | } else { | ||
611 | self.exprs.alloc(Expr::Missing) | ||
612 | }; | ||
613 | let body = self.collect_block_opt(e.loop_body()); | 605 | let body = self.collect_block_opt(e.loop_body()); |
606 | |||
607 | let condition = match e.condition() { | ||
608 | None => self.exprs.alloc(Expr::Missing), | ||
609 | Some(condition) => match condition.pat() { | ||
610 | None => self.collect_expr_opt(condition.expr()), | ||
611 | // if let -- desugar to match | ||
612 | Some(pat) => { | ||
613 | tested_by!(infer_while_let); | ||
614 | let pat = self.collect_pat(pat); | ||
615 | let match_expr = self.collect_expr_opt(condition.expr()); | ||
616 | let placeholder_pat = self.pats.alloc(Pat::Missing); | ||
617 | let break_ = self.exprs.alloc(Expr::Break { expr: None }); | ||
618 | let arms = vec![ | ||
619 | MatchArm { pats: vec![pat], expr: body, guard: None }, | ||
620 | MatchArm { pats: vec![placeholder_pat], expr: break_, guard: None }, | ||
621 | ]; | ||
622 | let match_expr = | ||
623 | self.exprs.alloc(Expr::Match { expr: match_expr, arms }); | ||
624 | return self.alloc_expr(Expr::Loop { body: match_expr }, syntax_ptr); | ||
625 | } | ||
626 | }, | ||
627 | }; | ||
628 | |||
614 | self.alloc_expr(Expr::While { condition, body }, syntax_ptr) | 629 | self.alloc_expr(Expr::While { condition, body }, syntax_ptr) |
615 | } | 630 | } |
616 | ast::ExprKind::ForExpr(e) => { | 631 | ast::ExprKind::ForExpr(e) => { |
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index 2d831f0d8..5b15eee90 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs | |||
@@ -10,4 +10,5 @@ test_utils::marks!( | |||
10 | std_prelude | 10 | std_prelude |
11 | match_ergonomics_ref | 11 | match_ergonomics_ref |
12 | trait_resolution_on_fn_type | 12 | trait_resolution_on_fn_type |
13 | infer_while_let | ||
13 | ); | 14 | ); |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index 36dea17a3..d5f7a4d25 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -145,6 +145,26 @@ mod collections { | |||
145 | } | 145 | } |
146 | 146 | ||
147 | #[test] | 147 | #[test] |
148 | fn infer_while_let() { | ||
149 | covers!(infer_while_let); | ||
150 | let (db, pos) = MockDatabase::with_position( | ||
151 | r#" | ||
152 | //- /main.rs | ||
153 | enum Option<T> { Some(T), None } | ||
154 | |||
155 | fn test() { | ||
156 | let foo: Option<f32> = None; | ||
157 | while let Option::Some(x) = foo { | ||
158 | <|>x | ||
159 | } | ||
160 | } | ||
161 | |||
162 | "#, | ||
163 | ); | ||
164 | assert_eq!("f32", type_at_pos(&db, pos)); | ||
165 | } | ||
166 | |||
167 | #[test] | ||
148 | fn infer_basics() { | 168 | fn infer_basics() { |
149 | assert_snapshot_matches!( | 169 | assert_snapshot_matches!( |
150 | infer(r#" | 170 | infer(r#" |
diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 7b9190314..0b3c96d26 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs | |||
@@ -414,13 +414,35 @@ fn main() { | |||
414 | }"#, | 414 | }"#, |
415 | ); | 415 | ); |
416 | 416 | ||
417 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ | 417 | assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r###" |
418 | InlayHint { | 418 | ⋮[ |
419 | range: [166; 170), | 419 | ⋮ InlayHint { |
420 | kind: TypeHint, | 420 | ⋮ range: [166; 170), |
421 | label: "CustomOption<Test>", | 421 | ⋮ kind: TypeHint, |
422 | }, | 422 | ⋮ label: "CustomOption<Test>", |
423 | ]"# | 423 | ⋮ }, |
424 | ⋮ InlayHint { | ||
425 | ⋮ range: [343; 347), | ||
426 | ⋮ kind: TypeHint, | ||
427 | ⋮ label: "&Test", | ||
428 | ⋮ }, | ||
429 | ⋮ InlayHint { | ||
430 | ⋮ range: [401; 402), | ||
431 | ⋮ kind: TypeHint, | ||
432 | ⋮ label: "&CustomOption<u32>", | ||
433 | ⋮ }, | ||
434 | ⋮ InlayHint { | ||
435 | ⋮ range: [404; 405), | ||
436 | ⋮ kind: TypeHint, | ||
437 | ⋮ label: "&u8", | ||
438 | ⋮ }, | ||
439 | ⋮ InlayHint { | ||
440 | ⋮ range: [549; 550), | ||
441 | ⋮ kind: TypeHint, | ||
442 | ⋮ label: "&u32", | ||
443 | ⋮ }, | ||
444 | ⋮] | ||
445 | "### | ||
424 | ); | 446 | ); |
425 | } | 447 | } |
426 | 448 | ||
diff --git a/crates/ra_parser/src/grammar/type_args.rs b/crates/ra_parser/src/grammar/type_args.rs index f391b63db..3db08b280 100644 --- a/crates/ra_parser/src/grammar/type_args.rs +++ b/crates/ra_parser/src/grammar/type_args.rs | |||
@@ -35,6 +35,13 @@ fn type_arg(p: &mut Parser) { | |||
35 | p.bump(); | 35 | p.bump(); |
36 | m.complete(p, LIFETIME_ARG); | 36 | m.complete(p, LIFETIME_ARG); |
37 | } | 37 | } |
38 | // test associated_type_bounds | ||
39 | // fn print_all<T: Iterator<Item: Display>>(printables: T) {} | ||
40 | IDENT if p.nth(1) == T![:] => { | ||
41 | name_ref(p); | ||
42 | type_params::bounds(p); | ||
43 | m.complete(p, ASSOC_TYPE_ARG); | ||
44 | } | ||
38 | IDENT if p.nth(1) == T![=] => { | 45 | IDENT if p.nth(1) == T![=] => { |
39 | name_ref(p); | 46 | name_ref(p); |
40 | p.bump(); | 47 | p.bump(); |
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs new file mode 100644 index 000000000..eb21a657b --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs | |||
@@ -0,0 +1 @@ | |||
fn print_all<T: Iterator<Item: Display>>(printables: T) {} | |||
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt new file mode 100644 index 000000000..33e75510d --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt | |||
@@ -0,0 +1,55 @@ | |||
1 | SOURCE_FILE@[0; 59) | ||
2 | FN_DEF@[0; 58) | ||
3 | FN_KW@[0; 2) "fn" | ||
4 | WHITESPACE@[2; 3) " " | ||
5 | NAME@[3; 12) | ||
6 | IDENT@[3; 12) "print_all" | ||
7 | TYPE_PARAM_LIST@[12; 40) | ||
8 | L_ANGLE@[12; 13) "<" | ||
9 | TYPE_PARAM@[13; 39) | ||
10 | NAME@[13; 14) | ||
11 | IDENT@[13; 14) "T" | ||
12 | COLON@[14; 15) ":" | ||
13 | WHITESPACE@[15; 16) " " | ||
14 | TYPE_BOUND_LIST@[16; 39) | ||
15 | TYPE_BOUND@[16; 39) | ||
16 | PATH_TYPE@[16; 39) | ||
17 | PATH@[16; 39) | ||
18 | PATH_SEGMENT@[16; 39) | ||
19 | NAME_REF@[16; 24) | ||
20 | IDENT@[16; 24) "Iterator" | ||
21 | TYPE_ARG_LIST@[24; 39) | ||
22 | L_ANGLE@[24; 25) "<" | ||
23 | ASSOC_TYPE_ARG@[25; 38) | ||
24 | NAME_REF@[25; 29) | ||
25 | IDENT@[25; 29) "Item" | ||
26 | COLON@[29; 30) ":" | ||
27 | WHITESPACE@[30; 31) " " | ||
28 | TYPE_BOUND_LIST@[31; 38) | ||
29 | TYPE_BOUND@[31; 38) | ||
30 | PATH_TYPE@[31; 38) | ||
31 | PATH@[31; 38) | ||
32 | PATH_SEGMENT@[31; 38) | ||
33 | NAME_REF@[31; 38) | ||
34 | IDENT@[31; 38) "Display" | ||
35 | R_ANGLE@[38; 39) ">" | ||
36 | R_ANGLE@[39; 40) ">" | ||
37 | PARAM_LIST@[40; 55) | ||
38 | L_PAREN@[40; 41) "(" | ||
39 | PARAM@[41; 54) | ||
40 | BIND_PAT@[41; 51) | ||
41 | NAME@[41; 51) | ||
42 | IDENT@[41; 51) "printables" | ||
43 | COLON@[51; 52) ":" | ||
44 | WHITESPACE@[52; 53) " " | ||
45 | PATH_TYPE@[53; 54) | ||
46 | PATH@[53; 54) | ||
47 | PATH_SEGMENT@[53; 54) | ||
48 | NAME_REF@[53; 54) | ||
49 | IDENT@[53; 54) "T" | ||
50 | R_PAREN@[54; 55) ")" | ||
51 | WHITESPACE@[55; 56) " " | ||
52 | BLOCK@[56; 58) | ||
53 | L_CURLY@[56; 57) "{" | ||
54 | R_CURLY@[57; 58) "}" | ||
55 | WHITESPACE@[58; 59) "\n" | ||
diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 9e9651801..1201f6e5a 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md | |||
@@ -70,7 +70,7 @@ Rust syntax tree structure and parser. See | |||
70 | 70 | ||
71 | - [rowan](https://github.com/rust-analyzer/rowan) library is used for constructing syntax trees. | 71 | - [rowan](https://github.com/rust-analyzer/rowan) library is used for constructing syntax trees. |
72 | - `grammar` module is the actual parser. It is a hand-written recursive descent parser, which | 72 | - `grammar` module is the actual parser. It is a hand-written recursive descent parser, which |
73 | produces a sequence of events like "start node X", "finish not Y". It works similarly to [kotlin's parser](https://github.com/JetBrains/kotlin/blob/4d951de616b20feca92f3e9cc9679b2de9e65195/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java), | 73 | produces a sequence of events like "start node X", "finish node Y". It works similarly to [kotlin's parser](https://github.com/JetBrains/kotlin/blob/4d951de616b20feca92f3e9cc9679b2de9e65195/compiler/frontend/src/org/jetbrains/kotlin/parsing/KotlinParsing.java), |
74 | which is a good source of inspiration for dealing with syntax errors and incomplete input. Original [libsyntax parser](https://github.com/rust-lang/rust/blob/6b99adeb11313197f409b4f7c4083c2ceca8a4fe/src/libsyntax/parse/parser.rs) | 74 | which is a good source of inspiration for dealing with syntax errors and incomplete input. Original [libsyntax parser](https://github.com/rust-lang/rust/blob/6b99adeb11313197f409b4f7c4083c2ceca8a4fe/src/libsyntax/parse/parser.rs) |
75 | is what we use for the definition of the Rust language. | 75 | is what we use for the definition of the Rust language. |
76 | - `parser_api/parser_impl` bridges the tree-agnostic parser from `grammar` with `rowan` trees. | 76 | - `parser_api/parser_impl` bridges the tree-agnostic parser from `grammar` with `rowan` trees. |
@@ -83,11 +83,11 @@ Rust syntax tree structure and parser. See | |||
83 | visiting the nodes (this is double plus cool, if you understand how | 83 | visiting the nodes (this is double plus cool, if you understand how |
84 | `Visitor` works, you understand the design of syntax trees). | 84 | `Visitor` works, you understand the design of syntax trees). |
85 | 85 | ||
86 | Tests for ra_syntax are mostly data-driven: `tests/data/parser` contains a bunch of `.rs` | 86 | Tests for ra_syntax are mostly data-driven: `test_data/parser` contains subdirectories with a bunch of `.rs` |
87 | (test vectors) and `.txt` files with corresponding syntax trees. During testing, we check | 87 | (test vectors) and `.txt` files with corresponding syntax trees. During testing, we check |
88 | `.rs` against `.txt`. If the `.txt` file is missing, it is created (this is how you update | 88 | `.rs` against `.txt`. If the `.txt` file is missing, it is created (this is how you update |
89 | tests). Additionally, running `cargo gen-tests` will walk the grammar module and collect | 89 | tests). Additionally, running `cargo gen-tests` will walk the grammar module and collect |
90 | all `//test test_name` comments into files inside `tests/data` directory. | 90 | all `// test test_name` comments into files inside `test_data/parser/inline` directory. |
91 | 91 | ||
92 | See [#93](https://github.com/rust-analyzer/rust-analyzer/pull/93) for an example PR which | 92 | See [#93](https://github.com/rust-analyzer/rust-analyzer/pull/93) for an example PR which |
93 | fixes a bug in the grammar. | 93 | fixes a bug in the grammar. |