mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
add impl works with lifetimes
This commit is contained in:
parent
ba02a55330
commit
15f15d92eb
6 changed files with 202 additions and 17 deletions
|
@ -80,7 +80,9 @@ pub fn add_impl<'a>(file: &'a File, offset: TextUnit) -> Option<impl FnOnce() ->
|
||||||
buf.push_str(" ");
|
buf.push_str(" ");
|
||||||
buf.push_str(name.text().as_str());
|
buf.push_str(name.text().as_str());
|
||||||
if let Some(type_params) = type_params {
|
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("<", ">")
|
.surround_with("<", ">")
|
||||||
.to_buf(&mut buf);
|
.to_buf(&mut buf);
|
||||||
}
|
}
|
||||||
|
@ -146,6 +148,11 @@ mod tests {
|
||||||
"struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
|
"struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n<|>\n}",
|
||||||
|file, off| add_impl(file, off).map(|f| f()),
|
|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()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,7 @@ impl FnScopes {
|
||||||
scope_for: HashMap::new()
|
scope_for: HashMap::new()
|
||||||
};
|
};
|
||||||
let root = scopes.root_scope();
|
let root = scopes.root_scope();
|
||||||
fn_def.param_list().into_iter()
|
scopes.add_params_bindings(root, fn_def.param_list());
|
||||||
.flat_map(|it| it.params())
|
|
||||||
.filter_map(|it| it.pat())
|
|
||||||
.for_each(|it| scopes.add_bindings(root, it));
|
|
||||||
|
|
||||||
if let Some(body) = fn_def.body() {
|
if let Some(body) = fn_def.body() {
|
||||||
compute_block_scopes(body, &mut scopes, root)
|
compute_block_scopes(body, &mut scopes, root)
|
||||||
}
|
}
|
||||||
|
@ -56,6 +52,12 @@ impl FnScopes {
|
||||||
.filter_map(ScopeEntry::new);
|
.filter_map(ScopeEntry::new);
|
||||||
self.scopes[scope].entries.extend(entries);
|
self.scopes[scope].entries.extend(entries);
|
||||||
}
|
}
|
||||||
|
fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
|
||||||
|
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) {
|
fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
|
||||||
self.scope_for.insert(node.owned(), scope);
|
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() {
|
for stmt in block.statements() {
|
||||||
match stmt {
|
match stmt {
|
||||||
ast::Stmt::LetStmt(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);
|
scope = scopes.new_scope(scope);
|
||||||
if let Some(pat) = stmt.pat() {
|
if let Some(pat) = stmt.pat() {
|
||||||
scopes.add_bindings(scope, pat);
|
scopes.add_bindings(scope, pat);
|
||||||
}
|
}
|
||||||
if let Some(expr) = stmt.initializer() {
|
|
||||||
scopes.set_scope(expr.syntax(), scope)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ast::Stmt::ExprStmt(expr_stmt) => {
|
ast::Stmt::ExprStmt(expr_stmt) => {
|
||||||
if let Some(expr) = expr_stmt.expr() {
|
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);
|
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()
|
expr.syntax().children()
|
||||||
.filter_map(ast::Expr::cast)
|
.filter_map(ast::Expr::cast)
|
||||||
|
@ -189,3 +206,53 @@ struct ScopeData {
|
||||||
parent: Option<ScopeId>,
|
parent: Option<ScopeId>,
|
||||||
entries: Vec<ScopeEntry>
|
entries: Vec<ScopeEntry>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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::<Vec<_>>();
|
||||||
|
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"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,28 @@ use {
|
||||||
SyntaxKind::*,
|
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<Self> {
|
||||||
|
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<Item = Expr<'a>> + 'a {
|
||||||
|
super::children(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ArrayExpr
|
// ArrayExpr
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ArrayExpr<'a> {
|
pub struct ArrayExpr<'a> {
|
||||||
|
@ -181,7 +203,15 @@ impl<'a> AstNode<'a> for CallExpr<'a> {
|
||||||
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CallExpr<'a> {}
|
impl<'a> CallExpr<'a> {
|
||||||
|
pub fn expr(self) -> Option<Expr<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn arg_list(self) -> Option<ArgList<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CastExpr
|
// CastExpr
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -705,7 +735,15 @@ impl<'a> AstNode<'a> for LambdaExpr<'a> {
|
||||||
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LambdaExpr<'a> {}
|
impl<'a> LambdaExpr<'a> {
|
||||||
|
pub fn param_list(self) -> Option<ParamList<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn body(self) -> Option<Expr<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// LetStmt
|
// LetStmt
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[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<Self> {
|
||||||
|
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<Self> {
|
||||||
|
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<Lifetime<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Literal
|
// Literal
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct Literal<'a> {
|
pub struct Literal<'a> {
|
||||||
|
@ -1813,6 +1891,10 @@ impl<'a> TypeParamList<'a> {
|
||||||
pub fn type_params(self) -> impl Iterator<Item = TypeParam<'a>> + 'a {
|
pub fn type_params(self) -> impl Iterator<Item = TypeParam<'a>> + 'a {
|
||||||
super::children(self)
|
super::children(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lifetime_params(self) -> impl Iterator<Item = LifetimeParam<'a>> + 'a {
|
||||||
|
super::children(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TypeRef
|
// TypeRef
|
||||||
|
|
|
@ -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> {
|
impl<'a> Name<'a> {
|
||||||
pub fn text(&self) -> SmolStr {
|
pub fn text(&self) -> SmolStr {
|
||||||
let ident = self.syntax().first_child()
|
let ident = self.syntax().first_child()
|
||||||
|
|
|
@ -344,7 +344,12 @@ Grammar(
|
||||||
"ArrayExpr": (),
|
"ArrayExpr": (),
|
||||||
"ParenExpr": (),
|
"ParenExpr": (),
|
||||||
"PathExpr": (),
|
"PathExpr": (),
|
||||||
"LambdaExpr": (),
|
"LambdaExpr": (
|
||||||
|
options: [
|
||||||
|
["param_list", "ParamList"],
|
||||||
|
["body", "Expr"],
|
||||||
|
]
|
||||||
|
),
|
||||||
"IfExpr": (
|
"IfExpr": (
|
||||||
options: [ ["condition", "Condition"] ]
|
options: [ ["condition", "Condition"] ]
|
||||||
),
|
),
|
||||||
|
@ -378,7 +383,12 @@ Grammar(
|
||||||
"StructLit": (),
|
"StructLit": (),
|
||||||
"NamedFieldList": (),
|
"NamedFieldList": (),
|
||||||
"NamedField": (),
|
"NamedField": (),
|
||||||
"CallExpr": (),
|
"CallExpr": (
|
||||||
|
options: [
|
||||||
|
[ "expr", "Expr" ],
|
||||||
|
[ "arg_list", "ArgList" ],
|
||||||
|
]
|
||||||
|
),
|
||||||
"IndexExpr": (),
|
"IndexExpr": (),
|
||||||
"MethodCallExpr": (),
|
"MethodCallExpr": (),
|
||||||
"FieldExpr": (),
|
"FieldExpr": (),
|
||||||
|
@ -457,8 +467,15 @@ Grammar(
|
||||||
"NameRef": (),
|
"NameRef": (),
|
||||||
"Attr": ( options: [ ["value", "TokenTree"] ] ),
|
"Attr": ( options: [ ["value", "TokenTree"] ] ),
|
||||||
"TokenTree": (),
|
"TokenTree": (),
|
||||||
"TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]),
|
"TypeParamList": (
|
||||||
|
collections: [
|
||||||
|
["type_params", "TypeParam" ],
|
||||||
|
["lifetime_params", "LifetimeParam" ],
|
||||||
|
]
|
||||||
|
),
|
||||||
"TypeParam": ( traits: ["NameOwner"] ),
|
"TypeParam": ( traits: ["NameOwner"] ),
|
||||||
|
"LifetimeParam": ( options: [ ["lifetime", "Lifetime"] ] ),
|
||||||
|
"Lifetime": (),
|
||||||
"WhereClause": (),
|
"WhereClause": (),
|
||||||
"ExprStmt": (
|
"ExprStmt": (
|
||||||
options: [ ["expr", "Expr"] ]
|
options: [ ["expr", "Expr"] ]
|
||||||
|
@ -492,5 +509,10 @@ Grammar(
|
||||||
),
|
),
|
||||||
"UseItem": (),
|
"UseItem": (),
|
||||||
"ExternCrateItem": (),
|
"ExternCrateItem": (),
|
||||||
|
"ArgList": (
|
||||||
|
collections: [
|
||||||
|
["args", "Expr"]
|
||||||
|
]
|
||||||
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -151,10 +151,11 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker {
|
||||||
p.eat(MOVE_KW);
|
p.eat(MOVE_KW);
|
||||||
params::param_list_opt_types(p);
|
params::param_list_opt_types(p);
|
||||||
if opt_fn_ret_type(p) {
|
if opt_fn_ret_type(p) {
|
||||||
block(p);
|
if !p.at(L_CURLY) {
|
||||||
} else {
|
p.error("expected `{`");
|
||||||
expr(p);
|
}
|
||||||
}
|
}
|
||||||
|
expr(p);
|
||||||
m.complete(p, LAMBDA_EXPR)
|
m.complete(p, LAMBDA_EXPR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue