diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index 684b8243a4..08a85f6e26 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs @@ -80,7 +80,9 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option buf.push_str(" "); buf.push_str(name.text().as_str()); if let Some(type_params) = type_params { - join(type_params.type_params().filter_map(|it| it.name()).map(|it| it.text())) + let lifetime_params = type_params.lifetime_params().filter_map(|it| it.lifetime()).map(|it| it.text()); + let type_params = type_params.type_params().filter_map(|it| it.name()).map(|it| it.text()); + join(lifetime_params.chain(type_params)) .surround_with("<", ">") .to_buf(&mut buf); } @@ -146,6 +148,11 @@ mod tests { "struct Foo {}\n\nimpl Foo {\n<|>\n}", |file, off| add_impl(file, off).map(|f| f()), ); + check_action( + "struct Foo<'a, T: Foo<'a>> {<|>}", + "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n<|>\n}", + |file, off| add_impl(file, off).map(|f| f()), + ); } } diff --git a/crates/libeditor/src/scope/fn_scope.rs b/crates/libeditor/src/scope/fn_scope.rs index e807c6c71f..a38647c433 100644 --- a/crates/libeditor/src/scope/fn_scope.rs +++ b/crates/libeditor/src/scope/fn_scope.rs @@ -24,11 +24,7 @@ impl FnScopes { scope_for: HashMap::new() }; let root = scopes.root_scope(); - fn_def.param_list().into_iter() - .flat_map(|it| it.params()) - .filter_map(|it| it.pat()) - .for_each(|it| scopes.add_bindings(root, it)); - + scopes.add_params_bindings(root, fn_def.param_list()); if let Some(body) = fn_def.body() { compute_block_scopes(body, &mut scopes, root) } @@ -56,6 +52,12 @@ impl FnScopes { .filter_map(ScopeEntry::new); self.scopes[scope].entries.extend(entries); } + fn add_params_bindings(&mut self, scope: ScopeId, params: Option) { + params.into_iter() + .flat_map(|it| it.params()) + .filter_map(|it| it.pat()) + .for_each(|it| self.add_bindings(scope, it)); + } fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) { self.scope_for.insert(node.owned(), scope); } @@ -102,13 +104,14 @@ fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: Sco for stmt in block.statements() { match stmt { ast::Stmt::LetStmt(stmt) => { + if let Some(expr) = stmt.initializer() { + scopes.set_scope(expr.syntax(), scope); + compute_expr_scopes(expr, scopes, scope); + } 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() { @@ -163,6 +166,20 @@ fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) { compute_block_scopes(block, scopes, scope); } }, + ast::Expr::LambdaExpr(e) => { + let mut scope = scopes.new_scope(scope); + scopes.add_params_bindings(scope, e.param_list()); + if let Some(body) = e.body() { + scopes.set_scope(body.syntax(), scope); + compute_expr_scopes(body, scopes, scope); + } + } + ast::Expr::CallExpr(e) => { + e.arg_list().into_iter() + .flat_map(|it| it.args()) + .chain(e.expr()) + .for_each(|expr| compute_expr_scopes(expr, scopes, scope)); + } _ => { expr.syntax().children() .filter_map(ast::Expr::cast) @@ -189,3 +206,53 @@ struct ScopeData { parent: Option, entries: Vec } + +#[cfg(test)] +mod tests { + use super::*; + use libsyntax2::File; + use {find_node_at_offset, test_utils::extract_offset}; + + fn do_check(code: &str, expected: &[&str]) { + let (off, code) = extract_offset(code); + let code = { + let mut buf = String::new(); + let off = u32::from(off) as usize; + buf.push_str(&code[..off]); + buf.push_str("marker"); + buf.push_str(&code[off..]); + buf + }; + let file = File::parse(&code); + let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap(); + let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap(); + let scopes = FnScopes::new(fn_def); + let actual = scopes.scope_chain(marker.syntax()) + .flat_map(|scope| scopes.entries(scope)) + .map(|it| it.name()) + .collect::>(); + assert_eq!(expected, actual.as_slice()); + } + + #[test] + fn test_lambda_scope() { + do_check(r" + fn quux(foo: i32) { + let f = |bar| { + <|> + }; + }", + &["bar", "foo"], + ); + } + + #[test] + fn test_call_scope() { + do_check(r" + fn quux() { + f(|x| <|> ); + }", + &["x"], + ); + } +} diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index e8743caa8d..d72e2091af 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -4,6 +4,28 @@ use { SyntaxKind::*, }; +// ArgList +#[derive(Debug, Clone, Copy)] +pub struct ArgList<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for ArgList<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + ARG_LIST => Some(ArgList { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> ArgList<'a> { + pub fn args(self) -> impl Iterator> + 'a { + super::children(self) + } +} + // ArrayExpr #[derive(Debug, Clone, Copy)] pub struct ArrayExpr<'a> { @@ -181,7 +203,15 @@ impl<'a> AstNode<'a> for CallExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> CallExpr<'a> {} +impl<'a> CallExpr<'a> { + pub fn expr(self) -> Option> { + super::child_opt(self) + } + + pub fn arg_list(self) -> Option> { + super::child_opt(self) + } +} // CastExpr #[derive(Debug, Clone, Copy)] @@ -705,7 +735,15 @@ impl<'a> AstNode<'a> for LambdaExpr<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> LambdaExpr<'a> {} +impl<'a> LambdaExpr<'a> { + pub fn param_list(self) -> Option> { + super::child_opt(self) + } + + pub fn body(self) -> Option> { + super::child_opt(self) + } +} // LetStmt #[derive(Debug, Clone, Copy)] @@ -733,6 +771,46 @@ impl<'a> LetStmt<'a> { } } +// Lifetime +#[derive(Debug, Clone, Copy)] +pub struct Lifetime<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for Lifetime<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + LIFETIME => Some(Lifetime { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> Lifetime<'a> {} + +// LifetimeParam +#[derive(Debug, Clone, Copy)] +pub struct LifetimeParam<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for LifetimeParam<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + LIFETIME_PARAM => Some(LifetimeParam { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> LifetimeParam<'a> { + pub fn lifetime(self) -> Option> { + super::child_opt(self) + } +} + // Literal #[derive(Debug, Clone, Copy)] pub struct Literal<'a> { @@ -1813,6 +1891,10 @@ impl<'a> TypeParamList<'a> { pub fn type_params(self) -> impl Iterator> + 'a { super::children(self) } + + pub fn lifetime_params(self) -> impl Iterator> + 'a { + super::children(self) + } } // TypeRef diff --git a/crates/libsyntax2/src/ast/mod.rs b/crates/libsyntax2/src/ast/mod.rs index 9941138a7a..3b5e9269f1 100644 --- a/crates/libsyntax2/src/ast/mod.rs +++ b/crates/libsyntax2/src/ast/mod.rs @@ -67,6 +67,12 @@ impl<'a> Attr<'a> { } } +impl<'a> Lifetime<'a> { + pub fn text(&self) -> SmolStr { + self.syntax().leaf_text().unwrap() + } +} + impl<'a> Name<'a> { pub fn text(&self) -> SmolStr { let ident = self.syntax().first_child() diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index f1907d1ce2..3c293d3e43 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -344,7 +344,12 @@ Grammar( "ArrayExpr": (), "ParenExpr": (), "PathExpr": (), - "LambdaExpr": (), + "LambdaExpr": ( + options: [ + ["param_list", "ParamList"], + ["body", "Expr"], + ] + ), "IfExpr": ( options: [ ["condition", "Condition"] ] ), @@ -378,7 +383,12 @@ Grammar( "StructLit": (), "NamedFieldList": (), "NamedField": (), - "CallExpr": (), + "CallExpr": ( + options: [ + [ "expr", "Expr" ], + [ "arg_list", "ArgList" ], + ] + ), "IndexExpr": (), "MethodCallExpr": (), "FieldExpr": (), @@ -457,8 +467,15 @@ Grammar( "NameRef": (), "Attr": ( options: [ ["value", "TokenTree"] ] ), "TokenTree": (), - "TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]), + "TypeParamList": ( + collections: [ + ["type_params", "TypeParam" ], + ["lifetime_params", "LifetimeParam" ], + ] + ), "TypeParam": ( traits: ["NameOwner"] ), + "LifetimeParam": ( options: [ ["lifetime", "Lifetime"] ] ), + "Lifetime": (), "WhereClause": (), "ExprStmt": ( options: [ ["expr", "Expr"] ] @@ -492,5 +509,10 @@ Grammar( ), "UseItem": (), "ExternCrateItem": (), + "ArgList": ( + collections: [ + ["args", "Expr"] + ] + ) }, ) diff --git a/crates/libsyntax2/src/grammar/expressions/atom.rs b/crates/libsyntax2/src/grammar/expressions/atom.rs index 0769bb5a8b..a17c27b31a 100644 --- a/crates/libsyntax2/src/grammar/expressions/atom.rs +++ b/crates/libsyntax2/src/grammar/expressions/atom.rs @@ -151,10 +151,11 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker { p.eat(MOVE_KW); params::param_list_opt_types(p); if opt_fn_ret_type(p) { - block(p); - } else { - expr(p); + if !p.at(L_CURLY) { + p.error("expected `{`"); + } } + expr(p); m.complete(p, LAMBDA_EXPR) }