Support if-let in scopes

This commit is contained in:
Aleksey Kladov 2018-08-27 12:22:09 +03:00
parent c16530c988
commit 07cbb7d73d
12 changed files with 289 additions and 104 deletions

View file

@ -1,4 +1,7 @@
use std::collections::HashMap; use std::{
fmt,
collections::HashMap,
};
use libsyntax2::{ use libsyntax2::{
File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr, File, TextUnit, AstNode, SyntaxNodeRef, SyntaxNode, SmolStr,
@ -49,9 +52,14 @@ fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
.filter_map(|it| it.pat()) .filter_map(|it| it.pat())
.for_each(|it| scopes.add_bindings(root, it)); .for_each(|it| scopes.add_bindings(root, it));
let mut scope = root;
if let Some(body) = fn_def.body() { if let Some(body) = fn_def.body() {
for stmt in body.statements() { 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 { match stmt {
ast::Stmt::LetStmt(stmt) => { ast::Stmt::LetStmt(stmt) => {
scope = scopes.new_scope(scope); scope = scopes.new_scope(scope);
@ -62,20 +70,71 @@ fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
scopes.set_scope(expr.syntax(), scope) scopes.set_scope(expr.syntax(), scope)
} }
} }
ast::Stmt::ExprStmt(expr) => { ast::Stmt::ExprStmt(expr_stmt) => {
scopes.set_scope(expr.syntax(), scope) if let Some(expr) = expr_stmt.expr() {
scopes.set_scope(expr.syntax(), scope);
compute_expr_scopes(expr, scopes, scope);
} }
} }
} }
if let Some(expr) = body.expr() { }
scopes.set_scope(expr.syntax(), 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<ScopeId> {
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
} }
} }
scopes
} }
type ScopeId = usize; type ScopeId = usize;
#[derive(Debug)]
struct FnScopes { struct FnScopes {
scopes: Vec<ScopeData>, scopes: Vec<ScopeData>,
scope_for: HashMap<SyntaxNode, ScopeId>, scope_for: HashMap<SyntaxNode, ScopeId>,
@ -120,6 +179,7 @@ impl FnScopes {
} }
} }
#[derive(Debug)]
struct ScopeData { struct ScopeData {
parent: Option<ScopeId>, parent: Option<ScopeId>,
entries: Vec<ScopeEntry> entries: Vec<ScopeEntry>
@ -149,3 +209,12 @@ impl ScopeEntry {
.unwrap() .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()
}
}

View file

@ -273,6 +273,19 @@ fn quux(x: i32) {
} }
", r#"[CompletionItem { name: "y" }, ", r#"[CompletionItem { name: "y" },
CompletionItem { name: "x" }]"#); 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 { fn file(text: &str) -> File {

View file

@ -141,7 +141,11 @@ impl<'a> AstNode<'a> for BlockExpr<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> BlockExpr<'a> {} impl<'a> BlockExpr<'a> {
pub fn block(self) -> Option<Block<'a>> {
super::child_opt(self)
}
}
// BreakExpr // BreakExpr
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -197,6 +201,32 @@ impl<'a> AstNode<'a> for CastExpr<'a> {
impl<'a> 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<Self> {
match syntax.kind() {
CONDITION => Some(Condition { syntax }),
_ => None,
}
}
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
}
impl<'a> Condition<'a> {
pub fn pat(self) -> Option<Pat<'a>> {
super::child_opt(self)
}
pub fn expr(self) -> Option<Expr<'a>> {
super::child_opt(self)
}
}
// ConstDef // ConstDef
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct ConstDef<'a> { pub struct ConstDef<'a> {
@ -403,7 +433,11 @@ impl<'a> AstNode<'a> for ExprStmt<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> ExprStmt<'a> {} impl<'a> ExprStmt<'a> {
pub fn expr(self) -> Option<Expr<'a>> {
super::child_opt(self)
}
}
// FieldExpr // FieldExpr
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -504,7 +538,11 @@ impl<'a> AstNode<'a> for ForExpr<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> ForExpr<'a> {} impl<'a> ForExpr<'a> {
pub fn body(self) -> Option<Block<'a>> {
super::child_opt(self)
}
}
// ForType // ForType
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -540,7 +578,11 @@ impl<'a> AstNode<'a> for IfExpr<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> IfExpr<'a> {} impl<'a> IfExpr<'a> {
pub fn condition(self) -> Option<Condition<'a>> {
super::child_opt(self)
}
}
// ImplItem // ImplItem
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -674,7 +716,11 @@ impl<'a> AstNode<'a> for LoopExpr<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> LoopExpr<'a> {} impl<'a> LoopExpr<'a> {
pub fn body(self) -> Option<Block<'a>> {
super::child_opt(self)
}
}
// MatchArm // MatchArm
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -1742,5 +1788,13 @@ impl<'a> AstNode<'a> for WhileExpr<'a> {
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
} }
impl<'a> WhileExpr<'a> {} impl<'a> WhileExpr<'a> {
pub fn condition(self) -> Option<Condition<'a>> {
super::child_opt(self)
}
pub fn body(self) -> Option<Block<'a>> {
super::child_opt(self)
}
}

