aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/early_return.rs2
-rw-r--r--crates/assists/src/handlers/extract_struct_from_enum_variant.rs12
-rw-r--r--crates/assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/assists/src/handlers/infer_function_return_type.rs4
-rw-r--r--crates/assists/src/handlers/inline_function.rs202
-rw-r--r--crates/assists/src/handlers/move_guard.rs4
-rw-r--r--crates/assists/src/handlers/pull_assignment_up.rs (renamed from crates/assists/src/handlers/extract_assignment.rs)151
-rw-r--r--crates/assists/src/handlers/replace_if_let_with_match.rs2
-rw-r--r--crates/assists/src/handlers/wrap_return_type_in_result.rs8
-rw-r--r--crates/assists/src/lib.rs10
-rw-r--r--crates/assists/src/tests/generated.rs81
-rw-r--r--crates/assists/src/utils.rs2
-rw-r--r--crates/completion/src/completions/unqualified_path.rs3
-rw-r--r--crates/completion/src/context.rs2
-rw-r--r--crates/hir/src/code_model.rs57
-rw-r--r--crates/hir_def/src/attr.rs56
-rw-r--r--crates/hir_def/src/body/lower.rs2
-rw-r--r--crates/hir_def/src/db.rs11
-rw-r--r--crates/hir_def/src/diagnostics.rs4
-rw-r--r--crates/hir_def/src/import_map.rs287
-rw-r--r--crates/hir_def/src/item_scope.rs3
-rw-r--r--crates/hir_def/src/nameres/collector.rs33
-rw-r--r--crates/hir_expand/src/proc_macro.rs10
-rw-r--r--crates/hir_ty/Cargo.toml6
-rw-r--r--crates/hir_ty/src/display.rs4
-rw-r--r--crates/ide/src/display/short_label.rs11
-rw-r--r--crates/ide/src/hover.rs268
-rw-r--r--crates/ide_db/src/imports_locator.rs21
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs6
-rw-r--r--crates/mbe/src/subtree_source.rs152
-rw-r--r--crates/mbe/src/syntax_bridge.rs51
-rw-r--r--crates/parser/src/grammar.rs4
-rw-r--r--crates/parser/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/config.rs159
-rw-r--r--crates/rust-analyzer/src/handlers.rs21
-rw-r--r--crates/rust-analyzer/src/lsp_utils.rs2
-rw-r--r--crates/rust-analyzer/src/reload.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs6
-rw-r--r--crates/rust-analyzer/tests/rust-analyzer/support.rs24
-rw-r--r--crates/ssr/src/parsing.rs15
-rw-r--r--crates/ssr/src/tests.rs91
-rw-r--r--crates/syntax/Cargo.toml2
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs2
-rw-r--r--crates/syntax/src/lib.rs9
-rw-r--r--crates/syntax/src/tests.rs9
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_attr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_multiple_stmts.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_open_parenthesis.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_semicolon.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rast1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/err/0000_unterminated_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rast9
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rast69
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_expr_block.rs5
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rast12
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rast21
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_macro_unterminated_let_stmt.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rast22
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_struct_item.rs3
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rast10
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_fn_call.rs1
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rast11
-rw-r--r--crates/syntax/test_data/parser/fragments/stmt/ok/0000_unterminated_let_stmt.rs1
-rw-r--r--crates/tt/src/buffer.rs105
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 @@
1use ast::make;
2use hir::{HasSource, PathResolution};
3use syntax::{
4 ast::{self, edit::AstNodeEdit, ArgListOwner},
5 AstNode,
6};
7use test_utils::mark;
8
9use 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// ```
35pub(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
83fn 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)]
95mod 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#"
105fn foo() { println!("Hello, World!"); }
106fn main() {
107 fo<|>o();
108}
109"#,
110 r#"
111fn foo() { println!("Hello, World!"); }
112fn 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#"
126fn foo(name: String) { println!("Hello, {}!", name); }
127fn main() {
128 foo<|>(String::from("Michael"));
129}
130"#,
131 r#"
132fn foo(name: String) { println!("Hello, {}!", name); }
133fn 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"
148struct Foo;
149impl Foo { fn bar(&self) {} }
150
151fn 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#"
162fn add(a: u32, b: u32) -> u32 { a + b }
163fn 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#"
173fn foo(a: u32, b: u32) -> u32 {
174 let x = a + b;
175 let y = x - b;
176 x * y
177}
178
179fn main() {
180 let x = foo<|>(1, 2);
181}
182"#,
183 r#"
184fn foo(a: u32, b: u32) -> u32 {
185 let x = a + b;
186 let y = x - b;
187 x * y
188}
189
190fn 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 @@
1use hir::AsName;
2use syntax::{ 1use 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// ```
40pub(crate) fn extract_assigment(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 39pub(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
66fn exprify_match(match_expr: &ast::MatchExpr, name: &hir::Name) -> Option<ast::Expr> { 73fn 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
85fn exprify_if(statement: &ast::IfExpr, name: &hir::Name) -> Option<ast::Expr> { 96fn 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
99fn exprify_block(block: &ast::BlockExpr, name: &hir::Name) -> Option<ast::BlockExpr> { 116fn 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
140fn 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)]
121mod tests { 164mod 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#"
131fn foo() { 174fn 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#"
158fn foo() { 201fn 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#"
197fn foo() { 240fn 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#"
216fn foo() { 259fn 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#"
247fn foo() { 290fn 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#"
278fn foo() { 321fn 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#"
295fn foo() { 338fn 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#"
310fn foo() { 353fn 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#"
375struct A(usize);
376
377fn 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#"
387struct A(usize);
388
389fn 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]
241fn doctest_extract_assignment() {
242 check_doc_test(
243 "extract_assignment",
244 r#####"
245fn main() {
246 let mut foo = 6;
247
248 if true {
249 <|>foo = 5;
250 } else {
251 foo = 4;
252 }
253}
254"#####,
255 r#####"
256fn 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]
270fn doctest_extract_module_to_file() { 241fn 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]
534fn doctest_inline_function() {
535 check_doc_test(
536 "inline_function",
537 r#####"
538fn add(a: u32, b: u32) -> u32 { a + b }
539fn main() {
540 let x = add<|>(1, 2);
541}
542"#####,
543 r#####"
544fn add(a: u32, b: u32) -> u32 { a + b }
545fn main() {
546 let x = {
547 let a = 1;
548 let b = 2;
549 a + b
550 };
551}
552"#####,
553 )
554}
555
556#[test]
563fn doctest_inline_local_variable() { 557fn 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]
764fn doctest_pull_assignment_up() {
765 check_doc_test(
766 "pull_assignment_up",
767 r#####"
768fn main() {
769 let mut foo = 6;
770
771 if true {
772 <|>foo = 5;
773 } else {
774 foo = 4;
775 }
776}
777"#####,
778 r#####"
779fn 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]
770fn doctest_qualify_path() { 793fn 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.
126fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 126fn 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;
5use base_db::{CrateDisplayName, CrateId, Edition, FileId}; 5use base_db::{CrateDisplayName, CrateId, Edition, FileId};
6use either::Either; 6use either::Either;
7use hir_def::{ 7use 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};
32use hir_ty::{ 30use 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
1318impl 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)]
1297pub struct LifetimeParam { 1333pub 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
3use std::{ops, sync::Arc}; 3use std::{ops, sync::Arc};
4 4
5use arena::map::ArenaMap;
5use base_db::CrateId; 6use base_db::CrateId;
6use cfg::{CfgExpr, CfgOptions}; 7use cfg::{CfgExpr, CfgOptions};
7use either::Either; 8use 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.
2use std::sync::Arc; 2use std::sync::Arc;
3 3
4use arena::map::ArenaMap;
4use base_db::{salsa, CrateId, SourceDatabase, Upcast}; 5use base_db::{salsa, CrateId, SourceDatabase, Upcast};
5use hir_expand::{db::AstDatabase, HirFileId}; 6use hir_expand::{db::AstDatabase, HirFileId};
6use syntax::SmolStr; 7use 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)]
137pub struct UnresolvedProcMacro { 141pub 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};
7use hir_expand::name::Name; 7use hir_expand::name::Name;
8use indexmap::{map::Entry, IndexMap}; 8use indexmap::{map::Entry, IndexMap};
9use itertools::Itertools; 9use itertools::Itertools;
10use rustc_hash::{FxHashMap, FxHashSet, FxHasher}; 10use rustc_hash::{FxHashSet, FxHasher};
11use smallvec::SmallVec; 11use test_utils::mark;
12use syntax::SmolStr;
13 12
14use crate::{ 13use 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
73impl ImportMap { 70impl 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
307fn 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> {
432mod tests { 446mod 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;
10use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
11use test_utils::mark; 11use test_utils::mark;
12 12
13use crate::ModuleId;
14use crate::{ 13use 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};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use 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
60fn eat_punct(cursor: &mut Cursor, c: char) -> bool { 60fn 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
70fn eat_subtree(cursor: &mut Cursor, kind: tt::DelimiterKind) -> bool { 70fn 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
80fn eat_ident(cursor: &mut Cursor, t: &str) -> bool { 80fn 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
90fn remove_derive_attrs(tt: &tt::Subtree) -> Option<tt::Subtree> { 90fn 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"
17log = "0.4.8" 17log = "0.4.8"
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19scoped-tls = "1" 19scoped-tls = "1"
20chalk-solve = { version = "0.45", default-features = false } 20chalk-solve = { version = "0.47", default-features = false }
21chalk-ir = "0.45" 21chalk-ir = "0.47"
22chalk-recursive = "0.45" 22chalk-recursive = "0.47"
23 23
24stdx = { path = "../stdx", version = "0.0.0" } 24stdx = { path = "../stdx", version = "0.0.0" }
25hir_def = { path = "../hir_def", version = "0.0.0" } 25hir_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
169impl DisplayTarget { 169impl 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
598fn write_bounds_like_dyn_trait( 598pub 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
90impl 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
90fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String> 101fn short_label_from_ty<T>(node: &T, ty: Option<ast::Type>, prefix: &str) -> Option<String>
91where 102where
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)]
71pub enum HoverAction { 71pub 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
176fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 177fn 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
196fn runnable_action( 199fn runnable_action(
@@ -225,45 +228,46 @@ fn runnable_action(
225} 228}
226 229
227fn goto_type_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { 230fn 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
269fn hover_markup( 273fn 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#"
3091struct Bar;
3092struct Foo<const BAR: Bar>;
3093
3094impl<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#"
3124trait Foo {}
3125
3126fn 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#"
3351struct Foo<T>(T);
3352trait Copy {}
3353trait Clone {}
3354trait Sized {}
3355impl<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#"
3367struct Foo<T>(T);
3368impl<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#"
3381struct Foo<T>(T);
3382impl<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#"
3398struct Foo<const LEN: usize>;
3399impl<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
4use hir::{import_map, Crate, MacroDef, ModuleDef, Semantics}; 4use hir::{import_map, AsAssocItem, Crate, MacroDef, ModuleDef, Semantics};
5use syntax::{ast, AstNode, SyntaxKind::NAME}; 5use syntax::{ast, AstNode, SyntaxKind::NAME};
6 6
7use crate::{ 7use 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
63fn find_imports<'a>( 78fn 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
3use parser::{Token, TokenSource}; 3use parser::{Token, TokenSource};
4use std::cell::{Cell, Ref, RefCell};
5use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T}; 4use syntax::{lex_single_syntax_kind, SmolStr, SyntaxKind, SyntaxKind::*, T};
6use tt::buffer::{Cursor, TokenBuffer}; 5use tt::buffer::TokenBuffer;
7 6
8#[derive(Debug, Clone, Eq, PartialEq)] 7#[derive(Debug, Clone, Eq, PartialEq)]
9struct TtToken { 8struct TtToken {
10 kind: SyntaxKind, 9 tt: Token,
11 is_joint_to_next: bool,
12 text: SmolStr, 10 text: SmolStr,
13} 11}
14 12
15pub(crate) struct SubtreeTokenSource<'a> { 13pub(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
21impl<'a> SubtreeTokenSource<'a> { 18impl<'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
32impl<'a> SubtreeTokenSource<'a> { 29impl<'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
119impl<'a> TokenSource for SubtreeTokenSource<'a> { 94impl<'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
161fn convert_literal(l: &tt::Literal) -> TtToken { 135fn 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
175fn convert_ident(ident: &tt::Ident) -> TtToken { 149fn 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
186fn convert_punct(p: tt::Punct) -> TtToken { 160fn 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
200fn convert_leaf(leaf: &tt::Leaf) -> TtToken { 174fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
@@ -208,6 +182,7 @@ fn convert_leaf(leaf: &tt::Leaf) -> TtToken {
208#[cfg(test)] 182#[cfg(test)]
209mod tests { 183mod 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
417impl<'a> SrcToken for (RawToken, &'a str) { 414impl<'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) {
431impl RawConvertor<'_> {} 428impl RawConvertor<'_> {}
432 429
433impl<'a> TokenConvertor for RawConvertor<'a> { 430impl<'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
604fn delim_to_str(d: Option<tt::DelimiterKind>, closing: bool) -> SmolStr { 601fn 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
617impl<'a> TreeSink for TtTreeSink<'a> { 613impl<'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)]
177pub struct Config { 177pub 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)]
290pub 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
303impl Config { 289impl 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
541macro_rules! try_ {
542 ($expr:expr) => {
543 || -> _ { Some($expr) }()
544 };
545}
546macro_rules! try_or {
547 ($expr:expr, $or:expr) => {
548 try_!($expr).unwrap_or($or)
549 };
550}
551
552impl 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)]
602enum ManifestOrProjectJson { 632enum 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
1556fn goto_location_command(snap: &GlobalStateSnapshot, nav: &NavigationTarget) -> Option<Command> { 1553fn 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::{
14use lsp_types::{ProgressParams, ProgressParamsValue}; 14use lsp_types::{ProgressParams, ProgressParamsValue};
15use project_model::{CargoConfig, ProjectManifest}; 15use project_model::{CargoConfig, ProjectManifest};
16use rust_analyzer::{ 16use rust_analyzer::{
17 config::{ClientCapsConfig, Config, FilesConfig, FilesWatcher, LinkedProject}, 17 config::{Config, FilesConfig, FilesWatcher, LinkedProject},
18 main_loop, 18 main_loop,
19}; 19};
20use serde::Serialize; 20use 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
90impl RuleBuilder { 97impl 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]
163fn 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]
175fn 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]
181fn 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]
187fn 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]
197fn 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]
207fn 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]
224fn 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]
239fn 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]
163fn ssr_function_to_method() { 254fn 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]
14itertools = "0.10.0" 14itertools = "0.10.0"
15rowan = "0.10.0" 15rowan = "0.10.0"
16rustc_lexer = { version = "695.0.0", package = "rustc-ap-rustc_lexer" } 16rustc_lexer = { version = "697.0.0", package = "rustc-ap-rustc_lexer" }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18arrayvec = "0.5.1" 18arrayvec = "0.5.1"
19once_cell = "1.3.1" 19once_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 {}
484impl BlockExpr { 484impl 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
215impl 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]
106fn 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]
106fn parser_fuzz_tests() { 115fn 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 @@
1struct 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
3use crate::{Subtree, TokenTree}; 3use crate::{Leaf, Subtree, TokenTree};
4 4
5#[derive(Copy, Clone, Debug, Eq, PartialEq)] 5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6struct EntryId(usize); 6struct EntryId(usize);
@@ -13,7 +13,7 @@ struct EntryPtr(EntryId, usize);
13#[derive(Debug)] 13#[derive(Debug)]
14enum Entry<'t> { 14enum 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
30impl<'t> TokenBuffer<'t> { 30trait 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( 34impl<'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
55impl<'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
66impl<'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)]
118pub enum TokenTreeRef<'a> {
119 Subtree(&'a Subtree, Option<&'a TokenTree>),
120 Leaf(&'a Leaf, &'a TokenTree),
121}
122
123impl<'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)]
92pub struct Cursor<'a> { 137pub 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 }