From 07cbb7d73deed8dac3eecdbdc7e1eaf6938a6cd6 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 27 Aug 2018 12:22:09 +0300 Subject: [PATCH] Support if-let in scopes --- crates/libeditor/src/completion.rs | 111 ++++++++++++++---- crates/libeditor/tests/test.rs | 13 ++ crates/libsyntax2/src/ast/generated.rs | 66 ++++++++++- crates/libsyntax2/src/ast/mod.rs | 12 ++ crates/libsyntax2/src/grammar.ron | 36 ++++-- .../src/grammar/expressions/atom.rs | 4 +- .../libsyntax2/src/syntax_kinds/generated.rs | 6 +- .../tests/data/parser/inline/0065_if_expr.txt | 31 +++-- .../tests/data/parser/inline/0076_cond.txt | 39 +++--- .../data/parser/inline/0077_while_expr.txt | 60 +++++----- .../inline/0086_no_semi_after_block.txt | 10 +- .../tests/data/parser/inline/0107_label.txt | 5 +- 12 files changed, 289 insertions(+), 104 deletions(-) diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index ec9388ee3a..69c039e832 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -1,4 +1,7 @@ -use std::collections::HashMap; +use std::{ + fmt, + collections::HashMap, +}; use libsyntax2::{ File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr, @@ -49,33 +52,89 @@ fn compute_scopes(fn_def: ast::FnDef) -> FnScopes { .filter_map(|it| it.pat()) .for_each(|it| scopes.add_bindings(root, it)); - let mut scope = root; if let Some(body) = fn_def.body() { - for stmt in body.statements() { - match stmt { - ast::Stmt::LetStmt(stmt) => { - scope = scopes.new_scope(scope); - if let Some(pat) = stmt.pat() { - scopes.add_bindings(scope, pat); - } - if let Some(expr) = stmt.initializer() { - scopes.set_scope(expr.syntax(), scope) - } - } - ast::Stmt::ExprStmt(expr) => { - scopes.set_scope(expr.syntax(), scope) - } - } - } - if let Some(expr) = body.expr() { - scopes.set_scope(expr.syntax(), scope) - } + compute_block_scopes(body, &mut scopes, root) } scopes } +fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) { + for stmt in block.statements() { + match stmt { + ast::Stmt::LetStmt(stmt) => { + scope = scopes.new_scope(scope); + if let Some(pat) = stmt.pat() { + scopes.add_bindings(scope, pat); + } + if let Some(expr) = stmt.initializer() { + scopes.set_scope(expr.syntax(), scope) + } + } + ast::Stmt::ExprStmt(expr_stmt) => { + if let Some(expr) = expr_stmt.expr() { + scopes.set_scope(expr.syntax(), scope); + compute_expr_scopes(expr, scopes, scope); + } + } + } + } + if let Some(expr) = block.expr() { + scopes.set_scope(expr.syntax(), scope); + compute_expr_scopes(expr, scopes, scope); + } +} + +fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { + match expr { + ast::Expr::IfExpr(e) => { + let cond_scope = e.condition().and_then(|cond| { + compute_cond_scopes(cond, scopes, scope) + }); + if let Some(block) = e.then_branch() { + compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); + } + if let Some(block) = e.else_branch() { + compute_block_scopes(block, scopes, scope); + } + }, + ast::Expr::WhileExpr(e) => { + let cond_scope = e.condition().and_then(|cond| { + compute_cond_scopes(cond, scopes, scope) + }); + if let Some(block) = e.body() { + compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope)); + } + }, + ast::Expr::BlockExpr(e) => { + if let Some(block) = e.block() { + compute_block_scopes(block, scopes, scope); + } + } + // ForExpr(e) => TODO, + _ => { + expr.syntax().children() + .filter_map(ast::Expr::cast) + .for_each(|expr| compute_expr_scopes(expr, scopes, scope)) + } + }; + + fn compute_cond_scopes(cond: ast::Condition, scopes: &mut FnScopes, scope: ScopeId) -> Option { + if let Some(expr) = cond.expr() { + compute_expr_scopes(expr, scopes, scope); + } + if let Some(pat) = cond.pat() { + let s = scopes.new_scope(scope); + scopes.add_bindings(s, pat); + Some(s) + } else { + None + } + } +} + type ScopeId = usize; +#[derive(Debug)] struct FnScopes { scopes: Vec, scope_for: HashMap, @@ -120,6 +179,7 @@ impl FnScopes { } } +#[derive(Debug)] struct ScopeData { parent: Option, entries: Vec @@ -149,3 +209,12 @@ impl ScopeEntry { .unwrap() } } + +impl fmt::Debug for ScopeEntry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("ScopeEntry") + .field("name", &self.name()) + .field("syntax", &self.syntax) + .finish() + } +} diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index 7979bfffe1..d051980b08 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs @@ -273,6 +273,19 @@ fn quux(x: i32) { } ", r#"[CompletionItem { name: "y" }, CompletionItem { name: "x" }]"#); + + do_check(r" +fn quux() { + if let Some(x) = foo() { + let y = 92; + }; + if let Some(a) = bar() { + let b = 62; + 1 + <|> + } +} +", r#"[CompletionItem { name: "b" }, + CompletionItem { name: "a" }]"#); } fn file(text: &str) -> File { diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 1dd161f527..6891e857c2 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -141,7 +141,11 @@ impl<'a> AstNode<'a> for BlockExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> BlockExpr<'a> {} +impl<'a> BlockExpr<'a> { + pub fn block(self) -> Option> { + super::child_opt(self) + } +} // BreakExpr #[derive(Debug, Clone, Copy)] @@ -197,6 +201,32 @@ impl<'a> AstNode<'a> for CastExpr<'a> { impl<'a> CastExpr<'a> {} +// Condition +#[derive(Debug, Clone, Copy)] +pub struct Condition<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for Condition<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + CONDITION => Some(Condition { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> Condition<'a> { + pub fn pat(self) -> Option> { + super::child_opt(self) + } + + pub fn expr(self) -> Option> { + super::child_opt(self) + } +} + // ConstDef #[derive(Debug, Clone, Copy)] pub struct ConstDef<'a> { @@ -403,7 +433,11 @@ impl<'a> AstNode<'a> for ExprStmt<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> ExprStmt<'a> {} +impl<'a> ExprStmt<'a> { + pub fn expr(self) -> Option> { + super::child_opt(self) + } +} // FieldExpr #[derive(Debug, Clone, Copy)] @@ -504,7 +538,11 @@ impl<'a> AstNode<'a> for ForExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> ForExpr<'a> {} +impl<'a> ForExpr<'a> { + pub fn body(self) -> Option> { + super::child_opt(self) + } +} // ForType #[derive(Debug, Clone, Copy)] @@ -540,7 +578,11 @@ impl<'a> AstNode<'a> for IfExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> IfExpr<'a> {} +impl<'a> IfExpr<'a> { + pub fn condition(self) -> Option> { + super::child_opt(self) + } +} // ImplItem #[derive(Debug, Clone, Copy)] @@ -674,7 +716,11 @@ impl<'a> AstNode<'a> for LoopExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> LoopExpr<'a> {} +impl<'a> LoopExpr<'a> { + pub fn body(self) -> Option> { + super::child_opt(self) + } +} // MatchArm #[derive(Debug, Clone, Copy)] @@ -1742,5 +1788,13 @@ impl<'a> AstNode<'a> for WhileExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> WhileExpr<'a> {} +impl<'a> WhileExpr<'a> { + pub fn condition(self) -> Option> { + super::child_opt(self) + } + + pub fn body(self) -> Option> { + super::child_opt(self) + } +} diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index 6217c5b74f..2ebee6a4f3 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs @@ -115,6 +115,18 @@ impl<'a> Module<'a> { } } +impl<'a> IfExpr<'a> { + pub fn then_branch(self) -> Option> { + self.blocks().nth(0) + } + pub fn else_branch(self) -> Option> { + self.blocks().nth(1) + } + fn blocks(self) -> impl Iterator> { + children(self) + } +} + fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option { children(parent).next() } diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index f3c3d30367..c9e128462b 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -162,9 +162,10 @@ Grammar( "PATH_EXPR", "LAMBDA_EXPR", "IF_EXPR", + "WHILE_EXPR", + "CONDITION", "LOOP_EXPR", "FOR_EXPR", - "WHILE_EXPR", "CONTINUE_EXPR", "BREAK_EXPR", "LABEL", @@ -336,14 +337,27 @@ Grammar( "ParenExpr": (), "PathExpr": (), "LambdaExpr": (), - "IfExpr": (), - "LoopExpr": (), - "ForExpr": (), - "WhileExpr": (), + "IfExpr": ( + options: [ ["condition", "Condition"] ] + ), + "LoopExpr": ( + options: [ ["body", "Block"] ] + ), + "ForExpr": ( + options: [ ["body", "Block"] ] + ), + "WhileExpr": ( + options: [ + ["condition", "Condition"], + ["body", "Block"], + ] + ), "ContinueExpr": (), "BreakExpr": (), "Label": (), - "BlockExpr": (), + "BlockExpr": ( + options: [ ["block", "Block"] ] + ), "ReturnExpr": (), "MatchExpr": (), "MatchArmList": (), @@ -432,11 +446,19 @@ Grammar( "TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]), "TypeParam": ( traits: ["NameOwner"] ), "WhereClause": (), - "ExprStmt": (), + "ExprStmt": ( + options: [ ["expr", "Expr"] ] + ), "LetStmt": ( options: [ ["pat", "Pat"], ["initializer", "Expr"], ]), + "Condition": ( + options: [ + [ "pat", "Pat" ], + [ "expr", "Expr" ], + ] + ), "Stmt": ( enum: ["ExprStmt", "LetStmt"], ), diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs index d9c3f998af..bb5402af7f 100644 --- a/crates/libsyntax2/src/grammar/expressions/atom.rs +++ b/crates/libsyntax2/src/grammar/expressions/atom.rs @@ -237,11 +237,13 @@ fn for_expr(p: &mut Parser, m: Option) -> CompletedMarker { // test cond // fn foo() { if let Some(_) = None {} } fn cond(p: &mut Parser) { + let m = p.start(); if p.eat(LET_KW) { patterns::pattern(p); p.expect(EQ); } - expr_no_struct(p) + expr_no_struct(p); + m.complete(p, CONDITION); } // test match_expr diff --git a/crates/libsyntax2/src/syntax_kinds/generated.rs b/crates/libsyntax2/src/syntax_kinds/generated.rs index 6a24cb19e6..0a22b11c22 100644 --- a/crates/libsyntax2/src/syntax_kinds/generated.rs +++ b/crates/libsyntax2/src/syntax_kinds/generated.rs @@ -158,9 +158,10 @@ pub enum SyntaxKind { PATH_EXPR, LAMBDA_EXPR, IF_EXPR, + WHILE_EXPR, + CONDITION, LOOP_EXPR, FOR_EXPR, - WHILE_EXPR, CONTINUE_EXPR, BREAK_EXPR, LABEL, @@ -418,9 +419,10 @@ impl SyntaxKind { PATH_EXPR => &SyntaxInfo { name: "PATH_EXPR" }, LAMBDA_EXPR => &SyntaxInfo { name: "LAMBDA_EXPR" }, IF_EXPR => &SyntaxInfo { name: "IF_EXPR" }, + WHILE_EXPR => &SyntaxInfo { name: "WHILE_EXPR" }, + CONDITION => &SyntaxInfo { name: "CONDITION" }, LOOP_EXPR => &SyntaxInfo { name: "LOOP_EXPR" }, FOR_EXPR => &SyntaxInfo { name: "FOR_EXPR" }, - WHILE_EXPR => &SyntaxInfo { name: "WHILE_EXPR" }, CONTINUE_EXPR => &SyntaxInfo { name: "CONTINUE_EXPR" }, BREAK_EXPR => &SyntaxInfo { name: "BREAK_EXPR" }, LABEL => &SyntaxInfo { name: "LABEL" }, diff --git a/crates/libsyntax2/tests/data/parser/inline/0065_if_expr.txt b/crates/libsyntax2/tests/data/parser/inline/0065_if_expr.txt index 290b11d33e..ae1a8101eb 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0065_if_expr.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0065_if_expr.txt @@ -15,8 +15,9 @@ ROOT@[0; 107) IF_EXPR@[15; 25) IF_KW@[15; 17) WHITESPACE@[17; 18) - LITERAL@[18; 22) - TRUE_KW@[18; 22) + CONDITION@[18; 22) + LITERAL@[18; 22) + TRUE_KW@[18; 22) WHITESPACE@[22; 23) BLOCK@[23; 25) L_CURLY@[23; 24) @@ -27,8 +28,9 @@ ROOT@[0; 107) IF_EXPR@[31; 49) IF_KW@[31; 33) WHITESPACE@[33; 34) - LITERAL@[34; 38) - TRUE_KW@[34; 38) + CONDITION@[34; 38) + LITERAL@[34; 38) + TRUE_KW@[34; 38) WHITESPACE@[38; 39) BLOCK@[39; 41) L_CURLY@[39; 40) @@ -45,8 +47,9 @@ ROOT@[0; 107) IF_EXPR@[55; 90) IF_KW@[55; 57) WHITESPACE@[57; 58) - LITERAL@[58; 62) - TRUE_KW@[58; 62) + CONDITION@[58; 62) + LITERAL@[58; 62) + TRUE_KW@[58; 62) WHITESPACE@[62; 63) BLOCK@[63; 65) L_CURLY@[63; 64) @@ -57,8 +60,9 @@ ROOT@[0; 107) IF_EXPR@[71; 90) IF_KW@[71; 73) WHITESPACE@[73; 74) - LITERAL@[74; 79) - FALSE_KW@[74; 79) + CONDITION@[74; 79) + LITERAL@[74; 79) + FALSE_KW@[74; 79) WHITESPACE@[79; 80) BLOCK@[80; 82) L_CURLY@[80; 81) @@ -75,11 +79,12 @@ ROOT@[0; 107) IF_EXPR@[96; 103) IF_KW@[96; 98) WHITESPACE@[98; 99) - PATH_EXPR@[99; 100) - PATH@[99; 100) - PATH_SEGMENT@[99; 100) - NAME_REF@[99; 100) - IDENT@[99; 100) "S" + CONDITION@[99; 100) + PATH_EXPR@[99; 100) + PATH@[99; 100) + PATH_SEGMENT@[99; 100) + NAME_REF@[99; 100) + IDENT@[99; 100) "S" WHITESPACE@[100; 101) BLOCK@[101; 103) L_CURLY@[101; 102) diff --git a/crates/libsyntax2/tests/data/parser/inline/0076_cond.txt b/crates/libsyntax2/tests/data/parser/inline/0076_cond.txt index 547d66fb3f..1808f10fbe 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0076_cond.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0076_cond.txt @@ -14,25 +14,26 @@ ROOT@[0; 38) IF_EXPR@[11; 35) IF_KW@[11; 13) WHITESPACE@[13; 14) - LET_KW@[14; 17) - WHITESPACE@[17; 18) - TUPLE_STRUCT_PAT@[18; 25) - PATH@[18; 22) - PATH_SEGMENT@[18; 22) - NAME_REF@[18; 22) - IDENT@[18; 22) "Some" - L_PAREN@[22; 23) - PLACEHOLDER_PAT@[23; 24) - UNDERSCORE@[23; 24) - R_PAREN@[24; 25) - WHITESPACE@[25; 26) - EQ@[26; 27) - WHITESPACE@[27; 28) - PATH_EXPR@[28; 32) - PATH@[28; 32) - PATH_SEGMENT@[28; 32) - NAME_REF@[28; 32) - IDENT@[28; 32) "None" + CONDITION@[14; 32) + LET_KW@[14; 17) + WHITESPACE@[17; 18) + TUPLE_STRUCT_PAT@[18; 25) + PATH@[18; 22) + PATH_SEGMENT@[18; 22) + NAME_REF@[18; 22) + IDENT@[18; 22) "Some" + L_PAREN@[22; 23) + PLACEHOLDER_PAT@[23; 24) + UNDERSCORE@[23; 24) + R_PAREN@[24; 25) + WHITESPACE@[25; 26) + EQ@[26; 27) + WHITESPACE@[27; 28) + PATH_EXPR@[28; 32) + PATH@[28; 32) + PATH_SEGMENT@[28; 32) + NAME_REF@[28; 32) + IDENT@[28; 32) "None" WHITESPACE@[32; 33) BLOCK@[33; 35) L_CURLY@[33; 34) diff --git a/crates/libsyntax2/tests/data/parser/inline/0077_while_expr.txt b/crates/libsyntax2/tests/data/parser/inline/0077_while_expr.txt index 0ce1577104..c73157c043 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0077_while_expr.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0077_while_expr.txt @@ -15,8 +15,9 @@ ROOT@[0; 70) WHILE_EXPR@[15; 28) WHILE_KW@[15; 20) WHITESPACE@[20; 21) - LITERAL@[21; 25) - TRUE_KW@[21; 25) + CONDITION@[21; 25) + LITERAL@[21; 25) + TRUE_KW@[21; 25) WHITESPACE@[25; 26) BLOCK@[26; 28) L_CURLY@[26; 27) @@ -27,33 +28,34 @@ ROOT@[0; 70) WHILE_EXPR@[34; 66) WHILE_KW@[34; 39) WHITESPACE@[39; 40) - LET_KW@[40; 43) - WHITESPACE@[43; 44) - TUPLE_STRUCT_PAT@[44; 51) - PATH@[44; 48) - PATH_SEGMENT@[44; 48) - NAME_REF@[44; 48) - IDENT@[44; 48) "Some" - L_PAREN@[48; 49) - BIND_PAT@[49; 50) - NAME@[49; 50) - IDENT@[49; 50) "x" - R_PAREN@[50; 51) - WHITESPACE@[51; 52) - EQ@[52; 53) - WHITESPACE@[53; 54) - METHOD_CALL_EXPR@[54; 63) - PATH_EXPR@[54; 56) - PATH@[54; 56) - PATH_SEGMENT@[54; 56) - NAME_REF@[54; 56) - IDENT@[54; 56) "it" - DOT@[56; 57) - NAME_REF@[57; 61) - IDENT@[57; 61) "next" - ARG_LIST@[61; 63) - L_PAREN@[61; 62) - R_PAREN@[62; 63) + CONDITION@[40; 63) + LET_KW@[40; 43) + WHITESPACE@[43; 44) + TUPLE_STRUCT_PAT@[44; 51) + PATH@[44; 48) + PATH_SEGMENT@[44; 48) + NAME_REF@[44; 48) + IDENT@[44; 48) "Some" + L_PAREN@[48; 49) + BIND_PAT@[49; 50) + NAME@[49; 50) + IDENT@[49; 50) "x" + R_PAREN@[50; 51) + WHITESPACE@[51; 52) + EQ@[52; 53) + WHITESPACE@[53; 54) + METHOD_CALL_EXPR@[54; 63) + PATH_EXPR@[54; 56) + PATH@[54; 56) + PATH_SEGMENT@[54; 56) + NAME_REF@[54; 56) + IDENT@[54; 56) "it" + DOT@[56; 57) + NAME_REF@[57; 61) + IDENT@[57; 61) "next" + ARG_LIST@[61; 63) + L_PAREN@[61; 62) + R_PAREN@[62; 63) WHITESPACE@[63; 64) BLOCK@[64; 66) L_CURLY@[64; 65) diff --git a/crates/libsyntax2/tests/data/parser/inline/0086_no_semi_after_block.txt b/crates/libsyntax2/tests/data/parser/inline/0086_no_semi_after_block.txt index e3164f9cc3..f1b0e27878 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0086_no_semi_after_block.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0086_no_semi_after_block.txt @@ -15,8 +15,9 @@ ROOT@[0; 107) IF_EXPR@[15; 25) IF_KW@[15; 17) WHITESPACE@[17; 18) - LITERAL@[18; 22) - TRUE_KW@[18; 22) + CONDITION@[18; 22) + LITERAL@[18; 22) + TRUE_KW@[18; 22) WHITESPACE@[22; 23) BLOCK@[23; 25) L_CURLY@[23; 24) @@ -46,8 +47,9 @@ ROOT@[0; 107) WHILE_EXPR@[58; 71) WHILE_KW@[58; 63) WHITESPACE@[63; 64) - LITERAL@[64; 68) - TRUE_KW@[64; 68) + CONDITION@[64; 68) + LITERAL@[64; 68) + TRUE_KW@[64; 68) WHITESPACE@[68; 69) BLOCK@[69; 71) L_CURLY@[69; 70) diff --git a/crates/libsyntax2/tests/data/parser/inline/0107_label.txt b/crates/libsyntax2/tests/data/parser/inline/0107_label.txt index ccf22d8df8..7685155955 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0107_label.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0107_label.txt @@ -31,8 +31,9 @@ ROOT@[0; 74) WHITESPACE@[34; 35) WHILE_KW@[35; 40) WHITESPACE@[40; 41) - LITERAL@[41; 45) - TRUE_KW@[41; 45) + CONDITION@[41; 45) + LITERAL@[41; 45) + TRUE_KW@[41; 45) WHITESPACE@[45; 46) BLOCK@[46; 48) L_CURLY@[46; 47)