View file

@ -115,6 +115,18 @@ impl<'a> Module<'a> {
} }
} }
impl<'a> IfExpr<'a> {
pub fn then_branch(self) -> Option<Block<'a>> {
self.blocks().nth(0)
}
pub fn else_branch(self) -> Option<Block<'a>> {
self.blocks().nth(1)
}
fn blocks(self) -> impl Iterator<Item=Block<'a>> {
children(self)
}
}
fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> { fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> {
children(parent).next() children(parent).next()
} }

View file

@ -162,9 +162,10 @@ Grammar(
"PATH_EXPR", "PATH_EXPR",
"LAMBDA_EXPR", "LAMBDA_EXPR",
"IF_EXPR", "IF_EXPR",
"WHILE_EXPR",
"CONDITION",
"LOOP_EXPR", "LOOP_EXPR",
"FOR_EXPR", "FOR_EXPR",
"WHILE_EXPR",
"CONTINUE_EXPR", "CONTINUE_EXPR",
"BREAK_EXPR", "BREAK_EXPR",
"LABEL", "LABEL",
@ -336,14 +337,27 @@ Grammar(
"ParenExpr": (), "ParenExpr": (),
"PathExpr": (), "PathExpr": (),
"LambdaExpr": (), "LambdaExpr": (),
"IfExpr": (), "IfExpr": (
"LoopExpr": (), options: [ ["condition", "Condition"] ]
"ForExpr": (), ),
"WhileExpr": (), "LoopExpr": (
options: [ ["body", "Block"] ]
),
"ForExpr": (
options: [ ["body", "Block"] ]
),
"WhileExpr": (
options: [
["condition", "Condition"],
["body", "Block"],
]
),
"ContinueExpr": (), "ContinueExpr": (),
"BreakExpr": (), "BreakExpr": (),
"Label": (), "Label": (),
"BlockExpr": (), "BlockExpr": (
options: [ ["block", "Block"] ]
),
"ReturnExpr": (), "ReturnExpr": (),
"MatchExpr": (), "MatchExpr": (),
"MatchArmList": (), "MatchArmList": (),
@ -432,11 +446,19 @@ Grammar(
"TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]), "TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]),
"TypeParam": ( traits: ["NameOwner"] ), "TypeParam": ( traits: ["NameOwner"] ),
"WhereClause": (), "WhereClause": (),
"ExprStmt": (), "ExprStmt": (
options: [ ["expr", "Expr"] ]
),
"LetStmt": ( options: [ "LetStmt": ( options: [
["pat", "Pat"], ["pat", "Pat"],
["initializer", "Expr"], ["initializer", "Expr"],
]), ]),
"Condition": (
options: [
[ "pat", "Pat" ],
[ "expr", "Expr" ],
]
),
"Stmt": ( "Stmt": (
enum: ["ExprStmt", "LetStmt"], enum: ["ExprStmt", "LetStmt"],
), ),

View file

@ -237,11 +237,13 @@ fn for_expr(p: &mut Parser, m: Option<Marker>) -> CompletedMarker {
// test cond // test cond
// fn foo() { if let Some(_) = None {} } // fn foo() { if let Some(_) = None {} }
fn cond(p: &mut Parser) { fn cond(p: &mut Parser) {
let m = p.start();
if p.eat(LET_KW) { if p.eat(LET_KW) {
patterns::pattern(p); patterns::pattern(p);
p.expect(EQ); p.expect(EQ);
} }
expr_no_struct(p) expr_no_struct(p);
m.complete(p, CONDITION);
} }
// test match_expr // test match_expr

View file

@ -158,9 +158,10 @@ pub enum SyntaxKind {
PATH_EXPR, PATH_EXPR,
LAMBDA_EXPR, LAMBDA_EXPR,
IF_EXPR, IF_EXPR,
WHILE_EXPR,
CONDITION,
LOOP_EXPR, LOOP_EXPR,
FOR_EXPR, FOR_EXPR,
WHILE_EXPR,
CONTINUE_EXPR, CONTINUE_EXPR,
BREAK_EXPR, BREAK_EXPR,
LABEL, LABEL,
@ -418,9 +419,10 @@ impl SyntaxKind {
PATH_EXPR => &SyntaxInfo { name: "PATH_EXPR" }, PATH_EXPR => &SyntaxInfo { name: "PATH_EXPR" },
LAMBDA_EXPR => &SyntaxInfo { name: "LAMBDA_EXPR" }, LAMBDA_EXPR => &SyntaxInfo { name: "LAMBDA_EXPR" },
IF_EXPR => &SyntaxInfo { name: "IF_EXPR" }, IF_EXPR => &SyntaxInfo { name: "IF_EXPR" },
WHILE_EXPR => &SyntaxInfo { name: "WHILE_EXPR" },
CONDITION => &SyntaxInfo { name: "CONDITION" },
LOOP_EXPR => &SyntaxInfo { name: "LOOP_EXPR" }, LOOP_EXPR => &SyntaxInfo { name: "LOOP_EXPR" },
FOR_EXPR => &SyntaxInfo { name: "FOR_EXPR" }, FOR_EXPR => &SyntaxInfo { name: "FOR_EXPR" },
WHILE_EXPR => &SyntaxInfo { name: "WHILE_EXPR" },
CONTINUE_EXPR => &SyntaxInfo { name: "CONTINUE_EXPR" }, CONTINUE_EXPR => &SyntaxInfo { name: "CONTINUE_EXPR" },
BREAK_EXPR => &SyntaxInfo { name: "BREAK_EXPR" }, BREAK_EXPR => &SyntaxInfo { name: "BREAK_EXPR" },
LABEL => &SyntaxInfo { name: "LABEL" }, LABEL => &SyntaxInfo { name: "LABEL" },

View file

@ -15,6 +15,7 @@ ROOT@[0; 107)
IF_EXPR@[15; 25) IF_EXPR@[15; 25)
IF_KW@[15; 17) IF_KW@[15; 17)
WHITESPACE@[17; 18) WHITESPACE@[17; 18)
CONDITION@[18; 22)
LITERAL@[18; 22) LITERAL@[18; 22)
TRUE_KW@[18; 22) TRUE_KW@[18; 22)
WHITESPACE@[22; 23) WHITESPACE@[22; 23)
@ -27,6 +28,7 @@ ROOT@[0; 107)
IF_EXPR@[31; 49) IF_EXPR@[31; 49)
IF_KW@[31; 33) IF_KW@[31; 33)
WHITESPACE@[33; 34) WHITESPACE@[33; 34)
CONDITION@[34; 38)
LITERAL@[34; 38) LITERAL@[34; 38)
TRUE_KW@[34; 38) TRUE_KW@[34; 38)
WHITESPACE@[38; 39) WHITESPACE@[38; 39)
@ -45,6 +47,7 @@ ROOT@[0; 107)
IF_EXPR@[55; 90) IF_EXPR@[55; 90)
IF_KW@[55; 57) IF_KW@[55; 57)
WHITESPACE@[57; 58) WHITESPACE@[57; 58)
CONDITION@[58; 62)
LITERAL@[58; 62) LITERAL@[58; 62)
TRUE_KW@[58; 62) TRUE_KW@[58; 62)
WHITESPACE@[62; 63) WHITESPACE@[62; 63)
@ -57,6 +60,7 @@ ROOT@[0; 107)
IF_EXPR@[71; 90) IF_EXPR@[71; 90)
IF_KW@[71; 73) IF_KW@[71; 73)
WHITESPACE@[73; 74) WHITESPACE@[73; 74)
CONDITION@[74; 79)
LITERAL@[74; 79) LITERAL@[74; 79)
FALSE_KW@[74; 79) FALSE_KW@[74; 79)
WHITESPACE@[79; 80) WHITESPACE@[79; 80)
@ -75,6 +79,7 @@ ROOT@[0; 107)
IF_EXPR@[96; 103) IF_EXPR@[96; 103)
IF_KW@[96; 98) IF_KW@[96; 98)
WHITESPACE@[98; 99) WHITESPACE@[98; 99)
CONDITION@[99; 100)
PATH_EXPR@[99; 100) PATH_EXPR@[99; 100)
PATH@[99; 100) PATH@[99; 100)
PATH_SEGMENT@[99; 100) PATH_SEGMENT@[99; 100)

View file

@ -14,6 +14,7 @@ ROOT@[0; 38)
IF_EXPR@[11; 35) IF_EXPR@[11; 35)
IF_KW@[11; 13) IF_KW@[11; 13)
WHITESPACE@[13; 14) WHITESPACE@[13; 14)
CONDITION@[14; 32)
LET_KW@[14; 17) LET_KW@[14; 17)
WHITESPACE@[17; 18) WHITESPACE@[17; 18)
TUPLE_STRUCT_PAT@[18; 25) TUPLE_STRUCT_PAT@[18; 25)

View file

@ -15,6 +15,7 @@ ROOT@[0; 70)
WHILE_EXPR@[15; 28) WHILE_EXPR@[15; 28)
WHILE_KW@[15; 20) WHILE_KW@[15; 20)
WHITESPACE@[20; 21) WHITESPACE@[20; 21)
CONDITION@[21; 25)
LITERAL@[21; 25) LITERAL@[21; 25)
TRUE_KW@[21; 25) TRUE_KW@[21; 25)
WHITESPACE@[25; 26) WHITESPACE@[25; 26)
@ -27,6 +28,7 @@ ROOT@[0; 70)
WHILE_EXPR@[34; 66) WHILE_EXPR@[34; 66)
WHILE_KW@[34; 39) WHILE_KW@[34; 39)
WHITESPACE@[39; 40) WHITESPACE@[39; 40)
CONDITION@[40; 63)
LET_KW@[40; 43) LET_KW@[40; 43)
WHITESPACE@[43; 44) WHITESPACE@[43; 44)
TUPLE_STRUCT_PAT@[44; 51) TUPLE_STRUCT_PAT@[44; 51)

View file

@ -15,6 +15,7 @@ ROOT@[0; 107)
IF_EXPR@[15; 25) IF_EXPR@[15; 25)
IF_KW@[15; 17) IF_KW@[15; 17)
WHITESPACE@[17; 18) WHITESPACE@[17; 18)
CONDITION@[18; 22)
LITERAL@[18; 22) LITERAL@[18; 22)
TRUE_KW@[18; 22) TRUE_KW@[18; 22)
WHITESPACE@[22; 23) WHITESPACE@[22; 23)
@ -46,6 +47,7 @@ ROOT@[0; 107)
WHILE_EXPR@[58; 71) WHILE_EXPR@[58; 71)
WHILE_KW@[58; 63) WHILE_KW@[58; 63)
WHITESPACE@[63; 64) WHITESPACE@[63; 64)
CONDITION@[64; 68)
LITERAL@[64; 68) LITERAL@[64; 68)
TRUE_KW@[64; 68) TRUE_KW@[64; 68)
WHITESPACE@[68; 69) WHITESPACE@[68; 69)

View file

@ -31,6 +31,7 @@ ROOT@[0; 74)
WHITESPACE@[34; 35) WHITESPACE@[34; 35)
WHILE_KW@[35; 40) WHILE_KW@[35; 40)
WHITESPACE@[40; 41) WHITESPACE@[40; 41)
CONDITION@[41; 45)
LITERAL@[41; 45) LITERAL@[41; 45)
TRUE_KW@[41; 45) TRUE_KW@[41; 45)
WHITESPACE@[45; 46) WHITESPACE@[45; 46)