diff options
Diffstat (limited to 'crates')
74 files changed, 1597 insertions, 534 deletions
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs index 7bcc318a9..2c48f32bf 100644 --- a/crates/assists/src/handlers/early_return.rs +++ b/crates/assists/src/handlers/early_return.rs | |||
@@ -69,7 +69,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
69 | 69 | ||
70 | let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; | 70 | let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; |
71 | 71 | ||
72 | if parent_block.expr()? != if_expr.clone().into() { | 72 | if parent_block.tail_expr()? != if_expr.clone().into() { |
73 | return None; | 73 | return None; |
74 | } | 74 | } |
75 | 75 | ||
diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index 030b9cd0c..6f35a061c 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs | |||
@@ -117,10 +117,14 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va | |||
117 | .into_iter() | 117 | .into_iter() |
118 | .filter(|(_, def)| match def { | 118 | .filter(|(_, def)| match def { |
119 | // only check type-namespace | 119 | // only check type-namespace |
120 | hir::ScopeDef::ModuleDef(def) => matches!(def, | 120 | hir::ScopeDef::ModuleDef(def) => matches!( |
121 | ModuleDef::Module(_) | ModuleDef::Adt(_) | | 121 | def, |
122 | ModuleDef::Variant(_) | ModuleDef::Trait(_) | | 122 | ModuleDef::Module(_) |
123 | ModuleDef::TypeAlias(_) | ModuleDef::BuiltinType(_) | 123 | | ModuleDef::Adt(_) |
124 | | ModuleDef::Variant(_) | ||
125 | | ModuleDef::Trait(_) | ||
126 | | ModuleDef::TypeAlias(_) | ||
127 | | ModuleDef::BuiltinType(_) | ||
124 | ), | 128 | ), |
125 | _ => false, | 129 | _ => false, |
126 | }) | 130 | }) |
diff --git a/crates/assists/src/handlers/extract_variable.rs b/crates/assists/src/handlers/extract_variable.rs index 9957012fe..291809205 100644 --- a/crates/assists/src/handlers/extract_variable.rs +++ b/crates/assists/src/handlers/extract_variable.rs | |||
@@ -139,7 +139,7 @@ impl Anchor { | |||
139 | fn from(to_extract: &ast::Expr) -> Option<Anchor> { | 139 | fn from(to_extract: &ast::Expr) -> Option<Anchor> { |
140 | to_extract.syntax().ancestors().find_map(|node| { | 140 | to_extract.syntax().ancestors().find_map(|node| { |
141 | if let Some(expr) = | 141 | if let Some(expr) = |
142 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.expr()) | 142 | node.parent().and_then(ast::BlockExpr::cast).and_then(|it| it.tail_expr()) |
143 | { | 143 | { |
144 | if expr.syntax() == &node { | 144 | if expr.syntax() == &node { |
145 | mark::hit!(test_extract_var_last_expr); | 145 | mark::hit!(test_extract_var_last_expr); |
diff --git a/crates/assists/src/handlers/infer_function_return_type.rs b/crates/assists/src/handlers/infer_function_return_type.rs index aa584eb03..f499cdfdc 100644 --- a/crates/assists/src/handlers/infer_function_return_type.rs +++ b/crates/assists/src/handlers/infer_function_return_type.rs | |||
@@ -89,7 +89,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla | |||
89 | let body = closure.body()?; | 89 | let body = closure.body()?; |
90 | let body_start = body.syntax().first_token()?.text_range().start(); | 90 | let body_start = body.syntax().first_token()?.text_range().start(); |
91 | let (tail_expr, wrap_expr) = match body { | 91 | let (tail_expr, wrap_expr) = match body { |
92 | ast::Expr::BlockExpr(block) => (block.expr()?, false), | 92 | ast::Expr::BlockExpr(block) => (block.tail_expr()?, false), |
93 | body => (body, true), | 93 | body => (body, true), |
94 | }; | 94 | }; |
95 | 95 | ||
@@ -101,7 +101,7 @@ fn extract_tail(ctx: &AssistContext) -> Option<(FnType, ast::Expr, InsertOrRepla | |||
101 | let action = ret_ty_to_action(func.ret_type(), rparen_pos)?; | 101 | let action = ret_ty_to_action(func.ret_type(), rparen_pos)?; |
102 | 102 | ||
103 | let body = func.body()?; | 103 | let body = func.body()?; |
104 | let tail_expr = body.expr()?; | 104 | let tail_expr = body.tail_expr()?; |
105 | 105 | ||
106 | let ret_range_end = body.l_curly_token()?.text_range().start(); | 106 | let ret_range_end = body.l_curly_token()?.text_range().start(); |
107 | let ret_range = TextRange::new(rparen_pos, ret_range_end); | 107 | let ret_range = TextRange::new(rparen_pos, ret_range_end); |
diff --git a/crates/assists/src/handlers/inline_function.rs b/crates/assists/src/handlers/inline_function.rs new file mode 100644 index 000000000..6e351bdcd --- /dev/null +++ b/crates/assists/src/handlers/inline_function.rs | |||
@@ -0,0 +1,202 @@ | |||
1 | use ast::make; | ||
2 | use hir::{HasSource, PathResolution}; | ||
3 | use syntax::{ | ||
4 | ast::{self, edit::AstNodeEdit, ArgListOwner}, | ||
5 | AstNode, | ||
6 | }; | ||
7 | use test_utils::mark; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_context::{AssistContext, Assists}, | ||
11 | AssistId, AssistKind, | ||
12 | }; | ||
13 | |||
14 | // Assist: inline_function | ||
15 | // | ||
16 | // Inlines a function body. | ||
17 | // | ||
18 | // ``` | ||
19 | // fn add(a: u32, b: u32) -> u32 { a + b } | ||
20 | // fn main() { | ||
21 | // let x = add<|>(1, 2); | ||
22 | // } | ||
23 | // ``` | ||
24 | // -> | ||
25 | // ``` | ||
26 | // fn add(a: u32, b: u32) -> u32 { a + b } | ||
27 | // fn main() { | ||
28 | // let x = { | ||
29 | // let a = 1; | ||
30 | // let b = 2; | ||
31 | // a + b | ||
32 | // }; | ||
33 | // } | ||
34 | // ``` | ||
35 | pub(crate) fn inline_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
36 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; | ||
37 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | ||
38 | let path = path_expr.path()?; | ||
39 | |||
40 | let function = match ctx.sema.resolve_path(&path)? { | ||
41 | PathResolution::Def(hir::ModuleDef::Function(f)) => f, | ||
42 | _ => return None, | ||
43 | }; | ||
44 | |||
45 | let function_source = function.source(ctx.db())?; | ||
46 | let arguments: Vec<_> = call.arg_list()?.args().collect(); | ||
47 | let parameters = function_parameter_patterns(&function_source.value)?; | ||
48 | |||
49 | if arguments.len() != parameters.len() { | ||
50 | // Can't inline the function because they've passed the wrong number of | ||
51 | // arguments to this function | ||
52 | mark::hit!(inline_function_incorrect_number_of_arguments); | ||
53 | return None; | ||
54 | } | ||
55 | |||
56 | let new_bindings = parameters.into_iter().zip(arguments); | ||
57 | |||
58 | let body = function_source.value.body()?; | ||
59 | |||
60 | acc.add( | ||
61 | AssistId("inline_function", AssistKind::RefactorInline), | ||
62 | format!("Inline `{}`", path), | ||
63 | call.syntax().text_range(), | ||
64 | |builder| { | ||
65 | let mut statements: Vec<ast::Stmt> = Vec::new(); | ||
66 | |||
67 | for (pattern, value) in new_bindings { | ||
68 | statements.push(make::let_stmt(pattern, Some(value)).into()); | ||
69 | } | ||
70 | |||
71 | statements.extend(body.statements()); | ||
72 | |||
73 | let original_indentation = call.indent_level(); | ||
74 | let replacement = make::block_expr(statements, body.tail_expr()) | ||
75 | .reset_indent() | ||
76 | .indent(original_indentation); | ||
77 | |||
78 | builder.replace_ast(ast::Expr::CallExpr(call), ast::Expr::BlockExpr(replacement)); | ||
79 | }, | ||
80 | ) | ||
81 | } | ||
82 | |||
83 | fn function_parameter_patterns(value: &ast::Fn) -> Option<Vec<ast::Pat>> { | ||
84 | let mut patterns = Vec::new(); | ||
85 | |||
86 | for param in value.param_list()?.params() { | ||
87 | let pattern = param.pat()?; | ||
88 | patterns.push(pattern); | ||
89 | } | ||
90 | |||
91 | Some(patterns) | ||
92 | } | ||
93 | |||
94 | #[cfg(test)] | ||
95 | mod tests { | ||
96 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
97 | |||
98 | use super::*; | ||
99 | |||
100 | #[test] | ||
101 | fn no_args_or_return_value_gets_inlined_without_block() { | ||
102 | check_assist( | ||
103 | inline_function, | ||
104 | r#" | ||
105 | fn foo() { println!("Hello, World!"); } | ||
106 | fn main() { | ||
107 | fo<|>o(); | ||
108 | } | ||
109 | "#, | ||
110 | r#" | ||
111 | fn foo() { println!("Hello, World!"); } | ||
112 | fn main() { | ||
113 | { | ||
114 | println!("Hello, World!"); | ||
115 | }; | ||
116 | } | ||
117 | "#, | ||
118 | ); | ||
119 | } | ||
120 | |||
121 | #[test] | ||
122 | fn args_with_side_effects() { | ||
123 | check_assist( | ||
124 | inline_function, | ||
125 | r#" | ||
126 | fn foo(name: String) { println!("Hello, {}!", name); } | ||
127 | fn main() { | ||
128 | foo<|>(String::from("Michael")); | ||
129 | } | ||
130 | "#, | ||
131 | r#" | ||
132 | fn foo(name: String) { println!("Hello, {}!", name); } | ||
133 | fn main() { | ||
134 | { | ||
135 | let name = String::from("Michael"); | ||
136 | println!("Hello, {}!", name); | ||
137 | }; | ||
138 | } | ||
139 | "#, | ||
140 | ); | ||
141 | } | ||
142 | |||
143 | #[test] | ||
144 | fn method_inlining_isnt_supported() { | ||
145 | check_assist_not_applicable( | ||
146 | inline_function, | ||
147 | r" | ||
148 | struct Foo; | ||
149 | impl Foo { fn bar(&self) {} } | ||
150 | |||
151 | fn main() { Foo.bar<|>(); } | ||
152 | ", | ||
153 | ); | ||
154 | } | ||
155 | |||
156 | #[test] | ||
157 | fn not_applicable_when_incorrect_number_of_parameters_are_provided() { | ||
158 | mark::check!(inline_function_incorrect_number_of_arguments); | ||
159 | check_assist_not_applicable( | ||
160 | inline_function, | ||
161 | r#" | ||
162 | fn add(a: u32, b: u32) -> u32 { a + b } | ||
163 | fn main() { let x = add<|>(42); } | ||
164 | "#, | ||
165 | ); | ||
166 | } | ||
167 | |||
168 | #[test] | ||
169 | fn function_with_multiple_statements() { | ||
170 | check_assist( | ||
171 | inline_function, | ||
172 | r#" | ||
173 | fn foo(a: u32, b: u32) -> u32 { | ||
174 | let x = a + b; | ||
175 | let y = x - b; | ||
176 | x * y | ||
177 | } | ||
178 | |||
179 | fn main() { | ||
180 | let x = foo<|>(1, 2); | ||
181 | } | ||
182 | "#, | ||
183 | r#" | ||
184 | fn foo(a: u32, b: u32) -> u32 { | ||
185 | let x = a + b; | ||
186 | let y = x - b; | ||
187 | x * y | ||
188 | } | ||
189 | |||
190 | fn main() { | ||
191 | let x = { | ||
192 | let a = 1; | ||
193 | let b = 2; | ||
194 | let x = a + b; | ||
195 | let y = x - b; | ||
196 | x * y | ||
197 | }; | ||
198 | } | ||
199 | "#, | ||
200 | ); | ||
201 | } | ||
202 | } | ||
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs index eaffd80ce..4318ca6dc 100644 --- a/crates/assists/src/handlers/move_guard.rs +++ b/crates/assists/src/handlers/move_guard.rs | |||
@@ -98,7 +98,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex | |||
98 | let mut replace_node = None; | 98 | let mut replace_node = None; |
99 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { | 99 | let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { |
100 | let block_expr = BlockExpr::cast(arm_body.syntax().clone())?; | 100 | let block_expr = BlockExpr::cast(arm_body.syntax().clone())?; |
101 | if let Expr::IfExpr(e) = block_expr.expr()? { | 101 | if let Expr::IfExpr(e) = block_expr.tail_expr()? { |
102 | replace_node = Some(block_expr.syntax().clone()); | 102 | replace_node = Some(block_expr.syntax().clone()); |
103 | Some(e) | 103 | Some(e) |
104 | } else { | 104 | } else { |
@@ -128,7 +128,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex | |||
128 | |edit| { | 128 | |edit| { |
129 | let then_only_expr = then_block.statements().next().is_none(); | 129 | let then_only_expr = then_block.statements().next().is_none(); |
130 | 130 | ||
131 | match &then_block.expr() { | 131 | match &then_block.tail_expr() { |
132 | Some(then_expr) if then_only_expr => { | 132 | Some(then_expr) if then_only_expr => { |
133 | edit.replace(replace_node.text_range(), then_expr.syntax().text()) | 133 | edit.replace(replace_node.text_range(), then_expr.syntax().text()) |
134 | } | 134 | } |
diff --git a/crates/assists/src/handlers/extract_assignment.rs b/crates/assists/src/handlers/pull_assignment_up.rs index 281cf5d24..63b662fad 100644 --- a/crates/assists/src/handlers/extract_assignment.rs +++ b/crates/assists/src/handlers/pull_assignment_up.rs | |||
@@ -1,4 +1,3 @@ | |||
1 | use hir::AsName; | ||
2 | use syntax::{ | 1 | use syntax::{ |
3 | ast::{self, edit::AstNodeEdit, make}, | 2 | ast::{self, edit::AstNodeEdit, make}, |
4 | AstNode, | 3 | AstNode, |
@@ -10,9 +9,9 @@ use crate::{ | |||
10 | AssistId, AssistKind, | 9 | AssistId, AssistKind, |
11 | }; | 10 | }; |
12 | 11 | ||
13 | // Assist: extract_assignment | 12 | // Assist: pull_assignment_up |
14 | // | 13 | // |
15 | // Extracts variable assigment to outside an if or match statement. | 14 | // Extracts variable assignment to outside an if or match statement. |
16 | // | 15 | // |
17 | // ``` | 16 | // ``` |
18 | // fn main() { | 17 | // fn main() { |
@@ -37,16 +36,24 @@ use crate::{ | |||
37 | // }; | 36 | // }; |
38 | // } | 37 | // } |
39 | // ``` | 38 | // ``` |
40 | pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 39 | pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
41 | let name = ctx.find_node_at_offset::<ast::NameRef>()?.as_name(); | 40 | let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?; |
41 | let name_expr = if assign_expr.op_kind()? == ast::BinOp::Assignment { | ||
42 | assign_expr.lhs()? | ||
43 | } else { | ||
44 | return None; | ||
45 | }; | ||
42 | 46 | ||
43 | let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { | 47 | let (old_stmt, new_stmt) = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() { |
44 | ( | 48 | ( |
45 | ast::Expr::cast(if_expr.syntax().to_owned())?, | 49 | ast::Expr::cast(if_expr.syntax().to_owned())?, |
46 | exprify_if(&if_expr, &name)?.indent(if_expr.indent_level()), | 50 | exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level()), |
47 | ) | 51 | ) |
48 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { | 52 | } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() { |
49 | (ast::Expr::cast(match_expr.syntax().to_owned())?, exprify_match(&match_expr, &name)?) | 53 | ( |
54 | ast::Expr::cast(match_expr.syntax().to_owned())?, | ||
55 | exprify_match(&match_expr, &ctx.sema, &name_expr)?, | ||
56 | ) | ||
50 | } else { | 57 | } else { |
51 | return None; | 58 | return None; |
52 | }; | 59 | }; |
@@ -54,22 +61,26 @@ pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
54 | let expr_stmt = make::expr_stmt(new_stmt); | 61 | let expr_stmt = make::expr_stmt(new_stmt); |
55 | 62 | ||
56 | acc.add( | 63 | acc.add( |
57 | AssistId("extract_assignment", AssistKind::RefactorExtract), | 64 | AssistId("pull_assignment_up", AssistKind::RefactorExtract), |
58 | "Extract assignment", | 65 | "Pull assignment up", |
59 | old_stmt.syntax().text_range(), | 66 | old_stmt.syntax().text_range(), |
60 | move |edit| { | 67 | move |edit| { |
61 | edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name, expr_stmt)); | 68 | edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt)); |
62 | }, | 69 | }, |
63 | ) | 70 | ) |
64 | } | 71 | } |
65 | 72 | ||
66 | fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::Expr> { | 73 | fn exprify_match( |
74 | match_expr: &ast::MatchExpr, | ||
75 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
76 | name: &ast::Expr, | ||
77 | ) -> Option<ast::Expr> { | ||
67 | let new_arm_list = match_expr | 78 | let new_arm_list = match_expr |
68 | .match_arm_list()? | 79 | .match_arm_list()? |
69 | .arms() | 80 | .arms() |
70 | .map(|arm| { | 81 | .map(|arm| { |
71 | if let ast::Expr::BlockExpr(block) = arm.expr()? { | 82 | if let ast::Expr::BlockExpr(block) = arm.expr()? { |
72 | let new_block = exprify_block(&block, name)?.indent(block.indent_level()); | 83 | let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level()); |
73 | Some(arm.replace_descendant(block, new_block)) | 84 | Some(arm.replace_descendant(block, new_block)) |
74 | } else { | 85 | } else { |
75 | None | 86 | None |
@@ -82,22 +93,32 @@ fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::E | |||
82 | Some(make::expr_match(match_expr.expr()?, new_arm_list)) | 93 | Some(make::expr_match(match_expr.expr()?, new_arm_list)) |
83 | } | 94 | } |
84 | 95 | ||
85 | fn exprify_if(statement: &ast::IfExpr, name: &hir::Name) -> Option<ast::Expr> { | 96 | fn exprify_if( |
86 | let then_branch = exprify_block(&statement.then_branch()?, name)?; | 97 | statement: &ast::IfExpr, |
98 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
99 | name: &ast::Expr, | ||
100 | ) -> Option<ast::Expr> { | ||
101 | let then_branch = exprify_block(&statement.then_branch()?, sema, name)?; | ||
87 | let else_branch = match statement.else_branch()? { | 102 | let else_branch = match statement.else_branch()? { |
88 | ast::ElseBranch::Block(ref block) => ast::ElseBranch::Block(exprify_block(block, name)?), | 103 | ast::ElseBranch::Block(ref block) => { |
104 | ast::ElseBranch::Block(exprify_block(block, sema, name)?) | ||
105 | } | ||
89 | ast::ElseBranch::IfExpr(expr) => { | 106 | ast::ElseBranch::IfExpr(expr) => { |
90 | mark::hit!(test_extract_assigment_chained_if); | 107 | mark::hit!(test_pull_assignment_up_chained_if); |
91 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( | 108 | ast::ElseBranch::IfExpr(ast::IfExpr::cast( |
92 | exprify_if(&expr, name)?.syntax().to_owned(), | 109 | exprify_if(&expr, sema, name)?.syntax().to_owned(), |
93 | )?) | 110 | )?) |
94 | } | 111 | } |
95 | }; | 112 | }; |
96 | Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) | 113 | Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch))) |
97 | } | 114 | } |
98 | 115 | ||
99 | fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockExpr> { | 116 | fn exprify_block( |
100 | if block.expr().is_some() { | 117 | block: &ast::BlockExpr, |
118 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
119 | name: &ast::Expr, | ||
120 | ) -> Option<ast::BlockExpr> { | ||
121 | if block.tail_expr().is_some() { | ||
101 | return None; | 122 | return None; |
102 | } | 123 | } |
103 | 124 | ||
@@ -106,8 +127,7 @@ fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockE | |||
106 | 127 | ||
107 | if let ast::Stmt::ExprStmt(stmt) = stmt { | 128 | if let ast::Stmt::ExprStmt(stmt) = stmt { |
108 | if let ast::Expr::BinExpr(expr) = stmt.expr()? { | 129 | if let ast::Expr::BinExpr(expr) = stmt.expr()? { |
109 | if expr.op_kind()? == ast::BinOp::Assignment | 130 | if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name) |
110 | && &expr.lhs()?.name_ref()?.as_name() == name | ||
111 | { | 131 | { |
112 | // The last statement in the block is an assignment to the name we want | 132 | // The last statement in the block is an assignment to the name we want |
113 | return Some(make::block_expr(stmts, Some(expr.rhs()?))); | 133 | return Some(make::block_expr(stmts, Some(expr.rhs()?))); |
@@ -117,6 +137,29 @@ fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockE | |||
117 | None | 137 | None |
118 | } | 138 | } |
119 | 139 | ||
140 | fn is_equivalent( | ||
141 | sema: &hir::Semantics<ide_db::RootDatabase>, | ||
142 | expr0: &ast::Expr, | ||
143 | expr1: &ast::Expr, | ||
144 | ) -> bool { | ||
145 | match (expr0, expr1) { | ||
146 | (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => { | ||
147 | mark::hit!(test_pull_assignment_up_field_assignment); | ||
148 | sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1) | ||
149 | } | ||
150 | (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => { | ||
151 | let path0 = path0.path(); | ||
152 | let path1 = path1.path(); | ||
153 | if let (Some(path0), Some(path1)) = (path0, path1) { | ||
154 | sema.resolve_path(&path0) == sema.resolve_path(&path1) | ||
155 | } else { | ||
156 | false | ||
157 | } | ||
158 | } | ||
159 | _ => false, | ||
160 | } | ||
161 | } | ||
162 | |||
120 | #[cfg(test)] | 163 | #[cfg(test)] |
121 | mod tests { | 164 | mod tests { |
122 | use super::*; | 165 | use super::*; |
@@ -124,9 +167,9 @@ mod tests { | |||
124 | use crate::tests::{check_assist, check_assist_not_applicable}; | 167 | use crate::tests::{check_assist, check_assist_not_applicable}; |
125 | 168 | ||
126 | #[test] | 169 | #[test] |
127 | fn test_extract_assignment_if() { | 170 | fn test_pull_assignment_up_if() { |
128 | check_assist( | 171 | check_assist( |
129 | extract_assigment, | 172 | pull_assignment_up, |
130 | r#" | 173 | r#" |
131 | fn foo() { | 174 | fn foo() { |
132 | let mut a = 1; | 175 | let mut a = 1; |
@@ -151,9 +194,9 @@ fn foo() { | |||
151 | } | 194 | } |
152 | 195 | ||
153 | #[test] | 196 | #[test] |
154 | fn test_extract_assignment_match() { | 197 | fn test_pull_assignment_up_match() { |
155 | check_assist( | 198 | check_assist( |
156 | extract_assigment, | 199 | pull_assignment_up, |
157 | r#" | 200 | r#" |
158 | fn foo() { | 201 | fn foo() { |
159 | let mut a = 1; | 202 | let mut a = 1; |
@@ -190,9 +233,9 @@ fn foo() { | |||
190 | } | 233 | } |
191 | 234 | ||
192 | #[test] | 235 | #[test] |
193 | fn test_extract_assignment_not_last_not_applicable() { | 236 | fn test_pull_assignment_up_not_last_not_applicable() { |
194 | check_assist_not_applicable( | 237 | check_assist_not_applicable( |
195 | extract_assigment, | 238 | pull_assignment_up, |
196 | r#" | 239 | r#" |
197 | fn foo() { | 240 | fn foo() { |
198 | let mut a = 1; | 241 | let mut a = 1; |
@@ -208,10 +251,10 @@ fn foo() { | |||
208 | } | 251 | } |
209 | 252 | ||
210 | #[test] | 253 | #[test] |
211 | fn test_extract_assignment_chained_if() { | 254 | fn test_pull_assignment_up_chained_if() { |
212 | mark::check!(test_extract_assigment_chained_if); | 255 | mark::check!(test_pull_assignment_up_chained_if); |
213 | check_assist( | 256 | check_assist( |
214 | extract_assigment, | 257 | pull_assignment_up, |
215 | r#" | 258 | r#" |
216 | fn foo() { | 259 | fn foo() { |
217 | let mut a = 1; | 260 | let mut a = 1; |
@@ -240,9 +283,9 @@ fn foo() { | |||
240 | } | 283 | } |
241 | 284 | ||
242 | #[test] | 285 | #[test] |
243 | fn test_extract_assigment_retains_stmts() { | 286 | fn test_pull_assignment_up_retains_stmts() { |
244 | check_assist( | 287 | check_assist( |
245 | extract_assigment, | 288 | pull_assignment_up, |
246 | r#" | 289 | r#" |
247 | fn foo() { | 290 | fn foo() { |
248 | let mut a = 1; | 291 | let mut a = 1; |
@@ -271,9 +314,9 @@ fn foo() { | |||
271 | } | 314 | } |
272 | 315 | ||
273 | #[test] | 316 | #[test] |
274 | fn extract_assignment_let_stmt_not_applicable() { | 317 | fn pull_assignment_up_let_stmt_not_applicable() { |
275 | check_assist_not_applicable( | 318 | check_assist_not_applicable( |
276 | extract_assigment, | 319 | pull_assignment_up, |
277 | r#" | 320 | r#" |
278 | fn foo() { | 321 | fn foo() { |
279 | let mut a = 1; | 322 | let mut a = 1; |
@@ -288,9 +331,9 @@ fn foo() { | |||
288 | } | 331 | } |
289 | 332 | ||
290 | #[test] | 333 | #[test] |
291 | fn extract_assignment_if_missing_assigment_not_applicable() { | 334 | fn pull_assignment_up_if_missing_assigment_not_applicable() { |
292 | check_assist_not_applicable( | 335 | check_assist_not_applicable( |
293 | extract_assigment, | 336 | pull_assignment_up, |
294 | r#" | 337 | r#" |
295 | fn foo() { | 338 | fn foo() { |
296 | let mut a = 1; | 339 | let mut a = 1; |
@@ -303,9 +346,9 @@ fn foo() { | |||
303 | } | 346 | } |
304 | 347 | ||
305 | #[test] | 348 | #[test] |
306 | fn extract_assignment_match_missing_assigment_not_applicable() { | 349 | fn pull_assignment_up_match_missing_assigment_not_applicable() { |
307 | check_assist_not_applicable( | 350 | check_assist_not_applicable( |
308 | extract_assigment, | 351 | pull_assignment_up, |
309 | r#" | 352 | r#" |
310 | fn foo() { | 353 | fn foo() { |
311 | let mut a = 1; | 354 | let mut a = 1; |
@@ -322,4 +365,36 @@ fn foo() { | |||
322 | }"#, | 365 | }"#, |
323 | ) | 366 | ) |
324 | } | 367 | } |
368 | |||
369 | #[test] | ||
370 | fn test_pull_assignment_up_field_assignment() { | ||
371 | mark::check!(test_pull_assignment_up_field_assignment); | ||
372 | check_assist( | ||
373 | pull_assignment_up, | ||
374 | r#" | ||
375 | struct A(usize); | ||
376 | |||
377 | fn foo() { | ||
378 | let mut a = A(1); | ||
379 | |||
380 | if true { | ||
381 | <|>a.0 = 2; | ||
382 | } else { | ||
383 | a.0 = 3; | ||
384 | } | ||
385 | }"#, | ||
386 | r#" | ||
387 | struct A(usize); | ||
388 | |||
389 | fn foo() { | ||
390 | let mut a = A(1); | ||
391 | |||
392 | a.0 = if true { | ||
393 | 2 | ||
394 | } else { | ||
395 | 3 | ||
396 | }; | ||
397 | }"#, | ||
398 | ) | ||
399 | } | ||
325 | } | 400 | } |
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs index 4a355c66f..b67219222 100644 --- a/crates/assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -138,7 +138,7 @@ pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext) | |||
138 | }; | 138 | }; |
139 | let else_expr = match else_expr { | 139 | let else_expr = match else_expr { |
140 | ast::Expr::BlockExpr(block) | 140 | ast::Expr::BlockExpr(block) |
141 | if block.statements().count() == 0 && block.expr().is_none() => | 141 | if block.statements().count() == 0 && block.tail_expr().is_none() => |
142 | { | 142 | { |
143 | None | 143 | None |
144 | } | 144 | } |
diff --git a/crates/assists/src/handlers/wrap_return_type_in_result.rs b/crates/assists/src/handlers/wrap_return_type_in_result.rs index 59e5debb1..358b61046 100644 --- a/crates/assists/src/handlers/wrap_return_type_in_result.rs +++ b/crates/assists/src/handlers/wrap_return_type_in_result.rs | |||
@@ -98,7 +98,7 @@ impl TailReturnCollector { | |||
98 | } | 98 | } |
99 | 99 | ||
100 | // Browse tail expressions for each block | 100 | // Browse tail expressions for each block |
101 | if let Some(expr) = block_expr.expr() { | 101 | if let Some(expr) = block_expr.tail_expr() { |
102 | if let Some(last_exprs) = get_tail_expr_from_block(&expr) { | 102 | if let Some(last_exprs) = get_tail_expr_from_block(&expr) { |
103 | for last_expr in last_exprs { | 103 | for last_expr in last_exprs { |
104 | let last_expr = match last_expr { | 104 | let last_expr = match last_expr { |
@@ -170,7 +170,7 @@ impl TailReturnCollector { | |||
170 | } | 170 | } |
171 | 171 | ||
172 | fn collect_tail_exprs(&mut self, block: &BlockExpr) { | 172 | fn collect_tail_exprs(&mut self, block: &BlockExpr) { |
173 | if let Some(expr) = block.expr() { | 173 | if let Some(expr) = block.tail_expr() { |
174 | self.handle_exprs(&expr, true); | 174 | self.handle_exprs(&expr, true); |
175 | self.fetch_tail_exprs(&expr); | 175 | self.fetch_tail_exprs(&expr); |
176 | } | 176 | } |
@@ -206,7 +206,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | |||
206 | Expr::IfExpr(if_expr) => { | 206 | Expr::IfExpr(if_expr) => { |
207 | let mut nodes = vec![]; | 207 | let mut nodes = vec![]; |
208 | for block in if_expr.blocks() { | 208 | for block in if_expr.blocks() { |
209 | if let Some(block_expr) = block.expr() { | 209 | if let Some(block_expr) = block.tail_expr() { |
210 | if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { | 210 | if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) { |
211 | nodes.extend(tail_exprs); | 211 | nodes.extend(tail_exprs); |
212 | } | 212 | } |
@@ -228,7 +228,7 @@ fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> { | |||
228 | while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) | 228 | while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)]) |
229 | } | 229 | } |
230 | Expr::BlockExpr(block_expr) => { | 230 | Expr::BlockExpr(block_expr) => { |
231 | block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) | 231 | block_expr.tail_expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())]) |
232 | } | 232 | } |
233 | Expr::MatchExpr(match_expr) => { | 233 | Expr::MatchExpr(match_expr) => { |
234 | let arm_list = match_expr.match_arm_list()?; | 234 | let arm_list = match_expr.match_arm_list()?; |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index 212464f85..9c2a95735 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -116,7 +116,6 @@ mod handlers { | |||
116 | mod convert_integer_literal; | 116 | mod convert_integer_literal; |
117 | mod early_return; | 117 | mod early_return; |
118 | mod expand_glob_import; | 118 | mod expand_glob_import; |
119 | mod extract_assignment; | ||
120 | mod extract_module_to_file; | 119 | mod extract_module_to_file; |
121 | mod extract_struct_from_enum_variant; | 120 | mod extract_struct_from_enum_variant; |
122 | mod extract_variable; | 121 | mod extract_variable; |
@@ -125,13 +124,14 @@ mod handlers { | |||
125 | mod flip_binexpr; | 124 | mod flip_binexpr; |
126 | mod flip_comma; | 125 | mod flip_comma; |
127 | mod flip_trait_bound; | 126 | mod flip_trait_bound; |
128 | mod generate_derive; | ||
129 | mod generate_default_from_enum_variant; | 127 | mod generate_default_from_enum_variant; |
128 | mod generate_derive; | ||
130 | mod generate_from_impl_for_enum; | 129 | mod generate_from_impl_for_enum; |
131 | mod generate_function; | 130 | mod generate_function; |
132 | mod generate_impl; | 131 | mod generate_impl; |
133 | mod generate_new; | 132 | mod generate_new; |
134 | mod infer_function_return_type; | 133 | mod infer_function_return_type; |
134 | mod inline_function; | ||
135 | mod inline_local_variable; | 135 | mod inline_local_variable; |
136 | mod introduce_named_lifetime; | 136 | mod introduce_named_lifetime; |
137 | mod invert_if; | 137 | mod invert_if; |
@@ -139,6 +139,7 @@ mod handlers { | |||
139 | mod merge_match_arms; | 139 | mod merge_match_arms; |
140 | mod move_bounds; | 140 | mod move_bounds; |
141 | mod move_guard; | 141 | mod move_guard; |
142 | mod pull_assignment_up; | ||
142 | mod qualify_path; | 143 | mod qualify_path; |
143 | mod raw_string; | 144 | mod raw_string; |
144 | mod remove_dbg; | 145 | mod remove_dbg; |
@@ -168,7 +169,6 @@ mod handlers { | |||
168 | convert_integer_literal::convert_integer_literal, | 169 | convert_integer_literal::convert_integer_literal, |
169 | early_return::convert_to_guarded_return, | 170 | early_return::convert_to_guarded_return, |
170 | expand_glob_import::expand_glob_import, | 171 | expand_glob_import::expand_glob_import, |
171 | extract_assignment::extract_assigment, | ||
172 | extract_module_to_file::extract_module_to_file, | 172 | extract_module_to_file::extract_module_to_file, |
173 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 173 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |
174 | extract_variable::extract_variable, | 174 | extract_variable::extract_variable, |
@@ -177,13 +177,14 @@ mod handlers { | |||
177 | flip_binexpr::flip_binexpr, | 177 | flip_binexpr::flip_binexpr, |
178 | flip_comma::flip_comma, | 178 | flip_comma::flip_comma, |
179 | flip_trait_bound::flip_trait_bound, | 179 | flip_trait_bound::flip_trait_bound, |
180 | generate_derive::generate_derive, | ||
181 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 180 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
181 | generate_derive::generate_derive, | ||
182 | generate_from_impl_for_enum::generate_from_impl_for_enum, | 182 | generate_from_impl_for_enum::generate_from_impl_for_enum, |
183 | generate_function::generate_function, | 183 | generate_function::generate_function, |
184 | generate_impl::generate_impl, | 184 | generate_impl::generate_impl, |
185 | generate_new::generate_new, | 185 | generate_new::generate_new, |
186 | infer_function_return_type::infer_function_return_type, | 186 | infer_function_return_type::infer_function_return_type, |
187 | inline_function::inline_function, | ||
187 | inline_local_variable::inline_local_variable, | 188 | inline_local_variable::inline_local_variable, |
188 | introduce_named_lifetime::introduce_named_lifetime, | 189 | introduce_named_lifetime::introduce_named_lifetime, |
189 | invert_if::invert_if, | 190 | invert_if::invert_if, |
@@ -192,6 +193,7 @@ mod handlers { | |||
192 | move_bounds::move_bounds_to_where_clause, | 193 | move_bounds::move_bounds_to_where_clause, |
193 | move_guard::move_arm_cond_to_match_guard, | 194 | move_guard::move_arm_cond_to_match_guard, |
194 | move_guard::move_guard_to_arm_body, | 195 | move_guard::move_guard_to_arm_body, |
196 | pull_assignment_up::pull_assignment_up, | ||
195 | qualify_path::qualify_path, | 197 | qualify_path::qualify_path, |
196 | raw_string::add_hash, | 198 | raw_string::add_hash, |
197 | raw_string::make_usual_string, | 199 | raw_string::make_usual_string, |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index b91a816e8..b15352cf3 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -238,35 +238,6 @@ fn qux(bar: Bar, baz: Baz) {} | |||
238 | } | 238 | } |
239 | 239 | ||
240 | #[test] | 240 | #[test] |
241 | fn doctest_extract_assignment() { | ||
242 | check_doc_test( | ||
243 | "extract_assignment", | ||
244 | r#####" | ||
245 | fn main() { | ||
246 | let mut foo = 6; | ||
247 | |||
248 | if true { | ||
249 | <|>foo = 5; | ||
250 | } else { | ||
251 | foo = 4; | ||
252 | } | ||
253 | } | ||
254 | "#####, | ||
255 | r#####" | ||
256 | fn main() { | ||
257 | let mut foo = 6; | ||
258 | |||
259 | foo = if true { | ||
260 | 5 | ||
261 | } else { | ||
262 | 4 | ||
263 | }; | ||
264 | } | ||
265 | "#####, | ||
266 | ) | ||
267 | } | ||
268 | |||
269 | #[test] | ||
270 | fn doctest_extract_module_to_file() { | 241 | fn doctest_extract_module_to_file() { |
271 | check_doc_test( | 242 | check_doc_test( |
272 | "extract_module_to_file", | 243 | "extract_module_to_file", |
@@ -560,6 +531,29 @@ fn foo() -> i32 { 42i32 } | |||
560 | } | 531 | } |
561 | 532 | ||
562 | #[test] | 533 | #[test] |
534 | fn doctest_inline_function() { | ||
535 | check_doc_test( | ||
536 | "inline_function", | ||
537 | r#####" | ||
538 | fn add(a: u32, b: u32) -> u32 { a + b } | ||
539 | fn main() { | ||
540 | let x = add<|>(1, 2); | ||
541 | } | ||
542 | "#####, | ||
543 | r#####" | ||
544 | fn add(a: u32, b: u32) -> u32 { a + b } | ||
545 | fn main() { | ||
546 | let x = { | ||
547 | let a = 1; | ||
548 | let b = 2; | ||
549 | a + b | ||
550 | }; | ||
551 | } | ||
552 | "#####, | ||
553 | ) | ||
554 | } | ||
555 | |||
556 | #[test] | ||
563 | fn doctest_inline_local_variable() { | 557 | fn doctest_inline_local_variable() { |
564 | check_doc_test( | 558 | check_doc_test( |
565 | "inline_local_variable", | 559 | "inline_local_variable", |
@@ -767,6 +761,35 @@ fn handle(action: Action) { | |||
767 | } | 761 | } |
768 | 762 | ||
769 | #[test] | 763 | #[test] |
764 | fn doctest_pull_assignment_up() { | ||
765 | check_doc_test( | ||
766 | "pull_assignment_up", | ||
767 | r#####" | ||
768 | fn main() { | ||
769 | let mut foo = 6; | ||
770 | |||
771 | if true { | ||
772 | <|>foo = 5; | ||
773 | } else { | ||
774 | foo = 4; | ||
775 | } | ||
776 | } | ||
777 | "#####, | ||
778 | r#####" | ||
779 | fn main() { | ||
780 | let mut foo = 6; | ||
781 | |||
782 | foo = if true { | ||
783 | 5 | ||
784 | } else { | ||
785 | 4 | ||
786 | }; | ||
787 | } | ||
788 | "#####, | ||
789 | ) | ||
790 | } | ||
791 | |||
792 | #[test] | ||
770 | fn doctest_qualify_path() { | 793 | fn doctest_qualify_path() { |
771 | check_doc_test( | 794 | check_doc_test( |
772 | "qualify_path", | 795 | "qualify_path", |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index b05596446..8212cd129 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -37,7 +37,7 @@ pub fn extract_trivial_expression(block: &ast::BlockExpr) -> Option<ast::Expr> { | |||
37 | non_trivial_children.next().is_some() | 37 | non_trivial_children.next().is_some() |
38 | }; | 38 | }; |
39 | 39 | ||
40 | if let Some(expr) = block.expr() { | 40 | if let Some(expr) = block.tail_expr() { |
41 | if has_anything_else(expr.syntax()) { | 41 | if has_anything_else(expr.syntax()) { |
42 | return None; | 42 | return None; |
43 | } | 43 | } |
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 81a6d00e2..2f41a3f96 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -124,8 +124,8 @@ fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &T | |||
124 | // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding | 124 | // Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding |
125 | // capability enabled. | 125 | // capability enabled. |
126 | fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { | 126 | fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { |
127 | let _p = profile::span("fuzzy_completion"); | ||
128 | let potential_import_name = ctx.token.to_string(); | 127 | let potential_import_name = ctx.token.to_string(); |
128 | let _p = profile::span("fuzzy_completion").detail(|| potential_import_name.clone()); | ||
129 | 129 | ||
130 | if potential_import_name.len() < 2 { | 130 | if potential_import_name.len() < 2 { |
131 | return None; | 131 | return None; |
@@ -142,6 +142,7 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
142 | Some(40), | 142 | Some(40), |
143 | potential_import_name, | 143 | potential_import_name, |
144 | true, | 144 | true, |
145 | true, | ||
145 | ) | 146 | ) |
146 | .filter_map(|import_candidate| { | 147 | .filter_map(|import_candidate| { |
147 | Some(match import_candidate { | 148 | Some(match import_candidate { |
diff --git a/crates/completion/src/context.rs b/crates/completion/src/context.rs index 41de324d8..f979697ab 100644 --- a/crates/completion/src/context.rs +++ b/crates/completion/src/context.rs | |||
@@ -458,7 +458,7 @@ impl<'a> CompletionContext<'a> { | |||
458 | } | 458 | } |
459 | if let Some(block) = ast::BlockExpr::cast(node) { | 459 | if let Some(block) = ast::BlockExpr::cast(node) { |
460 | return Some( | 460 | return Some( |
461 | block.expr().map(|e| e.syntax().text_range()) | 461 | block.tail_expr().map(|e| e.syntax().text_range()) |
462 | == Some(name_ref.syntax().text_range()), | 462 | == Some(name_ref.syntax().text_range()), |
463 | ); | 463 | ); |
464 | } | 464 | } |
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs index 62eccf475..cc1938333 100644 --- a/crates/hir/src/code_model.rs +++ b/crates/hir/src/code_model.rs | |||
@@ -5,9 +5,7 @@ use arrayvec::ArrayVec; | |||
5 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; | 5 | use base_db::{CrateDisplayName, CrateId, Edition, FileId}; |
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_def::{ | 7 | use hir_def::{ |
8 | adt::ReprKind, | 8 | adt::{ReprKind, StructKind, VariantData}, |
9 | adt::StructKind, | ||
10 | adt::VariantData, | ||
11 | builtin_type::BuiltinType, | 9 | builtin_type::BuiltinType, |
12 | expr::{BindingAnnotation, LabelId, Pat, PatId}, | 10 | expr::{BindingAnnotation, LabelId, Pat, PatId}, |
13 | import_map, | 11 | import_map, |
@@ -31,7 +29,7 @@ use hir_expand::{ | |||
31 | }; | 29 | }; |
32 | use hir_ty::{ | 30 | use hir_ty::{ |
33 | autoderef, | 31 | autoderef, |
34 | display::{HirDisplayError, HirFormatter}, | 32 | display::{write_bounds_like_dyn_trait, HirDisplayError, HirFormatter}, |
35 | method_resolution, | 33 | method_resolution, |
36 | traits::{FnTrait, Solution, SolutionVariables}, | 34 | traits::{FnTrait, Solution, SolutionVariables}, |
37 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, | 35 | ApplicationTy, BoundVar, CallableDefId, Canonical, DebruijnIndex, FnSig, GenericPredicate, |
@@ -745,6 +743,18 @@ impl Function { | |||
745 | db.function_data(self.id).name.clone() | 743 | db.function_data(self.id).name.clone() |
746 | } | 744 | } |
747 | 745 | ||
746 | /// Get this function's return type | ||
747 | pub fn ret_type(self, db: &dyn HirDatabase) -> Type { | ||
748 | let resolver = self.id.resolver(db.upcast()); | ||
749 | let ret_type = &db.function_data(self.id).ret_type; | ||
750 | let ctx = hir_ty::TyLoweringContext::new(db, &resolver); | ||
751 | let environment = TraitEnvironment::lower(db, &resolver); | ||
752 | Type { | ||
753 | krate: self.id.lookup(db.upcast()).container.module(db.upcast()).krate, | ||
754 | ty: InEnvironment { value: Ty::from_hir_ext(&ctx, ret_type).0, environment }, | ||
755 | } | ||
756 | } | ||
757 | |||
748 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { | 758 | pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> { |
749 | if !db.function_data(self.id).has_self_param { | 759 | if !db.function_data(self.id).has_self_param { |
750 | return None; | 760 | return None; |
@@ -1278,6 +1288,18 @@ impl TypeParam { | |||
1278 | } | 1288 | } |
1279 | } | 1289 | } |
1280 | 1290 | ||
1291 | pub fn trait_bounds(self, db: &dyn HirDatabase) -> Vec<Trait> { | ||
1292 | db.generic_predicates_for_param(self.id) | ||
1293 | .into_iter() | ||
1294 | .filter_map(|pred| match &pred.value { | ||
1295 | hir_ty::GenericPredicate::Implemented(trait_ref) => { | ||
1296 | Some(Trait::from(trait_ref.trait_)) | ||
1297 | } | ||
1298 | _ => None, | ||
1299 | }) | ||
1300 | .collect() | ||
1301 | } | ||
1302 | |||
1281 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { | 1303 | pub fn default(self, db: &dyn HirDatabase) -> Option<Type> { |
1282 | let params = db.generic_defaults(self.id.parent); | 1304 | let params = db.generic_defaults(self.id.parent); |
1283 | let local_idx = hir_ty::param_idx(db, self.id)?; | 1305 | let local_idx = hir_ty::param_idx(db, self.id)?; |
@@ -1293,6 +1315,20 @@ impl TypeParam { | |||
1293 | } | 1315 | } |
1294 | } | 1316 | } |
1295 | 1317 | ||
1318 | impl HirDisplay for TypeParam { | ||
1319 | fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> { | ||
1320 | write!(f, "{}", self.name(f.db))?; | ||
1321 | let bounds = f.db.generic_predicates_for_param(self.id); | ||
1322 | let substs = Substs::type_params(f.db, self.id.parent); | ||
1323 | let predicates = bounds.iter().cloned().map(|b| b.subst(&substs)).collect::<Vec<_>>(); | ||
1324 | if !(predicates.is_empty() || f.omit_verbose_types()) { | ||
1325 | write!(f, ": ")?; | ||
1326 | write_bounds_like_dyn_trait(&predicates, f)?; | ||
1327 | } | ||
1328 | Ok(()) | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1296 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 1332 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] |
1297 | pub struct LifetimeParam { | 1333 | pub struct LifetimeParam { |
1298 | pub(crate) id: LifetimeParamId, | 1334 | pub(crate) id: LifetimeParamId, |
@@ -1331,6 +1367,12 @@ impl ConstParam { | |||
1331 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { | 1367 | pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef { |
1332 | self.id.parent.into() | 1368 | self.id.parent.into() |
1333 | } | 1369 | } |
1370 | |||
1371 | pub fn ty(self, db: &dyn HirDatabase) -> Type { | ||
1372 | let def = self.id.parent; | ||
1373 | let krate = def.module(db.upcast()).krate; | ||
1374 | Type::new(db, krate, def, db.const_param_ty(self.id)) | ||
1375 | } | ||
1334 | } | 1376 | } |
1335 | 1377 | ||
1336 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 1378 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -1600,9 +1642,10 @@ impl Type { | |||
1600 | } | 1642 | } |
1601 | 1643 | ||
1602 | pub fn is_fn(&self) -> bool { | 1644 | pub fn is_fn(&self) -> bool { |
1603 | matches!(&self.ty.value, | 1645 | matches!( |
1604 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) | | 1646 | &self.ty.value, |
1605 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. }) | 1647 | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnDef(..), .. }) |
1648 | | Ty::Apply(ApplicationTy { ctor: TypeCtor::FnPtr { .. }, .. }) | ||
1606 | ) | 1649 | ) |
1607 | } | 1650 | } |
1608 | 1651 | ||
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 6b79e7bad..9e6426b31 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -2,6 +2,7 @@ | |||
2 | 2 | ||
3 | use std::{ops, sync::Arc}; | 3 | use std::{ops, sync::Arc}; |
4 | 4 | ||
5 | use arena::map::ArenaMap; | ||
5 | use base_db::CrateId; | 6 | use base_db::CrateId; |
6 | use cfg::{CfgExpr, CfgOptions}; | 7 | use cfg::{CfgExpr, CfgOptions}; |
7 | use either::Either; | 8 | use either::Either; |
@@ -21,7 +22,8 @@ use crate::{ | |||
21 | nameres::ModuleSource, | 22 | nameres::ModuleSource, |
22 | path::{ModPath, PathKind}, | 23 | path::{ModPath, PathKind}, |
23 | src::HasChildSource, | 24 | src::HasChildSource, |
24 | AdtId, AttrDefId, GenericParamId, Lookup, | 25 | AdtId, AttrDefId, EnumId, GenericParamId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, |
26 | VariantId, | ||
25 | }; | 27 | }; |
26 | 28 | ||
27 | /// Holds documentation | 29 | /// Holds documentation |
@@ -210,16 +212,10 @@ impl Attrs { | |||
210 | } | 212 | } |
211 | } | 213 | } |
212 | AttrDefId::FieldId(it) => { | 214 | AttrDefId::FieldId(it) => { |
213 | let src = it.parent.child_source(db); | 215 | return db.fields_attrs(it.parent)[it.local_id].clone(); |
214 | match &src.value[it.local_id] { | ||
215 | Either::Left(_tuple) => RawAttrs::default(), | ||
216 | Either::Right(record) => RawAttrs::from_attrs_owner(db, src.with_value(record)), | ||
217 | } | ||
218 | } | 216 | } |
219 | AttrDefId::EnumVariantId(var_id) => { | 217 | AttrDefId::EnumVariantId(it) => { |
220 | let src = var_id.parent.child_source(db); | 218 | return db.variants_attrs(it.parent)[it.local_id].clone(); |
221 | let src = src.as_ref().map(|it| &it[var_id.local_id]); | ||
222 | RawAttrs::from_attrs_owner(db, src.map(|it| it as &dyn AttrsOwner)) | ||
223 | } | 219 | } |
224 | AttrDefId::AdtId(it) => match it { | 220 | AttrDefId::AdtId(it) => match it { |
225 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), | 221 | AdtId::StructId(it) => attrs_from_item_tree(it.lookup(db).id, db), |
@@ -259,6 +255,46 @@ impl Attrs { | |||
259 | raw_attrs.filter(db, def.krate(db)) | 255 | raw_attrs.filter(db, def.krate(db)) |
260 | } | 256 | } |
261 | 257 | ||
258 | pub(crate) fn variants_attrs_query( | ||
259 | db: &dyn DefDatabase, | ||
260 | e: EnumId, | ||
261 | ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { | ||
262 | let krate = e.lookup(db).container.module(db).krate; | ||
263 | let src = e.child_source(db); | ||
264 | let mut res = ArenaMap::default(); | ||
265 | |||
266 | for (id, var) in src.value.iter() { | ||
267 | let attrs = RawAttrs::from_attrs_owner(db, src.with_value(var as &dyn AttrsOwner)) | ||
268 | .filter(db, krate); | ||
269 | |||
270 | res.insert(id, attrs) | ||
271 | } | ||
272 | |||
273 | Arc::new(res) | ||
274 | } | ||
275 | |||
276 | pub(crate) fn fields_attrs_query( | ||
277 | db: &dyn DefDatabase, | ||
278 | v: VariantId, | ||
279 | ) -> Arc<ArenaMap<LocalFieldId, Attrs>> { | ||
280 | let krate = v.module(db).krate; | ||
281 | let src = v.child_source(db); | ||
282 | let mut res = ArenaMap::default(); | ||
283 | |||
284 | for (id, fld) in src.value.iter() { | ||
285 | let attrs = match fld { | ||
286 | Either::Left(_tuple) => Attrs::default(), | ||
287 | Either::Right(record) => { | ||
288 | RawAttrs::from_attrs_owner(db, src.with_value(record)).filter(db, krate) | ||
289 | } | ||
290 | }; | ||
291 | |||
292 | res.insert(id, attrs); | ||
293 | } | ||
294 | |||
295 | Arc::new(res) | ||
296 | } | ||
297 | |||
262 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { | 298 | pub fn by_key(&self, key: &'static str) -> AttrQuery<'_> { |
263 | AttrQuery { attrs: self, key } | 299 | AttrQuery { attrs: self, key } |
264 | } | 300 | } |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 17c72779b..6be1eaade 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -695,7 +695,7 @@ impl ExprCollector<'_> { | |||
695 | self.collect_stmts_items(block.statements()); | 695 | self.collect_stmts_items(block.statements()); |
696 | let statements = | 696 | let statements = |
697 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); | 697 | block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); |
698 | let tail = block.expr().map(|e| self.collect_expr(e)); | 698 | let tail = block.tail_expr().map(|e| self.collect_expr(e)); |
699 | self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) | 699 | self.alloc_expr(Expr::Block { statements, tail, label: None }, syntax_node_ptr) |
700 | } | 700 | } |
701 | 701 | ||
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs index d1a459066..d3bf5b34c 100644 --- a/crates/hir_def/src/db.rs +++ b/crates/hir_def/src/db.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | //! Defines database & queries for name resolution. | 1 | //! Defines database & queries for name resolution. |
2 | use std::sync::Arc; | 2 | use std::sync::Arc; |
3 | 3 | ||
4 | use arena::map::ArenaMap; | ||
4 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; | 5 | use base_db::{salsa, CrateId, SourceDatabase, Upcast}; |
5 | use hir_expand::{db::AstDatabase, HirFileId}; | 6 | use hir_expand::{db::AstDatabase, HirFileId}; |
6 | use syntax::SmolStr; | 7 | use syntax::SmolStr; |
@@ -16,8 +17,8 @@ use crate::{ | |||
16 | lang_item::{LangItemTarget, LangItems}, | 17 | lang_item::{LangItemTarget, LangItems}, |
17 | nameres::CrateDefMap, | 18 | nameres::CrateDefMap, |
18 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, | 19 | AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc, |
19 | GenericDefId, ImplId, ImplLoc, StaticId, StaticLoc, StructId, StructLoc, TraitId, TraitLoc, | 20 | GenericDefId, ImplId, ImplLoc, LocalEnumVariantId, LocalFieldId, StaticId, StaticLoc, StructId, |
20 | TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, | 21 | StructLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc, VariantId, |
21 | }; | 22 | }; |
22 | 23 | ||
23 | #[salsa::query_group(InternDatabaseStorage)] | 24 | #[salsa::query_group(InternDatabaseStorage)] |
@@ -92,6 +93,12 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> { | |||
92 | #[salsa::invoke(GenericParams::generic_params_query)] | 93 | #[salsa::invoke(GenericParams::generic_params_query)] |
93 | fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>; | 94 | fn generic_params(&self, def: GenericDefId) -> Arc<GenericParams>; |
94 | 95 | ||
96 | #[salsa::invoke(Attrs::variants_attrs_query)] | ||
97 | fn variants_attrs(&self, def: EnumId) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>>; | ||
98 | |||
99 | #[salsa::invoke(Attrs::fields_attrs_query)] | ||
100 | fn fields_attrs(&self, def: VariantId) -> Arc<ArenaMap<LocalFieldId, Attrs>>; | ||
101 | |||
95 | #[salsa::invoke(Attrs::attrs_query)] | 102 | #[salsa::invoke(Attrs::attrs_query)] |
96 | fn attrs(&self, def: AttrDefId) -> Attrs; | 103 | fn attrs(&self, def: AttrDefId) -> Attrs; |
97 | 104 | ||
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index c71266dc0..ab3f059ce 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -133,6 +133,10 @@ impl Diagnostic for InactiveCode { | |||
133 | // This diagnostic is shown when a procedural macro can not be found. This usually means that | 133 | // This diagnostic is shown when a procedural macro can not be found. This usually means that |
134 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), | 134 | // procedural macro support is simply disabled (and hence is only a weak hint instead of an error), |
135 | // but can also indicate project setup problems. | 135 | // but can also indicate project setup problems. |
136 | // | ||
137 | // If you are seeing a lot of "proc macro not expanded" warnings, you can add this option to the | ||
138 | // `rust-analyzer.diagnostics.disabled` list to prevent them from showing. Alternatively you can | ||
139 | // enable support for procedural macros (see `rust-analyzer.procMacro.enable`). | ||
136 | #[derive(Debug, Clone, Eq, PartialEq)] | 140 | #[derive(Debug, Clone, Eq, PartialEq)] |
137 | pub struct UnresolvedProcMacro { | 141 | pub struct UnresolvedProcMacro { |
138 | pub file: HirFileId, | 142 | pub file: HirFileId, |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 30b22f51d..e5368b293 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -7,9 +7,8 @@ use fst::{self, Streamer}; | |||
7 | use hir_expand::name::Name; | 7 | use hir_expand::name::Name; |
8 | use indexmap::{map::Entry, IndexMap}; | 8 | use indexmap::{map::Entry, IndexMap}; |
9 | use itertools::Itertools; | 9 | use itertools::Itertools; |
10 | use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; | 10 | use rustc_hash::{FxHashSet, FxHasher}; |
11 | use smallvec::SmallVec; | 11 | use test_utils::mark; |
12 | use syntax::SmolStr; | ||
13 | 12 | ||
14 | use crate::{ | 13 | use crate::{ |
15 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, | 14 | db::DefDatabase, item_scope::ItemInNs, visibility::Visibility, AssocItemId, ModuleDefId, |
@@ -25,6 +24,8 @@ pub struct ImportInfo { | |||
25 | pub path: ImportPath, | 24 | pub path: ImportPath, |
26 | /// The module containing this item. | 25 | /// The module containing this item. |
27 | pub container: ModuleId, | 26 | pub container: ModuleId, |
27 | /// Whether the import is a trait associated item or not. | ||
28 | pub is_trait_assoc_item: bool, | ||
28 | } | 29 | } |
29 | 30 | ||
30 | #[derive(Debug, Clone, Eq, PartialEq)] | 31 | #[derive(Debug, Clone, Eq, PartialEq)] |
@@ -64,10 +65,6 @@ pub struct ImportMap { | |||
64 | /// the index of the first one. | 65 | /// the index of the first one. |
65 | importables: Vec<ItemInNs>, | 66 | importables: Vec<ItemInNs>, |
66 | fst: fst::Map<Vec<u8>>, | 67 | fst: fst::Map<Vec<u8>>, |
67 | |||
68 | /// Maps names of associated items to the item's ID. Only includes items whose defining trait is | ||
69 | /// exported. | ||
70 | assoc_map: FxHashMap<SmolStr, SmallVec<[AssocItemId; 1]>>, | ||
71 | } | 68 | } |
72 | 69 | ||
73 | impl ImportMap { | 70 | impl ImportMap { |
@@ -108,14 +105,27 @@ impl ImportMap { | |||
108 | 105 | ||
109 | for item in per_ns.iter_items() { | 106 | for item in per_ns.iter_items() { |
110 | let path = mk_path(); | 107 | let path = mk_path(); |
108 | let path_len = path.len(); | ||
109 | let import_info = | ||
110 | ImportInfo { path, container: module, is_trait_assoc_item: false }; | ||
111 | |||
112 | if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { | ||
113 | import_map.collect_trait_assoc_items( | ||
114 | db, | ||
115 | tr, | ||
116 | matches!(item, ItemInNs::Types(_)), | ||
117 | &import_info, | ||
118 | ); | ||
119 | } | ||
120 | |||
111 | match import_map.map.entry(item) { | 121 | match import_map.map.entry(item) { |
112 | Entry::Vacant(entry) => { | 122 | Entry::Vacant(entry) => { |
113 | entry.insert(ImportInfo { path, container: module }); | 123 | entry.insert(import_info); |
114 | } | 124 | } |
115 | Entry::Occupied(mut entry) => { | 125 | Entry::Occupied(mut entry) => { |
116 | // If the new path is shorter, prefer that one. | 126 | // If the new path is shorter, prefer that one. |
117 | if path.len() < entry.get().path.len() { | 127 | if path_len < entry.get().path.len() { |
118 | *entry.get_mut() = ImportInfo { path, container: module }; | 128 | *entry.get_mut() = import_info; |
119 | } else { | 129 | } else { |
120 | continue; | 130 | continue; |
121 | } | 131 | } |
@@ -128,11 +138,6 @@ impl ImportMap { | |||
128 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { | 138 | if let Some(ModuleDefId::ModuleId(mod_id)) = item.as_module_def_id() { |
129 | worklist.push((mod_id, mk_path())); | 139 | worklist.push((mod_id, mk_path())); |
130 | } | 140 | } |
131 | |||
132 | // If we've added a path to a trait, add the trait's methods to the method map. | ||
133 | if let Some(ModuleDefId::TraitId(tr)) = item.as_module_def_id() { | ||
134 | import_map.collect_trait_methods(db, tr); | ||
135 | } | ||
136 | } | 141 | } |
137 | } | 142 | } |
138 | } | 143 | } |
@@ -153,12 +158,10 @@ impl ImportMap { | |||
153 | } | 158 | } |
154 | } | 159 | } |
155 | 160 | ||
156 | let start = last_batch_start; | 161 | let key = fst_path(&importables[last_batch_start].1.path); |
157 | last_batch_start = idx + 1; | 162 | builder.insert(key, last_batch_start as u64).unwrap(); |
158 | |||
159 | let key = fst_path(&importables[start].1.path); | ||
160 | 163 | ||
161 | builder.insert(key, start as u64).unwrap(); | 164 | last_batch_start = idx + 1; |
162 | } | 165 | } |
163 | 166 | ||
164 | import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); | 167 | import_map.fst = fst::Map::new(builder.into_inner().unwrap()).unwrap(); |
@@ -176,10 +179,34 @@ impl ImportMap { | |||
176 | self.map.get(&item) | 179 | self.map.get(&item) |
177 | } | 180 | } |
178 | 181 | ||
179 | fn collect_trait_methods(&mut self, db: &dyn DefDatabase, tr: TraitId) { | 182 | fn collect_trait_assoc_items( |
180 | let data = db.trait_data(tr); | 183 | &mut self, |
181 | for (name, item) in data.items.iter() { | 184 | db: &dyn DefDatabase, |
182 | self.assoc_map.entry(name.to_string().into()).or_default().push(*item); | 185 | tr: TraitId, |
186 | is_type_in_ns: bool, | ||
187 | original_import_info: &ImportInfo, | ||
188 | ) { | ||
189 | for (assoc_item_name, item) in &db.trait_data(tr).items { | ||
190 | let module_def_id = match item { | ||
191 | AssocItemId::FunctionId(f) => ModuleDefId::from(*f), | ||
192 | AssocItemId::ConstId(c) => ModuleDefId::from(*c), | ||
193 | // cannot use associated type aliases directly: need a `<Struct as Trait>::TypeAlias` | ||
194 | // qualifier, ergo no need to store it for imports in import_map | ||
195 | AssocItemId::TypeAliasId(_) => { | ||
196 | mark::hit!(type_aliases_ignored); | ||
197 | continue; | ||
198 | } | ||
199 | }; | ||
200 | let assoc_item = if is_type_in_ns { | ||
201 | ItemInNs::Types(module_def_id) | ||
202 | } else { | ||
203 | ItemInNs::Values(module_def_id) | ||
204 | }; | ||
205 | |||
206 | let mut assoc_item_info = original_import_info.clone(); | ||
207 | assoc_item_info.path.segments.push(assoc_item_name.to_owned()); | ||
208 | assoc_item_info.is_trait_assoc_item = true; | ||
209 | self.map.insert(assoc_item, assoc_item_info); | ||
183 | } | 210 | } |
184 | } | 211 | } |
185 | } | 212 | } |
@@ -302,38 +329,38 @@ impl Query { | |||
302 | self.exclude_import_kinds.insert(import_kind); | 329 | self.exclude_import_kinds.insert(import_kind); |
303 | self | 330 | self |
304 | } | 331 | } |
305 | } | ||
306 | 332 | ||
307 | fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool { | 333 | fn import_matches(&self, import: &ImportInfo, enforce_lowercase: bool) -> bool { |
308 | let mut input = if query.name_only { | 334 | let mut input = if import.is_trait_assoc_item || self.name_only { |
309 | input_path.segments.last().unwrap().to_string() | 335 | import.path.segments.last().unwrap().to_string() |
310 | } else { | 336 | } else { |
311 | input_path.to_string() | 337 | import.path.to_string() |
312 | }; | 338 | }; |
313 | if enforce_lowercase || !query.case_sensitive { | 339 | if enforce_lowercase || !self.case_sensitive { |
314 | input.make_ascii_lowercase(); | 340 | input.make_ascii_lowercase(); |
315 | } | 341 | } |
316 | 342 | ||
317 | let query_string = | 343 | let query_string = |
318 | if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; | 344 | if !enforce_lowercase && self.case_sensitive { &self.query } else { &self.lowercased }; |
319 | 345 | ||
320 | match query.search_mode { | 346 | match self.search_mode { |
321 | SearchMode::Equals => &input == query_string, | 347 | SearchMode::Equals => &input == query_string, |
322 | SearchMode::Contains => input.contains(query_string), | 348 | SearchMode::Contains => input.contains(query_string), |
323 | SearchMode::Fuzzy => { | 349 | SearchMode::Fuzzy => { |
324 | let mut unchecked_query_chars = query_string.chars(); | 350 | let mut unchecked_query_chars = query_string.chars(); |
325 | let mut mismatching_query_char = unchecked_query_chars.next(); | 351 | let mut mismatching_query_char = unchecked_query_chars.next(); |
326 | 352 | ||
327 | for input_char in input.chars() { | 353 | for input_char in input.chars() { |
328 | match mismatching_query_char { | 354 | match mismatching_query_char { |
329 | None => return true, | 355 | None => return true, |
330 | Some(matching_query_char) if matching_query_char == input_char => { | 356 | Some(matching_query_char) if matching_query_char == input_char => { |
331 | mismatching_query_char = unchecked_query_chars.next(); | 357 | mismatching_query_char = unchecked_query_chars.next(); |
358 | } | ||
359 | _ => (), | ||
332 | } | 360 | } |
333 | _ => (), | ||
334 | } | 361 | } |
362 | mismatching_query_char.is_none() | ||
335 | } | 363 | } |
336 | mismatching_query_char.is_none() | ||
337 | } | 364 | } |
338 | } | 365 | } |
339 | } | 366 | } |
@@ -366,13 +393,13 @@ pub fn search_dependencies<'a>( | |||
366 | let import_map = &import_maps[indexed_value.index]; | 393 | let import_map = &import_maps[indexed_value.index]; |
367 | let importables = &import_map.importables[indexed_value.value as usize..]; | 394 | let importables = &import_map.importables[indexed_value.value as usize..]; |
368 | 395 | ||
369 | // Path shared by the importable items in this group. | 396 | let common_importable_data = &import_map.map[&importables[0]]; |
370 | let common_importables_path = &import_map.map[&importables[0]].path; | 397 | if !query.import_matches(common_importable_data, true) { |
371 | if !contains_query(&query, common_importables_path, true) { | ||
372 | continue; | 398 | continue; |
373 | } | 399 | } |
374 | 400 | ||
375 | let common_importables_path_fst = fst_path(common_importables_path); | 401 | // Path shared by the importable items in this group. |
402 | let common_importables_path_fst = fst_path(&common_importable_data.path); | ||
376 | // Add the items from this `ModPath` group. Those are all subsequent items in | 403 | // Add the items from this `ModPath` group. Those are all subsequent items in |
377 | // `importables` whose paths match `path`. | 404 | // `importables` whose paths match `path`. |
378 | let iter = importables | 405 | let iter = importables |
@@ -387,7 +414,7 @@ pub fn search_dependencies<'a>( | |||
387 | }) | 414 | }) |
388 | .filter(|item| { | 415 | .filter(|item| { |
389 | !query.case_sensitive // we've already checked the common importables path case-insensitively | 416 | !query.case_sensitive // we've already checked the common importables path case-insensitively |
390 | || contains_query(&query, &import_map.map[item].path, false) | 417 | || query.import_matches(&import_map.map[item], false) |
391 | }); | 418 | }); |
392 | res.extend(iter); | 419 | res.extend(iter); |
393 | 420 | ||
@@ -398,19 +425,6 @@ pub fn search_dependencies<'a>( | |||
398 | } | 425 | } |
399 | } | 426 | } |
400 | 427 | ||
401 | // Add all exported associated items whose names match the query (exactly). | ||
402 | for map in &import_maps { | ||
403 | if let Some(v) = map.assoc_map.get(&*query.query) { | ||
404 | res.extend(v.iter().map(|&assoc| { | ||
405 | ItemInNs::Types(match assoc { | ||
406 | AssocItemId::FunctionId(it) => it.into(), | ||
407 | AssocItemId::ConstId(it) => it.into(), | ||
408 | AssocItemId::TypeAliasId(it) => it.into(), | ||
409 | }) | ||
410 | })); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | res | 428 | res |
415 | } | 429 | } |
416 | 430 | ||
@@ -432,9 +446,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> { | |||
432 | mod tests { | 446 | mod tests { |
433 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; | 447 | use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; |
434 | use expect_test::{expect, Expect}; | 448 | use expect_test::{expect, Expect}; |
435 | use stdx::format_to; | 449 | use test_utils::mark; |
436 | 450 | ||
437 | use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup}; | 451 | use crate::{test_db::TestDB, AssocContainerId, Lookup}; |
438 | 452 | ||
439 | use super::*; | 453 | use super::*; |
440 | 454 | ||
@@ -451,46 +465,66 @@ mod tests { | |||
451 | 465 | ||
452 | let actual = search_dependencies(db.upcast(), krate, query) | 466 | let actual = search_dependencies(db.upcast(), krate, query) |
453 | .into_iter() | 467 | .into_iter() |
454 | .filter_map(|item| { | 468 | .filter_map(|dependency| { |
455 | let mark = match item { | 469 | let dependency_krate = dependency.krate(db.upcast())?; |
456 | ItemInNs::Types(ModuleDefId::FunctionId(_)) | 470 | let dependency_imports = db.import_map(dependency_krate); |
457 | | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", | 471 | |
458 | ItemInNs::Types(_) => "t", | 472 | let (path, mark) = match assoc_item_path(&db, &dependency_imports, dependency) { |
459 | ItemInNs::Values(_) => "v", | 473 | Some(assoc_item_path) => (assoc_item_path, "a"), |
460 | ItemInNs::Macros(_) => "m", | 474 | None => ( |
475 | dependency_imports.path_of(dependency)?.to_string(), | ||
476 | match dependency { | ||
477 | ItemInNs::Types(ModuleDefId::FunctionId(_)) | ||
478 | | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f", | ||
479 | ItemInNs::Types(_) => "t", | ||
480 | ItemInNs::Values(_) => "v", | ||
481 | ItemInNs::Macros(_) => "m", | ||
482 | }, | ||
483 | ), | ||
461 | }; | 484 | }; |
462 | item.krate(db.upcast()).map(|krate| { | 485 | |
463 | let map = db.import_map(krate); | 486 | Some(format!( |
464 | 487 | "{}::{} ({})\n", | |
465 | let path = match assoc_to_trait(&db, item) { | 488 | crate_graph[dependency_krate].display_name.as_ref()?, |
466 | Some(trait_) => { | 489 | path, |
467 | let mut full_path = map.path_of(trait_).unwrap().to_string(); | 490 | mark |
468 | if let ItemInNs::Types(ModuleDefId::FunctionId(function_id)) | 491 | )) |
469 | | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item | ||
470 | { | ||
471 | format_to!( | ||
472 | full_path, | ||
473 | "::{}", | ||
474 | FunctionData::fn_data_query(&db, function_id).name, | ||
475 | ); | ||
476 | } | ||
477 | full_path | ||
478 | } | ||
479 | None => map.path_of(item).unwrap().to_string(), | ||
480 | }; | ||
481 | |||
482 | format!( | ||
483 | "{}::{} ({})\n", | ||
484 | crate_graph[krate].display_name.as_ref().unwrap(), | ||
485 | path, | ||
486 | mark | ||
487 | ) | ||
488 | }) | ||
489 | }) | 492 | }) |
490 | .collect::<String>(); | 493 | .collect::<String>(); |
491 | expect.assert_eq(&actual) | 494 | expect.assert_eq(&actual) |
492 | } | 495 | } |
493 | 496 | ||
497 | fn assoc_item_path( | ||
498 | db: &dyn DefDatabase, | ||
499 | dependency_imports: &ImportMap, | ||
500 | dependency: ItemInNs, | ||
501 | ) -> Option<String> { | ||
502 | let dependency_assoc_item_id = match dependency { | ||
503 | ItemInNs::Types(ModuleDefId::FunctionId(id)) | ||
504 | | ItemInNs::Values(ModuleDefId::FunctionId(id)) => AssocItemId::from(id), | ||
505 | ItemInNs::Types(ModuleDefId::ConstId(id)) | ||
506 | | ItemInNs::Values(ModuleDefId::ConstId(id)) => AssocItemId::from(id), | ||
507 | ItemInNs::Types(ModuleDefId::TypeAliasId(id)) | ||
508 | | ItemInNs::Values(ModuleDefId::TypeAliasId(id)) => AssocItemId::from(id), | ||
509 | _ => return None, | ||
510 | }; | ||
511 | |||
512 | let trait_ = assoc_to_trait(db, dependency)?; | ||
513 | if let ModuleDefId::TraitId(tr) = trait_.as_module_def_id()? { | ||
514 | let trait_data = db.trait_data(tr); | ||
515 | let assoc_item_name = | ||
516 | trait_data.items.iter().find_map(|(assoc_item_name, assoc_item_id)| { | ||
517 | if &dependency_assoc_item_id == assoc_item_id { | ||
518 | Some(assoc_item_name) | ||
519 | } else { | ||
520 | None | ||
521 | } | ||
522 | })?; | ||
523 | return Some(format!("{}::{}", dependency_imports.path_of(trait_)?, assoc_item_name)); | ||
524 | } | ||
525 | None | ||
526 | } | ||
527 | |||
494 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { | 528 | fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> { |
495 | let assoc: AssocItemId = match item { | 529 | let assoc: AssocItemId = match item { |
496 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { | 530 | ItemInNs::Types(it) | ItemInNs::Values(it) => match it { |
@@ -749,6 +783,37 @@ mod tests { | |||
749 | } | 783 | } |
750 | 784 | ||
751 | #[test] | 785 | #[test] |
786 | fn fuzzy_import_trait_and_assoc_items() { | ||
787 | mark::check!(type_aliases_ignored); | ||
788 | let ra_fixture = r#" | ||
789 | //- /main.rs crate:main deps:dep | ||
790 | //- /dep.rs crate:dep | ||
791 | pub mod fmt { | ||
792 | pub trait Display { | ||
793 | type FmtTypeAlias; | ||
794 | const FMT_CONST: bool; | ||
795 | |||
796 | fn format_function(); | ||
797 | fn format_method(&self); | ||
798 | } | ||
799 | } | ||
800 | "#; | ||
801 | |||
802 | check_search( | ||
803 | ra_fixture, | ||
804 | "main", | ||
805 | Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy), | ||
806 | expect![[r#" | ||
807 | dep::fmt (t) | ||
808 | dep::fmt::Display (t) | ||
809 | dep::fmt::Display::FMT_CONST (a) | ||
810 | dep::fmt::Display::format_function (a) | ||
811 | dep::fmt::Display::format_method (a) | ||
812 | "#]], | ||
813 | ); | ||
814 | } | ||
815 | |||
816 | #[test] | ||
752 | fn search_mode() { | 817 | fn search_mode() { |
753 | let ra_fixture = r#" | 818 | let ra_fixture = r#" |
754 | //- /main.rs crate:main deps:dep | 819 | //- /main.rs crate:main deps:dep |
@@ -784,8 +849,8 @@ mod tests { | |||
784 | dep::Fmt (v) | 849 | dep::Fmt (v) |
785 | dep::Fmt (m) | 850 | dep::Fmt (m) |
786 | dep::fmt::Display (t) | 851 | dep::fmt::Display (t) |
852 | dep::fmt::Display::fmt (a) | ||
787 | dep::format (f) | 853 | dep::format (f) |
788 | dep::fmt::Display::fmt (f) | ||
789 | "#]], | 854 | "#]], |
790 | ); | 855 | ); |
791 | 856 | ||
@@ -798,7 +863,7 @@ mod tests { | |||
798 | dep::Fmt (t) | 863 | dep::Fmt (t) |
799 | dep::Fmt (v) | 864 | dep::Fmt (v) |
800 | dep::Fmt (m) | 865 | dep::Fmt (m) |
801 | dep::fmt::Display::fmt (f) | 866 | dep::fmt::Display::fmt (a) |
802 | "#]], | 867 | "#]], |
803 | ); | 868 | ); |
804 | 869 | ||
@@ -812,7 +877,7 @@ mod tests { | |||
812 | dep::Fmt (v) | 877 | dep::Fmt (v) |
813 | dep::Fmt (m) | 878 | dep::Fmt (m) |
814 | dep::fmt::Display (t) | 879 | dep::fmt::Display (t) |
815 | dep::fmt::Display::fmt (f) | 880 | dep::fmt::Display::fmt (a) |
816 | "#]], | 881 | "#]], |
817 | ); | 882 | ); |
818 | } | 883 | } |
@@ -853,7 +918,7 @@ mod tests { | |||
853 | dep::Fmt (v) | 918 | dep::Fmt (v) |
854 | dep::Fmt (m) | 919 | dep::Fmt (m) |
855 | dep::fmt::Display (t) | 920 | dep::fmt::Display (t) |
856 | dep::fmt::Display::fmt (f) | 921 | dep::fmt::Display::fmt (a) |
857 | "#]], | 922 | "#]], |
858 | ); | 923 | ); |
859 | 924 | ||
@@ -866,7 +931,7 @@ mod tests { | |||
866 | dep::Fmt (t) | 931 | dep::Fmt (t) |
867 | dep::Fmt (v) | 932 | dep::Fmt (v) |
868 | dep::Fmt (m) | 933 | dep::Fmt (m) |
869 | dep::fmt::Display::fmt (f) | 934 | dep::fmt::Display::fmt (a) |
870 | "#]], | 935 | "#]], |
871 | ); | 936 | ); |
872 | } | 937 | } |
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 62ab3b2bd..2750e1c91 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs | |||
@@ -10,10 +10,9 @@ use once_cell::sync::Lazy; | |||
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 10 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | use test_utils::mark; | 11 | use test_utils::mark; |
12 | 12 | ||
13 | use crate::ModuleId; | ||
14 | use crate::{ | 13 | use crate::{ |
15 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, | 14 | db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId, |
16 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, TraitId, | 15 | LocalModuleId, Lookup, MacroDefId, ModuleDefId, ModuleId, TraitId, |
17 | }; | 16 | }; |
18 | 17 | ||
19 | #[derive(Copy, Clone)] | 18 | #[derive(Copy, Clone)] |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index a636ec77d..77017e4ea 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -13,7 +13,7 @@ use hir_expand::{ | |||
13 | builtin_macro::find_builtin_macro, | 13 | builtin_macro::find_builtin_macro, |
14 | name::{AsName, Name}, | 14 | name::{AsName, Name}, |
15 | proc_macro::ProcMacroExpander, | 15 | proc_macro::ProcMacroExpander, |
16 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 16 | HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, |
17 | }; | 17 | }; |
18 | use hir_expand::{InFile, MacroCallLoc}; | 18 | use hir_expand::{InFile, MacroCallLoc}; |
19 | use rustc_hash::{FxHashMap, FxHashSet}; | 19 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -860,6 +860,37 @@ impl DefCollector<'_> { | |||
860 | } | 860 | } |
861 | 861 | ||
862 | fn finish(mut self) -> CrateDefMap { | 862 | fn finish(mut self) -> CrateDefMap { |
863 | // Emit diagnostics for all remaining unexpanded macros. | ||
864 | |||
865 | for directive in &self.unexpanded_macros { | ||
866 | let mut error = None; | ||
867 | directive.ast_id.as_call_id_with_errors( | ||
868 | self.db, | ||
869 | self.def_map.krate, | ||
870 | |path| { | ||
871 | let resolved_res = self.def_map.resolve_path_fp_with_macro( | ||
872 | self.db, | ||
873 | ResolveMode::Other, | ||
874 | directive.module_id, | ||
875 | &path, | ||
876 | BuiltinShadowMode::Module, | ||
877 | ); | ||
878 | resolved_res.resolved_def.take_macros() | ||
879 | }, | ||
880 | &mut |e| { | ||
881 | error.get_or_insert(e); | ||
882 | }, | ||
883 | ); | ||
884 | |||
885 | if let Some(err) = error { | ||
886 | self.def_map.diagnostics.push(DefDiagnostic::macro_error( | ||
887 | directive.module_id, | ||
888 | MacroCallKind::FnLike(directive.ast_id.ast_id), | ||
889 | err.to_string(), | ||
890 | )); | ||
891 | } | ||
892 | } | ||
893 | |||
863 | // Emit diagnostics for all remaining unresolved imports. | 894 | // Emit diagnostics for all remaining unresolved imports. |
864 | 895 | ||
865 | // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't | 896 | // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't |
diff --git a/crates/hir_expand/src/proc_macro.rs b/crates/hir_expand/src/proc_macro.rs index 7c77f6ce0..1923daca5 100644 --- a/crates/hir_expand/src/proc_macro.rs +++ b/crates/hir_expand/src/proc_macro.rs | |||
@@ -58,7 +58,7 @@ impl ProcMacroExpander { | |||
58 | } | 58 | } |
59 | 59 | ||
60 | fn eat_punct(cursor: &mut Cursor, c: char) -> bool { | 60 | fn eat_punct(cursor: &mut Cursor, c: char) -> bool { |
61 | if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = cursor.token_tree() { | 61 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = cursor.token_tree() { |
62 | if punct.char == c { | 62 | if punct.char == c { |
63 | *cursor = cursor.bump(); | 63 | *cursor = cursor.bump(); |
64 | return true; | 64 | return true; |
@@ -68,7 +68,7 @@ fn eat_punct(cursor: &mut Cursor, c: char) -> bool { | |||
68 | } | 68 | } |
69 | 69 | ||
70 | fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { | 70 | fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { |
71 | if let Some(tt::TokenTree::Subtree(subtree)) = cursor.token_tree() { | 71 | if let Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) = cursor.token_tree() { |
72 | if Some(kind) == subtree.delimiter_kind() { | 72 | if Some(kind) == subtree.delimiter_kind() { |
73 | *cursor = cursor.bump_subtree(); | 73 | *cursor = cursor.bump_subtree(); |
74 | return true; | 74 | return true; |
@@ -78,7 +78,7 @@ fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { | 80 | fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { |
81 | if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = cursor.token_tree() { | 81 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = cursor.token_tree() { |
82 | if t == ident.text.as_str() { | 82 | if t == ident.text.as_str() { |
83 | *cursor = cursor.bump(); | 83 | *cursor = cursor.bump(); |
84 | return true; | 84 | return true; |
@@ -88,7 +88,7 @@ fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { | |||
88 | } | 88 | } |
89 | 89 | ||
90 | fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { | 90 | fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { |
91 | let buffer = TokenBuffer::new(&tt.token_trees); | 91 | let buffer = TokenBuffer::from_tokens(&tt.token_trees); |
92 | let mut p = buffer.begin(); | 92 | let mut p = buffer.begin(); |
93 | let mut result = tt::Subtree::default(); | 93 | let mut result = tt::Subtree::default(); |
94 | 94 | ||
@@ -106,7 +106,7 @@ fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { | |||
106 | } | 106 | } |
107 | } | 107 | } |
108 | 108 | ||
109 | result.token_trees.push(curr.token_tree()?.clone()); | 109 | result.token_trees.push(curr.token_tree()?.cloned()); |
110 | p = curr.bump(); | 110 | p = curr.bump(); |
111 | } | 111 | } |
112 | 112 | ||
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index 3d1778590..b0a453961 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,9 +17,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.45", default-features = false } | 20 | chalk-solve = { version = "0.47", default-features = false } |
21 | chalk-ir = "0.45" | 21 | chalk-ir = "0.47" |
22 | chalk-recursive = "0.45" | 22 | chalk-recursive = "0.47" |
23 | 23 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs index 0e827a29e..e9e949c47 100644 --- a/crates/hir_ty/src/display.rs +++ b/crates/hir_ty/src/display.rs | |||
@@ -168,7 +168,7 @@ pub enum DisplayTarget { | |||
168 | 168 | ||
169 | impl DisplayTarget { | 169 | impl DisplayTarget { |
170 | fn is_source_code(&self) -> bool { | 170 | fn is_source_code(&self) -> bool { |
171 | matches!(self, Self::SourceCode {..}) | 171 | matches!(self, Self::SourceCode { .. }) |
172 | } | 172 | } |
173 | fn is_test(&self) -> bool { | 173 | fn is_test(&self) -> bool { |
174 | matches!(self, Self::Test) | 174 | matches!(self, Self::Test) |
@@ -595,7 +595,7 @@ impl HirDisplay for FnSig { | |||
595 | } | 595 | } |
596 | } | 596 | } |
597 | 597 | ||
598 | fn write_bounds_like_dyn_trait( | 598 | pub fn write_bounds_like_dyn_trait( |
599 | predicates: &[GenericPredicate], | 599 | predicates: &[GenericPredicate], |
600 | f: &mut HirFormatter, | 600 | f: &mut HirFormatter, |
601 | ) -> Result<(), HirDisplayError> { | 601 | ) -> Result<(), HirDisplayError> { |
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs index ea49d9f97..990f740b8 100644 --- a/crates/ide/src/display/short_label.rs +++ b/crates/ide/src/display/short_label.rs | |||
@@ -87,6 +87,17 @@ impl ShortLabel for ast::Variant { | |||
87 | } | 87 | } |
88 | } | 88 | } |
89 | 89 | ||
90 | impl ShortLabel for ast::ConstParam { | ||
91 | fn short_label(&self) -> Option<String> { | ||
92 | let mut buf = "const ".to_owned(); | ||
93 | buf.push_str(self.name()?.text().as_str()); | ||
94 | if let Some(type_ref) = self.ty() { | ||
95 | format_to!(buf, ": {}", type_ref.syntax()); | ||
96 | } | ||
97 | Some(buf) | ||
98 | } | ||
99 | } | ||
100 | |||
90 | fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String> | 101 | fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String> |
91 | where | 102 | where |
92 | T: NameOwner + VisibilityOwner, | 103 | T: NameOwner + VisibilityOwner, |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 2737c900f..f2ad95cb6 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -70,7 +70,7 @@ impl HoverConfig { | |||
70 | #[derive(Debug, Clone)] | 70 | #[derive(Debug, Clone)] |
71 | pub enum HoverAction { | 71 | pub enum HoverAction { |
72 | Runnable(Runnable), | 72 | Runnable(Runnable), |
73 | Implementaion(FilePosition), | 73 | Implementation(FilePosition), |
74 | GoToType(Vec<HoverGotoTypeData>), | 74 | GoToType(Vec<HoverGotoTypeData>), |
75 | } | 75 | } |
76 | 76 | ||
@@ -116,12 +116,13 @@ pub(crate) fn hover( | |||
116 | }; | 116 | }; |
117 | if let Some(definition) = definition { | 117 | if let Some(definition) = definition { |
118 | if let Some(markup) = hover_for_definition(db, definition) { | 118 | if let Some(markup) = hover_for_definition(db, definition) { |
119 | let markup = markup.as_str(); | ||
119 | let markup = if !markdown { | 120 | let markup = if !markdown { |
120 | remove_markdown(&markup.as_str()) | 121 | remove_markdown(markup) |
121 | } else if links_in_hover { | 122 | } else if links_in_hover { |
122 | rewrite_links(db, &markup.as_str(), &definition) | 123 | rewrite_links(db, markup, &definition) |
123 | } else { | 124 | } else { |
124 | remove_links(&markup.as_str()) | 125 | remove_links(markup) |
125 | }; | 126 | }; |
126 | res.markup = Markup::from(markup); | 127 | res.markup = Markup::from(markup); |
127 | if let Some(action) = show_implementations_action(db, definition) { | 128 | if let Some(action) = show_implementations_action(db, definition) { |
@@ -175,22 +176,24 @@ pub(crate) fn hover( | |||
175 | 176 | ||
176 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 177 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
177 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | 178 | fn to_action(nav_target: NavigationTarget) -> HoverAction { |
178 | HoverAction::Implementaion(FilePosition { | 179 | HoverAction::Implementation(FilePosition { |
179 | file_id: nav_target.file_id, | 180 | file_id: nav_target.file_id, |
180 | offset: nav_target.focus_or_full_range().start(), | 181 | offset: nav_target.focus_or_full_range().start(), |
181 | }) | 182 | }) |
182 | } | 183 | } |
183 | 184 | ||
184 | match def { | 185 | let adt = match def { |
185 | Definition::ModuleDef(it) => match it { | 186 | Definition::ModuleDef(ModuleDef::Trait(it)) => return it.try_to_nav(db).map(to_action), |
186 | ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.try_to_nav(db)?)), | 187 | Definition::ModuleDef(ModuleDef::Adt(it)) => Some(it), |
187 | ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.try_to_nav(db)?)), | 188 | Definition::SelfType(it) => it.target_ty(db).as_adt(), |
188 | ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.try_to_nav(db)?)), | ||
189 | ModuleDef::Trait(it) => Some(to_action(it.try_to_nav(db)?)), | ||
190 | _ => None, | ||
191 | }, | ||
192 | _ => None, | 189 | _ => None, |
190 | }?; | ||
191 | match adt { | ||
192 | Adt::Struct(it) => it.try_to_nav(db), | ||
193 | Adt::Union(it) => it.try_to_nav(db), | ||
194 | Adt::Enum(it) => it.try_to_nav(db), | ||
193 | } | 195 | } |
196 | .map(to_action) | ||
194 | } | 197 | } |
195 | 198 | ||
196 | fn runnable_action( | 199 | fn runnable_action( |
@@ -225,45 +228,46 @@ fn runnable_action( | |||
225 | } | 228 | } |
226 | 229 | ||
227 | fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | 230 | fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { |
228 | match def { | 231 | let mut targets: Vec<ModuleDef> = Vec::new(); |
229 | Definition::Local(it) => { | 232 | let mut push_new_def = |item: ModuleDef| { |
230 | let mut targets: Vec<ModuleDef> = Vec::new(); | 233 | if !targets.contains(&item) { |
231 | let mut push_new_def = |item: ModuleDef| { | 234 | targets.push(item); |
232 | if !targets.contains(&item) { | ||
233 | targets.push(item); | ||
234 | } | ||
235 | }; | ||
236 | |||
237 | it.ty(db).walk(db, |t| { | ||
238 | if let Some(adt) = t.as_adt() { | ||
239 | push_new_def(adt.into()); | ||
240 | } else if let Some(trait_) = t.as_dyn_trait() { | ||
241 | push_new_def(trait_.into()); | ||
242 | } else if let Some(traits) = t.as_impl_traits(db) { | ||
243 | traits.into_iter().for_each(|it| push_new_def(it.into())); | ||
244 | } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { | ||
245 | push_new_def(trait_.into()); | ||
246 | } | ||
247 | }); | ||
248 | |||
249 | let targets = targets | ||
250 | .into_iter() | ||
251 | .filter_map(|it| { | ||
252 | Some(HoverGotoTypeData { | ||
253 | mod_path: render_path( | ||
254 | db, | ||
255 | it.module(db)?, | ||
256 | it.name(db).map(|name| name.to_string()), | ||
257 | ), | ||
258 | nav: it.try_to_nav(db)?, | ||
259 | }) | ||
260 | }) | ||
261 | .collect(); | ||
262 | |||
263 | Some(HoverAction::GoToType(targets)) | ||
264 | } | 235 | } |
265 | _ => None, | 236 | }; |
237 | |||
238 | if let Definition::TypeParam(it) = def { | ||
239 | it.trait_bounds(db).into_iter().for_each(|it| push_new_def(it.into())); | ||
240 | } else { | ||
241 | let ty = match def { | ||
242 | Definition::Local(it) => it.ty(db), | ||
243 | Definition::ConstParam(it) => it.ty(db), | ||
244 | _ => return None, | ||
245 | }; | ||
246 | |||
247 | ty.walk(db, |t| { | ||
248 | if let Some(adt) = t.as_adt() { | ||
249 | push_new_def(adt.into()); | ||
250 | } else if let Some(trait_) = t.as_dyn_trait() { | ||
251 | push_new_def(trait_.into()); | ||
252 | } else if let Some(traits) = t.as_impl_traits(db) { | ||
253 | traits.into_iter().for_each(|it| push_new_def(it.into())); | ||
254 | } else if let Some(trait_) = t.as_associated_type_parent_trait(db) { | ||
255 | push_new_def(trait_.into()); | ||
256 | } | ||
257 | }); | ||
266 | } | 258 | } |
259 | |||
260 | let targets = targets | ||
261 | .into_iter() | ||
262 | .filter_map(|it| { | ||
263 | Some(HoverGotoTypeData { | ||
264 | mod_path: render_path(db, it.module(db)?, it.name(db).map(|name| name.to_string())), | ||
265 | nav: it.try_to_nav(db)?, | ||
266 | }) | ||
267 | }) | ||
268 | .collect(); | ||
269 | |||
270 | Some(HoverAction::GoToType(targets)) | ||
267 | } | 271 | } |
268 | 272 | ||
269 | fn hover_markup( | 273 | fn hover_markup( |
@@ -370,10 +374,8 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> { | |||
370 | } | 374 | } |
371 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), | 375 | Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), |
372 | Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), | 376 | Definition::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), |
373 | Definition::TypeParam(_) | Definition::ConstParam(_) => { | 377 | Definition::TypeParam(type_param) => Some(Markup::fenced_block(&type_param.display(db))), |
374 | // FIXME: Hover for generic param | 378 | Definition::ConstParam(it) => from_def_source(db, it, None), |
375 | None | ||
376 | } | ||
377 | }; | 379 | }; |
378 | 380 | ||
379 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> | 381 | fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> |
@@ -1393,7 +1395,7 @@ fn bar() { fo<|>o(); } | |||
1393 | r"unsafe trait foo<|>() {}", | 1395 | r"unsafe trait foo<|>() {}", |
1394 | expect![[r#" | 1396 | expect![[r#" |
1395 | [ | 1397 | [ |
1396 | Implementaion( | 1398 | Implementation( |
1397 | FilePosition { | 1399 | FilePosition { |
1398 | file_id: FileId( | 1400 | file_id: FileId( |
1399 | 0, | 1401 | 0, |
@@ -2105,7 +2107,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2105 | r#"trait foo<|>() {}"#, | 2107 | r#"trait foo<|>() {}"#, |
2106 | expect![[r#" | 2108 | expect![[r#" |
2107 | [ | 2109 | [ |
2108 | Implementaion( | 2110 | Implementation( |
2109 | FilePosition { | 2111 | FilePosition { |
2110 | file_id: FileId( | 2112 | file_id: FileId( |
2111 | 0, | 2113 | 0, |
@@ -2124,7 +2126,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2124 | r"struct foo<|>() {}", | 2126 | r"struct foo<|>() {}", |
2125 | expect![[r#" | 2127 | expect![[r#" |
2126 | [ | 2128 | [ |
2127 | Implementaion( | 2129 | Implementation( |
2128 | FilePosition { | 2130 | FilePosition { |
2129 | file_id: FileId( | 2131 | file_id: FileId( |
2130 | 0, | 2132 | 0, |
@@ -2143,7 +2145,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2143 | r#"union foo<|>() {}"#, | 2145 | r#"union foo<|>() {}"#, |
2144 | expect![[r#" | 2146 | expect![[r#" |
2145 | [ | 2147 | [ |
2146 | Implementaion( | 2148 | Implementation( |
2147 | FilePosition { | 2149 | FilePosition { |
2148 | file_id: FileId( | 2150 | file_id: FileId( |
2149 | 0, | 2151 | 0, |
@@ -2162,7 +2164,7 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2162 | r"enum foo<|>() { A, B }", | 2164 | r"enum foo<|>() { A, B }", |
2163 | expect![[r#" | 2165 | expect![[r#" |
2164 | [ | 2166 | [ |
2165 | Implementaion( | 2167 | Implementation( |
2166 | FilePosition { | 2168 | FilePosition { |
2167 | file_id: FileId( | 2169 | file_id: FileId( |
2168 | 0, | 2170 | 0, |
@@ -2176,6 +2178,25 @@ fn foo() { let bar = Bar; bar.fo<|>o(); } | |||
2176 | } | 2178 | } |
2177 | 2179 | ||
2178 | #[test] | 2180 | #[test] |
2181 | fn test_hover_self_has_impl_action() { | ||
2182 | check_actions( | ||
2183 | r#"struct foo where Self<|>:;"#, | ||
2184 | expect![[r#" | ||
2185 | [ | ||
2186 | Implementation( | ||
2187 | FilePosition { | ||
2188 | file_id: FileId( | ||
2189 | 0, | ||
2190 | ), | ||
2191 | offset: 7, | ||
2192 | }, | ||
2193 | ), | ||
2194 | ] | ||
2195 | "#]], | ||
2196 | ); | ||
2197 | } | ||
2198 | |||
2199 | #[test] | ||
2179 | fn test_hover_test_has_action() { | 2200 | fn test_hover_test_has_action() { |
2180 | check_actions( | 2201 | check_actions( |
2181 | r#" | 2202 | r#" |
@@ -3064,6 +3085,71 @@ fn main() { let s<|>t = test().get(); } | |||
3064 | } | 3085 | } |
3065 | 3086 | ||
3066 | #[test] | 3087 | #[test] |
3088 | fn test_hover_const_param_has_goto_type_action() { | ||
3089 | check_actions( | ||
3090 | r#" | ||
3091 | struct Bar; | ||
3092 | struct Foo<const BAR: Bar>; | ||
3093 | |||
3094 | impl<const BAR: Bar> Foo<BAR<|>> {} | ||
3095 | "#, | ||
3096 | expect![[r#" | ||
3097 | [ | ||
3098 | GoToType( | ||
3099 | [ | ||
3100 | HoverGotoTypeData { | ||
3101 | mod_path: "test::Bar", | ||
3102 | nav: NavigationTarget { | ||
3103 | file_id: FileId( | ||
3104 | 0, | ||
3105 | ), | ||
3106 | full_range: 0..11, | ||
3107 | focus_range: 7..10, | ||
3108 | name: "Bar", | ||
3109 | kind: Struct, | ||
3110 | description: "struct Bar", | ||
3111 | }, | ||
3112 | }, | ||
3113 | ], | ||
3114 | ), | ||
3115 | ] | ||
3116 | "#]], | ||
3117 | ); | ||
3118 | } | ||
3119 | |||
3120 | #[test] | ||
3121 | fn test_hover_type_param_has_goto_type_action() { | ||
3122 | check_actions( | ||
3123 | r#" | ||
3124 | trait Foo {} | ||
3125 | |||
3126 | fn foo<T: Foo>(t: T<|>){} | ||
3127 | "#, | ||
3128 | expect![[r#" | ||
3129 | [ | ||
3130 | GoToType( | ||
3131 | [ | ||
3132 | HoverGotoTypeData { | ||
3133 | mod_path: "test::Foo", | ||
3134 | nav: NavigationTarget { | ||
3135 | file_id: FileId( | ||
3136 | 0, | ||
3137 | ), | ||
3138 | full_range: 0..12, | ||
3139 | focus_range: 6..9, | ||
3140 | name: "Foo", | ||
3141 | kind: Trait, | ||
3142 | description: "trait Foo", | ||
3143 | }, | ||
3144 | }, | ||
3145 | ], | ||
3146 | ), | ||
3147 | ] | ||
3148 | "#]], | ||
3149 | ); | ||
3150 | } | ||
3151 | |||
3152 | #[test] | ||
3067 | fn hover_displays_normalized_crate_names() { | 3153 | fn hover_displays_normalized_crate_names() { |
3068 | check( | 3154 | check( |
3069 | r#" | 3155 | r#" |
@@ -3257,4 +3343,68 @@ fn foo() { | |||
3257 | "#]], | 3343 | "#]], |
3258 | ); | 3344 | ); |
3259 | } | 3345 | } |
3346 | |||
3347 | #[test] | ||
3348 | fn hover_type_param() { | ||
3349 | check( | ||
3350 | r#" | ||
3351 | struct Foo<T>(T); | ||
3352 | trait Copy {} | ||
3353 | trait Clone {} | ||
3354 | trait Sized {} | ||
3355 | impl<T: Copy + Clone> Foo<T<|>> where T: Sized {} | ||
3356 | "#, | ||
3357 | expect![[r#" | ||
3358 | *T* | ||
3359 | |||
3360 | ```rust | ||
3361 | T: Copy + Clone + Sized | ||
3362 | ``` | ||
3363 | "#]], | ||
3364 | ); | ||
3365 | check( | ||
3366 | r#" | ||
3367 | struct Foo<T>(T); | ||
3368 | impl<T> Foo<T<|>> {} | ||
3369 | "#, | ||
3370 | expect![[r#" | ||
3371 | *T* | ||
3372 | |||
3373 | ```rust | ||
3374 | T | ||
3375 | ``` | ||
3376 | "#]], | ||
3377 | ); | ||
3378 | // lifetimes aren't being substituted yet | ||
3379 | check( | ||
3380 | r#" | ||
3381 | struct Foo<T>(T); | ||
3382 | impl<T: 'static> Foo<T<|>> {} | ||
3383 | "#, | ||
3384 | expect![[r#" | ||
3385 | *T* | ||
3386 | |||
3387 | ```rust | ||
3388 | T: {error} | ||
3389 | ``` | ||
3390 | "#]], | ||
3391 | ); | ||
3392 | } | ||
3393 | |||
3394 | #[test] | ||
3395 | fn hover_const_param() { | ||
3396 | check( | ||
3397 | r#" | ||
3398 | struct Foo<const LEN: usize>; | ||
3399 | impl<const LEN: usize> Foo<LEN<|>> {} | ||
3400 | "#, | ||
3401 | expect![[r#" | ||
3402 | *LEN* | ||
3403 | |||
3404 | ```rust | ||
3405 | const LEN: usize | ||
3406 | ``` | ||
3407 | "#]], | ||
3408 | ); | ||
3409 | } | ||
3260 | } | 3410 | } |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index 0f4c2ca47..0782ab070 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! This module contains an import search funcionality that is provided to the assists module. | 1 | //! This module contains an import search funcionality that is provided to the assists module. |
2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. | 2 | //! Later, this should be moved away to a separate crate that is accessible from the assists module. |
3 | 3 | ||
4 | use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics}; | 4 | use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics}; |
5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; | 5 | use syntax::{ast, AstNode, SyntaxKind::NAME}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
@@ -40,8 +40,9 @@ pub fn find_similar_imports<'a>( | |||
40 | krate: Crate, | 40 | krate: Crate, |
41 | limit: Option<usize>, | 41 | limit: Option<usize>, |
42 | fuzzy_search_string: String, | 42 | fuzzy_search_string: String, |
43 | ignore_assoc_items: bool, | ||
43 | name_only: bool, | 44 | name_only: bool, |
44 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 45 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> + 'a { |
45 | let _p = profile::span("find_similar_imports"); | 46 | let _p = profile::span("find_similar_imports"); |
46 | 47 | ||
47 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) | 48 | let mut external_query = import_map::Query::new(fuzzy_search_string.clone()) |
@@ -57,7 +58,21 @@ pub fn find_similar_imports<'a>( | |||
57 | external_query = external_query.limit(limit); | 58 | external_query = external_query.limit(limit); |
58 | } | 59 | } |
59 | 60 | ||
60 | find_imports(sema, krate, local_query, external_query) | 61 | let db = sema.db; |
62 | find_imports(sema, krate, local_query, external_query).filter(move |import_candidate| { | ||
63 | if ignore_assoc_items { | ||
64 | match import_candidate { | ||
65 | Either::Left(ModuleDef::Function(function)) => function.as_assoc_item(db).is_none(), | ||
66 | Either::Left(ModuleDef::Const(const_)) => const_.as_assoc_item(db).is_none(), | ||
67 | Either::Left(ModuleDef::TypeAlias(type_alias)) => { | ||
68 | type_alias.as_assoc_item(db).is_none() | ||
69 | } | ||
70 | _ => true, | ||
71 | } | ||
72 | } else { | ||
73 | true | ||
74 | } | ||
75 | }) | ||
61 | } | 76 | } |
62 | 77 | ||
63 | fn find_imports<'a>( | 78 | fn find_imports<'a>( |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index ab5f87c48..fdc8844ce 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -309,7 +309,7 @@ impl<'a> TtIter<'a> { | |||
309 | } | 309 | } |
310 | } | 310 | } |
311 | 311 | ||
312 | let buffer = TokenBuffer::new(&self.inner.as_slice()); | 312 | let buffer = TokenBuffer::from_tokens(&self.inner.as_slice()); |
313 | let mut src = SubtreeTokenSource::new(&buffer); | 313 | let mut src = SubtreeTokenSource::new(&buffer); |
314 | let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; | 314 | let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false }; |
315 | 315 | ||
@@ -336,11 +336,11 @@ impl<'a> TtIter<'a> { | |||
336 | err = Some(err!("no tokens consumed")); | 336 | err = Some(err!("no tokens consumed")); |
337 | } | 337 | } |
338 | let res = match res.len() { | 338 | let res = match res.len() { |
339 | 1 => Some(res[0].clone()), | 339 | 1 => Some(res[0].cloned()), |
340 | 0 => None, | 340 | 0 => None, |
341 | _ => Some(tt::TokenTree::Subtree(tt::Subtree { | 341 | _ => Some(tt::TokenTree::Subtree(tt::Subtree { |
342 | delimiter: None, | 342 | delimiter: None, |
343 | token_trees: res.into_iter().cloned().collect(), | 343 | token_trees: res.into_iter().map(|it| it.cloned()).collect(), |
344 | })), | 344 | })), |
345 | }; | 345 | }; |
346 | ExpandResult { value: res, err } | 346 | ExpandResult { value: res, err } |
diff --git a/crates/mbe/src/subtree_source.rs b/crates/mbe/src/subtree_source.rs index d10d4b70e..d7433bd35 100644 --- a/crates/mbe/src/subtree_source.rs +++ b/crates/mbe/src/subtree_source.rs | |||
@@ -1,129 +1,104 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use parser::{Token, TokenSource}; | 3 | use parser::{Token, TokenSource}; |
4 | use std::cell::{Cell, Ref, RefCell}; | ||
5 | use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; | 4 | use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; |
6 | use tt::buffer::{Cursor, TokenBuffer}; | 5 | use tt::buffer::TokenBuffer; |
7 | 6 | ||
8 | #[derive(Debug, Clone, Eq, PartialEq)] | 7 | #[derive(Debug, Clone, Eq, PartialEq)] |
9 | struct TtToken { | 8 | struct TtToken { |
10 | kind: SyntaxKind, | 9 | tt: Token, |
11 | is_joint_to_next: bool, | ||
12 | text: SmolStr, | 10 | text: SmolStr, |
13 | } | 11 | } |
14 | 12 | ||
15 | pub(crate) struct SubtreeTokenSource<'a> { | 13 | pub(crate) struct SubtreeTokenSource { |
16 | cached_cursor: Cell<Cursor<'a>>, | 14 | cached: Vec<TtToken>, |
17 | cached: RefCell<Vec<Option<TtToken>>>, | ||
18 | curr: (Token, usize), | 15 | curr: (Token, usize), |
19 | } | 16 | } |
20 | 17 | ||
21 | impl<'a> SubtreeTokenSource<'a> { | 18 | impl<'a> SubtreeTokenSource { |
22 | // Helper function used in test | 19 | // Helper function used in test |
23 | #[cfg(test)] | 20 | #[cfg(test)] |
24 | pub(crate) fn text(&self) -> SmolStr { | 21 | pub(crate) fn text(&self) -> SmolStr { |
25 | match *self.get(self.curr.1) { | 22 | match self.cached.get(self.curr.1) { |
26 | Some(ref tt) => tt.text.clone(), | 23 | Some(ref tt) => tt.text.clone(), |
27 | _ => SmolStr::new(""), | 24 | _ => SmolStr::new(""), |
28 | } | 25 | } |
29 | } | 26 | } |
30 | } | 27 | } |
31 | 28 | ||
32 | impl<'a> SubtreeTokenSource<'a> { | 29 | impl<'a> SubtreeTokenSource { |
33 | pub(crate) fn new(buffer: &'a TokenBuffer) -> SubtreeTokenSource<'a> { | 30 | pub(crate) fn new(buffer: &TokenBuffer) -> SubtreeTokenSource { |
34 | let cursor = buffer.begin(); | 31 | let mut current = buffer.begin(); |
32 | let mut cached = Vec::with_capacity(100); | ||
35 | 33 | ||
36 | let mut res = SubtreeTokenSource { | 34 | while !current.eof() { |
37 | curr: (Token { kind: EOF, is_jointed_to_next: false }, 0), | 35 | let cursor = current; |
38 | cached_cursor: Cell::new(cursor), | 36 | let tt = cursor.token_tree(); |
39 | cached: RefCell::new(Vec::with_capacity(10)), | ||
40 | }; | ||
41 | res.curr = (res.mk_token(0), 0); | ||
42 | res | ||
43 | } | ||
44 | 37 | ||
45 | fn mk_token(&self, pos: usize) -> Token { | 38 | // Check if it is lifetime |
46 | match *self.get(pos) { | 39 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(punct), _)) = tt { |
47 | Some(ref tt) => Token { kind: tt.kind, is_jointed_to_next: tt.is_joint_to_next }, | ||
48 | None => Token { kind: EOF, is_jointed_to_next: false }, | ||
49 | } | ||
50 | } | ||
51 | |||
52 | fn get(&self, pos: usize) -> Ref<Option<TtToken>> { | ||
53 | fn is_lifetime(c: Cursor) -> Option<(Cursor, SmolStr)> { | ||
54 | let tkn = c.token_tree(); | ||
55 | |||
56 | if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(punct))) = tkn { | ||
57 | if punct.char == '\'' { | 40 | if punct.char == '\'' { |
58 | let next = c.bump(); | 41 | let next = cursor.bump(); |
59 | if let Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) = next.token_tree() { | 42 | if let Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Ident(ident), _)) = |
60 | let res_cursor = next.bump(); | 43 | next.token_tree() |
61 | let text = SmolStr::new("'".to_string() + &ident.to_string()); | 44 | { |
62 | 45 | let text = SmolStr::new("'".to_string() + &ident.text); | |
63 | return Some((res_cursor, text)); | 46 | cached.push(TtToken { |
47 | tt: Token { kind: LIFETIME_IDENT, is_jointed_to_next: false }, | ||
48 | text, | ||
49 | }); | ||
50 | current = next.bump(); | ||
51 | continue; | ||
64 | } else { | 52 | } else { |
65 | panic!("Next token must be ident : {:#?}", next.token_tree()); | 53 | panic!("Next token must be ident : {:#?}", next.token_tree()); |
66 | } | 54 | } |
67 | } | 55 | } |
68 | } | 56 | } |
69 | 57 | ||
70 | None | 58 | current = match tt { |
71 | } | 59 | Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { |
72 | 60 | cached.push(convert_leaf(&leaf)); | |
73 | if pos < self.cached.borrow().len() { | 61 | cursor.bump() |
74 | return Ref::map(self.cached.borrow(), |c| &c[pos]); | ||
75 | } | ||
76 | |||
77 | { | ||
78 | let mut cached = self.cached.borrow_mut(); | ||
79 | while pos >= cached.len() { | ||
80 | let cursor = self.cached_cursor.get(); | ||
81 | if cursor.eof() { | ||
82 | cached.push(None); | ||
83 | continue; | ||
84 | } | 62 | } |
85 | 63 | Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { | |
86 | if let Some((curr, text)) = is_lifetime(cursor) { | 64 | cached.push(convert_delim(subtree.delimiter_kind(), false)); |
87 | cached.push(Some(TtToken { | 65 | cursor.subtree().unwrap() |
88 | kind: LIFETIME_IDENT, | ||
89 | is_joint_to_next: false, | ||
90 | text, | ||
91 | })); | ||
92 | self.cached_cursor.set(curr); | ||
93 | continue; | ||
94 | } | 66 | } |
95 | 67 | None => { | |
96 | match cursor.token_tree() { | 68 | if let Some(subtree) = cursor.end() { |
97 | Some(tt::TokenTree::Leaf(leaf)) => { | 69 | cached.push(convert_delim(subtree.delimiter_kind(), true)); |
98 | cached.push(Some(convert_leaf(&leaf))); | 70 | cursor.bump() |
99 | self.cached_cursor.set(cursor.bump()); | 71 | } else { |
100 | } | 72 | continue; |
101 | Some(tt::TokenTree::Subtree(subtree)) => { | ||
102 | self.cached_cursor.set(cursor.subtree().unwrap()); | ||
103 | cached.push(Some(convert_delim(subtree.delimiter_kind(), false))); | ||
104 | } | ||
105 | None => { | ||
106 | if let Some(subtree) = cursor.end() { | ||
107 | cached.push(Some(convert_delim(subtree.delimiter_kind(), true))); | ||
108 | self.cached_cursor.set(cursor.bump()); | ||
109 | } | ||
110 | } | 73 | } |
111 | } | 74 | } |
112 | } | 75 | }; |
113 | } | 76 | } |
114 | 77 | ||
115 | Ref::map(self.cached.borrow(), |c| &c[pos]) | 78 | let mut res = SubtreeTokenSource { |
79 | curr: (Token { kind: EOF, is_jointed_to_next: false }, 0), | ||
80 | cached, | ||
81 | }; | ||
82 | res.curr = (res.token(0), 0); | ||
83 | res | ||
84 | } | ||
85 | |||
86 | fn token(&self, pos: usize) -> Token { | ||
87 | match self.cached.get(pos) { | ||
88 | Some(it) => it.tt, | ||
89 | None => Token { kind: EOF, is_jointed_to_next: false }, | ||
90 | } | ||
116 | } | 91 | } |
117 | } | 92 | } |
118 | 93 | ||
119 | impl<'a> TokenSource for SubtreeTokenSource<'a> { | 94 | impl<'a> TokenSource for SubtreeTokenSource { |
120 | fn current(&self) -> Token { | 95 | fn current(&self) -> Token { |
121 | self.curr.0 | 96 | self.curr.0 |
122 | } | 97 | } |
123 | 98 | ||
124 | /// Lookahead n token | 99 | /// Lookahead n token |
125 | fn lookahead_nth(&self, n: usize) -> Token { | 100 | fn lookahead_nth(&self, n: usize) -> Token { |
126 | self.mk_token(self.curr.1 + n) | 101 | self.token(self.curr.1 + n) |
127 | } | 102 | } |
128 | 103 | ||
129 | /// bump cursor to next token | 104 | /// bump cursor to next token |
@@ -131,13 +106,12 @@ impl<'a> TokenSource for SubtreeTokenSource<'a> { | |||
131 | if self.current().kind == EOF { | 106 | if self.current().kind == EOF { |
132 | return; | 107 | return; |
133 | } | 108 | } |
134 | 109 | self.curr = (self.token(self.curr.1 + 1), self.curr.1 + 1); | |
135 | self.curr = (self.mk_token(self.curr.1 + 1), self.curr.1 + 1); | ||
136 | } | 110 | } |
137 | 111 | ||
138 | /// Is the current token a specified keyword? | 112 | /// Is the current token a specified keyword? |
139 | fn is_keyword(&self, kw: &str) -> bool { | 113 | fn is_keyword(&self, kw: &str) -> bool { |
140 | match *self.get(self.curr.1) { | 114 | match self.cached.get(self.curr.1) { |
141 | Some(ref t) => t.text == *kw, | 115 | Some(ref t) => t.text == *kw, |
142 | _ => false, | 116 | _ => false, |
143 | } | 117 | } |
@@ -155,7 +129,7 @@ fn convert_delim(d: Option<tt::DelimiterKind>, closing: bool) -> TtToken { | |||
155 | let idx = closing as usize; | 129 | let idx = closing as usize; |
156 | let kind = kinds[idx]; | 130 | let kind = kinds[idx]; |
157 | let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; | 131 | let text = if !texts.is_empty() { &texts[idx..texts.len() - (1 - idx)] } else { "" }; |
158 | TtToken { kind, is_joint_to_next: false, text: SmolStr::new(text) } | 132 | TtToken { tt: Token { kind, is_jointed_to_next: false }, text: SmolStr::new(text) } |
159 | } | 133 | } |
160 | 134 | ||
161 | fn convert_literal(l: &tt::Literal) -> TtToken { | 135 | fn convert_literal(l: &tt::Literal) -> TtToken { |
@@ -169,7 +143,7 @@ fn convert_literal(l: &tt::Literal) -> TtToken { | |||
169 | }) | 143 | }) |
170 | .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); | 144 | .unwrap_or_else(|| panic!("Fail to convert given literal {:#?}", &l)); |
171 | 145 | ||
172 | TtToken { kind, is_joint_to_next: false, text: l.text.clone() } | 146 | TtToken { tt: Token { kind, is_jointed_to_next: false }, text: l.text.clone() } |
173 | } | 147 | } |
174 | 148 | ||
175 | fn convert_ident(ident: &tt::Ident) -> TtToken { | 149 | fn convert_ident(ident: &tt::Ident) -> TtToken { |
@@ -180,7 +154,7 @@ fn convert_ident(ident: &tt::Ident) -> TtToken { | |||
180 | _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT), | 154 | _ => SyntaxKind::from_keyword(ident.text.as_str()).unwrap_or(IDENT), |
181 | }; | 155 | }; |
182 | 156 | ||
183 | TtToken { kind, is_joint_to_next: false, text: ident.text.clone() } | 157 | TtToken { tt: Token { kind, is_jointed_to_next: false }, text: ident.text.clone() } |
184 | } | 158 | } |
185 | 159 | ||
186 | fn convert_punct(p: tt::Punct) -> TtToken { | 160 | fn convert_punct(p: tt::Punct) -> TtToken { |
@@ -194,7 +168,7 @@ fn convert_punct(p: tt::Punct) -> TtToken { | |||
194 | let s: &str = p.char.encode_utf8(&mut buf); | 168 | let s: &str = p.char.encode_utf8(&mut buf); |
195 | SmolStr::new(s) | 169 | SmolStr::new(s) |
196 | }; | 170 | }; |
197 | TtToken { kind, is_joint_to_next: p.spacing == tt::Spacing::Joint, text } | 171 | TtToken { tt: Token { kind, is_jointed_to_next: p.spacing == tt::Spacing::Joint }, text } |
198 | } | 172 | } |
199 | 173 | ||
200 | fn convert_leaf(leaf: &tt::Leaf) -> TtToken { | 174 | fn convert_leaf(leaf: &tt::Leaf) -> TtToken { |
@@ -208,6 +182,7 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken { | |||
208 | #[cfg(test)] | 182 | #[cfg(test)] |
209 | mod tests { | 183 | mod tests { |
210 | use super::{convert_literal, TtToken}; | 184 | use super::{convert_literal, TtToken}; |
185 | use parser::Token; | ||
211 | use syntax::{SmolStr, SyntaxKind}; | 186 | use syntax::{SmolStr, SyntaxKind}; |
212 | 187 | ||
213 | #[test] | 188 | #[test] |
@@ -218,8 +193,7 @@ mod tests { | |||
218 | text: SmolStr::new("-42.0") | 193 | text: SmolStr::new("-42.0") |
219 | }), | 194 | }), |
220 | TtToken { | 195 | TtToken { |
221 | kind: SyntaxKind::FLOAT_NUMBER, | 196 | tt: Token { kind: SyntaxKind::FLOAT_NUMBER, is_jointed_to_next: false }, |
222 | is_joint_to_next: false, | ||
223 | text: SmolStr::new("-42.0") | 197 | text: SmolStr::new("-42.0") |
224 | } | 198 | } |
225 | ); | 199 | ); |
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 265c0d63d..671036e1c 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -70,15 +70,12 @@ pub fn token_tree_to_syntax_node( | |||
70 | tt: &tt::Subtree, | 70 | tt: &tt::Subtree, |
71 | fragment_kind: FragmentKind, | 71 | fragment_kind: FragmentKind, |
72 | ) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> { | 72 | ) -> Result<(Parse<SyntaxNode>, TokenMap), ExpandError> { |
73 | let tmp; | 73 | let buffer = match tt { |
74 | let tokens = match tt { | 74 | tt::Subtree { delimiter: None, token_trees } => { |
75 | tt::Subtree { delimiter: None, token_trees } => token_trees.as_slice(), | 75 | TokenBuffer::from_tokens(token_trees.as_slice()) |
76 | _ => { | ||
77 | tmp = [tt.clone().into()]; | ||
78 | &tmp[..] | ||
79 | } | 76 | } |
77 | _ => TokenBuffer::from_subtree(tt), | ||
80 | }; | 78 | }; |
81 | let buffer = TokenBuffer::new(&tokens); | ||
82 | let mut token_source = SubtreeTokenSource::new(&buffer); | 79 | let mut token_source = SubtreeTokenSource::new(&buffer); |
83 | let mut tree_sink = TtTreeSink::new(buffer.begin()); | 80 | let mut tree_sink = TtTreeSink::new(buffer.begin()); |
84 | parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind); | 81 | parser::parse_fragment(&mut token_source, &mut tree_sink, fragment_kind); |
@@ -414,7 +411,7 @@ trait TokenConvertor { | |||
414 | fn id_alloc(&mut self) -> &mut TokenIdAlloc; | 411 | fn id_alloc(&mut self) -> &mut TokenIdAlloc; |
415 | } | 412 | } |
416 | 413 | ||
417 | impl<'a> SrcToken for (RawToken, &'a str) { | 414 | impl<'a> SrcToken for (&'a RawToken, &'a str) { |
418 | fn kind(&self) -> SyntaxKind { | 415 | fn kind(&self) -> SyntaxKind { |
419 | self.0.kind | 416 | self.0.kind |
420 | } | 417 | } |
@@ -431,7 +428,7 @@ impl<'a> SrcToken for (RawToken, &'a str) { | |||
431 | impl RawConvertor<'_> {} | 428 | impl RawConvertor<'_> {} |
432 | 429 | ||
433 | impl<'a> TokenConvertor for RawConvertor<'a> { | 430 | impl<'a> TokenConvertor for RawConvertor<'a> { |
434 | type Token = (RawToken, &'a str); | 431 | type Token = (&'a RawToken, &'a str); |
435 | 432 | ||
436 | fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> { | 433 | fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> { |
437 | convert_doc_comment(&doc_comment(token.1)) | 434 | convert_doc_comment(&doc_comment(token.1)) |
@@ -442,11 +439,11 @@ impl<'a> TokenConvertor for RawConvertor<'a> { | |||
442 | let range = TextRange::at(self.offset, token.len); | 439 | let range = TextRange::at(self.offset, token.len); |
443 | self.offset += token.len; | 440 | self.offset += token.len; |
444 | 441 | ||
445 | Some(((*token, &self.text[range]), range)) | 442 | Some(((token, &self.text[range]), range)) |
446 | } | 443 | } |
447 | 444 | ||
448 | fn peek(&self) -> Option<Self::Token> { | 445 | fn peek(&self) -> Option<Self::Token> { |
449 | let token = self.inner.as_slice().get(0).cloned(); | 446 | let token = self.inner.as_slice().get(0); |
450 | 447 | ||
451 | token.map(|it| { | 448 | token.map(|it| { |
452 | let range = TextRange::at(self.offset, it.len); | 449 | let range = TextRange::at(self.offset, it.len); |
@@ -601,17 +598,16 @@ impl<'a> TtTreeSink<'a> { | |||
601 | } | 598 | } |
602 | } | 599 | } |
603 | 600 | ||
604 | fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> SmolStr { | 601 | fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> &'static str { |
605 | let texts = match d { | 602 | let texts = match d { |
606 | Some(tt::DelimiterKind::Parenthesis) => "()", | 603 | Some(tt::DelimiterKind::Parenthesis) => "()", |
607 | Some(tt::DelimiterKind::Brace) => "{}", | 604 | Some(tt::DelimiterKind::Brace) => "{}", |
608 | Some(tt::DelimiterKind::Bracket) => "[]", | 605 | Some(tt::DelimiterKind::Bracket) => "[]", |
609 | None => return "".into(), | 606 | None => return "", |
610 | }; | 607 | }; |
611 | 608 | ||
612 | let idx = closing as usize; | 609 | let idx = closing as usize; |
613 | let text = &texts[idx..texts.len() - (1 - idx)]; | 610 | &texts[idx..texts.len() - (1 - idx)] |
614 | text.into() | ||
615 | } | 611 | } |
616 | 612 | ||
617 | impl<'a> TreeSink for TtTreeSink<'a> { | 613 | impl<'a> TreeSink for TtTreeSink<'a> { |
@@ -626,29 +622,32 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
626 | 622 | ||
627 | let mut last = self.cursor; | 623 | let mut last = self.cursor; |
628 | for _ in 0..n_tokens { | 624 | for _ in 0..n_tokens { |
625 | let tmp_str: SmolStr; | ||
629 | if self.cursor.eof() { | 626 | if self.cursor.eof() { |
630 | break; | 627 | break; |
631 | } | 628 | } |
632 | last = self.cursor; | 629 | last = self.cursor; |
633 | let text: SmolStr = match self.cursor.token_tree() { | 630 | let text: &str = match self.cursor.token_tree() { |
634 | Some(tt::TokenTree::Leaf(leaf)) => { | 631 | Some(tt::buffer::TokenTreeRef::Leaf(leaf, _)) => { |
635 | // Mark the range if needed | 632 | // Mark the range if needed |
636 | let (text, id) = match leaf { | 633 | let (text, id) = match leaf { |
637 | tt::Leaf::Ident(ident) => (ident.text.clone(), ident.id), | 634 | tt::Leaf::Ident(ident) => (&ident.text, ident.id), |
638 | tt::Leaf::Punct(punct) => { | 635 | tt::Leaf::Punct(punct) => { |
639 | assert!(punct.char.is_ascii()); | 636 | assert!(punct.char.is_ascii()); |
640 | let char = &(punct.char as u8); | 637 | let char = &(punct.char as u8); |
641 | let text = std::str::from_utf8(std::slice::from_ref(char)).unwrap(); | 638 | tmp_str = SmolStr::new_inline( |
642 | (SmolStr::new_inline(text), punct.id) | 639 | std::str::from_utf8(std::slice::from_ref(char)).unwrap(), |
640 | ); | ||
641 | (&tmp_str, punct.id) | ||
643 | } | 642 | } |
644 | tt::Leaf::Literal(lit) => (lit.text.clone(), lit.id), | 643 | tt::Leaf::Literal(lit) => (&lit.text, lit.id), |
645 | }; | 644 | }; |
646 | let range = TextRange::at(self.text_pos, TextSize::of(text.as_str())); | 645 | let range = TextRange::at(self.text_pos, TextSize::of(text.as_str())); |
647 | self.token_map.insert(id, range); | 646 | self.token_map.insert(id, range); |
648 | self.cursor = self.cursor.bump(); | 647 | self.cursor = self.cursor.bump(); |
649 | text | 648 | text |
650 | } | 649 | } |
651 | Some(tt::TokenTree::Subtree(subtree)) => { | 650 | Some(tt::buffer::TokenTreeRef::Subtree(subtree, _)) => { |
652 | self.cursor = self.cursor.subtree().unwrap(); | 651 | self.cursor = self.cursor.subtree().unwrap(); |
653 | if let Some(id) = subtree.delimiter.map(|it| it.id) { | 652 | if let Some(id) = subtree.delimiter.map(|it| it.id) { |
654 | self.open_delims.insert(id, self.text_pos); | 653 | self.open_delims.insert(id, self.text_pos); |
@@ -672,7 +671,7 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
672 | } | 671 | } |
673 | }; | 672 | }; |
674 | self.buf += &text; | 673 | self.buf += &text; |
675 | self.text_pos += TextSize::of(text.as_str()); | 674 | self.text_pos += TextSize::of(text); |
676 | } | 675 | } |
677 | 676 | ||
678 | let text = SmolStr::new(self.buf.as_str()); | 677 | let text = SmolStr::new(self.buf.as_str()); |
@@ -682,8 +681,8 @@ impl<'a> TreeSink for TtTreeSink<'a> { | |||
682 | // Add whitespace between adjoint puncts | 681 | // Add whitespace between adjoint puncts |
683 | let next = last.bump(); | 682 | let next = last.bump(); |
684 | if let ( | 683 | if let ( |
685 | Some(tt::TokenTree::Leaf(tt::Leaf::Punct(curr))), | 684 | Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)), |
686 | Some(tt::TokenTree::Leaf(tt::Leaf::Punct(_))), | 685 | Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(_), _)), |
687 | ) = (last.token_tree(), next.token_tree()) | 686 | ) = (last.token_tree(), next.token_tree()) |
688 | { | 687 | { |
689 | // Note: We always assume the semi-colon would be the last token in | 688 | // Note: We always assume the semi-colon would be the last token in |
@@ -742,7 +741,7 @@ mod tests { | |||
742 | ) | 741 | ) |
743 | .expand_tt("literals!(foo);"); | 742 | .expand_tt("literals!(foo);"); |
744 | let tts = &[expansion.into()]; | 743 | let tts = &[expansion.into()]; |
745 | let buffer = tt::buffer::TokenBuffer::new(tts); | 744 | let buffer = tt::buffer::TokenBuffer::from_tokens(tts); |
746 | let mut tt_src = SubtreeTokenSource::new(&buffer); | 745 | let mut tt_src = SubtreeTokenSource::new(&buffer); |
747 | let mut tokens = vec![]; | 746 | let mut tokens = vec![]; |
748 | while tt_src.current().kind != EOF { | 747 | while tt_src.current().kind != EOF { |
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 63cc90027..bb9ffea8b 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -66,6 +66,10 @@ pub(crate) mod fragments { | |||
66 | expressions::stmt(p, expressions::StmtWithSemi::No) | 66 | expressions::stmt(p, expressions::StmtWithSemi::No) |
67 | } | 67 | } |
68 | 68 | ||
69 | pub(crate) fn stmt_optional_semi(p: &mut Parser) { | ||
70 | expressions::stmt(p, expressions::StmtWithSemi::Optional) | ||
71 | } | ||
72 | |||
69 | pub(crate) fn opt_visibility(p: &mut Parser) { | 73 | pub(crate) fn opt_visibility(p: &mut Parser) { |
70 | let _ = super::opt_visibility(p); | 74 | let _ = super::opt_visibility(p); |
71 | } | 75 | } |
diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 811e740f9..9dfe63028 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs | |||
@@ -88,6 +88,7 @@ pub enum FragmentKind { | |||
88 | Path, | 88 | Path, |
89 | Expr, | 89 | Expr, |
90 | Statement, | 90 | Statement, |
91 | StatementOptionalSemi, | ||
91 | Type, | 92 | Type, |
92 | Pattern, | 93 | Pattern, |
93 | Item, | 94 | Item, |
@@ -118,6 +119,7 @@ pub fn parse_fragment( | |||
118 | FragmentKind::Visibility => grammar::fragments::opt_visibility, | 119 | FragmentKind::Visibility => grammar::fragments::opt_visibility, |
119 | FragmentKind::MetaItem => grammar::fragments::meta_item, | 120 | FragmentKind::MetaItem => grammar::fragments::meta_item, |
120 | FragmentKind::Statement => grammar::fragments::stmt, | 121 | FragmentKind::Statement => grammar::fragments::stmt, |
122 | FragmentKind::StatementOptionalSemi => grammar::fragments::stmt_optional_semi, | ||
121 | FragmentKind::Items => grammar::fragments::macro_items, | 123 | FragmentKind::Items => grammar::fragments::macro_items, |
122 | FragmentKind::Statements => grammar::fragments::macro_stmts, | 124 | FragmentKind::Statements => grammar::fragments::macro_stmts, |
123 | FragmentKind::Attr => grammar::fragments::attr, | 125 | FragmentKind::Attr => grammar::fragments::attr, |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 685a9fdf0..a80652e83 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -175,7 +175,7 @@ config_data! { | |||
175 | 175 | ||
176 | #[derive(Debug, Clone)] | 176 | #[derive(Debug, Clone)] |
177 | pub struct Config { | 177 | pub struct Config { |
178 | pub client_caps: ClientCapsConfig, | 178 | pub caps: lsp_types::ClientCapabilities, |
179 | 179 | ||
180 | pub publish_diagnostics: bool, | 180 | pub publish_diagnostics: bool, |
181 | pub diagnostics: DiagnosticsConfig, | 181 | pub diagnostics: DiagnosticsConfig, |
@@ -286,26 +286,12 @@ pub struct RunnablesConfig { | |||
286 | pub cargo_extra_args: Vec<String>, | 286 | pub cargo_extra_args: Vec<String>, |
287 | } | 287 | } |
288 | 288 | ||
289 | #[derive(Debug, Clone, Default)] | ||
290 | pub struct ClientCapsConfig { | ||
291 | pub location_link: bool, | ||
292 | pub line_folding_only: bool, | ||
293 | pub hierarchical_symbols: bool, | ||
294 | pub code_action_literals: bool, | ||
295 | pub work_done_progress: bool, | ||
296 | pub code_action_group: bool, | ||
297 | pub code_action_resolve: bool, | ||
298 | pub hover_actions: bool, | ||
299 | pub status_notification: bool, | ||
300 | pub signature_help_label_offsets: bool, | ||
301 | } | ||
302 | |||
303 | impl Config { | 289 | impl Config { |
304 | pub fn new(root_path: AbsPathBuf) -> Self { | 290 | pub fn new(root_path: AbsPathBuf) -> Self { |
305 | // Defaults here don't matter, we'll immediately re-write them with | 291 | // Defaults here don't matter, we'll immediately re-write them with |
306 | // ConfigData. | 292 | // ConfigData. |
307 | let mut res = Config { | 293 | let mut res = Config { |
308 | client_caps: ClientCapsConfig::default(), | 294 | caps: lsp_types::ClientCapabilities::default(), |
309 | 295 | ||
310 | publish_diagnostics: false, | 296 | publish_diagnostics: false, |
311 | diagnostics: DiagnosticsConfig::default(), | 297 | diagnostics: DiagnosticsConfig::default(), |
@@ -505,38 +491,11 @@ impl Config { | |||
505 | } | 491 | } |
506 | 492 | ||
507 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { | 493 | pub fn update_caps(&mut self, caps: &ClientCapabilities) { |
494 | self.caps = caps.clone(); | ||
508 | if let Some(doc_caps) = caps.text_document.as_ref() { | 495 | if let Some(doc_caps) = caps.text_document.as_ref() { |
509 | if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { | 496 | if let Some(value) = doc_caps.hover.as_ref().and_then(|it| it.content_format.as_ref()) { |
510 | self.hover.markdown = value.contains(&MarkupKind::Markdown) | 497 | self.hover.markdown = value.contains(&MarkupKind::Markdown) |
511 | } | 498 | } |
512 | if let Some(value) = doc_caps.definition.as_ref().and_then(|it| it.link_support) { | ||
513 | self.client_caps.location_link = value; | ||
514 | } | ||
515 | if let Some(value) = doc_caps.folding_range.as_ref().and_then(|it| it.line_folding_only) | ||
516 | { | ||
517 | self.client_caps.line_folding_only = value | ||
518 | } | ||
519 | if let Some(value) = doc_caps | ||
520 | .document_symbol | ||
521 | .as_ref() | ||
522 | .and_then(|it| it.hierarchical_document_symbol_support) | ||
523 | { | ||
524 | self.client_caps.hierarchical_symbols = value | ||
525 | } | ||
526 | if let Some(value) = | ||
527 | doc_caps.code_action.as_ref().map(|it| it.code_action_literal_support.is_some()) | ||
528 | { | ||
529 | self.client_caps.code_action_literals = value; | ||
530 | } | ||
531 | if let Some(value) = doc_caps | ||
532 | .signature_help | ||
533 | .as_ref() | ||
534 | .and_then(|it| it.signature_information.as_ref()) | ||
535 | .and_then(|it| it.parameter_information.as_ref()) | ||
536 | .and_then(|it| it.label_offset_support) | ||
537 | { | ||
538 | self.client_caps.signature_help_label_offsets = value; | ||
539 | } | ||
540 | 499 | ||
541 | self.completion.allow_snippets(false); | 500 | self.completion.allow_snippets(false); |
542 | self.completion.active_resolve_capabilities = | 501 | self.completion.active_resolve_capabilities = |
@@ -548,20 +507,6 @@ impl Config { | |||
548 | } | 507 | } |
549 | } | 508 | } |
550 | } | 509 | } |
551 | |||
552 | if let Some(code_action) = &doc_caps.code_action { | ||
553 | if let Some(resolve_support) = &code_action.resolve_support { | ||
554 | if resolve_support.properties.iter().any(|it| it == "edit") { | ||
555 | self.client_caps.code_action_resolve = true; | ||
556 | } | ||
557 | } | ||
558 | } | ||
559 | } | ||
560 | |||
561 | if let Some(window_caps) = caps.window.as_ref() { | ||
562 | if let Some(value) = window_caps.work_done_progress { | ||
563 | self.client_caps.work_done_progress = value; | ||
564 | } | ||
565 | } | 510 | } |
566 | 511 | ||
567 | self.assist.allow_snippets(false); | 512 | self.assist.allow_snippets(false); |
@@ -571,10 +516,6 @@ impl Config { | |||
571 | 516 | ||
572 | let snippet_text_edit = get_bool("snippetTextEdit"); | 517 | let snippet_text_edit = get_bool("snippetTextEdit"); |
573 | self.assist.allow_snippets(snippet_text_edit); | 518 | self.assist.allow_snippets(snippet_text_edit); |
574 | |||
575 | self.client_caps.code_action_group = get_bool("codeActionGroup"); | ||
576 | self.client_caps.hover_actions = get_bool("hoverActions"); | ||
577 | self.client_caps.status_notification = get_bool("statusNotification"); | ||
578 | } | 519 | } |
579 | 520 | ||
580 | if let Some(workspace_caps) = caps.workspace.as_ref() { | 521 | if let Some(workspace_caps) = caps.workspace.as_ref() { |
@@ -597,6 +538,95 @@ impl Config { | |||
597 | } | 538 | } |
598 | } | 539 | } |
599 | 540 | ||
541 | macro_rules! try_ { | ||
542 | ($expr:expr) => { | ||
543 | || -> _ { Some($expr) }() | ||
544 | }; | ||
545 | } | ||
546 | macro_rules! try_or { | ||
547 | ($expr:expr, $or:expr) => { | ||
548 | try_!($expr).unwrap_or($or) | ||
549 | }; | ||
550 | } | ||
551 | |||
552 | impl Config { | ||
553 | pub fn location_link(&self) -> bool { | ||
554 | try_or!(self.caps.text_document.as_ref()?.definition?.link_support?, false) | ||
555 | } | ||
556 | pub fn line_folding_only(&self) -> bool { | ||
557 | try_or!(self.caps.text_document.as_ref()?.folding_range.as_ref()?.line_folding_only?, false) | ||
558 | } | ||
559 | pub fn hierarchical_symbols(&self) -> bool { | ||
560 | try_or!( | ||
561 | self.caps | ||
562 | .text_document | ||
563 | .as_ref()? | ||
564 | .document_symbol | ||
565 | .as_ref()? | ||
566 | .hierarchical_document_symbol_support?, | ||
567 | false | ||
568 | ) | ||
569 | } | ||
570 | pub fn code_action_literals(&self) -> bool { | ||
571 | try_!(self | ||
572 | .caps | ||
573 | .text_document | ||
574 | .as_ref()? | ||
575 | .code_action | ||
576 | .as_ref()? | ||
577 | .code_action_literal_support | ||
578 | .as_ref()?) | ||
579 | .is_some() | ||
580 | } | ||
581 | pub fn work_done_progress(&self) -> bool { | ||
582 | try_or!(self.caps.window.as_ref()?.work_done_progress?, false) | ||
583 | } | ||
584 | pub fn code_action_resolve(&self) -> bool { | ||
585 | try_or!( | ||
586 | self.caps | ||
587 | .text_document | ||
588 | .as_ref()? | ||
589 | .code_action | ||
590 | .as_ref()? | ||
591 | .resolve_support | ||
592 | .as_ref()? | ||
593 | .properties | ||
594 | .as_slice(), | ||
595 | &[] | ||
596 | ) | ||
597 | .iter() | ||
598 | .any(|it| it == "edit") | ||
599 | } | ||
600 | pub fn signature_help_label_offsets(&self) -> bool { | ||
601 | try_or!( | ||
602 | self.caps | ||
603 | .text_document | ||
604 | .as_ref()? | ||
605 | .signature_help | ||
606 | .as_ref()? | ||
607 | .signature_information | ||
608 | .as_ref()? | ||
609 | .parameter_information | ||
610 | .as_ref()? | ||
611 | .label_offset_support?, | ||
612 | false | ||
613 | ) | ||
614 | } | ||
615 | |||
616 | fn experimental(&self, index: &'static str) -> bool { | ||
617 | try_or!(self.caps.experimental.as_ref()?.get(index)?.as_bool()?, false) | ||
618 | } | ||
619 | pub fn code_action_group(&self) -> bool { | ||
620 | self.experimental("codeActionGroup") | ||
621 | } | ||
622 | pub fn hover_actions(&self) -> bool { | ||
623 | self.experimental("hoverActions") | ||
624 | } | ||
625 | pub fn status_notification(&self) -> bool { | ||
626 | self.experimental("statusNotification") | ||
627 | } | ||
628 | } | ||
629 | |||
600 | #[derive(Deserialize)] | 630 | #[derive(Deserialize)] |
601 | #[serde(untagged)] | 631 | #[serde(untagged)] |
602 | enum ManifestOrProjectJson { | 632 | enum ManifestOrProjectJson { |
@@ -777,9 +807,8 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String { | |||
777 | fields | 807 | fields |
778 | .iter() | 808 | .iter() |
779 | .map(|(field, _ty, doc, default)| { | 809 | .map(|(field, _ty, doc, default)| { |
780 | let name = field.replace("_", "."); | 810 | let name = format!("rust-analyzer.{}", field.replace("_", ".")); |
781 | let name = format!("rust-analyzer.{} (default: `{}`)", name, default); | 811 | format!("[[{}]]{} (default: `{}`)::\n{}\n", name, name, default, doc.join(" ")) |
782 | format!("{}::\n{}\n", name, doc.join(" ")) | ||
783 | }) | 812 | }) |
784 | .collect::<String>() | 813 | .collect::<String>() |
785 | } | 814 | } |
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 3e49904c3..071b34cda 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -321,7 +321,7 @@ pub(crate) fn handle_document_symbol( | |||
321 | acc | 321 | acc |
322 | }; | 322 | }; |
323 | 323 | ||
324 | let res = if snap.config.client_caps.hierarchical_symbols { | 324 | let res = if snap.config.hierarchical_symbols() { |
325 | document_symbols.into() | 325 | document_symbols.into() |
326 | } else { | 326 | } else { |
327 | let url = to_proto::url(&snap, file_id); | 327 | let url = to_proto::url(&snap, file_id); |
@@ -728,7 +728,7 @@ pub(crate) fn handle_folding_range( | |||
728 | let folds = snap.analysis.folding_ranges(file_id)?; | 728 | let folds = snap.analysis.folding_ranges(file_id)?; |
729 | let text = snap.analysis.file_text(file_id)?; | 729 | let text = snap.analysis.file_text(file_id)?; |
730 | let line_index = snap.analysis.file_line_index(file_id)?; | 730 | let line_index = snap.analysis.file_line_index(file_id)?; |
731 | let line_folding_only = snap.config.client_caps.line_folding_only; | 731 | let line_folding_only = snap.config.line_folding_only(); |
732 | let res = folds | 732 | let res = folds |
733 | .into_iter() | 733 | .into_iter() |
734 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) | 734 | .map(|it| to_proto::folding_range(&*text, &line_index, line_folding_only, it)) |
@@ -747,11 +747,8 @@ pub(crate) fn handle_signature_help( | |||
747 | None => return Ok(None), | 747 | None => return Ok(None), |
748 | }; | 748 | }; |
749 | let concise = !snap.config.call_info_full; | 749 | let concise = !snap.config.call_info_full; |
750 | let res = to_proto::signature_help( | 750 | let res = |
751 | call_info, | 751 | to_proto::signature_help(call_info, concise, snap.config.signature_help_label_offsets()); |
752 | concise, | ||
753 | snap.config.client_caps.signature_help_label_offsets, | ||
754 | ); | ||
755 | Ok(Some(res)) | 752 | Ok(Some(res)) |
756 | } | 753 | } |
757 | 754 | ||
@@ -941,7 +938,7 @@ pub(crate) fn handle_code_action( | |||
941 | // We intentionally don't support command-based actions, as those either | 938 | // We intentionally don't support command-based actions, as those either |
942 | // requires custom client-code anyway, or requires server-initiated edits. | 939 | // requires custom client-code anyway, or requires server-initiated edits. |
943 | // Server initiated edits break causality, so we avoid those as well. | 940 | // Server initiated edits break causality, so we avoid those as well. |
944 | if !snap.config.client_caps.code_action_literals { | 941 | if !snap.config.code_action_literals() { |
945 | return Ok(None); | 942 | return Ok(None); |
946 | } | 943 | } |
947 | 944 | ||
@@ -971,7 +968,7 @@ pub(crate) fn handle_code_action( | |||
971 | add_quick_fixes(&snap, frange, &line_index, &mut res)?; | 968 | add_quick_fixes(&snap, frange, &line_index, &mut res)?; |
972 | } | 969 | } |
973 | 970 | ||
974 | if snap.config.client_caps.code_action_resolve { | 971 | if snap.config.code_action_resolve() { |
975 | for (index, assist) in | 972 | for (index, assist) in |
976 | snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() | 973 | snap.analysis.assists(&assists_config, false, frange)?.into_iter().enumerate() |
977 | { | 974 | { |
@@ -1554,7 +1551,7 @@ fn debug_single_command(runnable: &lsp_ext::Runnable) -> Command { | |||
1554 | } | 1551 | } |
1555 | 1552 | ||
1556 | fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> { | 1553 | fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> { |
1557 | let value = if snap.config.client_caps.location_link { | 1554 | let value = if snap.config.location_link() { |
1558 | let link = to_proto::location_link(snap, None, nav.clone()).ok()?; | 1555 | let link = to_proto::location_link(snap, None, nav.clone()).ok()?; |
1559 | to_value(link).ok()? | 1556 | to_value(link).ok()? |
1560 | } else { | 1557 | } else { |
@@ -1653,14 +1650,14 @@ fn prepare_hover_actions( | |||
1653 | file_id: FileId, | 1650 | file_id: FileId, |
1654 | actions: &[HoverAction], | 1651 | actions: &[HoverAction], |
1655 | ) -> Vec<lsp_ext::CommandLinkGroup> { | 1652 | ) -> Vec<lsp_ext::CommandLinkGroup> { |
1656 | if snap.config.hover.none() || !snap.config.client_caps.hover_actions { | 1653 | if snap.config.hover.none() || !snap.config.hover_actions() { |
1657 | return Vec::new(); | 1654 | return Vec::new(); |
1658 | } | 1655 | } |
1659 | 1656 | ||
1660 | actions | 1657 | actions |
1661 | .iter() | 1658 | .iter() |
1662 | .filter_map(|it| match it { | 1659 | .filter_map(|it| match it { |
1663 | HoverAction::Implementaion(position) => show_impl_command_link(snap, position), | 1660 | HoverAction::Implementation(position) => show_impl_command_link(snap, position), |
1664 | HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()), | 1661 | HoverAction::Runnable(r) => runnable_action_links(snap, file_id, r.clone()), |
1665 | HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), | 1662 | HoverAction::GoToType(targets) => goto_type_action_links(snap, targets), |
1666 | }) | 1663 | }) |
diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index 60c12e4e2..40de56dad 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs | |||
@@ -46,7 +46,7 @@ impl GlobalState { | |||
46 | message: Option<String>, | 46 | message: Option<String>, |
47 | fraction: Option<f64>, | 47 | fraction: Option<f64>, |
48 | ) { | 48 | ) { |
49 | if !self.config.client_caps.work_done_progress { | 49 | if !self.config.work_done_progress() { |
50 | return; | 50 | return; |
51 | } | 51 | } |
52 | let percentage = fraction.map(|f| { | 52 | let percentage = fraction.map(|f| { |
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 79e39e3a5..ce5cedeb3 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs | |||
@@ -79,7 +79,7 @@ impl GlobalState { | |||
79 | } | 79 | } |
80 | pub(crate) fn transition(&mut self, new_status: Status) { | 80 | pub(crate) fn transition(&mut self, new_status: Status) { |
81 | self.status = new_status; | 81 | self.status = new_status; |
82 | if self.config.client_caps.status_notification { | 82 | if self.config.status_notification() { |
83 | let lsp_status = match new_status { | 83 | let lsp_status = match new_status { |
84 | Status::Loading => lsp_ext::Status::Loading, | 84 | Status::Loading => lsp_ext::Status::Loading, |
85 | Status::Ready => lsp_ext::Status::Ready, | 85 | Status::Ready => lsp_ext::Status::Ready, |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 999b18351..e0413ec06 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -605,7 +605,7 @@ pub(crate) fn goto_definition_response( | |||
605 | src: Option<FileRange>, | 605 | src: Option<FileRange>, |
606 | targets: Vec<NavigationTarget>, | 606 | targets: Vec<NavigationTarget>, |
607 | ) -> Result<lsp_types::GotoDefinitionResponse> { | 607 | ) -> Result<lsp_types::GotoDefinitionResponse> { |
608 | if snap.config.client_caps.location_link { | 608 | if snap.config.location_link() { |
609 | let links = targets | 609 | let links = targets |
610 | .into_iter() | 610 | .into_iter() |
611 | .map(|nav| location_link(snap, src, nav)) | 611 | .map(|nav| location_link(snap, src, nav)) |
@@ -785,7 +785,7 @@ pub(crate) fn unresolved_code_action( | |||
785 | assert!(assist.source_change.is_none()); | 785 | assert!(assist.source_change.is_none()); |
786 | let res = lsp_ext::CodeAction { | 786 | let res = lsp_ext::CodeAction { |
787 | title: assist.label.to_string(), | 787 | title: assist.label.to_string(), |
788 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), | 788 | group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), |
789 | kind: Some(code_action_kind(assist.id.1)), | 789 | kind: Some(code_action_kind(assist.id.1)), |
790 | edit: None, | 790 | edit: None, |
791 | is_preferred: None, | 791 | is_preferred: None, |
@@ -805,7 +805,7 @@ pub(crate) fn resolved_code_action( | |||
805 | let res = lsp_ext::CodeAction { | 805 | let res = lsp_ext::CodeAction { |
806 | edit: Some(snippet_workspace_edit(snap, change)?), | 806 | edit: Some(snippet_workspace_edit(snap, change)?), |
807 | title: assist.label.to_string(), | 807 | title: assist.label.to_string(), |
808 | group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), | 808 | group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), |
809 | kind: Some(code_action_kind(assist.id.1)), | 809 | kind: Some(code_action_kind(assist.id.1)), |
810 | is_preferred: None, | 810 | is_preferred: None, |
811 | data: None, | 811 | data: None, |
diff --git a/crates/rust-analyzer/tests/rust-analyzer/support.rs b/crates/rust-analyzer/tests/rust-analyzer/support.rs index 456125789..aac7dbcce 100644 --- a/crates/rust-analyzer/tests/rust-analyzer/support.rs +++ b/crates/rust-analyzer/tests/rust-analyzer/support.rs | |||
@@ -14,7 +14,7 @@ use lsp_types::{ | |||
14 | use lsp_types::{ProgressParams, ProgressParamsValue}; | 14 | use lsp_types::{ProgressParams, ProgressParamsValue}; |
15 | use project_model::{CargoConfig, ProjectManifest}; | 15 | use project_model::{CargoConfig, ProjectManifest}; |
16 | use rust_analyzer::{ | 16 | use rust_analyzer::{ |
17 | config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject}, | 17 | config::{Config, FilesConfig, FilesWatcher, LinkedProject}, |
18 | main_loop, | 18 | main_loop, |
19 | }; | 19 | }; |
20 | use serde::Serialize; | 20 | use serde::Serialize; |
@@ -84,10 +84,24 @@ impl<'a> Project<'a> { | |||
84 | .collect::<Vec<_>>(); | 84 | .collect::<Vec<_>>(); |
85 | 85 | ||
86 | let mut config = Config { | 86 | let mut config = Config { |
87 | client_caps: ClientCapsConfig { | 87 | caps: lsp_types::ClientCapabilities { |
88 | location_link: true, | 88 | text_document: Some(lsp_types::TextDocumentClientCapabilities { |
89 | code_action_literals: true, | 89 | definition: Some(lsp_types::GotoCapability { |
90 | work_done_progress: true, | 90 | link_support: Some(true), |
91 | ..Default::default() | ||
92 | }), | ||
93 | code_action: Some(lsp_types::CodeActionClientCapabilities { | ||
94 | code_action_literal_support: Some( | ||
95 | lsp_types::CodeActionLiteralSupport::default(), | ||
96 | ), | ||
97 | ..Default::default() | ||
98 | }), | ||
99 | ..Default::default() | ||
100 | }), | ||
101 | window: Some(lsp_types::WindowClientCapabilities { | ||
102 | work_done_progress: Some(true), | ||
103 | ..Default::default() | ||
104 | }), | ||
91 | ..Default::default() | 105 | ..Default::default() |
92 | }, | 106 | }, |
93 | cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, | 107 | cargo: CargoConfig { no_sysroot: !self.with_sysroot, ..Default::default() }, |
diff --git a/crates/ssr/src/parsing.rs b/crates/ssr/src/parsing.rs index f3b084baf..3d5e4feb7 100644 --- a/crates/ssr/src/parsing.rs +++ b/crates/ssr/src/parsing.rs | |||
@@ -73,11 +73,18 @@ impl ParsedRule { | |||
73 | placeholders_by_stand_in: pattern.placeholders_by_stand_in(), | 73 | placeholders_by_stand_in: pattern.placeholders_by_stand_in(), |
74 | rules: Vec::new(), | 74 | rules: Vec::new(), |
75 | }; | 75 | }; |
76 | builder.try_add(ast::Expr::parse(&raw_pattern), raw_template.map(ast::Expr::parse)); | 76 | |
77 | let raw_template_stmt = raw_template.map(ast::Stmt::parse); | ||
78 | if let raw_template_expr @ Some(Ok(_)) = raw_template.map(ast::Expr::parse) { | ||
79 | builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_expr); | ||
80 | } else { | ||
81 | builder.try_add(ast::Expr::parse(&raw_pattern), raw_template_stmt.clone()); | ||
82 | } | ||
77 | builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse)); | 83 | builder.try_add(ast::Type::parse(&raw_pattern), raw_template.map(ast::Type::parse)); |
78 | builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse)); | 84 | builder.try_add(ast::Item::parse(&raw_pattern), raw_template.map(ast::Item::parse)); |
79 | builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse)); | 85 | builder.try_add(ast::Path::parse(&raw_pattern), raw_template.map(ast::Path::parse)); |
80 | builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); | 86 | builder.try_add(ast::Pat::parse(&raw_pattern), raw_template.map(ast::Pat::parse)); |
87 | builder.try_add(ast::Stmt::parse(&raw_pattern), raw_template_stmt); | ||
81 | builder.build() | 88 | builder.build() |
82 | } | 89 | } |
83 | } | 90 | } |
@@ -88,7 +95,11 @@ struct RuleBuilder { | |||
88 | } | 95 | } |
89 | 96 | ||
90 | impl RuleBuilder { | 97 | impl RuleBuilder { |
91 | fn try_add<T: AstNode>(&mut self, pattern: Result<T, ()>, template: Option<Result<T, ()>>) { | 98 | fn try_add<T: AstNode, T2: AstNode>( |
99 | &mut self, | ||
100 | pattern: Result<T, ()>, | ||
101 | template: Option<Result<T2, ()>>, | ||
102 | ) { | ||
92 | match (pattern, template) { | 103 | match (pattern, template) { |
93 | (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule { | 104 | (Ok(pattern), Some(Ok(template))) => self.rules.push(ParsedRule { |
94 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), | 105 | placeholders_by_stand_in: self.placeholders_by_stand_in.clone(), |
diff --git a/crates/ssr/src/tests.rs b/crates/ssr/src/tests.rs index 63131f6ca..db9cb8ca1 100644 --- a/crates/ssr/src/tests.rs +++ b/crates/ssr/src/tests.rs | |||
@@ -160,6 +160,97 @@ fn assert_match_failure_reason(pattern: &str, code: &str, snippet: &str, expecte | |||
160 | } | 160 | } |
161 | 161 | ||
162 | #[test] | 162 | #[test] |
163 | fn ssr_let_stmt_in_macro_match() { | ||
164 | assert_matches( | ||
165 | "let a = 0", | ||
166 | r#" | ||
167 | macro_rules! m1 { ($a:stmt) => {$a}; } | ||
168 | fn f() {m1!{ let a = 0 };}"#, | ||
169 | // FIXME: Whitespace is not part of the matched block | ||
170 | &["leta=0"], | ||
171 | ); | ||
172 | } | ||
173 | |||
174 | #[test] | ||
175 | fn ssr_let_stmt_in_fn_match() { | ||
176 | assert_matches("let $a = 10;", "fn main() { let x = 10; x }", &["let x = 10;"]); | ||
177 | assert_matches("let $a = $b;", "fn main() { let x = 10; x }", &["let x = 10;"]); | ||
178 | } | ||
179 | |||
180 | #[test] | ||
181 | fn ssr_block_expr_match() { | ||
182 | assert_matches("{ let $a = $b; }", "fn main() { let x = 10; }", &["{ let x = 10; }"]); | ||
183 | assert_matches("{ let $a = $b; $c }", "fn main() { let x = 10; x }", &["{ let x = 10; x }"]); | ||
184 | } | ||
185 | |||
186 | #[test] | ||
187 | fn ssr_let_stmt_replace() { | ||
188 | // Pattern and template with trailing semicolon | ||
189 | assert_ssr_transform( | ||
190 | "let $a = $b; ==>> let $a = 11;", | ||
191 | "fn main() { let x = 10; x }", | ||
192 | expect![["fn main() { let x = 11; x }"]], | ||
193 | ); | ||
194 | } | ||
195 | |||
196 | #[test] | ||
197 | fn ssr_let_stmt_replace_expr() { | ||
198 | // Trailing semicolon should be dropped from the new expression | ||
199 | assert_ssr_transform( | ||
200 | "let $a = $b; ==>> $b", | ||
201 | "fn main() { let x = 10; }", | ||
202 | expect![["fn main() { 10 }"]], | ||
203 | ); | ||
204 | } | ||
205 | |||
206 | #[test] | ||
207 | fn ssr_blockexpr_replace_stmt_with_stmt() { | ||
208 | assert_ssr_transform( | ||
209 | "if $a() {$b;} ==>> $b;", | ||
210 | "{ | ||
211 | if foo() { | ||
212 | bar(); | ||
213 | } | ||
214 | Ok(()) | ||
215 | }", | ||
216 | expect![[r#"{ | ||
217 | bar(); | ||
218 | Ok(()) | ||
219 | }"#]], | ||
220 | ); | ||
221 | } | ||
222 | |||
223 | #[test] | ||
224 | fn ssr_blockexpr_match_trailing_expr() { | ||
225 | assert_matches( | ||
226 | "if $a() {$b;}", | ||
227 | "{ | ||
228 | if foo() { | ||
229 | bar(); | ||
230 | } | ||
231 | }", | ||
232 | &["if foo() { | ||
233 | bar(); | ||
234 | }"], | ||
235 | ); | ||
236 | } | ||
237 | |||
238 | #[test] | ||
239 | fn ssr_blockexpr_replace_trailing_expr_with_stmt() { | ||
240 | assert_ssr_transform( | ||
241 | "if $a() {$b;} ==>> $b;", | ||
242 | "{ | ||
243 | if foo() { | ||
244 | bar(); | ||
245 | } | ||
246 | }", | ||
247 | expect![["{ | ||
248 | bar(); | ||
249 | }"]], | ||
250 | ); | ||
251 | } | ||
252 | |||
253 | #[test] | ||
163 | fn ssr_function_to_method() { | 254 | fn ssr_function_to_method() { |
164 | assert_ssr_transform( | 255 | assert_ssr_transform( |
165 | "my_function($a, $b) ==>> ($a).my_method($b)", | 256 | "my_function($a, $b) ==>> ($a).my_method($b)", |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 181077944..cfeaed9e6 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.10.0" | 14 | itertools = "0.10.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index c5b80bffe..92ed2ee9d 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs | |||
@@ -484,7 +484,7 @@ impl ast::AttrsOwner for BlockExpr {} | |||
484 | impl BlockExpr { | 484 | impl BlockExpr { |
485 | pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } | 485 | pub fn l_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['{']) } |
486 | pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } | 486 | pub fn statements(&self) -> AstChildren<Stmt> { support::children(&self.syntax) } |
487 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } | 487 | pub fn tail_expr(&self) -> Option<Expr> { support::child(&self.syntax) } |
488 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } | 488 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } |
489 | } | 489 | } |
490 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 490 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 4d272f367..ea7482bb1 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs | |||
@@ -212,6 +212,13 @@ impl ast::Attr { | |||
212 | } | 212 | } |
213 | } | 213 | } |
214 | 214 | ||
215 | impl ast::Stmt { | ||
216 | /// Returns `text`, parsed as statement, but only if it has no errors. | ||
217 | pub fn parse(text: &str) -> Result<Self, ()> { | ||
218 | parsing::parse_text_fragment(text, parser::FragmentKind::StatementOptionalSemi) | ||
219 | } | ||
220 | } | ||
221 | |||
215 | /// Matches a `SyntaxNode` against an `ast` type. | 222 | /// Matches a `SyntaxNode` against an `ast` type. |
216 | /// | 223 | /// |
217 | /// # Example: | 224 | /// # Example: |
@@ -283,7 +290,7 @@ fn api_walkthrough() { | |||
283 | 290 | ||
284 | // Let's get the `1 + 1` expression! | 291 | // Let's get the `1 + 1` expression! |
285 | let body: ast::BlockExpr = func.body().unwrap(); | 292 | let body: ast::BlockExpr = func.body().unwrap(); |
286 | let expr: ast::Expr = body.expr().unwrap(); | 293 | let expr: ast::Expr = body.tail_expr().unwrap(); |
287 | 294 | ||
288 | // Enums are used to group related ast nodes together, and can be used for | 295 | // Enums are used to group related ast nodes together, and can be used for |
289 | // matching. However, because there are no public fields, it's possible to | 296 | // matching. However, because there are no public fields, it's possible to |
diff --git a/crates/syntax/src/tests.rs b/crates/syntax/src/tests.rs index 8c217dfe0..9d3433c9d 100644 --- a/crates/syntax/src/tests.rs +++ b/crates/syntax/src/tests.rs | |||
@@ -103,6 +103,15 @@ fn type_parser_tests() { | |||
103 | } | 103 | } |
104 | 104 | ||
105 | #[test] | 105 | #[test] |
106 | fn stmt_parser_tests() { | ||
107 | fragment_parser_dir_test( | ||
108 | &["parser/fragments/stmt/ok"], | ||
109 | &["parser/fragments/stmt/err"], | ||
110 | crate::ast::Stmt::parse, | ||
111 | ); | ||
112 | } | ||
113 | |||
114 | #[test] | ||
106 | fn parser_fuzz_tests() { | 115 | fn parser_fuzz_tests() { |
107 | for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { | 116 | for (_, text) in collect_rust_files(&test_data_dir(), &["parser/fuzz-failures"]) { |
108 | fuzz::check_parser(&text) | 117 | fuzz::check_parser(&text) |
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast | |||
@@ -0,0 +1 @@ | |||
ERROR | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs new file mode 100644 index 000000000..988df0705 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs | |||
@@ -0,0 +1 @@ | |||
#[foo] | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast | |||
@@ -0,0 +1 @@ | |||
ERROR | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs new file mode 100644 index 000000000..7e3b2fd49 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs | |||
@@ -0,0 +1 @@ | |||
a(); b(); c() | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast | |||
@@ -0,0 +1 @@ | |||
ERROR | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs new file mode 100644 index 000000000..2d06f3766 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs | |||
@@ -0,0 +1 @@ | |||
( | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast | |||
@@ -0,0 +1 @@ | |||
ERROR | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs new file mode 100644 index 000000000..092bc2b04 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs | |||
@@ -0,0 +1 @@ | |||
; | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast new file mode 100644 index 000000000..5df7507e2 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast | |||
@@ -0,0 +1 @@ | |||
ERROR | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs new file mode 100644 index 000000000..ca49acb07 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs | |||
@@ -0,0 +1 @@ | |||
1 + | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast new file mode 100644 index 000000000..274fdf16d --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast | |||
@@ -0,0 +1,9 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] | ||
4 | [email protected] "1" | ||
5 | [email protected] " " | ||
6 | [email protected] "+" | ||
7 | [email protected] " " | ||
8 | [email protected] | ||
9 | [email protected] "1" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs new file mode 100644 index 000000000..8d2f0971e --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs | |||
@@ -0,0 +1 @@ | |||
1 + 1 | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast new file mode 100644 index 000000000..6c946091f --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast | |||
@@ -0,0 +1,69 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] "{" | ||
4 | [email protected] "\n " | ||
5 | [email protected] | ||
6 | [email protected] "let" | ||
7 | [email protected] " " | ||
8 | [email protected] | ||
9 | [email protected] | ||
10 | [email protected] "x" | ||
11 | [email protected] " " | ||
12 | [email protected] "=" | ||
13 | [email protected] " " | ||
14 | [email protected] | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] "foo" | ||
20 | [email protected] | ||
21 | [email protected] "(" | ||
22 | [email protected] ")" | ||
23 | [email protected] ";" | ||
24 | [email protected] "\n " | ||
25 | [email protected] | ||
26 | [email protected] "let" | ||
27 | [email protected] " " | ||
28 | [email protected] | ||
29 | [email protected] | ||
30 | [email protected] "y" | ||
31 | [email protected] " " | ||
32 | [email protected] "=" | ||
33 | [email protected] " " | ||
34 | [email protected] | ||
35 | [email protected] | ||
36 | [email protected] | ||
37 | [email protected] | ||
38 | [email protected] | ||
39 | [email protected] "bar" | ||
40 | [email protected] | ||
41 | [email protected] "(" | ||
42 | [email protected] ")" | ||
43 | [email protected] ";" | ||
44 | [email protected] "\n " | ||
45 | [email protected] | ||
46 | [email protected] | ||
47 | [email protected] | ||
48 | [email protected] | ||
49 | [email protected] | ||
50 | [email protected] "Ok" | ||
51 | [email protected] | ||
52 | [email protected] "(" | ||
53 | [email protected] | ||
54 | [email protected] | ||
55 | [email protected] | ||
56 | [email protected] | ||
57 | [email protected] | ||
58 | [email protected] "x" | ||
59 | [email protected] " " | ||
60 | [email protected] "+" | ||
61 | [email protected] " " | ||
62 | [email protected] | ||
63 | [email protected] | ||
64 | [email protected] | ||
65 | [email protected] | ||
66 | [email protected] "y" | ||
67 | [email protected] ")" | ||
68 | [email protected] "\n" | ||
69 | [email protected] "}" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs new file mode 100644 index 000000000..ffa5c1e66 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs | |||
@@ -0,0 +1,5 @@ | |||
1 | { | ||
2 | let x = foo(); | ||
3 | let y = bar(); | ||
4 | Ok(x + y) | ||
5 | } | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast new file mode 100644 index 000000000..8c186da93 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast | |||
@@ -0,0 +1,11 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] | ||
4 | [email protected] | ||
5 | [email protected] | ||
6 | [email protected] | ||
7 | [email protected] "foo" | ||
8 | [email protected] | ||
9 | [email protected] "(" | ||
10 | [email protected] ")" | ||
11 | [email protected] ";" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs new file mode 100644 index 000000000..a280f9a5c --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs | |||
@@ -0,0 +1 @@ | |||
foo(); | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast new file mode 100644 index 000000000..8ab38da21 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast | |||
@@ -0,0 +1,12 @@ | |||
1 | [email protected] | ||
2 | [email protected] "let" | ||
3 | [email protected] " " | ||
4 | [email protected] | ||
5 | [email protected] | ||
6 | [email protected] "x" | ||
7 | [email protected] " " | ||
8 | [email protected] "=" | ||
9 | [email protected] " " | ||
10 | [email protected] | ||
11 | [email protected] "10" | ||
12 | [email protected] ";" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs new file mode 100644 index 000000000..de8a7f1fc --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs | |||
@@ -0,0 +1 @@ | |||
let x = 10; | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast new file mode 100644 index 000000000..81d6df29a --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast | |||
@@ -0,0 +1,21 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] | ||
4 | [email protected] | ||
5 | [email protected] | ||
6 | [email protected] "m1" | ||
7 | [email protected] "!" | ||
8 | [email protected] | ||
9 | [email protected] "{" | ||
10 | [email protected] " " | ||
11 | [email protected] "let" | ||
12 | [email protected] " " | ||
13 | [email protected] "a" | ||
14 | [email protected] " " | ||
15 | [email protected] "=" | ||
16 | [email protected] " " | ||
17 | [email protected] "0" | ||
18 | [email protected] ";" | ||
19 | [email protected] " " | ||
20 | [email protected] "}" | ||
21 | [email protected] ";" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs new file mode 100644 index 000000000..075f30159 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs | |||
@@ -0,0 +1 @@ | |||
m1!{ let a = 0; }; | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast new file mode 100644 index 000000000..81d6df29a --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast | |||
@@ -0,0 +1,21 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] | ||
4 | [email protected] | ||
5 | [email protected] | ||
6 | [email protected] "m1" | ||
7 | [email protected] "!" | ||
8 | [email protected] | ||
9 | [email protected] "{" | ||
10 | [email protected] " " | ||
11 | [email protected] "let" | ||
12 | [email protected] " " | ||
13 | [email protected] "a" | ||
14 | [email protected] " " | ||
15 | [email protected] "=" | ||
16 | [email protected] " " | ||
17 | [email protected] "0" | ||
18 | [email protected] ";" | ||
19 | [email protected] " " | ||
20 | [email protected] "}" | ||
21 | [email protected] ";" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs new file mode 100644 index 000000000..075f30159 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs | |||
@@ -0,0 +1 @@ | |||
m1!{ let a = 0; }; | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast new file mode 100644 index 000000000..64c5d2969 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast | |||
@@ -0,0 +1,22 @@ | |||
1 | [email protected] | ||
2 | [email protected] "struct" | ||
3 | [email protected] " " | ||
4 | [email protected] | ||
5 | [email protected] "Foo" | ||
6 | [email protected] " " | ||
7 | [email protected] | ||
8 | [email protected] "{" | ||
9 | [email protected] "\n " | ||
10 | [email protected] | ||
11 | [email protected] | ||
12 | [email protected] "bar" | ||
13 | [email protected] ":" | ||
14 | [email protected] " " | ||
15 | [email protected] | ||
16 | [email protected] | ||
17 | [email protected] | ||
18 | [email protected] | ||
19 | [email protected] "u32" | ||
20 | [email protected] "," | ||
21 | [email protected] "\n" | ||
22 | [email protected] "}" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs new file mode 100644 index 000000000..e5473e3ac --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | struct Foo { | ||
2 | bar: u32, | ||
3 | } | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast new file mode 100644 index 000000000..9089906bc --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast | |||
@@ -0,0 +1,10 @@ | |||
1 | [email protected] | ||
2 | [email protected] | ||
3 | [email protected] | ||
4 | [email protected] | ||
5 | [email protected] | ||
6 | [email protected] | ||
7 | [email protected] "foo" | ||
8 | [email protected] | ||
9 | [email protected] "(" | ||
10 | [email protected] ")" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs new file mode 100644 index 000000000..eb28ef440 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs | |||
@@ -0,0 +1 @@ | |||
foo() | |||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast new file mode 100644 index 000000000..37663671f --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast | |||
@@ -0,0 +1,11 @@ | |||
1 | [email protected] | ||
2 | [email protected] "let" | ||
3 | [email protected] " " | ||
4 | [email protected] | ||
5 | [email protected] | ||
6 | [email protected] "x" | ||
7 | [email protected] " " | ||
8 | [email protected] "=" | ||
9 | [email protected] " " | ||
10 | [email protected] | ||
11 | [email protected] "10" | ||
diff --git a/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs new file mode 100644 index 000000000..78364b2a9 --- /dev/null +++ b/crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs | |||
@@ -0,0 +1 @@ | |||
let x = 10 | |||
diff --git a/crates/tt/src/buffer.rs b/crates/tt/src/buffer.rs index 02c771f70..3606c887d 100644 --- a/crates/tt/src/buffer.rs +++ b/crates/tt/src/buffer.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use crate::{Subtree, TokenTree}; | 3 | use crate::{Leaf, Subtree, TokenTree}; |
4 | 4 | ||
5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | 5 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] |
6 | struct EntryId(usize); | 6 | struct EntryId(usize); |
@@ -13,7 +13,7 @@ struct EntryPtr(EntryId, usize); | |||
13 | #[derive(Debug)] | 13 | #[derive(Debug)] |
14 | enum Entry<'t> { | 14 | enum Entry<'t> { |
15 | // Mimicking types from proc-macro. | 15 | // Mimicking types from proc-macro. |
16 | Subtree(&'t TokenTree, EntryId), | 16 | Subtree(Option<&'t TokenTree>, &'t Subtree, EntryId), |
17 | Leaf(&'t TokenTree), | 17 | Leaf(&'t TokenTree), |
18 | // End entries contain a pointer to the entry from the containing | 18 | // End entries contain a pointer to the entry from the containing |
19 | // token tree, or None if this is the outermost level. | 19 | // token tree, or None if this is the outermost level. |
@@ -27,37 +27,64 @@ pub struct TokenBuffer<'t> { | |||
27 | buffers: Vec<Box<[Entry<'t>]>>, | 27 | buffers: Vec<Box<[Entry<'t>]>>, |
28 | } | 28 | } |
29 | 29 | ||
30 | impl<'t> TokenBuffer<'t> { | 30 | trait TokenList<'a> { |
31 | pub fn new(tokens: &'t [TokenTree]) -> TokenBuffer<'t> { | 31 | fn entries(&self) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec<Entry<'a>>); |
32 | let mut buffers = vec![]; | 32 | } |
33 | |||
34 | let idx = TokenBuffer::new_inner(tokens, &mut buffers, None); | ||
35 | assert_eq!(idx, 0); | ||
36 | |||
37 | TokenBuffer { buffers } | ||
38 | } | ||
39 | 33 | ||
40 | fn new_inner( | 34 | impl<'a> TokenList<'a> for &'a [TokenTree] { |
41 | tokens: &'t [TokenTree], | 35 | fn entries(&self) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec<Entry<'a>>) { |
42 | buffers: &mut Vec<Box<[Entry<'t>]>>, | ||
43 | next: Option<EntryPtr>, | ||
44 | ) -> usize { | ||
45 | // Must contain everything in tokens and then the Entry::End | 36 | // Must contain everything in tokens and then the Entry::End |
46 | let start_capacity = tokens.len() + 1; | 37 | let start_capacity = self.len() + 1; |
47 | let mut entries = Vec::with_capacity(start_capacity); | 38 | let mut entries = Vec::with_capacity(start_capacity); |
48 | let mut children = vec![]; | 39 | let mut children = vec![]; |
49 | 40 | for (idx, tt) in self.iter().enumerate() { | |
50 | for (idx, tt) in tokens.iter().enumerate() { | ||
51 | match tt { | 41 | match tt { |
52 | TokenTree::Leaf(_) => { | 42 | TokenTree::Leaf(_) => { |
53 | entries.push(Entry::Leaf(tt)); | 43 | entries.push(Entry::Leaf(tt)); |
54 | } | 44 | } |
55 | TokenTree::Subtree(subtree) => { | 45 | TokenTree::Subtree(subtree) => { |
56 | entries.push(Entry::End(None)); | 46 | entries.push(Entry::End(None)); |
57 | children.push((idx, (subtree, tt))); | 47 | children.push((idx, (subtree, Some(tt)))); |
58 | } | 48 | } |
59 | } | 49 | } |
60 | } | 50 | } |
51 | (children, entries) | ||
52 | } | ||
53 | } | ||
54 | |||
55 | impl<'a> TokenList<'a> for &'a Subtree { | ||
56 | fn entries(&self) -> (Vec<(usize, (&'a Subtree, Option<&'a TokenTree>))>, Vec<Entry<'a>>) { | ||
57 | // Must contain everything in tokens and then the Entry::End | ||
58 | let mut entries = vec![]; | ||
59 | let mut children = vec![]; | ||
60 | entries.push(Entry::End(None)); | ||
61 | children.push((0usize, (*self, None))); | ||
62 | (children, entries) | ||
63 | } | ||
64 | } | ||
65 | |||
66 | impl<'t> TokenBuffer<'t> { | ||
67 | pub fn from_tokens(tokens: &'t [TokenTree]) -> TokenBuffer<'t> { | ||
68 | Self::new(tokens) | ||
69 | } | ||
70 | |||
71 | pub fn from_subtree(subtree: &'t Subtree) -> TokenBuffer<'t> { | ||
72 | Self::new(subtree) | ||
73 | } | ||
74 | |||
75 | fn new<T: TokenList<'t>>(tokens: T) -> TokenBuffer<'t> { | ||
76 | let mut buffers = vec![]; | ||
77 | let idx = TokenBuffer::new_inner(tokens, &mut buffers, None); | ||
78 | assert_eq!(idx, 0); | ||
79 | TokenBuffer { buffers } | ||
80 | } | ||
81 | |||
82 | fn new_inner<T: TokenList<'t>>( | ||
83 | tokens: T, | ||
84 | buffers: &mut Vec<Box<[Entry<'t>]>>, | ||
85 | next: Option<EntryPtr>, | ||
86 | ) -> usize { | ||
87 | let (children, mut entries) = tokens.entries(); | ||
61 | 88 | ||
62 | entries.push(Entry::End(next)); | 89 | entries.push(Entry::End(next)); |
63 | let res = buffers.len(); | 90 | let res = buffers.len(); |
@@ -65,11 +92,11 @@ impl<'t> TokenBuffer<'t> { | |||
65 | 92 | ||
66 | for (child_idx, (subtree, tt)) in children { | 93 | for (child_idx, (subtree, tt)) in children { |
67 | let idx = TokenBuffer::new_inner( | 94 | let idx = TokenBuffer::new_inner( |
68 | &subtree.token_trees, | 95 | subtree.token_trees.as_slice(), |
69 | buffers, | 96 | buffers, |
70 | Some(EntryPtr(EntryId(res), child_idx + 1)), | 97 | Some(EntryPtr(EntryId(res), child_idx + 1)), |
71 | ); | 98 | ); |
72 | buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, EntryId(idx)); | 99 | buffers[res].as_mut()[child_idx] = Entry::Subtree(tt, subtree, EntryId(idx)); |
73 | } | 100 | } |
74 | 101 | ||
75 | res | 102 | res |
@@ -87,6 +114,24 @@ impl<'t> TokenBuffer<'t> { | |||
87 | } | 114 | } |
88 | } | 115 | } |
89 | 116 | ||
117 | #[derive(Debug)] | ||
118 | pub enum TokenTreeRef<'a> { | ||
119 | Subtree(&'a Subtree, Option<&'a TokenTree>), | ||
120 | Leaf(&'a Leaf, &'a TokenTree), | ||
121 | } | ||
122 | |||
123 | impl<'a> TokenTreeRef<'a> { | ||
124 | pub fn cloned(&self) -> TokenTree { | ||
125 | match &self { | ||
126 | TokenTreeRef::Subtree(subtree, tt) => match tt { | ||
127 | Some(it) => (*it).clone(), | ||
128 | None => (*subtree).clone().into(), | ||
129 | }, | ||
130 | TokenTreeRef::Leaf(_, tt) => (*tt).clone(), | ||
131 | } | ||
132 | } | ||
133 | } | ||
134 | |||
90 | /// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 | 135 | /// A safe version of `Cursor` from `syn` crate https://github.com/dtolnay/syn/blob/6533607f91686545cb034d2838beea338d9d0742/src/buffer.rs#L125 |
91 | #[derive(Copy, Clone, Debug)] | 136 | #[derive(Copy, Clone, Debug)] |
92 | pub struct Cursor<'a> { | 137 | pub struct Cursor<'a> { |
@@ -114,12 +159,11 @@ impl<'a> Cursor<'a> { | |||
114 | match self.entry() { | 159 | match self.entry() { |
115 | Some(Entry::End(Some(ptr))) => { | 160 | Some(Entry::End(Some(ptr))) => { |
116 | let idx = ptr.1; | 161 | let idx = ptr.1; |
117 | if let Some(Entry::Subtree(TokenTree::Subtree(subtree), _)) = | 162 | if let Some(Entry::Subtree(_, subtree, _)) = |
118 | self.buffer.entry(&EntryPtr(ptr.0, idx - 1)) | 163 | self.buffer.entry(&EntryPtr(ptr.0, idx - 1)) |
119 | { | 164 | { |
120 | return Some(subtree); | 165 | return Some(subtree); |
121 | } | 166 | } |
122 | |||
123 | None | 167 | None |
124 | } | 168 | } |
125 | _ => None, | 169 | _ => None, |
@@ -134,7 +178,7 @@ impl<'a> Cursor<'a> { | |||
134 | /// a cursor into that subtree | 178 | /// a cursor into that subtree |
135 | pub fn subtree(self) -> Option<Cursor<'a>> { | 179 | pub fn subtree(self) -> Option<Cursor<'a>> { |
136 | match self.entry() { | 180 | match self.entry() { |
137 | Some(Entry::Subtree(_, entry_id)) => { | 181 | Some(Entry::Subtree(_, _, entry_id)) => { |
138 | Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0))) | 182 | Some(Cursor::create(self.buffer, EntryPtr(*entry_id, 0))) |
139 | } | 183 | } |
140 | _ => None, | 184 | _ => None, |
@@ -142,10 +186,13 @@ impl<'a> Cursor<'a> { | |||
142 | } | 186 | } |
143 | 187 | ||
144 | /// If the cursor is pointing at a `TokenTree`, returns it | 188 | /// If the cursor is pointing at a `TokenTree`, returns it |
145 | pub fn token_tree(self) -> Option<&'a TokenTree> { | 189 | pub fn token_tree(self) -> Option<TokenTreeRef<'a>> { |
146 | match self.entry() { | 190 | match self.entry() { |
147 | Some(Entry::Leaf(tt)) => Some(tt), | 191 | Some(Entry::Leaf(tt)) => match tt { |
148 | Some(Entry::Subtree(tt, _)) => Some(tt), | 192 | TokenTree::Leaf(leaf) => Some(TokenTreeRef::Leaf(leaf, *tt)), |
193 | TokenTree::Subtree(subtree) => Some(TokenTreeRef::Subtree(subtree, Some(tt))), | ||
194 | }, | ||
195 | Some(Entry::Subtree(tt, subtree, _)) => Some(TokenTreeRef::Subtree(subtree, *tt)), | ||
149 | Some(Entry::End(_)) => None, | 196 | Some(Entry::End(_)) => None, |
150 | None => None, | 197 | None => None, |
151 | } | 198 | } |
@@ -172,7 +219,7 @@ impl<'a> Cursor<'a> { | |||
172 | /// a cursor into that subtree | 219 | /// a cursor into that subtree |
173 | pub fn bump_subtree(self) -> Cursor<'a> { | 220 | pub fn bump_subtree(self) -> Cursor<'a> { |
174 | match self.entry() { | 221 | match self.entry() { |
175 | Some(Entry::Subtree(_, _)) => self.subtree().unwrap(), | 222 | Some(Entry::Subtree(_, _, _)) => self.subtree().unwrap(), |
176 | _ => self.bump(), | 223 | _ => self.bump(), |
177 | } | 224 | } |
178 | } | 225 | } |