aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/expr.rs103
-rw-r--r--crates/ra_hir/src/marks.rs1
-rw-r--r--crates/ra_hir/src/ty/tests.rs20
-rw-r--r--crates/ra_ide_api/src/inlay_hints.rs36
-rw-r--r--crates/ra_parser/src/grammar/type_args.rs7
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0138_associated_type_bounds.txt55
-rw-r--r--docs/dev/architecture.md6
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};
14use test_utils::tested_by;
14 15
15use crate::{ 16use 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};
21use crate::{
22 path::GenericArgs,
23 ty::primitive::{FloatTy, IntTy, UncertainFloatTy, UncertainIntTy},
24};
25 24
26pub use self::scope::ExprScopes; 25pub 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]
148fn infer_while_let() {
149 covers!(infer_while_let);
150 let (db, pos) = MockDatabase::with_position(
151 r#"
152//- /main.rs
153enum Option<T> { Some(T), None }
154
155fn 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]
148fn infer_basics() { 168fn 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 @@
1SOURCE_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
86Tests for ra_syntax are mostly data-driven: `tests/data/parser` contains a bunch of `.rs` 86Tests 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
89tests). Additionally, running `cargo gen-tests` will walk the grammar module and collect 89tests). Additionally, running `cargo gen-tests` will walk the grammar module and collect
90all `//test test_name` comments into files inside `tests/data` directory. 90all `// test test_name` comments into files inside `test_data/parser/inline` directory.
91 91
92See [#93](https://github.com/rust-analyzer/rust-analyzer/pull/93) for an example PR which 92See [#93](https://github.com/rust-analyzer/rust-analyzer/pull/93) for an example PR which
93fixes a bug in the grammar. 93fixes a bug in the grammar.