aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/Cargo.toml3
-rw-r--r--crates/ra_assists/src/assist_ctx.rs6
-rw-r--r--crates/ra_assists/src/ast_transform.rs12
-rw-r--r--crates/ra_assists/src/doc_tests/generated.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs49
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs40
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs94
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs103
-rw-r--r--crates/ra_assists/src/lib.rs29
-rw-r--r--crates/ra_assists/src/marks.rs1
-rw-r--r--crates/ra_fmt/Cargo.toml2
-rw-r--r--crates/ra_hir/Cargo.toml2
-rw-r--r--crates/ra_hir/src/code_model.rs26
-rw-r--r--crates/ra_hir/src/db.rs7
-rw-r--r--crates/ra_hir/src/from_id.rs21
-rw-r--r--crates/ra_hir/src/lib.rs2
-rw-r--r--crates/ra_hir/src/semantics.rs6
-rw-r--r--crates/ra_hir/src/source_analyzer.rs44
-rw-r--r--crates/ra_hir_def/src/db.rs7
-rw-r--r--crates/ra_hir_def/src/item_scope.rs6
-rw-r--r--crates/ra_hir_def/src/lib.rs8
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs60
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs22
-rw-r--r--crates/ra_hir_expand/src/db.rs11
-rw-r--r--crates/ra_hir_expand/src/eager.rs20
-rw-r--r--crates/ra_hir_expand/src/hygiene.rs1
-rw-r--r--crates/ra_hir_expand/src/lib.rs13
-rw-r--r--crates/ra_hir_expand/src/proc_macro.rs33
-rw-r--r--crates/ra_hir_ty/src/db.rs1
-rw-r--r--crates/ra_hir_ty/src/tests/macros.rs19
-rw-r--r--crates/ra_ide/Cargo.toml2
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs140
-rw-r--r--crates/ra_ide/src/completion/complete_record_literal.rs62
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs2
-rw-r--r--crates/ra_ide/src/expand_macro.rs12
-rw-r--r--crates/ra_ide/src/goto_definition.rs24
-rw-r--r--crates/ra_ide/src/inlay_hints.rs176
-rw-r--r--crates/ra_ide_db/Cargo.toml1
-rw-r--r--crates/ra_ide_db/src/change.rs12
-rw-r--r--crates/ra_ide_db/src/defs.rs31
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs8
-rw-r--r--crates/ra_ide_db/src/search.rs66
-rw-r--r--crates/ra_mbe/src/syntax_bridge.rs361
-rw-r--r--crates/ra_mbe/src/tests.rs62
-rw-r--r--crates/ra_parser/src/grammar/expressions/atom.rs20
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/algo.rs127
-rw-r--r--crates/ra_syntax/src/ast/edit.rs109
-rw-r--r--crates/ra_syntax/src/ast/make.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt30
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.rs1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.txt45
-rw-r--r--crates/rust-analyzer/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/config.rs3
-rw-r--r--crates/rust-analyzer/src/conv.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop.rs1
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs44
-rw-r--r--crates/rust-analyzer/src/req.rs1
59 files changed, 1420 insertions, 582 deletions
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index 85adddb5b..a87f4052a 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -11,7 +11,8 @@ doctest = false
11format-buf = "1.0.0" 11format-buf = "1.0.0"
12join_to_string = "0.1.3" 12join_to_string = "0.1.3"
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.8.2" 14itertools = "0.9.0"
15either = "1.5.3"
15 16
16ra_syntax = { path = "../ra_syntax" } 17ra_syntax = { path = "../ra_syntax" }
17ra_text_edit = { path = "../ra_text_edit" } 18ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index 62182cf03..c3e653299 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -11,6 +11,7 @@ use ra_syntax::{
11use ra_text_edit::TextEditBuilder; 11use ra_text_edit::TextEditBuilder;
12 12
13use crate::{AssistAction, AssistId, AssistLabel, GroupLabel, ResolvedAssist}; 13use crate::{AssistAction, AssistId, AssistLabel, GroupLabel, ResolvedAssist};
14use algo::SyntaxRewriter;
14 15
15#[derive(Clone, Debug)] 16#[derive(Clone, Debug)]
16pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); 17pub(crate) struct Assist(pub(crate) Vec<AssistInfo>);
@@ -234,6 +235,11 @@ impl ActionBuilder {
234 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { 235 pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) {
235 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) 236 algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit)
236 } 237 }
238 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
239 let node = rewriter.rewrite_root().unwrap();
240 let new = rewriter.rewrite(&node);
241 algo::diff(&node, &new).into_text_edit(&mut self.edit)
242 }
237 243
238 fn build(self) -> AssistAction { 244 fn build(self) -> AssistAction {
239 AssistAction { 245 AssistAction {
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 45558c448..52b4c82db 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -3,7 +3,10 @@ use rustc_hash::FxHashMap;
3 3
4use hir::{PathResolution, SemanticsScope}; 4use hir::{PathResolution, SemanticsScope};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::ast::{self, AstNode}; 6use ra_syntax::{
7 algo::SyntaxRewriter,
8 ast::{self, AstNode},
9};
7 10
8pub trait AstTransform<'a> { 11pub trait AstTransform<'a> {
9 fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>; 12 fn get_substitution(&self, node: &ra_syntax::SyntaxNode) -> Option<ra_syntax::SyntaxNode>;
@@ -153,15 +156,14 @@ impl<'a> QualifyPaths<'a> {
153} 156}
154 157
155pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N { 158pub fn apply<'a, N: AstNode>(transformer: &dyn AstTransform<'a>, node: N) -> N {
156 let syntax = node.syntax(); 159 SyntaxRewriter::from_fn(|element| match element {
157 let result = ra_syntax::algo::replace_descendants(syntax, |element| match element {
158 ra_syntax::SyntaxElement::Node(n) => { 160 ra_syntax::SyntaxElement::Node(n) => {
159 let replacement = transformer.get_substitution(&n)?; 161 let replacement = transformer.get_substitution(&n)?;
160 Some(replacement.into()) 162 Some(replacement.into())
161 } 163 }
162 _ => None, 164 _ => None,
163 }); 165 })
164 N::cast(result).unwrap() 166 .rewrite_ast(&node)
165} 167}
166 168
167impl<'a> AstTransform<'a> for QualifyPaths<'a> { 169impl<'a> AstTransform<'a> for QualifyPaths<'a> {
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs
index aef6793e8..62dcb3808 100644
--- a/crates/ra_assists/src/doc_tests/generated.rs
+++ b/crates/ra_assists/src/doc_tests/generated.rs
@@ -275,8 +275,8 @@ enum Action { Move { distance: u32 }, Stop }
275 275
276fn handle(action: Action) { 276fn handle(action: Action) {
277 match action { 277 match action {
278 Action::Move { distance } => (), 278 Action::Move { distance } => {}
279 Action::Stop => (), 279 Action::Stop => {}
280 } 280 }
281} 281}
282"#####, 282"#####,
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index e5920b6f6..722f207e2 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -151,7 +151,7 @@ fn add_missing_impl_members_inner(
151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)), 151 ast::ImplItem::FnDef(def) => ast::ImplItem::FnDef(add_body(def)),
152 _ => it, 152 _ => it,
153 }) 153 })
154 .map(|it| edit::strip_attrs_and_docs(&it)); 154 .map(|it| edit::remove_attrs_and_docs(&it));
155 let new_impl_item_list = impl_item_list.append_items(items); 155 let new_impl_item_list = impl_item_list.append_items(items);
156 let cursor_position = { 156 let cursor_position = {
157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap(); 157 let first_new_item = new_impl_item_list.impl_items().nth(n_existing_items).unwrap();
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index bb280f633..99682e023 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -17,6 +17,7 @@ use crate::{
17 utils::insert_use_statement, 17 utils::insert_use_statement,
18 AssistId, 18 AssistId,
19}; 19};
20use either::Either;
20 21
21// Assist: auto_import 22// Assist: auto_import
22// 23//
@@ -58,6 +59,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
58 group.finish() 59 group.finish()
59} 60}
60 61
62#[derive(Debug)]
61struct AutoImportAssets { 63struct AutoImportAssets {
62 import_candidate: ImportCandidate, 64 import_candidate: ImportCandidate,
63 module_with_name_to_import: Module, 65 module_with_name_to_import: Module,
@@ -127,14 +129,14 @@ impl AutoImportAssets {
127 ImportsLocator::new(db) 129 ImportsLocator::new(db)
128 .find_imports(&self.get_search_query()) 130 .find_imports(&self.get_search_query())
129 .into_iter() 131 .into_iter()
130 .filter_map(|module_def| match &self.import_candidate { 132 .filter_map(|candidate| match &self.import_candidate {
131 ImportCandidate::TraitAssocItem(assoc_item_type, _) => { 133 ImportCandidate::TraitAssocItem(assoc_item_type, _) => {
132 let located_assoc_item = match module_def { 134 let located_assoc_item = match candidate {
133 ModuleDef::Function(located_function) => located_function 135 Either::Left(ModuleDef::Function(located_function)) => located_function
134 .as_assoc_item(db) 136 .as_assoc_item(db)
135 .map(|assoc| assoc.container(db)) 137 .map(|assoc| assoc.container(db))
136 .and_then(Self::assoc_to_trait), 138 .and_then(Self::assoc_to_trait),
137 ModuleDef::Const(located_const) => located_const 139 Either::Left(ModuleDef::Const(located_const)) => located_const
138 .as_assoc_item(db) 140 .as_assoc_item(db)
139 .map(|assoc| assoc.container(db)) 141 .map(|assoc| assoc.container(db))
140 .and_then(Self::assoc_to_trait), 142 .and_then(Self::assoc_to_trait),
@@ -153,10 +155,11 @@ impl AutoImportAssets {
153 |_, assoc| Self::assoc_to_trait(assoc.container(db)), 155 |_, assoc| Self::assoc_to_trait(assoc.container(db)),
154 ) 156 )
155 .map(ModuleDef::from) 157 .map(ModuleDef::from)
158 .map(Either::Left)
156 } 159 }
157 ImportCandidate::TraitMethod(function_callee, _) => { 160 ImportCandidate::TraitMethod(function_callee, _) => {
158 let located_assoc_item = 161 let located_assoc_item =
159 if let ModuleDef::Function(located_function) = module_def { 162 if let Either::Left(ModuleDef::Function(located_function)) = candidate {
160 located_function 163 located_function
161 .as_assoc_item(db) 164 .as_assoc_item(db)
162 .map(|assoc| assoc.container(db)) 165 .map(|assoc| assoc.container(db))
@@ -179,10 +182,18 @@ impl AutoImportAssets {
179 }, 182 },
180 ) 183 )
181 .map(ModuleDef::from) 184 .map(ModuleDef::from)
185 .map(Either::Left)
186 }
187 _ => Some(candidate),
188 })
189 .filter_map(|candidate| match candidate {
190 Either::Left(module_def) => {
191 self.module_with_name_to_import.find_use_path(db, module_def)
192 }
193 Either::Right(macro_def) => {
194 self.module_with_name_to_import.find_use_path(db, macro_def)
182 } 195 }
183 _ => Some(module_def),
184 }) 196 })
185 .filter_map(|module_def| self.module_with_name_to_import.find_use_path(db, module_def))
186 .filter(|use_path| !use_path.segments.is_empty()) 197 .filter(|use_path| !use_path.segments.is_empty())
187 .take(20) 198 .take(20)
188 .collect::<BTreeSet<_>>() 199 .collect::<BTreeSet<_>>()
@@ -440,6 +451,30 @@ mod tests {
440 } 451 }
441 452
442 #[test] 453 #[test]
454 fn macro_import() {
455 check_assist(
456 auto_import,
457 r"
458 //- /lib.rs crate:crate_with_macro
459 #[macro_export]
460 macro_rules! foo {
461 () => ()
462 }
463
464 //- /main.rs crate:main deps:crate_with_macro
465 fn main() {
466 foo<|>
467 }",
468 r"use crate_with_macro::foo;
469
470fn main() {
471 foo<|>
472}
473",
474 );
475 }
476
477 #[test]
443 fn auto_import_target() { 478 fn auto_import_target() {
444 check_assist_target( 479 check_assist_target(
445 auto_import, 480 auto_import,
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 54e0a6c84..cd6d1ee6c 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -2,13 +2,14 @@ use ra_syntax::{
2 ast::{self, NameOwner, VisibilityOwner}, 2 ast::{self, NameOwner, VisibilityOwner},
3 AstNode, 3 AstNode,
4 SyntaxKind::{ 4 SyntaxKind::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, IDENT, MODULE, STRUCT_DEF, TRAIT_DEF, 5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 VISIBILITY, WHITESPACE, 6 WHITESPACE,
7 }, 7 },
8 SyntaxNode, TextUnit, T, 8 SyntaxNode, TextUnit, T,
9}; 9};
10 10
11use crate::{Assist, AssistCtx, AssistId}; 11use crate::{Assist, AssistCtx, AssistId};
12use test_utils::tested_by;
12 13
13// Assist: change_visibility 14// Assist: change_visibility
14// 15//
@@ -47,13 +48,16 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> {
47 } 48 }
48 (vis_offset(&parent), keyword.text_range()) 49 (vis_offset(&parent), keyword.text_range())
49 } else { 50 } else {
50 let ident = ctx.token_at_offset().find(|leaf| leaf.kind() == IDENT)?; 51 let field_name: ast::Name = ctx.find_node_at_offset()?;
51 let field = ident.parent().ancestors().find_map(ast::RecordFieldDef::cast)?; 52 let field = field_name.syntax().ancestors().find_map(ast::RecordFieldDef::cast)?;
52 if field.name()?.syntax().text_range() != ident.text_range() && field.visibility().is_some() 53 if field.name()? != field_name {
53 { 54 tested_by!(change_visibility_field_false_positive);
54 return None; 55 return None;
55 } 56 }
56 (vis_offset(field.syntax()), ident.text_range()) 57 if field.visibility().is_some() {
58 return None;
59 }
60 (vis_offset(field.syntax()), field_name.syntax().text_range())
57 }; 61 };
58 62
59 ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { 63 ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| {
@@ -98,8 +102,11 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> {
98 102
99#[cfg(test)] 103#[cfg(test)]
100mod tests { 104mod tests {
105 use test_utils::covers;
106
107 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target};
108
101 use super::*; 109 use super::*;
102 use crate::helpers::{check_assist, check_assist_target};
103 110
104 #[test] 111 #[test]
105 fn change_visibility_adds_pub_crate_to_items() { 112 fn change_visibility_adds_pub_crate_to_items() {
@@ -120,8 +127,17 @@ mod tests {
120 fn change_visibility_works_with_struct_fields() { 127 fn change_visibility_works_with_struct_fields() {
121 check_assist( 128 check_assist(
122 change_visibility, 129 change_visibility,
123 "struct S { <|>field: u32 }", 130 r"struct S { <|>field: u32 }",
124 "struct S { <|>pub(crate) field: u32 }", 131 r"struct S { <|>pub(crate) field: u32 }",
132 )
133 }
134
135 #[test]
136 fn change_visibility_field_false_positive() {
137 covers!(change_visibility_field_false_positive);
138 check_assist_not_applicable(
139 change_visibility,
140 r"struct S { field: [(); { let <|>x = ();}] }",
125 ) 141 )
126 } 142 }
127 143
@@ -144,7 +160,7 @@ mod tests {
144 fn change_visibility_handles_comment_attrs() { 160 fn change_visibility_handles_comment_attrs() {
145 check_assist( 161 check_assist(
146 change_visibility, 162 change_visibility,
147 " 163 r"
148 /// docs 164 /// docs
149 165
150 // comments 166 // comments
@@ -152,7 +168,7 @@ mod tests {
152 #[derive(Debug)] 168 #[derive(Debug)]
153 <|>struct Foo; 169 <|>struct Foo;
154 ", 170 ",
155 " 171 r"
156 /// docs 172 /// docs
157 173
158 // comments 174 // comments
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 7463b2af7..add82e5b1 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,7 +2,7 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, ModuleDef, Semantics};
6use itertools::Itertools; 6use itertools::Itertools;
7use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
8 8
@@ -30,8 +30,8 @@ use ast::{MatchArm, Pat};
30// 30//
31// fn handle(action: Action) { 31// fn handle(action: Action) {
32// match action { 32// match action {
33// Action::Move { distance } => (), 33// Action::Move { distance } => {}
34// Action::Stop => (), 34// Action::Stop => {}
35// } 35// }
36// } 36// }
37// ``` 37// ```
@@ -57,7 +57,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
57 .into_iter() 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant)) 58 .filter_map(|variant| build_pat(ctx.db, module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect() 61 .collect()
62 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 62 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
63 // Partial fill not currently supported for tuple of enums. 63 // Partial fill not currently supported for tuple of enums.
@@ -86,7 +86,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
86 ast::Pat::from(make::tuple_pat(patterns)) 86 ast::Pat::from(make::tuple_pat(patterns))
87 }) 87 })
88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
89 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 89 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
90 .collect() 90 .collect()
91 } else { 91 } else {
92 return None; 92 return None;
@@ -154,7 +154,7 @@ fn resolve_tuple_of_enum_def(
154} 154}
155 155
156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 156fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, ModuleDef::from(var))?);
158 158
159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though 159 // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
160 let pat: ast::Pat = match var.source(db).value.kind() { 160 let pat: ast::Pat = match var.source(db).value.kind() {
@@ -192,8 +192,8 @@ mod tests {
192 fn main() { 192 fn main() {
193 match A::As<|> { 193 match A::As<|> {
194 A::As, 194 A::As,
195 A::Bs{x,y:Some(_)} => (), 195 A::Bs{x,y:Some(_)} => {}
196 A::Cs(_, Some(_)) => (), 196 A::Cs(_, Some(_)) => {}
197 } 197 }
198 } 198 }
199 "#, 199 "#,
@@ -227,8 +227,8 @@ mod tests {
227 } 227 }
228 fn main() { 228 fn main() {
229 match A::As<|> { 229 match A::As<|> {
230 A::Bs{x,y:Some(_)} => (), 230 A::Bs{x,y:Some(_)} => {}
231 A::Cs(_, Some(_)) => (), 231 A::Cs(_, Some(_)) => {}
232 } 232 }
233 } 233 }
234 "#, 234 "#,
@@ -240,9 +240,9 @@ mod tests {
240 } 240 }
241 fn main() { 241 fn main() {
242 match <|>A::As { 242 match <|>A::As {
243 A::Bs{x,y:Some(_)} => (), 243 A::Bs{x,y:Some(_)} => {}
244 A::Cs(_, Some(_)) => (), 244 A::Cs(_, Some(_)) => {}
245 A::As => (), 245 A::As => {}
246 } 246 }
247 } 247 }
248 "#, 248 "#,
@@ -261,7 +261,7 @@ mod tests {
261 } 261 }
262 fn main() { 262 fn main() {
263 match A::As<|> { 263 match A::As<|> {
264 A::Cs(_) | A::Bs => (), 264 A::Cs(_) | A::Bs => {}
265 } 265 }
266 } 266 }
267 "#, 267 "#,
@@ -273,8 +273,8 @@ mod tests {
273 } 273 }
274 fn main() { 274 fn main() {
275 match <|>A::As { 275 match <|>A::As {
276 A::Cs(_) | A::Bs => (), 276 A::Cs(_) | A::Bs => {}
277 A::As => (), 277 A::As => {}
278 } 278 }
279 } 279 }
280 "#, 280 "#,
@@ -299,8 +299,8 @@ mod tests {
299 } 299 }
300 fn main() { 300 fn main() {
301 match A::As<|> { 301 match A::As<|> {
302 A::Bs if 0 < 1 => (), 302 A::Bs if 0 < 1 => {}
303 A::Ds(_value) => (), 303 A::Ds(_value) => { let x = 1; }
304 A::Es(B::Xs) => (), 304 A::Es(B::Xs) => (),
305 } 305 }
306 } 306 }
@@ -319,11 +319,11 @@ mod tests {
319 } 319 }
320 fn main() { 320 fn main() {
321 match <|>A::As { 321 match <|>A::As {
322 A::Bs if 0 < 1 => (), 322 A::Bs if 0 < 1 => {}
323 A::Ds(_value) => (), 323 A::Ds(_value) => { let x = 1; }
324 A::Es(B::Xs) => (), 324 A::Es(B::Xs) => (),
325 A::As => (), 325 A::As => {}
326 A::Cs => (), 326 A::Cs => {}
327 } 327 }
328 } 328 }
329 "#, 329 "#,
@@ -360,11 +360,11 @@ mod tests {
360 fn main() { 360 fn main() {
361 let a = A::As; 361 let a = A::As;
362 match <|>a { 362 match <|>a {
363 A::As => (), 363 A::As => {}
364 A::Bs => (), 364 A::Bs => {}
365 A::Cs(_) => (), 365 A::Cs(_) => {}
366 A::Ds(_, _) => (), 366 A::Ds(_, _) => {}
367 A::Es { x, y } => (), 367 A::Es { x, y } => {}
368 } 368 }
369 } 369 }
370 "#, 370 "#,
@@ -405,10 +405,10 @@ mod tests {
405 let a = A::One; 405 let a = A::One;
406 let b = B::One; 406 let b = B::One;
407 match <|>(a, b) { 407 match <|>(a, b) {
408 (A::One, B::One) => (), 408 (A::One, B::One) => {}
409 (A::One, B::Two) => (), 409 (A::One, B::Two) => {}
410 (A::Two, B::One) => (), 410 (A::Two, B::One) => {}
411 (A::Two, B::Two) => (), 411 (A::Two, B::Two) => {}
412 } 412 }
413 } 413 }
414 "#, 414 "#,
@@ -449,10 +449,10 @@ mod tests {
449 let a = A::One; 449 let a = A::One;
450 let b = B::One; 450 let b = B::One;
451 match <|>(&a, &b) { 451 match <|>(&a, &b) {
452 (A::One, B::One) => (), 452 (A::One, B::One) => {}
453 (A::One, B::Two) => (), 453 (A::One, B::Two) => {}
454 (A::Two, B::One) => (), 454 (A::Two, B::One) => {}
455 (A::Two, B::Two) => (), 455 (A::Two, B::Two) => {}
456 } 456 }
457 } 457 }
458 "#, 458 "#,
@@ -477,7 +477,7 @@ mod tests {
477 let a = A::One; 477 let a = A::One;
478 let b = B::One; 478 let b = B::One;
479 match (a<|>, b) { 479 match (a<|>, b) {
480 (A::Two, B::One) => (), 480 (A::Two, B::One) => {}
481 } 481 }
482 } 482 }
483 "#, 483 "#,
@@ -502,10 +502,10 @@ mod tests {
502 let a = A::One; 502 let a = A::One;
503 let b = B::One; 503 let b = B::One;
504 match (a<|>, b) { 504 match (a<|>, b) {
505 (A::Two, B::One) => (), 505 (A::Two, B::One) => {}
506 (A::One, B::One) => (), 506 (A::One, B::One) => {}
507 (A::One, B::Two) => (), 507 (A::One, B::Two) => {}
508 (A::Two, B::Two) => (), 508 (A::Two, B::Two) => {}
509 } 509 }
510 } 510 }
511 "#, 511 "#,
@@ -555,7 +555,7 @@ mod tests {
555 555
556 fn foo(a: &A) { 556 fn foo(a: &A) {
557 match <|>a { 557 match <|>a {
558 A::As => (), 558 A::As => {}
559 } 559 }
560 } 560 }
561 "#, 561 "#,
@@ -580,7 +580,7 @@ mod tests {
580 580
581 fn foo(a: &mut A) { 581 fn foo(a: &mut A) {
582 match <|>a { 582 match <|>a {
583 A::Es { x, y } => (), 583 A::Es { x, y } => {}
584 } 584 }
585 } 585 }
586 "#, 586 "#,
@@ -611,7 +611,7 @@ mod tests {
611 611
612 fn main() { 612 fn main() {
613 match E::X { 613 match E::X {
614 <|>_ => {}, 614 <|>_ => {}
615 } 615 }
616 } 616 }
617 "#, 617 "#,
@@ -620,8 +620,8 @@ mod tests {
620 620
621 fn main() { 621 fn main() {
622 match <|>E::X { 622 match <|>E::X {
623 E::X => (), 623 E::X => {}
624 E::Y => (), 624 E::Y => {}
625 } 625 }
626 } 626 }
627 "#, 627 "#,
@@ -648,8 +648,8 @@ mod tests {
648 648
649 fn main() { 649 fn main() {
650 match <|>X { 650 match <|>X {
651 X => (), 651 X => {}
652 foo::E::Y => (), 652 foo::E::Y => {}
653 } 653 }
654 } 654 }
655 "#, 655 "#,
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 89bc975bd..9c57d1e30 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -1,9 +1,9 @@
1use std::iter::successors; 1use std::iter::successors;
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 algo::neighbor, 4 algo::{neighbor, SyntaxRewriter},
5 ast::{self, edit::AstNodeEdit, make}, 5 ast::{self, edit::AstNodeEdit, make},
6 AstNode, AstToken, Direction, InsertPosition, SyntaxElement, TextRange, T, 6 AstNode, Direction, InsertPosition, SyntaxElement, T,
7}; 7};
8 8
9use crate::{Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
@@ -22,9 +22,10 @@ use crate::{Assist, AssistCtx, AssistId};
22// ``` 22// ```
23pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> { 23pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
24 let tree: ast::UseTree = ctx.find_node_at_offset()?; 24 let tree: ast::UseTree = ctx.find_node_at_offset()?;
25 let (new_tree, to_delete) = if let Some(use_item) = 25 let mut rewriter = SyntaxRewriter::default();
26 tree.syntax().parent().and_then(ast::UseItem::cast) 26 let mut offset = ctx.frange.range.start();
27 { 27
28 if let Some(use_item) = tree.syntax().parent().and_then(ast::UseItem::cast) {
28 let (merged, to_delete) = next_prev() 29 let (merged, to_delete) = next_prev()
29 .filter_map(|dir| neighbor(&use_item, dir)) 30 .filter_map(|dir| neighbor(&use_item, dir))
30 .filter_map(|it| Some((it.clone(), it.use_tree()?))) 31 .filter_map(|it| Some((it.clone(), it.use_tree()?)))
@@ -32,42 +33,28 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
32 Some((try_merge_trees(&tree, &use_tree)?, use_item.clone())) 33 Some((try_merge_trees(&tree, &use_tree)?, use_item.clone()))
33 })?; 34 })?;
34 35
35 let mut range = to_delete.syntax().text_range(); 36 rewriter.replace_ast(&tree, &merged);
36 let next_ws = to_delete 37 rewriter += to_delete.remove();
37 .syntax() 38
38 .next_sibling_or_token() 39 if to_delete.syntax().text_range().end() < offset {
39 .and_then(|it| it.into_token()) 40 offset -= to_delete.syntax().text_range().len();
40 .and_then(ast::Whitespace::cast);
41 if let Some(ws) = next_ws {
42 range = range.extend_to(&ws.syntax().text_range())
43 } 41 }
44 (merged, range)
45 } else { 42 } else {
46 let (merged, to_delete) = next_prev() 43 let (merged, to_delete) = next_prev()
47 .filter_map(|dir| neighbor(&tree, dir)) 44 .filter_map(|dir| neighbor(&tree, dir))
48 .find_map(|use_tree| Some((try_merge_trees(&tree, &use_tree)?, use_tree.clone())))?; 45 .find_map(|use_tree| Some((try_merge_trees(&tree, &use_tree)?, use_tree.clone())))?;
49 46
50 let mut range = to_delete.syntax().text_range(); 47 rewriter.replace_ast(&tree, &merged);
51 if let Some((dir, nb)) = next_prev().find_map(|dir| Some((dir, neighbor(&to_delete, dir)?))) 48 rewriter += to_delete.remove();
52 { 49
53 let nb_range = nb.syntax().text_range(); 50 if to_delete.syntax().text_range().end() < offset {
54 if dir == Direction::Prev { 51 offset -= to_delete.syntax().text_range().len();
55 range = TextRange::from_to(nb_range.end(), range.end());
56 } else {
57 range = TextRange::from_to(range.start(), nb_range.start());
58 }
59 } 52 }
60 (merged, range)
61 }; 53 };
62 54
63 let mut offset = ctx.frange.range.start();
64 ctx.add_assist(AssistId("merge_imports"), "Merge imports", |edit| { 55 ctx.add_assist(AssistId("merge_imports"), "Merge imports", |edit| {
65 edit.replace_ast(tree, new_tree); 56 edit.rewrite(rewriter);
66 edit.delete(to_delete); 57 // FIXME: we only need because our diff is imprecise
67
68 if to_delete.end() <= offset {
69 offset -= to_delete.len();
70 }
71 edit.set_cursor(offset); 58 edit.set_cursor(offset);
72 }) 59 })
73} 60}
@@ -156,7 +143,7 @@ use std::fmt::Debug;
156use std::fmt<|>::Display; 143use std::fmt<|>::Display;
157", 144",
158 r" 145 r"
159use std::fmt<|>::{Display, Debug}; 146use std::fmt:<|>:{Display, Debug};
160", 147",
161 ); 148 );
162 } 149 }
@@ -178,7 +165,57 @@ use std::{fmt<|>::{Debug, Display}};
178use std::{fmt::Debug, fmt<|>::Display}; 165use std::{fmt::Debug, fmt<|>::Display};
179", 166",
180 r" 167 r"
181use std::{fmt<|>::{Display, Debug}}; 168use std::{fmt::<|>{Display, Debug}};
169",
170 );
171 }
172
173 #[test]
174 fn removes_just_enough_whitespace() {
175 check_assist(
176 merge_imports,
177 r"
178use foo<|>::bar;
179use foo::baz;
180
181/// Doc comment
182",
183 r"
184use foo<|>::{bar, baz};
185
186/// Doc comment
187",
188 );
189 }
190
191 #[test]
192 fn works_with_trailing_comma() {
193 check_assist(
194 merge_imports,
195 r"
196use {
197 foo<|>::bar,
198 foo::baz,
199};
200",
201 r"
202use {
203 foo<|>::{bar, baz},
204};
205",
206 );
207 check_assist(
208 merge_imports,
209 r"
210use {
211 foo::baz,
212 foo<|>::bar,
213};
214",
215 r"
216use {
217 foo::{bar<|>, baz},
218};
182", 219",
183 ); 220 );
184 } 221 }
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index b8704ea7d..bcc9b3f10 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -165,7 +165,6 @@ mod helpers {
165 165
166 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 166 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
167 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 167 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
168 use ra_syntax::TextRange;
169 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset}; 168 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
170 169
171 use crate::{AssistCtx, AssistHandler}; 170 use crate::{AssistCtx, AssistHandler};
@@ -175,8 +174,7 @@ mod helpers {
175 let (mut db, file_id) = RootDatabase::with_single_file(text); 174 let (mut db, file_id) = RootDatabase::with_single_file(text);
176 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`, 175 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`,
177 // but it looks like this might need specialization? :( 176 // but it looks like this might need specialization? :(
178 let local_roots = vec![db.file_source_root(file_id)]; 177 db.set_local_roots(Arc::new(vec![db.file_source_root(file_id)]));
179 db.set_local_roots(Arc::new(local_roots));
180 (db, file_id) 178 (db, file_id)
181 } 179 }
182 180
@@ -206,11 +204,24 @@ mod helpers {
206 } 204 }
207 205
208 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) { 206 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) {
209 let (range_or_offset, before) = extract_range_or_offset(before); 207 let (text_without_caret, file_with_caret_id, range_or_offset, db) =
210 let range: TextRange = range_or_offset.into(); 208 if before.contains("//-") {
209 let (mut db, position) = RootDatabase::with_position(before);
210 db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)]));
211 (
212 db.file_text(position.file_id).as_ref().to_owned(),
213 position.file_id,
214 RangeOrOffset::Offset(position.offset),
215 db,
216 )
217 } else {
218 let (range_or_offset, text_without_caret) = extract_range_or_offset(before);
219 let (db, file_id) = with_single_file(&text_without_caret);
220 (text_without_caret, file_id, range_or_offset, db)
221 };
222
223 let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() };
211 224
212 let (db, file_id) = with_single_file(&before);
213 let frange = FileRange { file_id, range };
214 let sema = Semantics::new(&db); 225 let sema = Semantics::new(&db);
215 let assist_ctx = AssistCtx::new(&sema, frange, true); 226 let assist_ctx = AssistCtx::new(&sema, frange, true);
216 227
@@ -218,7 +229,7 @@ mod helpers {
218 (Some(assist), ExpectedResult::After(after)) => { 229 (Some(assist), ExpectedResult::After(after)) => {
219 let action = assist.0[0].action.clone().unwrap(); 230 let action = assist.0[0].action.clone().unwrap();
220 231
221 let mut actual = action.edit.apply(&before); 232 let mut actual = action.edit.apply(&text_without_caret);
222 match action.cursor_position { 233 match action.cursor_position {
223 None => { 234 None => {
224 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { 235 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
@@ -237,7 +248,7 @@ mod helpers {
237 (Some(assist), ExpectedResult::Target(target)) => { 248 (Some(assist), ExpectedResult::Target(target)) => {
238 let action = assist.0[0].action.clone().unwrap(); 249 let action = assist.0[0].action.clone().unwrap();
239 let range = action.target.expect("expected target on action"); 250 let range = action.target.expect("expected target on action");
240 assert_eq_text!(&before[range], target); 251 assert_eq_text!(&text_without_caret[range], target);
241 } 252 }
242 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), 253 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
243 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => { 254 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {
diff --git a/crates/ra_assists/src/marks.rs b/crates/ra_assists/src/marks.rs
index 22404ee80..6c2a2b8b6 100644
--- a/crates/ra_assists/src/marks.rs
+++ b/crates/ra_assists/src/marks.rs
@@ -7,4 +7,5 @@ test_utils::marks![
7 not_applicable_outside_of_bind_pat 7 not_applicable_outside_of_bind_pat
8 test_not_inline_mut_variable 8 test_not_inline_mut_variable
9 test_not_applicable_if_variable_unused 9 test_not_applicable_if_variable_unused
10 change_visibility_field_false_positive
10]; 11];
diff --git a/crates/ra_fmt/Cargo.toml b/crates/ra_fmt/Cargo.toml
index ea9befeaf..e9d057afc 100644
--- a/crates/ra_fmt/Cargo.toml
+++ b/crates/ra_fmt/Cargo.toml
@@ -9,6 +9,6 @@ publish = false
9doctest = false 9doctest = false
10 10
11[dependencies] 11[dependencies]
12itertools = "0.8.2" 12itertools = "0.9.0"
13 13
14ra_syntax = { path = "../ra_syntax" } 14ra_syntax = { path = "../ra_syntax" }
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 42193b492..ba7b39a19 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -13,7 +13,7 @@ rustc-hash = "1.1.0"
13either = "1.5.3" 13either = "1.5.3"
14arrayvec = "0.5.1" 14arrayvec = "0.5.1"
15 15
16itertools = "0.8.2" 16itertools = "0.9.0"
17 17
18ra_syntax = { path = "../ra_syntax" } 18ra_syntax = { path = "../ra_syntax" }
19ra_db = { path = "../ra_db" } 19ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e91abf6f5..cd2a8fc62 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -33,7 +33,11 @@ use ra_syntax::{
33}; 33};
34use rustc_hash::FxHashSet; 34use rustc_hash::FxHashSet;
35 35
36use crate::{db::HirDatabase, has_source::HasSource, CallableDef, HirDisplay, InFile, Name}; 36use crate::{
37 db::{DefDatabase, HirDatabase},
38 has_source::HasSource,
39 CallableDef, HirDisplay, InFile, Name,
40};
37 41
38/// hir::Crate describes a single crate. It's the main interface with which 42/// hir::Crate describes a single crate. It's the main interface with which
39/// a crate's dependencies interact. Mostly, it should be just a proxy for the 43/// a crate's dependencies interact. Mostly, it should be just a proxy for the
@@ -230,6 +234,10 @@ impl Module {
230 .collect() 234 .collect()
231 } 235 }
232 236
237 pub fn visibility_of(self, db: &dyn HirDatabase, def: &ModuleDef) -> Option<Visibility> {
238 db.crate_def_map(self.id.krate)[self.id.local_id].scope.visibility_of(def.clone().into())
239 }
240
233 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) { 241 pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
234 let _p = profile("Module::diagnostics"); 242 let _p = profile("Module::diagnostics");
235 let crate_def_map = db.crate_def_map(self.id.krate); 243 let crate_def_map = db.crate_def_map(self.id.krate);
@@ -274,20 +282,10 @@ impl Module {
274 /// this module, if possible. 282 /// this module, if possible.
275 pub fn find_use_path( 283 pub fn find_use_path(
276 self, 284 self,
277 db: &dyn HirDatabase, 285 db: &dyn DefDatabase,
278 item: ModuleDef, 286 item: impl Into<ItemInNs>,
279 ) -> Option<hir_def::path::ModPath> { 287 ) -> Option<hir_def::path::ModPath> {
280 // FIXME expose namespace choice 288 hir_def::find_path::find_path(db, item.into(), self.into())
281 hir_def::find_path::find_path(db.upcast(), determine_item_namespace(item), self.into())
282 }
283}
284
285fn determine_item_namespace(module_def: ModuleDef) -> ItemInNs {
286 match module_def {
287 ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
288 ItemInNs::Values(module_def.into())
289 }
290 _ => ItemInNs::Types(module_def.into()),
291 } 289 }
292} 290}
293 291
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index fcba95091..ec931b34f 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -10,15 +10,16 @@ pub use hir_def::db::{
10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery, 10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
11}; 11};
12pub use hir_expand::db::{ 12pub use hir_expand::db::{
13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternMacroQuery, MacroArgQuery, MacroDefQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternEagerExpansionQuery, InternMacroQuery,
14 MacroExpandQuery, ParseMacroQuery, 14 MacroArgQuery, MacroDefQuery, MacroExpandQuery, ParseMacroQuery,
15}; 15};
16pub use hir_ty::db::{ 16pub use hir_ty::db::{
17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery, 17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, FieldTypesQuery,
18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase, 18 GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery, HirDatabase,
19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery, 19 HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery, ImplsForTraitQuery,
20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery, 20 ImplsInCrateQuery, InferQueryQuery, InternAssocTyValueQuery, InternChalkImplQuery,
21 InternTypeCtorQuery, StructDatumQuery, TraitDatumQuery, TraitSolveQuery, TyQuery, ValueTyQuery, 21 InternTypeCtorQuery, InternTypeParamIdQuery, StructDatumQuery, TraitDatumQuery,
22 TraitSolveQuery, TyQuery, ValueTyQuery,
22}; 23};
23 24
24#[test] 25#[test]
diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs
index c179b13c6..62fb52e72 100644
--- a/crates/ra_hir/src/from_id.rs
+++ b/crates/ra_hir/src/from_id.rs
@@ -9,8 +9,8 @@ use hir_def::{
9}; 9};
10 10
11use crate::{ 11use crate::{
12 Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, Local, ModuleDef, StructField, 12 code_model::ItemInNs, Adt, AssocItem, AttrDef, DefWithBody, EnumVariant, GenericDef, Local,
13 VariantDef, 13 MacroDef, ModuleDef, StructField, VariantDef,
14}; 14};
15 15
16macro_rules! from_id { 16macro_rules! from_id {
@@ -228,3 +228,20 @@ impl From<(DefWithBodyId, PatId)> for Local {
228 Local { parent, pat_id } 228 Local { parent, pat_id }
229 } 229 }
230} 230}
231
232impl From<MacroDef> for ItemInNs {
233 fn from(macro_def: MacroDef) -> Self {
234 ItemInNs::Macros(macro_def.into())
235 }
236}
237
238impl From<ModuleDef> for ItemInNs {
239 fn from(module_def: ModuleDef) -> Self {
240 match module_def {
241 ModuleDef::Static(_) | ModuleDef::Const(_) | ModuleDef::Function(_) => {
242 ItemInNs::Values(module_def.into())
243 }
244 _ => ItemInNs::Types(module_def.into()),
245 }
246 }
247}
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index 713d45f48..5af7e5d6d 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -54,7 +54,7 @@ pub use crate::{
54 Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency, 54 Adt, AsAssocItem, AssocItem, AssocItemContainer, AttrDef, Const, Crate, CrateDependency,
55 DefWithBody, Docs, Enum, EnumVariant, FieldSource, Function, GenericDef, HasAttrs, 55 DefWithBody, Docs, Enum, EnumVariant, FieldSource, Function, GenericDef, HasAttrs,
56 HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct, 56 HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, ScopeDef, Static, Struct,
57 StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, 57 StructField, Trait, Type, TypeAlias, TypeParam, Union, VariantDef, Visibility,
58 }, 58 },
59 has_source::HasSource, 59 has_source::HasSource,
60 semantics::{original_range, PathResolution, Semantics, SemanticsScope}, 60 semantics::{original_range, PathResolution, Semantics, SemanticsScope},
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index d982f6ffa..16a5fe968 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -173,11 +173,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
173 } 173 }
174 174
175 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { 175 pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
176 self.analyze(call.syntax()).resolve_method_call(call) 176 self.analyze(call.syntax()).resolve_method_call(self.db, call)
177 } 177 }
178 178
179 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> { 179 pub fn resolve_field(&self, field: &ast::FieldExpr) -> Option<StructField> {
180 self.analyze(field.syntax()).resolve_field(field) 180 self.analyze(field.syntax()).resolve_field(self.db, field)
181 } 181 }
182 182
183 pub fn resolve_record_field( 183 pub fn resolve_record_field(
@@ -188,7 +188,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
188 } 188 }
189 189
190 pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> { 190 pub fn resolve_record_literal(&self, record_lit: &ast::RecordLit) -> Option<VariantDef> {
191 self.analyze(record_lit.syntax()).resolve_record_literal(record_lit) 191 self.analyze(record_lit.syntax()).resolve_record_literal(self.db, record_lit)
192 } 192 }
193 193
194 pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<VariantDef> { 194 pub fn resolve_record_pattern(&self, record_pat: &ast::RecordPat) -> Option<VariantDef> {
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 10c12c910..815ca158c 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -78,9 +78,15 @@ impl SourceAnalyzer {
78 } 78 }
79 } 79 }
80 80
81 fn expr_id(&self, expr: &ast::Expr) -> Option<ExprId> { 81 fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprId> {
82 let src = InFile { file_id: self.file_id, value: expr }; 82 let src = match expr {
83 self.body_source_map.as_ref()?.node_expr(src) 83 ast::Expr::MacroCall(call) => {
84 self.expand_expr(db, InFile::new(self.file_id, call.clone()))?
85 }
86 _ => InFile::new(self.file_id, expr.clone()),
87 };
88 let sm = self.body_source_map.as_ref()?;
89 sm.node_expr(src.as_ref())
84 } 90 }
85 91
86 fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> { 92 fn pat_id(&self, pat: &ast::Pat) -> Option<PatId> {
@@ -104,14 +110,7 @@ impl SourceAnalyzer {
104 } 110 }
105 111
106 pub(crate) fn type_of(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> { 112 pub(crate) fn type_of(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<Type> {
107 let expr_id = match expr { 113 let expr_id = self.expr_id(db, expr)?;
108 ast::Expr::MacroCall(call) => {
109 let expr = self.expand_expr(db, InFile::new(self.file_id, call.clone()))?;
110 self.body_source_map.as_ref()?.node_expr(expr.as_ref())
111 }
112 _ => self.expr_id(expr),
113 }?;
114
115 let ty = self.infer.as_ref()?[expr_id].clone(); 114 let ty = self.infer.as_ref()?[expr_id].clone();
116 Type::new_with_resolver(db, &self.resolver, ty) 115 Type::new_with_resolver(db, &self.resolver, ty)
117 } 116 }
@@ -122,13 +121,21 @@ impl SourceAnalyzer {
122 Type::new_with_resolver(db, &self.resolver, ty) 121 Type::new_with_resolver(db, &self.resolver, ty)
123 } 122 }
124 123
125 pub(crate) fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> { 124 pub(crate) fn resolve_method_call(
126 let expr_id = self.expr_id(&call.clone().into())?; 125 &self,
126 db: &dyn HirDatabase,
127 call: &ast::MethodCallExpr,
128 ) -> Option<Function> {
129 let expr_id = self.expr_id(db, &call.clone().into())?;
127 self.infer.as_ref()?.method_resolution(expr_id).map(Function::from) 130 self.infer.as_ref()?.method_resolution(expr_id).map(Function::from)
128 } 131 }
129 132
130 pub(crate) fn resolve_field(&self, field: &ast::FieldExpr) -> Option<crate::StructField> { 133 pub(crate) fn resolve_field(
131 let expr_id = self.expr_id(&field.clone().into())?; 134 &self,
135 db: &dyn HirDatabase,
136 field: &ast::FieldExpr,
137 ) -> Option<crate::StructField> {
138 let expr_id = self.expr_id(db, &field.clone().into())?;
132 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into()) 139 self.infer.as_ref()?.field_resolution(expr_id).map(|it| it.into())
133 } 140 }
134 141
@@ -138,7 +145,7 @@ impl SourceAnalyzer {
138 field: &ast::RecordField, 145 field: &ast::RecordField,
139 ) -> Option<(crate::StructField, Option<Local>)> { 146 ) -> Option<(crate::StructField, Option<Local>)> {
140 let (expr_id, local) = match field.expr() { 147 let (expr_id, local) = match field.expr() {
141 Some(it) => (self.expr_id(&it)?, None), 148 Some(it) => (self.expr_id(db, &it)?, None),
142 None => { 149 None => {
143 let src = InFile { file_id: self.file_id, value: field }; 150 let src = InFile { file_id: self.file_id, value: field };
144 let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?; 151 let expr_id = self.body_source_map.as_ref()?.field_init_shorthand_expr(src)?;
@@ -159,9 +166,10 @@ impl SourceAnalyzer {
159 166
160 pub(crate) fn resolve_record_literal( 167 pub(crate) fn resolve_record_literal(
161 &self, 168 &self,
169 db: &dyn HirDatabase,
162 record_lit: &ast::RecordLit, 170 record_lit: &ast::RecordLit,
163 ) -> Option<crate::VariantDef> { 171 ) -> Option<crate::VariantDef> {
164 let expr_id = self.expr_id(&record_lit.clone().into())?; 172 let expr_id = self.expr_id(db, &record_lit.clone().into())?;
165 self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into()) 173 self.infer.as_ref()?.variant_resolution_for_expr(expr_id).map(|it| it.into())
166 } 174 }
167 175
@@ -207,7 +215,7 @@ impl SourceAnalyzer {
207 path: &ast::Path, 215 path: &ast::Path,
208 ) -> Option<PathResolution> { 216 ) -> Option<PathResolution> {
209 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) { 217 if let Some(path_expr) = path.syntax().parent().and_then(ast::PathExpr::cast) {
210 let expr_id = self.expr_id(&path_expr.into())?; 218 let expr_id = self.expr_id(db, &path_expr.into())?;
211 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) { 219 if let Some(assoc) = self.infer.as_ref()?.assoc_resolutions_for_expr(expr_id) {
212 return Some(PathResolution::AssocItem(assoc.into())); 220 return Some(PathResolution::AssocItem(assoc.into()));
213 } 221 }
diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs
index 7f8c1ea21..5dc7395f5 100644
--- a/crates/ra_hir_def/src/db.rs
+++ b/crates/ra_hir_def/src/db.rs
@@ -48,6 +48,7 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
48 fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>; 48 fn raw_items(&self, file_id: HirFileId) -> Arc<RawItems>;
49 49
50 #[salsa::invoke(crate_def_map_wait)] 50 #[salsa::invoke(crate_def_map_wait)]
51 #[salsa::transparent]
51 fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>; 52 fn crate_def_map(&self, krate: CrateId) -> Arc<CrateDefMap>;
52 53
53 #[salsa::invoke(CrateDefMap::crate_def_map_query)] 54 #[salsa::invoke(CrateDefMap::crate_def_map_query)]
@@ -109,12 +110,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
109 fn documentation(&self, def: AttrDefId) -> Option<Documentation>; 110 fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
110} 111}
111 112
112// impl<T: DefDatabase> Upcast<dyn AstDatabase> for T {
113// fn upcast(&self) -> &dyn AstDatabase {
114// &*self
115// }
116// }
117
118fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> { 113fn crate_def_map_wait(db: &impl DefDatabase, krate: CrateId) -> Arc<CrateDefMap> {
119 let _p = profile("crate_def_map:wait"); 114 let _p = profile("crate_def_map:wait");
120 db.crate_def_map_query(krate) 115 db.crate_def_map_query(krate)
diff --git a/crates/ra_hir_def/src/item_scope.rs b/crates/ra_hir_def/src/item_scope.rs
index 5e943b780..259b9ff03 100644
--- a/crates/ra_hir_def/src/item_scope.rs
+++ b/crates/ra_hir_def/src/item_scope.rs
@@ -68,6 +68,12 @@ impl ItemScope {
68 self.impls.iter().copied() 68 self.impls.iter().copied()
69 } 69 }
70 70
71 pub fn visibility_of(&self, def: ModuleDefId) -> Option<Visibility> {
72 self.name_of(ItemInNs::Types(def))
73 .or_else(|| self.name_of(ItemInNs::Values(def)))
74 .map(|(_, v)| v)
75 }
76
71 /// Iterate over all module scoped macros 77 /// Iterate over all module scoped macros
72 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a { 78 pub(crate) fn macros<'a>(&'a self) -> impl Iterator<Item = (&'a Name, MacroDefId)> + 'a {
73 self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_))) 79 self.visible.iter().filter_map(|(name, def)| def.take_macros().map(|macro_| (name, macro_)))
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index 516dd773e..bd32ac20a 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -475,6 +475,12 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
475 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 475 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
476 ) -> Option<MacroCallId> { 476 ) -> Option<MacroCallId> {
477 let def = resolver(self.path.clone())?; 477 let def = resolver(self.path.clone())?;
478 Some(def.as_lazy_macro(db.upcast(), MacroCallKind::Attr(self.ast_id)).into()) 478 Some(
479 def.as_lazy_macro(
480 db.upcast(),
481 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
482 )
483 .into(),
484 )
479 } 485 }
480} 486}
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 5b292c250..9c125f32f 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -7,6 +7,7 @@ use hir_expand::{
7 builtin_derive::find_builtin_derive, 7 builtin_derive::find_builtin_derive,
8 builtin_macro::find_builtin_macro, 8 builtin_macro::find_builtin_macro,
9 name::{name, AsName, Name}, 9 name::{name, AsName, Name},
10 proc_macro::ProcMacroExpander,
10 HirFileId, MacroCallId, MacroDefId, MacroDefKind, 11 HirFileId, MacroCallId, MacroDefId, MacroDefKind,
11}; 12};
12use ra_cfg::CfgOptions; 13use ra_cfg::CfgOptions;
@@ -64,6 +65,9 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr
64 unexpanded_attribute_macros: Vec::new(), 65 unexpanded_attribute_macros: Vec::new(),
65 mod_dirs: FxHashMap::default(), 66 mod_dirs: FxHashMap::default(),
66 cfg_options, 67 cfg_options,
68
69 // FIXME: pass proc-macro from crate-graph
70 proc_macros: Default::default(),
67 }; 71 };
68 collector.collect(); 72 collector.collect();
69 collector.finish() 73 collector.finish()
@@ -122,6 +126,7 @@ struct DefCollector<'a> {
122 unexpanded_attribute_macros: Vec<DeriveDirective>, 126 unexpanded_attribute_macros: Vec<DeriveDirective>,
123 mod_dirs: FxHashMap<LocalModuleId, ModDir>, 127 mod_dirs: FxHashMap<LocalModuleId, ModDir>,
124 cfg_options: &'a CfgOptions, 128 cfg_options: &'a CfgOptions,
129 proc_macros: Vec<(Name, ProcMacroExpander)>,
125} 130}
126 131
127impl DefCollector<'_> { 132impl DefCollector<'_> {
@@ -177,6 +182,24 @@ impl DefCollector<'_> {
177 for directive in unresolved_imports { 182 for directive in unresolved_imports {
178 self.record_resolved_import(&directive) 183 self.record_resolved_import(&directive)
179 } 184 }
185
186 // Record proc-macros
187 self.collect_proc_macro();
188 }
189
190 fn collect_proc_macro(&mut self) {
191 let proc_macros = std::mem::take(&mut self.proc_macros);
192 for (name, expander) in proc_macros {
193 let krate = self.def_map.krate;
194
195 let macro_id = MacroDefId {
196 ast_id: None,
197 krate: Some(krate),
198 kind: MacroDefKind::CustomDerive(expander),
199 };
200
201 self.define_proc_macro(name.clone(), macro_id);
202 }
180 } 203 }
181 204
182 /// Define a macro with `macro_rules`. 205 /// Define a macro with `macro_rules`.
@@ -238,6 +261,18 @@ impl DefCollector<'_> {
238 self.def_map.modules[module_id].scope.define_legacy_macro(name, mac); 261 self.def_map.modules[module_id].scope.define_legacy_macro(name, mac);
239 } 262 }
240 263
264 /// Define a proc macro
265 ///
266 /// A proc macro is similar to normal macro scope, but it would not visiable in legacy textual scoped.
267 /// And unconditionally exported.
268 fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) {
269 self.update(
270 self.def_map.root,
271 &[(name, PerNs::macros(macro_, Visibility::Public))],
272 Visibility::Public,
273 );
274 }
275
241 /// Import macros from `#[macro_use] extern crate`. 276 /// Import macros from `#[macro_use] extern crate`.
242 fn import_macros_from_extern_crate( 277 fn import_macros_from_extern_crate(
243 &mut self, 278 &mut self,
@@ -537,8 +572,9 @@ impl DefCollector<'_> {
537 true 572 true
538 }); 573 });
539 attribute_macros.retain(|directive| { 574 attribute_macros.retain(|directive| {
540 if let Some(call_id) = 575 if let Some(call_id) = directive
541 directive.ast_id.as_call_id(self.db, |path| self.resolve_attribute_macro(&path)) 576 .ast_id
577 .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path))
542 { 578 {
543 resolved.push((directive.module_id, call_id, 0)); 579 resolved.push((directive.module_id, call_id, 0));
544 res = ReachedFixedPoint::No; 580 res = ReachedFixedPoint::No;
@@ -562,9 +598,11 @@ impl DefCollector<'_> {
562 res 598 res
563 } 599 }
564 600
565 fn resolve_attribute_macro(&self, path: &ModPath) -> Option<MacroDefId> { 601 fn resolve_attribute_macro(
566 // FIXME this is currently super hacky, just enough to support the 602 &self,
567 // built-in derives 603 directive: &DeriveDirective,
604 path: &ModPath,
605 ) -> Option<MacroDefId> {
568 if let Some(name) = path.as_ident() { 606 if let Some(name) = path.as_ident() {
569 // FIXME this should actually be handled with the normal name 607 // FIXME this should actually be handled with the normal name
570 // resolution; the std lib defines built-in stubs for the derives, 608 // resolution; the std lib defines built-in stubs for the derives,
@@ -573,7 +611,15 @@ impl DefCollector<'_> {
573 return Some(def_id); 611 return Some(def_id);
574 } 612 }
575 } 613 }
576 None 614 let resolved_res = self.def_map.resolve_path_fp_with_macro(
615 self.db,
616 ResolveMode::Other,
617 directive.module_id,
618 &path,
619 BuiltinShadowMode::Module,
620 );
621
622 resolved_res.resolved_def.take_macros()
577 } 623 }
578 624
579 fn collect_macro_expansion( 625 fn collect_macro_expansion(
@@ -776,7 +822,6 @@ impl ModCollector<'_, '_> {
776 // FIXME: check attrs to see if this is an attribute macro invocation; 822 // FIXME: check attrs to see if this is an attribute macro invocation;
777 // in which case we don't add the invocation, just a single attribute 823 // in which case we don't add the invocation, just a single attribute
778 // macro invocation 824 // macro invocation
779
780 self.collect_derives(attrs, def); 825 self.collect_derives(attrs, def);
781 826
782 let name = def.name.clone(); 827 let name = def.name.clone();
@@ -955,6 +1000,7 @@ mod tests {
955 unexpanded_attribute_macros: Vec::new(), 1000 unexpanded_attribute_macros: Vec::new(),
956 mod_dirs: FxHashMap::default(), 1001 mod_dirs: FxHashMap::default(),
957 cfg_options: &CfgOptions::default(), 1002 cfg_options: &CfgOptions::default(),
1003 proc_macros: Default::default(),
958 }; 1004 };
959 collector.collect(); 1005 collector.collect();
960 collector.def_map 1006 collector.def_map
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index a5b50a832..79aea5806 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -229,9 +229,12 @@ fn partial_ord_expand(
229mod tests { 229mod tests {
230 use super::*; 230 use super::*;
231 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; 231 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
232 use name::{known, Name};
232 use ra_db::{fixture::WithFixture, SourceDatabase}; 233 use ra_db::{fixture::WithFixture, SourceDatabase};
233 234
234 fn expand_builtin_derive(s: &str, expander: BuiltinDeriveExpander) -> String { 235 fn expand_builtin_derive(s: &str, name: Name) -> String {
236 let def = find_builtin_derive(&name).unwrap();
237
235 let (db, file_id) = TestDB::with_single_file(&s); 238 let (db, file_id) = TestDB::with_single_file(&s);
236 let parsed = db.parse(file_id); 239 let parsed = db.parse(file_id);
237 let items: Vec<_> = 240 let items: Vec<_> =
@@ -239,14 +242,9 @@ mod tests {
239 242
240 let ast_id_map = db.ast_id_map(file_id.into()); 243 let ast_id_map = db.ast_id_map(file_id.into());
241 244
242 // the first one should be a macro_rules 245 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
243 let def =
244 MacroDefId { krate: None, ast_id: None, kind: MacroDefKind::BuiltInDerive(expander) };
245 246
246 let loc = MacroCallLoc { 247 let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) };
247 def,
248 kind: MacroCallKind::Attr(AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]))),
249 };
250 248
251 let id: MacroCallId = db.intern_macro(loc).into(); 249 let id: MacroCallId = db.intern_macro(loc).into();
252 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 250 let parsed = db.parse_or_expand(id.as_file()).unwrap();
@@ -263,7 +261,7 @@ mod tests {
263 #[derive(Copy)] 261 #[derive(Copy)]
264 struct Foo; 262 struct Foo;
265"#, 263"#,
266 BuiltinDeriveExpander::Copy, 264 known::Copy,
267 ); 265 );
268 266
269 assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}"); 267 assert_eq!(expanded, "impl< >std::marker::CopyforFoo< >{}");
@@ -276,7 +274,7 @@ mod tests {
276 #[derive(Copy)] 274 #[derive(Copy)]
277 struct Foo<A, B>; 275 struct Foo<A, B>;
278"#, 276"#,
279 BuiltinDeriveExpander::Copy, 277 known::Copy,
280 ); 278 );
281 279
282 assert_eq!( 280 assert_eq!(
@@ -292,7 +290,7 @@ mod tests {
292 #[derive(Copy)] 290 #[derive(Copy)]
293 struct Foo<A, B, 'a, 'b>; 291 struct Foo<A, B, 'a, 'b>;
294"#, 292"#,
295 BuiltinDeriveExpander::Copy, 293 known::Copy,
296 ); 294 );
297 295
298 // We currently just ignore lifetimes 296 // We currently just ignore lifetimes
@@ -310,7 +308,7 @@ mod tests {
310 #[derive(Clone)] 308 #[derive(Clone)]
311 struct Foo<A, B>; 309 struct Foo<A, B>;
312"#, 310"#,
313 BuiltinDeriveExpander::Clone, 311 known::Clone,
314 ); 312 );
315 313
316 assert_eq!( 314 assert_eq!(
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index 5a696542f..047452306 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -11,7 +11,7 @@ use ra_syntax::{algo::diff, AstNode, Parse, SyntaxKind::*, SyntaxNode};
11use crate::{ 11use crate::{
12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId, 12 ast_id_map::AstIdMap, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallLoc, EagerMacroId,
13 HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind, 13 HirFileId, HirFileIdRepr, LazyMacroId, MacroCallId, MacroCallLoc, MacroDefId, MacroDefKind,
14 MacroFile, 14 MacroFile, ProcMacroExpander,
15}; 15};
16 16
17#[derive(Debug, Clone, Eq, PartialEq)] 17#[derive(Debug, Clone, Eq, PartialEq)]
@@ -19,6 +19,7 @@ pub enum TokenExpander {
19 MacroRules(mbe::MacroRules), 19 MacroRules(mbe::MacroRules),
20 Builtin(BuiltinFnLikeExpander), 20 Builtin(BuiltinFnLikeExpander),
21 BuiltinDerive(BuiltinDeriveExpander), 21 BuiltinDerive(BuiltinDeriveExpander),
22 ProcMacro(ProcMacroExpander),
22} 23}
23 24
24impl TokenExpander { 25impl TokenExpander {
@@ -33,6 +34,7 @@ impl TokenExpander {
33 // FIXME switch these to ExpandResult as well 34 // FIXME switch these to ExpandResult as well
34 TokenExpander::Builtin(it) => it.expand(db, id, tt).into(), 35 TokenExpander::Builtin(it) => it.expand(db, id, tt).into(),
35 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), 36 TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(),
37 TokenExpander::ProcMacro(it) => it.expand(db, id, tt).into(),
36 } 38 }
37 } 39 }
38 40
@@ -41,6 +43,7 @@ impl TokenExpander {
41 TokenExpander::MacroRules(it) => it.map_id_down(id), 43 TokenExpander::MacroRules(it) => it.map_id_down(id),
42 TokenExpander::Builtin(..) => id, 44 TokenExpander::Builtin(..) => id,
43 TokenExpander::BuiltinDerive(..) => id, 45 TokenExpander::BuiltinDerive(..) => id,
46 TokenExpander::ProcMacro(..) => id,
44 } 47 }
45 } 48 }
46 49
@@ -49,6 +52,7 @@ impl TokenExpander {
49 TokenExpander::MacroRules(it) => it.map_id_up(id), 52 TokenExpander::MacroRules(it) => it.map_id_up(id),
50 TokenExpander::Builtin(..) => (id, mbe::Origin::Call), 53 TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
51 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), 54 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
55 TokenExpander::ProcMacro(..) => (id, mbe::Origin::Call),
52 } 56 }
53 } 57 }
54} 58}
@@ -130,7 +134,10 @@ pub(crate) fn macro_def(
130 MacroDefKind::BuiltInDerive(expander) => { 134 MacroDefKind::BuiltInDerive(expander) => {
131 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default()))) 135 Some(Arc::new((TokenExpander::BuiltinDerive(expander), mbe::TokenMap::default())))
132 } 136 }
133 MacroDefKind::BuiltInEager(_expander) => None, 137 MacroDefKind::BuiltInEager(_) => None,
138 MacroDefKind::CustomDerive(expander) => {
139 Some(Arc::new((TokenExpander::ProcMacro(expander), mbe::TokenMap::default())))
140 }
134 } 141 }
135} 142}
136 143
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
index 4cbce4df5..932f47c30 100644
--- a/crates/ra_hir_expand/src/eager.rs
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -26,8 +26,8 @@ use crate::{
26}; 26};
27 27
28use ra_parser::FragmentKind; 28use ra_parser::FragmentKind;
29use ra_syntax::{algo::replace_descendants, SyntaxElement, SyntaxNode}; 29use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
30use std::{collections::HashMap, sync::Arc}; 30use std::sync::Arc;
31 31
32pub fn expand_eager_macro( 32pub fn expand_eager_macro(
33 db: &dyn AstDatabase, 33 db: &dyn AstDatabase,
@@ -95,10 +95,10 @@ fn eager_macro_recur(
95 curr: InFile<SyntaxNode>, 95 curr: InFile<SyntaxNode>,
96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
97) -> Option<SyntaxNode> { 97) -> Option<SyntaxNode> {
98 let mut original = curr.value.clone(); 98 let original = curr.value.clone();
99 99
100 let children = curr.value.descendants().filter_map(ast::MacroCall::cast); 100 let children = curr.value.descendants().filter_map(ast::MacroCall::cast);
101 let mut replaces: HashMap<SyntaxElement, SyntaxElement> = HashMap::default(); 101 let mut rewriter = SyntaxRewriter::default();
102 102
103 // Collect replacement 103 // Collect replacement
104 for child in children { 104 for child in children {
@@ -112,19 +112,17 @@ fn eager_macro_recur(
112 } 112 }
113 MacroDefKind::Declarative 113 MacroDefKind::Declarative
114 | MacroDefKind::BuiltIn(_) 114 | MacroDefKind::BuiltIn(_)
115 | MacroDefKind::BuiltInDerive(_) => { 115 | MacroDefKind::BuiltInDerive(_)
116 | MacroDefKind::CustomDerive(_) => {
116 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; 117 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?;
117 // replace macro inside 118 // replace macro inside
118 eager_macro_recur(db, expanded, macro_resolver)? 119 eager_macro_recur(db, expanded, macro_resolver)?
119 } 120 }
120 }; 121 };
121 122
122 replaces.insert(child.syntax().clone().into(), insert.into()); 123 rewriter.replace(child.syntax(), &insert);
123 } 124 }
124 125
125 if !replaces.is_empty() { 126 let res = rewriter.rewrite(&original);
126 original = replace_descendants(&original, |n| replaces.get(n).cloned()); 127 Some(res)
127 }
128
129 Some(original)
130} 128}
diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs
index dfbac494f..0b41d0e95 100644
--- a/crates/ra_hir_expand/src/hygiene.rs
+++ b/crates/ra_hir_expand/src/hygiene.rs
@@ -30,6 +30,7 @@ impl Hygiene {
30 MacroDefKind::BuiltIn(_) => None, 30 MacroDefKind::BuiltIn(_) => None,
31 MacroDefKind::BuiltInDerive(_) => None, 31 MacroDefKind::BuiltInDerive(_) => None,
32 MacroDefKind::BuiltInEager(_) => None, 32 MacroDefKind::BuiltInEager(_) => None,
33 MacroDefKind::CustomDerive(_) => None,
33 } 34 }
34 } 35 }
35 MacroCallId::EagerMacro(_id) => None, 36 MacroCallId::EagerMacro(_id) => None,
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 6b59ea4c9..86299459f 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -11,6 +11,7 @@ pub mod hygiene;
11pub mod diagnostics; 11pub mod diagnostics;
12pub mod builtin_derive; 12pub mod builtin_derive;
13pub mod builtin_macro; 13pub mod builtin_macro;
14pub mod proc_macro;
14pub mod quote; 15pub mod quote;
15pub mod eager; 16pub mod eager;
16 17
@@ -27,6 +28,7 @@ use ra_syntax::{
27use crate::ast_id_map::FileAstId; 28use crate::ast_id_map::FileAstId;
28use crate::builtin_derive::BuiltinDeriveExpander; 29use crate::builtin_derive::BuiltinDeriveExpander;
29use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander}; 30use crate::builtin_macro::{BuiltinFnLikeExpander, EagerExpander};
31use crate::proc_macro::ProcMacroExpander;
30 32
31#[cfg(test)] 33#[cfg(test)]
32mod test_db; 34mod test_db;
@@ -217,6 +219,7 @@ pub enum MacroDefKind {
217 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander 219 // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander
218 BuiltInDerive(BuiltinDeriveExpander), 220 BuiltInDerive(BuiltinDeriveExpander),
219 BuiltInEager(EagerExpander), 221 BuiltInEager(EagerExpander),
222 CustomDerive(ProcMacroExpander),
220} 223}
221 224
222#[derive(Debug, Clone, PartialEq, Eq, Hash)] 225#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -228,21 +231,23 @@ pub struct MacroCallLoc {
228#[derive(Debug, Clone, PartialEq, Eq, Hash)] 231#[derive(Debug, Clone, PartialEq, Eq, Hash)]
229pub enum MacroCallKind { 232pub enum MacroCallKind {
230 FnLike(AstId<ast::MacroCall>), 233 FnLike(AstId<ast::MacroCall>),
231 Attr(AstId<ast::ModuleItem>), 234 Attr(AstId<ast::ModuleItem>, String),
232} 235}
233 236
234impl MacroCallKind { 237impl MacroCallKind {
235 pub fn file_id(&self) -> HirFileId { 238 pub fn file_id(&self) -> HirFileId {
236 match self { 239 match self {
237 MacroCallKind::FnLike(ast_id) => ast_id.file_id, 240 MacroCallKind::FnLike(ast_id) => ast_id.file_id,
238 MacroCallKind::Attr(ast_id) => ast_id.file_id, 241 MacroCallKind::Attr(ast_id, _) => ast_id.file_id,
239 } 242 }
240 } 243 }
241 244
242 pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> { 245 pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
243 match self { 246 match self {
244 MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), 247 MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
245 MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), 248 MacroCallKind::Attr(ast_id, _) => {
249 ast_id.with_value(ast_id.to_node(db).syntax().clone())
250 }
246 } 251 }
247 } 252 }
248 253
@@ -251,7 +256,7 @@ impl MacroCallKind {
251 MacroCallKind::FnLike(ast_id) => { 256 MacroCallKind::FnLike(ast_id) => {
252 Some(ast_id.to_node(db).token_tree()?.syntax().clone()) 257 Some(ast_id.to_node(db).token_tree()?.syntax().clone())
253 } 258 }
254 MacroCallKind::Attr(ast_id) => Some(ast_id.to_node(db).syntax().clone()), 259 MacroCallKind::Attr(ast_id, _) => Some(ast_id.to_node(db).syntax().clone()),
255 } 260 }
256 } 261 }
257} 262}
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs
new file mode 100644
index 000000000..a8dee2052
--- /dev/null
+++ b/crates/ra_hir_expand/src/proc_macro.rs
@@ -0,0 +1,33 @@
1//! Proc Macro Expander stub
2
3use crate::{db::AstDatabase, LazyMacroId, MacroCallKind, MacroCallLoc};
4use ra_db::CrateId;
5
6#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
7pub struct ProcMacroExpander {
8 krate: CrateId,
9}
10
11impl ProcMacroExpander {
12 pub fn new(krate: CrateId) -> ProcMacroExpander {
13 ProcMacroExpander { krate }
14 }
15
16 pub fn expand(
17 &self,
18 db: &dyn AstDatabase,
19 id: LazyMacroId,
20 _tt: &tt::Subtree,
21 ) -> Result<tt::Subtree, mbe::ExpandError> {
22 let loc: MacroCallLoc = db.lookup_intern_macro(id);
23 let name = match loc.kind {
24 MacroCallKind::FnLike(_) => return Err(mbe::ExpandError::ConversionError),
25 MacroCallKind::Attr(_, name) => name,
26 };
27
28 log::debug!("Proc-macro-expanding name = {}", name);
29
30 // Return nothing for now
31 return Ok(tt::Subtree::default());
32 }
33}
diff --git a/crates/ra_hir_ty/src/db.rs b/crates/ra_hir_ty/src/db.rs
index 11fc2ac3d..1462b053f 100644
--- a/crates/ra_hir_ty/src/db.rs
+++ b/crates/ra_hir_ty/src/db.rs
@@ -22,6 +22,7 @@ use hir_expand::name::Name;
22#[salsa::requires(salsa::Database)] 22#[salsa::requires(salsa::Database)]
23pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { 23pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
24 #[salsa::invoke(infer_wait)] 24 #[salsa::invoke(infer_wait)]
25 #[salsa::transparent]
25 fn infer(&self, def: DefWithBodyId) -> Arc<InferenceResult>; 26 fn infer(&self, def: DefWithBodyId) -> Arc<InferenceResult>;
26 27
27 #[salsa::invoke(crate::infer::infer_query)] 28 #[salsa::invoke(crate::infer::infer_query)]
diff --git a/crates/ra_hir_ty/src/tests/macros.rs b/crates/ra_hir_ty/src/tests/macros.rs
index 2e309a379..eb97288f1 100644
--- a/crates/ra_hir_ty/src/tests/macros.rs
+++ b/crates/ra_hir_ty/src/tests/macros.rs
@@ -639,3 +639,22 @@ mod clone {
639 ); 639 );
640 assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos)); 640 assert_eq!("(Wrapper<S>, {unknown})", type_at_pos(&db, pos));
641} 641}
642
643#[test]
644fn infer_custom_derive_simple() {
645 // FIXME: this test current now do nothing
646 let (db, pos) = TestDB::with_position(
647 r#"
648//- /main.rs crate:main
649use foo::Foo;
650
651#[derive(Foo)]
652struct S{}
653
654fn test() {
655 S{}<|>;
656}
657"#,
658 );
659 assert_eq!("S", type_at_pos(&db, pos));
660}
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml
index 7235c944c..36eec0e60 100644
--- a/crates/ra_ide/Cargo.toml
+++ b/crates/ra_ide/Cargo.toml
@@ -14,7 +14,7 @@ wasm = []
14either = "1.5.3" 14either = "1.5.3"
15format-buf = "1.0.0" 15format-buf = "1.0.0"
16indexmap = "1.3.2" 16indexmap = "1.3.2"
17itertools = "0.8.2" 17itertools = "0.9.0"
18join_to_string = "0.1.3" 18join_to_string = "0.1.3"
19log = "0.4.8" 19log = "0.4.8"
20rustc-hash = "1.1.0" 20rustc-hash = "1.1.0"
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 0ba382165..0a00054b2 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -1,6 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ast::AstNode, TextRange, TextUnit}; 3use ra_syntax::{
4 ast::{self, AstNode},
5 TextRange, TextUnit,
6};
4use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
5 8
6use crate::{ 9use crate::{
@@ -21,13 +24,8 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
21 None => return, 24 None => return,
22 }; 25 };
23 26
24 let receiver_text = if ctx.dot_receiver_is_ambiguous_float_literal { 27 let receiver_text =
25 let text = dot_receiver.syntax().text(); 28 get_receiver_text(dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
26 let without_dot = ..text.len() - TextUnit::of_char('.');
27 text.slice(without_dot).to_string()
28 } else {
29 dot_receiver.syntax().text().to_string()
30 };
31 29
32 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { 30 let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) {
33 Some(it) => it, 31 Some(it) => it,
@@ -35,10 +33,17 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
35 }; 33 };
36 34
37 if receiver_ty.is_bool() || receiver_ty.is_unknown() { 35 if receiver_ty.is_bool() || receiver_ty.is_unknown() {
38 postfix_snippet(ctx, "if", "if expr {}", &format!("if {} {{$0}}", receiver_text))
39 .add_to(acc);
40 postfix_snippet( 36 postfix_snippet(
41 ctx, 37 ctx,
38 &dot_receiver,
39 "if",
40 "if expr {}",
41 &format!("if {} {{$0}}", receiver_text),
42 )
43 .add_to(acc);
44 postfix_snippet(
45 ctx,
46 &dot_receiver,
42 "while", 47 "while",
43 "while expr {}", 48 "while expr {}",
44 &format!("while {} {{\n$0\n}}", receiver_text), 49 &format!("while {} {{\n$0\n}}", receiver_text),
@@ -46,28 +51,70 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
46 .add_to(acc); 51 .add_to(acc);
47 } 52 }
48 53
49 postfix_snippet(ctx, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc); 54 // !&&&42 is a compiler error, ergo process it before considering the references
55 postfix_snippet(ctx, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)).add_to(acc);
50 56
51 postfix_snippet(ctx, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc); 57 postfix_snippet(ctx, &dot_receiver, "ref", "&expr", &format!("&{}", receiver_text)).add_to(acc);
52 postfix_snippet(ctx, "refm", "&mut expr", &format!("&mut {}", receiver_text)).add_to(acc); 58 postfix_snippet(ctx, &dot_receiver, "refm", "&mut expr", &format!("&mut {}", receiver_text))
59 .add_to(acc);
60
61 // The rest of the postfix completions create an expression that moves an argument,
62 // so it's better to consider references now to avoid breaking the compilation
63 let dot_receiver = include_references(dot_receiver);
64 let receiver_text =
65 get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal);
53 66
54 postfix_snippet( 67 postfix_snippet(
55 ctx, 68 ctx,
69 &dot_receiver,
56 "match", 70 "match",
57 "match expr {}", 71 "match expr {}",
58 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), 72 &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text),
59 ) 73 )
60 .add_to(acc); 74 .add_to(acc);
61 75
62 postfix_snippet(ctx, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text)).add_to(acc); 76 postfix_snippet(
77 ctx,
78 &dot_receiver,
79 "box",
80 "Box::new(expr)",
81 &format!("Box::new({})", receiver_text),
82 )
83 .add_to(acc);
63 84
64 postfix_snippet(ctx, "box", "Box::new(expr)", &format!("Box::new({})", receiver_text)) 85 postfix_snippet(ctx, &dot_receiver, "dbg", "dbg!(expr)", &format!("dbg!({})", receiver_text))
65 .add_to(acc); 86 .add_to(acc);
66} 87}
67 88
68fn postfix_snippet(ctx: &CompletionContext, label: &str, detail: &str, snippet: &str) -> Builder { 89fn get_receiver_text(receiver: &ast::Expr, receiver_is_ambiguous_float_literal: bool) -> String {
90 if receiver_is_ambiguous_float_literal {
91 let text = receiver.syntax().text();
92 let without_dot = ..text.len() - TextUnit::of_char('.');
93 text.slice(without_dot).to_string()
94 } else {
95 receiver.to_string()
96 }
97}
98
99fn include_references(initial_element: &ast::Expr) -> ast::Expr {
100 let mut resulting_element = initial_element.clone();
101 while let Some(parent_ref_element) =
102 resulting_element.syntax().parent().and_then(ast::RefExpr::cast)
103 {
104 resulting_element = ast::Expr::from(parent_ref_element);
105 }
106 resulting_element
107}
108
109fn postfix_snippet(
110 ctx: &CompletionContext,
111 receiver: &ast::Expr,
112 label: &str,
113 detail: &str,
114 snippet: &str,
115) -> Builder {
69 let edit = { 116 let edit = {
70 let receiver_syntax = ctx.dot_receiver.as_ref().expect("no receiver available").syntax(); 117 let receiver_syntax = receiver.syntax();
71 let receiver_range = ctx.sema.original_range(receiver_syntax).range; 118 let receiver_range = ctx.sema.original_range(receiver_syntax).range;
72 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end()); 119 let delete_range = TextRange::from_to(receiver_range.start(), ctx.source_range().end());
73 TextEdit::replace(delete_range, snippet.to_string()) 120 TextEdit::replace(delete_range, snippet.to_string())
@@ -340,4 +387,63 @@ mod tests {
340 "### 387 "###
341 ); 388 );
342 } 389 }
390
391 #[test]
392 fn postfix_completion_for_references() {
393 assert_debug_snapshot!(
394 do_postfix_completion(
395 r#"
396 fn main() {
397 &&&&42.<|>
398 }
399 "#,
400 ),
401 @r###"
402 [
403 CompletionItem {
404 label: "box",
405 source_range: [56; 56),
406 delete: [49; 56),
407 insert: "Box::new(&&&&42)",
408 detail: "Box::new(expr)",
409 },
410 CompletionItem {
411 label: "dbg",
412 source_range: [56; 56),
413 delete: [49; 56),
414 insert: "dbg!(&&&&42)",
415 detail: "dbg!(expr)",
416 },
417 CompletionItem {
418 label: "match",
419 source_range: [56; 56),
420 delete: [49; 56),
421 insert: "match &&&&42 {\n ${1:_} => {$0\\},\n}",
422 detail: "match expr {}",
423 },
424 CompletionItem {
425 label: "not",
426 source_range: [56; 56),
427 delete: [53; 56),
428 insert: "!42",
429 detail: "!expr",
430 },
431 CompletionItem {
432 label: "ref",
433 source_range: [56; 56),
434 delete: [53; 56),
435 insert: "&42",
436 detail: "&expr",
437 },
438 CompletionItem {
439 label: "refm",
440 source_range: [56; 56),
441 delete: [53; 56),
442 insert: "&mut 42",
443 detail: "&mut expr",
444 },
445 ]
446 "###
447 );
448 }
343} 449}
diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs
index 83ed1d52c..e4e764f58 100644
--- a/crates/ra_ide/src/completion/complete_record_literal.rs
+++ b/crates/ra_ide/src/completion/complete_record_literal.rs
@@ -1,6 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use crate::completion::{CompletionContext, Completions}; 3use crate::completion::{CompletionContext, Completions};
4use ra_syntax::SmolStr;
4 5
5/// Complete fields in fields literals. 6/// Complete fields in fields literals.
6pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { 7pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) {
@@ -11,8 +12,24 @@ pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionCon
11 _ => return, 12 _ => return,
12 }; 13 };
13 14
15 let already_present_names: Vec<SmolStr> = ctx
16 .record_lit_syntax
17 .as_ref()
18 .and_then(|record_literal| record_literal.record_field_list())
19 .map(|field_list| field_list.fields())
20 .map(|fields| {
21 fields
22 .into_iter()
23 .filter_map(|field| field.name_ref())
24 .map(|name_ref| name_ref.text().clone())
25 .collect()
26 })
27 .unwrap_or_default();
28
14 for (field, field_ty) in ty.variant_fields(ctx.db, variant) { 29 for (field, field_ty) in ty.variant_fields(ctx.db, variant) {
15 acc.add_field(ctx, field, &field_ty); 30 if !already_present_names.contains(&SmolStr::from(field.name(ctx.db).to_string())) {
31 acc.add_field(ctx, field, &field_ty);
32 }
16 } 33 }
17} 34}
18 35
@@ -178,4 +195,47 @@ mod tests {
178 ] 195 ]
179 "###); 196 "###);
180 } 197 }
198
199 #[test]
200 fn only_missing_fields_are_completed() {
201 let completions = complete(
202 r"
203 struct S {
204 foo1: u32,
205 foo2: u32,
206 bar: u32,
207 baz: u32,
208 }
209
210 fn main() {
211 let foo1 = 1;
212 let s = S {
213 foo1,
214 foo2: 5,
215 <|>
216 }
217 }
218 ",
219 );
220 assert_debug_snapshot!(completions, @r###"
221 [
222 CompletionItem {
223 label: "bar",
224 source_range: [302; 302),
225 delete: [302; 302),
226 insert: "bar",
227 kind: Field,
228 detail: "u32",
229 },
230 CompletionItem {
231 label: "baz",
232 source_range: [302; 302),
233 delete: [302; 302),
234 insert: "baz",
235 kind: Field,
236 detail: "u32",
237 },
238 ]
239 "###);
240 }
181} 241}
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index 7fefa2c7a..ded1ff3bc 100644
--- a/crates/ra_ide/src/completion/complete_trait_impl.rs
+++ b/crates/ra_ide/src/completion/complete_trait_impl.rs
@@ -193,7 +193,7 @@ fn add_const_impl(
193} 193}
194 194
195fn make_const_compl_syntax(const_: &ast::ConstDef) -> String { 195fn make_const_compl_syntax(const_: &ast::ConstDef) -> String {
196 let const_ = edit::strip_attrs_and_docs(const_); 196 let const_ = edit::remove_attrs_and_docs(const_);
197 197
198 let const_start = const_.syntax().text_range().start(); 198 let const_start = const_.syntax().text_range().start();
199 let const_end = const_.syntax().text_range().end(); 199 let const_end = const_.syntax().text_range().end();
diff --git a/crates/ra_ide/src/expand_macro.rs b/crates/ra_ide/src/expand_macro.rs
index e58526f31..f536ba3e7 100644
--- a/crates/ra_ide/src/expand_macro.rs
+++ b/crates/ra_ide/src/expand_macro.rs
@@ -3,10 +3,9 @@
3use hir::Semantics; 3use hir::Semantics;
4use ra_ide_db::RootDatabase; 4use ra_ide_db::RootDatabase;
5use ra_syntax::{ 5use ra_syntax::{
6 algo::{find_node_at_offset, replace_descendants}, 6 algo::{find_node_at_offset, SyntaxRewriter},
7 ast, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, WalkEvent, T, 7 ast, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T,
8}; 8};
9use rustc_hash::FxHashMap;
10 9
11use crate::FilePosition; 10use crate::FilePosition;
12 11
@@ -37,7 +36,7 @@ fn expand_macro_recur(
37 let mut expanded = sema.expand(macro_call)?; 36 let mut expanded = sema.expand(macro_call)?;
38 37
39 let children = expanded.descendants().filter_map(ast::MacroCall::cast); 38 let children = expanded.descendants().filter_map(ast::MacroCall::cast);
40 let mut replaces: FxHashMap<SyntaxElement, SyntaxElement> = FxHashMap::default(); 39 let mut rewriter = SyntaxRewriter::default();
41 40
42 for child in children.into_iter() { 41 for child in children.into_iter() {
43 if let Some(new_node) = expand_macro_recur(sema, &child) { 42 if let Some(new_node) = expand_macro_recur(sema, &child) {
@@ -47,12 +46,13 @@ fn expand_macro_recur(
47 if expanded == *child.syntax() { 46 if expanded == *child.syntax() {
48 expanded = new_node; 47 expanded = new_node;
49 } else { 48 } else {
50 replaces.insert(child.syntax().clone().into(), new_node.into()); 49 rewriter.replace(child.syntax(), &new_node)
51 } 50 }
52 } 51 }
53 } 52 }
54 53
55 Some(replace_descendants(&expanded, |n| replaces.get(n).cloned())) 54 let res = rewriter.rewrite(&expanded);
55 Some(res)
56} 56}
57 57
58// FIXME: It would also be cool to share logic here and in the mbe tests, 58// FIXME: It would also be cool to share logic here and in the mbe tests,
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 502fcb0cf..8aed94d16 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -104,6 +104,9 @@ mod tests {
104 let (analysis, pos) = analysis_and_position(ra_fixture); 104 let (analysis, pos) = analysis_and_position(ra_fixture);
105 105
106 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; 106 let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info;
107 if navs.len() == 0 {
108 panic!("unresolved reference")
109 }
107 assert_eq!(navs.len(), 1); 110 assert_eq!(navs.len(), 1);
108 111
109 let nav = navs.pop().unwrap(); 112 let nav = navs.pop().unwrap();
@@ -359,7 +362,7 @@ mod tests {
359 fn goto_def_for_fields() { 362 fn goto_def_for_fields() {
360 covers!(ra_ide_db::goto_def_for_fields); 363 covers!(ra_ide_db::goto_def_for_fields);
361 check_goto( 364 check_goto(
362 " 365 r"
363 //- /lib.rs 366 //- /lib.rs
364 struct Foo { 367 struct Foo {
365 spam: u32, 368 spam: u32,
@@ -378,7 +381,7 @@ mod tests {
378 fn goto_def_for_record_fields() { 381 fn goto_def_for_record_fields() {
379 covers!(ra_ide_db::goto_def_for_record_fields); 382 covers!(ra_ide_db::goto_def_for_record_fields);
380 check_goto( 383 check_goto(
381 " 384 r"
382 //- /lib.rs 385 //- /lib.rs
383 struct Foo { 386 struct Foo {
384 spam: u32, 387 spam: u32,
@@ -396,6 +399,23 @@ mod tests {
396 } 399 }
397 400
398 #[test] 401 #[test]
402 fn goto_def_for_record_fields_macros() {
403 check_goto(
404 r"
405 //- /lib.rs
406 macro_rules! m { () => { 92 };}
407 struct Foo { spam: u32 }
408
409 fn bar() -> Foo {
410 Foo { spam<|>: m!() }
411 }
412 ",
413 "spam RECORD_FIELD_DEF FileId(1) [45; 54) [45; 49)",
414 "spam: u32|spam",
415 );
416 }
417
418 #[test]
399 fn goto_for_tuple_fields() { 419 fn goto_for_tuple_fields() {
400 check_goto( 420 check_goto(
401 " 421 "
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index ecd615cf4..f4f0751c0 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -5,7 +5,7 @@ use ra_ide_db::RootDatabase;
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ 6use ra_syntax::{
7 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 7 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
8 match_ast, SmolStr, TextRange, 8 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange,
9}; 9};
10 10
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
@@ -14,12 +14,13 @@ use crate::{FileId, FunctionSignature};
14pub struct InlayHintsOptions { 14pub struct InlayHintsOptions {
15 pub type_hints: bool, 15 pub type_hints: bool,
16 pub parameter_hints: bool, 16 pub parameter_hints: bool,
17 pub chaining_hints: bool,
17 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
18} 19}
19 20
20impl Default for InlayHintsOptions { 21impl Default for InlayHintsOptions {
21 fn default() -> Self { 22 fn default() -> Self {
22 Self { type_hints: true, parameter_hints: true, max_length: None } 23 Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None }
23 } 24 }
24} 25}
25 26
@@ -27,6 +28,7 @@ impl Default for InlayHintsOptions {
27pub enum InlayKind { 28pub enum InlayKind {
28 TypeHint, 29 TypeHint,
29 ParameterHint, 30 ParameterHint,
31 ChainingHint,
30} 32}
31 33
32#[derive(Debug)] 34#[derive(Debug)]
@@ -47,6 +49,10 @@ pub(crate) fn inlay_hints(
47 49
48 let mut res = Vec::new(); 50 let mut res = Vec::new();
49 for node in file.syntax().descendants() { 51 for node in file.syntax().descendants() {
52 if let Some(expr) = ast::Expr::cast(node.clone()) {
53 get_chaining_hints(&mut res, &sema, options, expr);
54 }
55
50 match_ast! { 56 match_ast! {
51 match node { 57 match node {
52 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 58 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
@@ -59,6 +65,46 @@ pub(crate) fn inlay_hints(
59 res 65 res
60} 66}
61 67
68fn get_chaining_hints(
69 acc: &mut Vec<InlayHint>,
70 sema: &Semantics<RootDatabase>,
71 options: &InlayHintsOptions,
72 expr: ast::Expr,
73) -> Option<()> {
74 if !options.chaining_hints {
75 return None;
76 }
77
78 let ty = sema.type_of_expr(&expr)?;
79 if ty.is_unknown() {
80 return None;
81 }
82
83 let mut tokens = expr
84 .syntax()
85 .siblings_with_tokens(Direction::Next)
86 .filter_map(NodeOrToken::into_token)
87 .filter(|t| match t.kind() {
88 SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
89 SyntaxKind::COMMENT => false,
90 _ => true,
91 });
92
93 // Chaining can be defined as an expression whose next sibling tokens are newline and dot
94 // Ignoring extra whitespace and comments
95 let next = tokens.next()?.kind();
96 let next_next = tokens.next()?.kind();
97 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
98 let label = ty.display_truncated(sema.db, options.max_length).to_string();
99 acc.push(InlayHint {
100 range: expr.syntax().text_range(),
101 kind: InlayKind::ChainingHint,
102 label: label.into(),
103 });
104 }
105 Some(())
106}
107
62fn get_param_name_hints( 108fn get_param_name_hints(
63 acc: &mut Vec<InlayHint>, 109 acc: &mut Vec<InlayHint>,
64 sema: &Semantics<RootDatabase>, 110 sema: &Semantics<RootDatabase>,
@@ -238,7 +284,7 @@ mod tests {
238 let _x = foo(4, 4); 284 let _x = foo(4, 4);
239 }"#, 285 }"#,
240 ); 286 );
241 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, max_length: None}).unwrap(), @r###" 287 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
242 [ 288 [
243 InlayHint { 289 InlayHint {
244 range: [106; 107), 290 range: [106; 107),
@@ -262,7 +308,7 @@ mod tests {
262 let _x = foo(4, 4); 308 let _x = foo(4, 4);
263 }"#, 309 }"#,
264 ); 310 );
265 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, max_length: None}).unwrap(), @r###"[]"###); 311 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
266 } 312 }
267 313
268 #[test] 314 #[test]
@@ -274,7 +320,7 @@ mod tests {
274 let _x = foo(4, 4); 320 let _x = foo(4, 4);
275 }"#, 321 }"#,
276 ); 322 );
277 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, max_length: None}).unwrap(), @r###" 323 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
278 [ 324 [
279 InlayHint { 325 InlayHint {
280 range: [97; 99), 326 range: [97; 99),
@@ -1052,4 +1098,124 @@ fn main() {
1052 "### 1098 "###
1053 ); 1099 );
1054 } 1100 }
1101
1102 #[test]
1103 fn chaining_hints_ignore_comments() {
1104 let (analysis, file_id) = single_file(
1105 r#"
1106 struct A(B);
1107 impl A { fn into_b(self) -> B { self.0 } }
1108 struct B(C);
1109 impl B { fn into_c(self) -> C { self.0 } }
1110 struct C;
1111
1112 fn main() {
1113 let c = A(B(C))
1114 .into_b() // This is a comment
1115 .into_c();
1116 }"#,
1117 );
1118 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1119 [
1120 InlayHint {
1121 range: [232; 269),
1122 kind: ChainingHint,
1123 label: "B",
1124 },
1125 InlayHint {
1126 range: [232; 239),
1127 kind: ChainingHint,
1128 label: "A",
1129 },
1130 ]"###);
1131 }
1132
1133 #[test]
1134 fn chaining_hints_without_newlines() {
1135 let (analysis, file_id) = single_file(
1136 r#"
1137 struct A(B);
1138 impl A { fn into_b(self) -> B { self.0 } }
1139 struct B(C);
1140 impl B { fn into_c(self) -> C { self.0 } }
1141 struct C;
1142
1143 fn main() {
1144 let c = A(B(C)).into_b().into_c();
1145 }"#,
1146 );
1147 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1148 }
1149
1150 #[test]
1151 fn struct_access_chaining_hints() {
1152 let (analysis, file_id) = single_file(
1153 r#"
1154 struct A { pub b: B }
1155 struct B { pub c: C }
1156 struct C(pub bool);
1157
1158 fn main() {
1159 let x = A { b: B { c: C(true) } }
1160 .b
1161 .c
1162 .0;
1163 }"#,
1164 );
1165 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1166 [
1167 InlayHint {
1168 range: [150; 221),
1169 kind: ChainingHint,
1170 label: "C",
1171 },
1172 InlayHint {
1173 range: [150; 198),
1174 kind: ChainingHint,
1175 label: "B",
1176 },
1177 InlayHint {
1178 range: [150; 175),
1179 kind: ChainingHint,
1180 label: "A",
1181 },
1182 ]"###);
1183 }
1184
1185 #[test]
1186 fn generic_chaining_hints() {
1187 let (analysis, file_id) = single_file(
1188 r#"
1189 struct A<T>(T);
1190 struct B<T>(T);
1191 struct C<T>(T);
1192 struct X<T,R>(T, R);
1193
1194 impl<T> A<T> {
1195 fn new(t: T) -> Self { A(t) }
1196 fn into_b(self) -> B<T> { B(self.0) }
1197 }
1198 impl<T> B<T> {
1199 fn into_c(self) -> C<T> { C(self.0) }
1200 }
1201 fn main() {
1202 let c = A::new(X(42, true))
1203 .into_b()
1204 .into_c();
1205 }"#,
1206 );
1207 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1208 [
1209 InlayHint {
1210 range: [403; 452),
1211 kind: ChainingHint,
1212 label: "B<X<i32, bool>>",
1213 },
1214 InlayHint {
1215 range: [403; 422),
1216 kind: ChainingHint,
1217 label: "A<X<i32, bool>>",
1218 },
1219 ]"###);
1220 }
1055} 1221}
diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml
index de4f5bce0..c3921bd40 100644
--- a/crates/ra_ide_db/Cargo.toml
+++ b/crates/ra_ide_db/Cargo.toml
@@ -17,6 +17,7 @@ fst = { version = "0.4", default-features = false }
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
18superslice = "1.0.0" 18superslice = "1.0.0"
19once_cell = "1.3.1" 19once_cell = "1.3.1"
20either = "1.5.3"
20 21
21ra_syntax = { path = "../ra_syntax" } 22ra_syntax = { path = "../ra_syntax" }
22ra_text_edit = { path = "../ra_text_edit" } 23ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 628cf6416..8446ef88e 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -311,6 +311,7 @@ impl RootDatabase {
311 hir::db::MacroDefQuery 311 hir::db::MacroDefQuery
312 hir::db::ParseMacroQuery 312 hir::db::ParseMacroQuery
313 hir::db::MacroExpandQuery 313 hir::db::MacroExpandQuery
314 hir::db::InternEagerExpansionQuery
314 315
315 // DefDatabase 316 // DefDatabase
316 hir::db::RawItemsQuery 317 hir::db::RawItemsQuery
@@ -359,14 +360,21 @@ impl RootDatabase {
359 hir::db::ImplsInCrateQuery 360 hir::db::ImplsInCrateQuery
360 hir::db::ImplsForTraitQuery 361 hir::db::ImplsForTraitQuery
361 hir::db::InternTypeCtorQuery 362 hir::db::InternTypeCtorQuery
363 hir::db::InternTypeParamIdQuery
362 hir::db::InternChalkImplQuery 364 hir::db::InternChalkImplQuery
363 hir::db::InternAssocTyValueQuery 365 hir::db::InternAssocTyValueQuery
364 hir::db::AssociatedTyDataQuery 366 hir::db::AssociatedTyDataQuery
365 hir::db::AssociatedTyValueQuery
366 hir::db::TraitSolveQuery
367 hir::db::TraitDatumQuery 367 hir::db::TraitDatumQuery
368 hir::db::StructDatumQuery 368 hir::db::StructDatumQuery
369 hir::db::ImplDatumQuery 369 hir::db::ImplDatumQuery
370 hir::db::AssociatedTyValueQuery
371 hir::db::TraitSolveQuery
372
373 // SymbolsDatabase
374 crate::symbol_index::FileSymbolsQuery
375
376 // LineIndexDatabase
377 crate::LineIndexQuery
370 ]; 378 ];
371 acc.sort_by_key(|it| std::cmp::Reverse(it.1)); 379 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
372 acc 380 acc
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 97961bb6d..e9934844e 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -6,12 +6,12 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 Adt, FieldSource, HasSource, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, 9 HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, Semantics,
10 Semantics, StructField, TypeParam, 10 StructField, TypeParam, Visibility,
11}; 11};
12use ra_prof::profile; 12use ra_prof::profile;
13use ra_syntax::{ 13use ra_syntax::{
14 ast::{self, AstNode, VisibilityOwner}, 14 ast::{self, AstNode},
15 match_ast, 15 match_ast,
16}; 16};
17use test_utils::tested_by; 17use test_utils::tested_by;
@@ -41,28 +41,13 @@ impl Definition {
41 } 41 }
42 } 42 }
43 43
44 pub fn visibility(&self, db: &RootDatabase) -> Option<ast::Visibility> { 44 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
45 let module = self.module(db);
46
45 match self { 47 match self {
46 Definition::Macro(_) => None, 48 Definition::Macro(_) => None,
47 Definition::StructField(sf) => match sf.source(db).value { 49 Definition::StructField(sf) => Some(sf.visibility(db)),
48 FieldSource::Named(it) => it.visibility(), 50 Definition::ModuleDef(def) => module?.visibility_of(db, def),
49 FieldSource::Pos(it) => it.visibility(),
50 },
51 Definition::ModuleDef(def) => match def {
52 ModuleDef::Module(it) => it.declaration_source(db)?.value.visibility(),
53 ModuleDef::Function(it) => it.source(db).value.visibility(),
54 ModuleDef::Adt(adt) => match adt {
55 Adt::Struct(it) => it.source(db).value.visibility(),
56 Adt::Union(it) => it.source(db).value.visibility(),
57 Adt::Enum(it) => it.source(db).value.visibility(),
58 },
59 ModuleDef::Const(it) => it.source(db).value.visibility(),
60 ModuleDef::Static(it) => it.source(db).value.visibility(),
61 ModuleDef::Trait(it) => it.source(db).value.visibility(),
62 ModuleDef::TypeAlias(it) => it.source(db).value.visibility(),
63 ModuleDef::EnumVariant(_) => None,
64 ModuleDef::BuiltinType(_) => None,
65 },
66 Definition::SelfType(_) => None, 51 Definition::SelfType(_) => None,
67 Definition::Local(_) => None, 52 Definition::Local(_) => None,
68 Definition::TypeParam(_) => None, 53 Definition::TypeParam(_) => None,
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index c96351982..bf0d8db60 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{ModuleDef, Semantics}; 4use hir::{MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -10,6 +10,7 @@ use crate::{
10 symbol_index::{self, FileSymbol, Query}, 10 symbol_index::{self, FileSymbol, Query},
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either;
13 14
14pub struct ImportsLocator<'a> { 15pub struct ImportsLocator<'a> {
15 sema: Semantics<'a, RootDatabase>, 16 sema: Semantics<'a, RootDatabase>,
@@ -20,7 +21,7 @@ impl<'a> ImportsLocator<'a> {
20 Self { sema: Semantics::new(db) } 21 Self { sema: Semantics::new(db) }
21 } 22 }
22 23
23 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef> { 24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
24 let _p = profile("search_for_imports"); 25 let _p = profile("search_for_imports");
25 let db = self.sema.db; 26 let db = self.sema.db;
26 27
@@ -43,7 +44,8 @@ impl<'a> ImportsLocator<'a> {
43 .chain(lib_results.into_iter()) 44 .chain(lib_results.into_iter())
44 .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) 45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
45 .filter_map(|name_definition_to_import| match name_definition_to_import { 46 .filter_map(|name_definition_to_import| match name_definition_to_import {
46 Definition::ModuleDef(module_def) => Some(module_def), 47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
47 _ => None, 49 _ => None,
48 }) 50 })
49 .collect() 51 .collect()
diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs
index 117454695..05a0eed30 100644
--- a/crates/ra_ide_db/src/search.rs
+++ b/crates/ra_ide_db/src/search.rs
@@ -6,7 +6,7 @@
6 6
7use std::mem; 7use std::mem;
8 8
9use hir::{DefWithBody, HasSource, ModuleSource, Semantics}; 9use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility};
10use once_cell::unsync::Lazy; 10use once_cell::unsync::Lazy;
11use ra_db::{FileId, FileRange, SourceDatabaseExt}; 11use ra_db::{FileId, FileRange, SourceDatabaseExt};
12use ra_prof::profile; 12use ra_prof::profile;
@@ -123,51 +123,47 @@ impl Definition {
123 return SearchScope::new(res); 123 return SearchScope::new(res);
124 } 124 }
125 125
126 let vis = self.visibility(db).as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); 126 let vis = self.visibility(db);
127 127
128 if vis.as_str() == "pub(super)" { 128 // FIXME:
129 if let Some(parent_module) = module.parent(db) { 129 // The following logic are wrong that it does not search
130 let mut res = FxHashMap::default(); 130 // for submodules within other files recursively.
131 let parent_src = parent_module.definition_source(db);
132 let file_id = parent_src.file_id.original_file(db);
133 131
134 match parent_src.value { 132 if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) {
135 ModuleSource::Module(m) => { 133 let module: Module = module.into();
136 let range = Some(m.syntax().text_range()); 134 let mut res = FxHashMap::default();
137 res.insert(file_id, range); 135 let src = module.definition_source(db);
138 } 136 let file_id = src.file_id.original_file(db);
139 ModuleSource::SourceFile(_) => { 137
140 res.insert(file_id, None); 138 match src.value {
141 res.extend(parent_module.children(db).map(|m| { 139 ModuleSource::Module(m) => {
142 let src = m.definition_source(db); 140 let range = Some(m.syntax().text_range());
143 (src.file_id.original_file(db), None) 141 res.insert(file_id, range);
144 })); 142 }
145 } 143 ModuleSource::SourceFile(_) => {
144 res.insert(file_id, None);
145 res.extend(module.children(db).map(|m| {
146 let src = m.definition_source(db);
147 (src.file_id.original_file(db), None)
148 }));
146 } 149 }
147 return SearchScope::new(res);
148 } 150 }
151 return SearchScope::new(res);
149 } 152 }
150 153
151 if vis.as_str() != "" { 154 if let Some(Visibility::Public) = vis {
152 let source_root_id = db.file_source_root(file_id); 155 let source_root_id = db.file_source_root(file_id);
153 let source_root = db.source_root(source_root_id); 156 let source_root = db.source_root(source_root_id);
154 let mut res = source_root.walk().map(|id| (id, None)).collect::<FxHashMap<_, _>>(); 157 let mut res = source_root.walk().map(|id| (id, None)).collect::<FxHashMap<_, _>>();
155 158
156 // FIXME: add "pub(in path)" 159 let krate = module.krate();
157 160 for rev_dep in krate.reverse_dependencies(db) {
158 if vis.as_str() == "pub(crate)" { 161 let root_file = rev_dep.root_file(db);
159 return SearchScope::new(res); 162 let source_root_id = db.file_source_root(root_file);
160 } 163 let source_root = db.source_root(source_root_id);
161 if vis.as_str() == "pub" { 164 res.extend(source_root.walk().map(|id| (id, None)));
162 let krate = module.krate();
163 for rev_dep in krate.reverse_dependencies(db) {
164 let root_file = rev_dep.root_file(db);
165 let source_root_id = db.file_source_root(root_file);
166 let source_root = db.source_root(source_root_id);
167 res.extend(source_root.walk().map(|id| (id, None)));
168 }
169 return SearchScope::new(res);
170 } 165 }
166 return SearchScope::new(res);
171 } 167 }
172 168
173 let mut res = FxHashMap::default(); 169 let mut res = FxHashMap::default();
diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs
index fcb73fbc7..8e8ae2b29 100644
--- a/crates/ra_mbe/src/syntax_bridge.rs
+++ b/crates/ra_mbe/src/syntax_bridge.rs
@@ -3,12 +3,11 @@
3use ra_parser::{FragmentKind, ParseError, TreeSink}; 3use ra_parser::{FragmentKind, ParseError, TreeSink};
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, make::tokens::doc_comment}, 5 ast::{self, make::tokens::doc_comment},
6 tokenize, AstToken, NodeOrToken, Parse, SmolStr, SyntaxKind, 6 tokenize, AstToken, Parse, SmolStr, SyntaxKind,
7 SyntaxKind::*, 7 SyntaxKind::*,
8 SyntaxNode, SyntaxTreeBuilder, TextRange, TextUnit, Token, T, 8 SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextUnit, Token as RawToken, T,
9}; 9};
10use rustc_hash::FxHashMap; 10use rustc_hash::FxHashMap;
11use std::iter::successors;
12use tt::buffer::{Cursor, TokenBuffer}; 11use tt::buffer::{Cursor, TokenBuffer};
13 12
14use crate::subtree_source::SubtreeTokenSource; 13use crate::subtree_source::SubtreeTokenSource;
@@ -34,7 +33,7 @@ impl TokenTextRange {
34} 33}
35 34
36/// Maps `tt::TokenId` to the relative range of the original token. 35/// Maps `tt::TokenId` to the relative range of the original token.
37#[derive(Debug, PartialEq, Eq, Default)] 36#[derive(Debug, PartialEq, Eq, Clone, Default)]
38pub struct TokenMap { 37pub struct TokenMap {
39 /// Maps `tt::TokenId` to the *relative* source range. 38 /// Maps `tt::TokenId` to the *relative* source range.
40 entries: Vec<(tt::TokenId, TokenTextRange)>, 39 entries: Vec<(tt::TokenId, TokenTextRange)>,
@@ -50,10 +49,8 @@ pub fn ast_to_token_tree(ast: &impl ast::AstNode) -> Option<(tt::Subtree, TokenM
50/// will consume). 49/// will consume).
51pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> { 50pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> Option<(tt::Subtree, TokenMap)> {
52 let global_offset = node.text_range().start(); 51 let global_offset = node.text_range().start();
53 let mut c = Convertor { 52 let mut c = Convertor::new(node, global_offset);
54 id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } }, 53 let subtree = c.go()?;
55 };
56 let subtree = c.go(node)?;
57 Some((subtree, c.id_alloc.map)) 54 Some((subtree, c.id_alloc.map))
58} 55}
59 56
@@ -152,6 +149,10 @@ impl TokenMap {
152 } 149 }
153 } 150 }
154 } 151 }
152
153 fn remove_delim(&mut self, token_id: tt::TokenId) {
154 self.entries.retain(|(tid, _)| *tid != token_id);
155 }
155} 156}
156 157
157/// Returns the textual content of a doc comment block as a quoted string 158/// Returns the textual content of a doc comment block as a quoted string
@@ -237,25 +238,26 @@ impl TokenIdAlloc {
237 token_id 238 token_id
238 } 239 }
239 240
240 fn delim(&mut self, open_abs_range: TextRange, close_abs_range: TextRange) -> tt::TokenId {
241 let open_relative_range = open_abs_range - self.global_offset;
242 let close_relative_range = close_abs_range - self.global_offset;
243 let token_id = tt::TokenId(self.next_id);
244 self.next_id += 1;
245
246 self.map.insert_delim(token_id, open_relative_range, close_relative_range);
247 token_id
248 }
249
250 fn open_delim(&mut self, open_abs_range: TextRange) -> tt::TokenId { 241 fn open_delim(&mut self, open_abs_range: TextRange) -> tt::TokenId {
251 let token_id = tt::TokenId(self.next_id); 242 let token_id = tt::TokenId(self.next_id);
252 self.next_id += 1; 243 self.next_id += 1;
253 self.map.insert_delim(token_id, open_abs_range, open_abs_range); 244 self.map.insert_delim(
245 token_id,
246 open_abs_range - self.global_offset,
247 open_abs_range - self.global_offset,
248 );
254 token_id 249 token_id
255 } 250 }
256 251
257 fn close_delim(&mut self, id: tt::TokenId, close_abs_range: TextRange) { 252 fn close_delim(&mut self, id: tt::TokenId, close_abs_range: Option<TextRange>) {
258 self.map.update_close_delim(id, close_abs_range); 253 match close_abs_range {
254 None => {
255 self.map.remove_delim(id);
256 }
257 Some(close) => {
258 self.map.update_close_delim(id, close - self.global_offset);
259 }
260 }
259 } 261 }
260} 262}
261 263
@@ -264,10 +266,20 @@ struct RawConvertor<'a> {
264 text: &'a str, 266 text: &'a str,
265 offset: TextUnit, 267 offset: TextUnit,
266 id_alloc: TokenIdAlloc, 268 id_alloc: TokenIdAlloc,
267 inner: std::slice::Iter<'a, Token>, 269 inner: std::slice::Iter<'a, RawToken>,
268} 270}
269 271
270impl RawConvertor<'_> { 272trait SrcToken {
273 fn kind(&self) -> SyntaxKind;
274
275 fn to_char(&self) -> Option<char>;
276
277 fn to_text(&self) -> SmolStr;
278}
279
280trait TokenConvertor {
281 type Token: SrcToken;
282
271 fn go(&mut self) -> Option<tt::Subtree> { 283 fn go(&mut self) -> Option<tt::Subtree> {
272 let mut subtree = tt::Subtree::default(); 284 let mut subtree = tt::Subtree::default();
273 subtree.delimiter = None; 285 subtree.delimiter = None;
@@ -285,33 +297,22 @@ impl RawConvertor<'_> {
285 Some(subtree) 297 Some(subtree)
286 } 298 }
287 299
288 fn bump(&mut self) -> Option<(Token, TextRange)> {
289 let token = self.inner.next()?;
290 let range = TextRange::offset_len(self.offset, token.len);
291 self.offset += token.len;
292 Some((*token, range))
293 }
294
295 fn peek(&self) -> Option<Token> {
296 self.inner.as_slice().get(0).cloned()
297 }
298
299 fn collect_leaf(&mut self, result: &mut Vec<tt::TokenTree>) { 300 fn collect_leaf(&mut self, result: &mut Vec<tt::TokenTree>) {
300 let (token, range) = match self.bump() { 301 let (token, range) = match self.bump() {
301 None => return, 302 None => return,
302 Some(it) => it, 303 Some(it) => it,
303 }; 304 };
304 305
305 let k: SyntaxKind = token.kind; 306 let k: SyntaxKind = token.kind();
306 if k == COMMENT { 307 if k == COMMENT {
307 let node = doc_comment(&self.text[range]); 308 if let Some(tokens) = self.convert_doc_comment(&token) {
308 if let Some(tokens) = convert_doc_comment(&node) {
309 result.extend(tokens); 309 result.extend(tokens);
310 } 310 }
311 return; 311 return;
312 } 312 }
313 313
314 result.push(if k.is_punct() { 314 result.push(if k.is_punct() {
315 assert_eq!(range.len().to_usize(), 1);
315 let delim = match k { 316 let delim = match k {
316 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), 317 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])),
317 T!['{'] => Some((tt::DelimiterKind::Brace, T!['}'])), 318 T!['{'] => Some((tt::DelimiterKind::Brace, T!['}'])),
@@ -321,40 +322,51 @@ impl RawConvertor<'_> {
321 322
322 if let Some((kind, closed)) = delim { 323 if let Some((kind, closed)) = delim {
323 let mut subtree = tt::Subtree::default(); 324 let mut subtree = tt::Subtree::default();
324 let id = self.id_alloc.open_delim(range); 325 let id = self.id_alloc().open_delim(range);
325 subtree.delimiter = Some(tt::Delimiter { kind, id }); 326 subtree.delimiter = Some(tt::Delimiter { kind, id });
326 327
327 while self.peek().map(|it| it.kind != closed).unwrap_or(false) { 328 while self.peek().map(|it| it.kind() != closed).unwrap_or(false) {
328 self.collect_leaf(&mut subtree.token_trees); 329 self.collect_leaf(&mut subtree.token_trees);
329 } 330 }
330 let last_range = match self.bump() { 331 let last_range = match self.bump() {
331 None => return, 332 None => {
333 // For error resilience, we insert an char punct for the opening delim here
334 self.id_alloc().close_delim(id, None);
335 let leaf: tt::Leaf = tt::Punct {
336 id: self.id_alloc().alloc(range),
337 char: token.to_char().unwrap(),
338 spacing: tt::Spacing::Alone,
339 }
340 .into();
341 result.push(leaf.into());
342 result.extend(subtree.token_trees);
343 return;
344 }
332 Some(it) => it.1, 345 Some(it) => it.1,
333 }; 346 };
334 self.id_alloc.close_delim(id, last_range); 347 self.id_alloc().close_delim(id, Some(last_range));
335 subtree.into() 348 subtree.into()
336 } else { 349 } else {
337 let spacing = match self.peek() { 350 let spacing = match self.peek() {
338 Some(next) 351 Some(next)
339 if next.kind.is_trivia() 352 if next.kind().is_trivia()
340 || next.kind == T!['['] 353 || next.kind() == T!['[']
341 || next.kind == T!['{'] 354 || next.kind() == T!['{']
342 || next.kind == T!['('] => 355 || next.kind() == T!['('] =>
343 { 356 {
344 tt::Spacing::Alone 357 tt::Spacing::Alone
345 } 358 }
346 Some(next) if next.kind.is_punct() => tt::Spacing::Joint, 359 Some(next) if next.kind().is_punct() => tt::Spacing::Joint,
347 _ => tt::Spacing::Alone, 360 _ => tt::Spacing::Alone,
348 }; 361 };
349 let char = 362 let char = token.to_char().expect("Token from lexer must be single char");
350 self.text[range].chars().next().expect("Token from lexer must be single char");
351 363
352 tt::Leaf::from(tt::Punct { char, spacing, id: self.id_alloc.alloc(range) }).into() 364 tt::Leaf::from(tt::Punct { char, spacing, id: self.id_alloc().alloc(range) }).into()
353 } 365 }
354 } else { 366 } else {
355 macro_rules! make_leaf { 367 macro_rules! make_leaf {
356 ($i:ident) => { 368 ($i:ident) => {
357 tt::$i { id: self.id_alloc.alloc(range), text: self.text[range].into() }.into() 369 tt::$i { id: self.id_alloc().alloc(range), text: token.to_text() }.into()
358 }; 370 };
359 } 371 }
360 let leaf: tt::Leaf = match k { 372 let leaf: tt::Leaf = match k {
@@ -368,133 +380,168 @@ impl RawConvertor<'_> {
368 leaf.into() 380 leaf.into()
369 }); 381 });
370 } 382 }
383
384 fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>>;
385
386 fn bump(&mut self) -> Option<(Self::Token, TextRange)>;
387
388 fn peek(&self) -> Option<Self::Token>;
389
390 fn id_alloc(&mut self) -> &mut TokenIdAlloc;
391}
392
393impl<'a> SrcToken for (RawToken, &'a str) {
394 fn kind(&self) -> SyntaxKind {
395 self.0.kind
396 }
397
398 fn to_char(&self) -> Option<char> {
399 self.1.chars().next()
400 }
401
402 fn to_text(&self) -> SmolStr {
403 self.1.into()
404 }
405}
406
407impl RawConvertor<'_> {}
408
409impl<'a> TokenConvertor for RawConvertor<'a> {
410 type Token = (RawToken, &'a str);
411
412 fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
413 convert_doc_comment(&doc_comment(token.1))
414 }
415
416 fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
417 let token = self.inner.next()?;
418 let range = TextRange::offset_len(self.offset, token.len);
419 self.offset += token.len;
420
421 Some(((*token, &self.text[range]), range))
422 }
423
424 fn peek(&self) -> Option<Self::Token> {
425 let token = self.inner.as_slice().get(0).cloned();
426
427 token.map(|it| {
428 let range = TextRange::offset_len(self.offset, it.len);
429 (it, &self.text[range])
430 })
431 }
432
433 fn id_alloc(&mut self) -> &mut TokenIdAlloc {
434 &mut self.id_alloc
435 }
371} 436}
372 437
373// FIXME: There are some duplicate logic between RawConvertor and Convertor
374// It would be nice to refactor to converting SyntaxNode to ra_parser::Token and thus
375// use RawConvertor directly. But performance-wise it may not be a good idea ?
376struct Convertor { 438struct Convertor {
377 id_alloc: TokenIdAlloc, 439 id_alloc: TokenIdAlloc,
440 current: Option<SyntaxToken>,
441 range: TextRange,
442 punct_offset: Option<(SyntaxToken, TextUnit)>,
378} 443}
379 444
380impl Convertor { 445impl Convertor {
381 fn go(&mut self, tt: &SyntaxNode) -> Option<tt::Subtree> { 446 fn new(node: &SyntaxNode, global_offset: TextUnit) -> Convertor {
382 // This tree is empty 447 Convertor {
383 if tt.first_child_or_token().is_none() { 448 id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } },
384 return Some(tt::Subtree { token_trees: vec![], delimiter: None }); 449 current: node.first_token(),
450 range: node.text_range(),
451 punct_offset: None,
385 } 452 }
453 }
454}
386 455
387 let first_child = tt.first_child_or_token()?; 456enum SynToken {
388 let last_child = tt.last_child_or_token()?; 457 Ordiniary(SyntaxToken),
458 Punch(SyntaxToken, TextUnit),
459}
389 460
390 // ignore trivial first_child and last_child 461impl SynToken {
391 let first_child = successors(Some(first_child), |it| { 462 fn token(&self) -> &SyntaxToken {
392 if it.kind().is_trivia() { 463 match self {
393 it.next_sibling_or_token() 464 SynToken::Ordiniary(it) => it,
394 } else { 465 SynToken::Punch(it, _) => it,
395 None
396 }
397 })
398 .last()
399 .unwrap();
400 if first_child.kind().is_trivia() {
401 return Some(tt::Subtree { token_trees: vec![], delimiter: None });
402 } 466 }
467 }
468}
403 469
404 let last_child = successors(Some(last_child), |it| { 470impl SrcToken for SynToken {
405 if it.kind().is_trivia() { 471 fn kind(&self) -> SyntaxKind {
406 it.prev_sibling_or_token() 472 self.token().kind()
407 } else { 473 }
408 None 474 fn to_char(&self) -> Option<char> {
475 match self {
476 SynToken::Ordiniary(_) => None,
477 SynToken::Punch(it, i) => it.text().chars().nth(i.to_usize()),
478 }
479 }
480 fn to_text(&self) -> SmolStr {
481 self.token().text().clone()
482 }
483}
484
485impl TokenConvertor for Convertor {
486 type Token = SynToken;
487 fn convert_doc_comment(&self, token: &Self::Token) -> Option<Vec<tt::TokenTree>> {
488 convert_doc_comment(token.token())
489 }
490
491 fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
492 if let Some((punct, offset)) = self.punct_offset.clone() {
493 if offset.to_usize() + 1 < punct.text().len() {
494 let offset = offset + TextUnit::from_usize(1);
495 let range = punct.text_range();
496 self.punct_offset = Some((punct.clone(), offset));
497 let range = TextRange::offset_len(range.start() + offset, TextUnit::from_usize(1));
498 return Some((SynToken::Punch(punct, offset), range));
409 } 499 }
410 }) 500 }
411 .last() 501
412 .unwrap(); 502 let curr = self.current.clone()?;
413 503 if !curr.text_range().is_subrange(&self.range) {
414 let (delimiter_kind, skip_first) = match (first_child.kind(), last_child.kind()) { 504 return None;
415 (T!['('], T![')']) => (Some(tt::DelimiterKind::Parenthesis), true), 505 }
416 (T!['{'], T!['}']) => (Some(tt::DelimiterKind::Brace), true), 506 self.current = curr.next_token();
417 (T!['['], T![']']) => (Some(tt::DelimiterKind::Bracket), true), 507
418 _ => (None, false), 508 let token = if curr.kind().is_punct() {
509 let range = curr.text_range();
510 let range = TextRange::offset_len(range.start(), TextUnit::from_usize(1));
511 self.punct_offset = Some((curr.clone(), TextUnit::from_usize(0)));
512 (SynToken::Punch(curr, TextUnit::from_usize(0)), range)
513 } else {
514 self.punct_offset = None;
515 let range = curr.text_range();
516 (SynToken::Ordiniary(curr), range)
419 }; 517 };
420 let delimiter = delimiter_kind.map(|kind| tt::Delimiter {
421 kind,
422 id: self.id_alloc.delim(first_child.text_range(), last_child.text_range()),
423 });
424 518
425 let mut token_trees = Vec::new(); 519 Some(token)
426 let mut child_iter = tt.children_with_tokens().skip(skip_first as usize).peekable(); 520 }
427 521
428 while let Some(child) = child_iter.next() { 522 fn peek(&self) -> Option<Self::Token> {
429 if skip_first && (child == first_child || child == last_child) { 523 if let Some((punct, mut offset)) = self.punct_offset.clone() {
430 continue; 524 offset = offset + TextUnit::from_usize(1);
525 if offset.to_usize() < punct.text().len() {
526 return Some(SynToken::Punch(punct, offset));
431 } 527 }
528 }
432 529
433 match child { 530 let curr = self.current.clone()?;
434 NodeOrToken::Token(token) => { 531 if !curr.text_range().is_subrange(&self.range) {
435 if let Some(doc_tokens) = convert_doc_comment(&token) { 532 return None;
436 token_trees.extend(doc_tokens);
437 } else if token.kind().is_trivia() {
438 continue;
439 } else if token.kind().is_punct() {
440 // we need to pull apart joined punctuation tokens
441 let last_spacing = match child_iter.peek() {
442 Some(NodeOrToken::Token(token)) => {
443 if token.kind().is_punct() {
444 tt::Spacing::Joint
445 } else {
446 tt::Spacing::Alone
447 }
448 }
449 _ => tt::Spacing::Alone,
450 };
451 let spacing_iter = std::iter::repeat(tt::Spacing::Joint)
452 .take(token.text().len() - 1)
453 .chain(std::iter::once(last_spacing));
454 for (char, spacing) in token.text().chars().zip(spacing_iter) {
455 token_trees.push(
456 tt::Leaf::from(tt::Punct {
457 char,
458 spacing,
459 id: self.id_alloc.alloc(token.text_range()),
460 })
461 .into(),
462 );
463 }
464 } else {
465 macro_rules! make_leaf {
466 ($i:ident) => {
467 tt::$i {
468 id: self.id_alloc.alloc(token.text_range()),
469 text: token.text().clone(),
470 }
471 .into()
472 };
473 }
474
475 let child: tt::Leaf = match token.kind() {
476 T![true] | T![false] => make_leaf!(Literal),
477 IDENT | LIFETIME => make_leaf!(Ident),
478 k if k.is_keyword() => make_leaf!(Ident),
479 k if k.is_literal() => make_leaf!(Literal),
480 _ => return None,
481 };
482 token_trees.push(child.into());
483 }
484 }
485 NodeOrToken::Node(node) => {
486 let child_subtree = self.go(&node)?;
487 if child_subtree.delimiter.is_none() && node.kind() != SyntaxKind::TOKEN_TREE {
488 token_trees.extend(child_subtree.token_trees);
489 } else {
490 token_trees.push(child_subtree.into());
491 }
492 }
493 };
494 } 533 }
495 534
496 let res = tt::Subtree { delimiter, token_trees }; 535 let token = if curr.kind().is_punct() {
497 Some(res) 536 SynToken::Punch(curr, TextUnit::from_usize(0))
537 } else {
538 SynToken::Ordiniary(curr)
539 };
540 Some(token)
541 }
542
543 fn id_alloc(&mut self) -> &mut TokenIdAlloc {
544 &mut self.id_alloc
498 } 545 }
499} 546}
500 547
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs
index 44f381938..a7fcea0ac 100644
--- a/crates/ra_mbe/src/tests.rs
+++ b/crates/ra_mbe/src/tests.rs
@@ -427,22 +427,28 @@ MACRO_ITEMS@[0; 40)
427 ); 427 );
428} 428}
429 429
430#[test] 430fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree {
431fn test_expand_literals_to_token_tree() { 431 if let tt::TokenTree::Subtree(subtree) = tt {
432 fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { 432 return &subtree;
433 if let tt::TokenTree::Subtree(subtree) = tt {
434 return &subtree;
435 }
436 unreachable!("It is not a subtree");
437 } 433 }
434 unreachable!("It is not a subtree");
435}
436fn to_literal(tt: &tt::TokenTree) -> &tt::Literal {
437 if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt {
438 return lit;
439 }
440 unreachable!("It is not a literal");
441}
438 442
439 fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { 443fn to_punct(tt: &tt::TokenTree) -> &tt::Punct {
440 if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { 444 if let tt::TokenTree::Leaf(tt::Leaf::Punct(lit)) = tt {
441 return lit; 445 return lit;
442 }
443 unreachable!("It is not a literal");
444 } 446 }
447 unreachable!("It is not a Punct");
448}
445 449
450#[test]
451fn test_expand_literals_to_token_tree() {
446 let expansion = parse_macro( 452 let expansion = parse_macro(
447 r#" 453 r#"
448 macro_rules! literals { 454 macro_rules! literals {
@@ -471,6 +477,22 @@ fn test_expand_literals_to_token_tree() {
471} 477}
472 478
473#[test] 479#[test]
480fn test_attr_to_token_tree() {
481 let expansion = parse_to_token_tree_by_syntax(
482 r#"
483 #[derive(Copy)]
484 struct Foo;
485 "#,
486 );
487
488 assert_eq!(to_punct(&expansion.token_trees[0]).char, '#');
489 assert_eq!(
490 to_subtree(&expansion.token_trees[1]).delimiter_kind(),
491 Some(tt::DelimiterKind::Bracket)
492 );
493}
494
495#[test]
474fn test_two_idents() { 496fn test_two_idents() {
475 parse_macro( 497 parse_macro(
476 r#" 498 r#"
@@ -1427,8 +1449,8 @@ impl MacroFixture {
1427 let macro_invocation = 1449 let macro_invocation =
1428 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); 1450 source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
1429 1451
1430 let (invocation_tt, _) = 1452 let (invocation_tt, _) = ast_to_token_tree(&macro_invocation.token_tree().unwrap())
1431 ast_to_token_tree(&macro_invocation.token_tree().unwrap()).unwrap(); 1453 .ok_or_else(|| ExpandError::ConversionError)?;
1432 1454
1433 self.rules.expand(&invocation_tt).result() 1455 self.rules.expand(&invocation_tt).result()
1434 } 1456 }
@@ -1517,6 +1539,16 @@ pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture {
1517 MacroFixture { rules } 1539 MacroFixture { rules }
1518} 1540}
1519 1541
1542pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree {
1543 let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap();
1544 let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0;
1545
1546 let parsed = parse_to_token_tree(ra_fixture).unwrap().0;
1547 assert_eq!(tt, parsed);
1548
1549 parsed
1550}
1551
1520fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { 1552fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String {
1521 let mut level = 0; 1553 let mut level = 0;
1522 let mut buf = String::new(); 1554 let mut buf = String::new();
@@ -1662,5 +1694,5 @@ fn test_expand_bad_literal() {
1662 macro_rules! foo { ($i:literal) => {}; } 1694 macro_rules! foo { ($i:literal) => {}; }
1663 "#, 1695 "#,
1664 ) 1696 )
1665 .assert_expand_err(r#"foo!(&k");"#, &ExpandError::BindingError("".to_string())); 1697 .assert_expand_err(r#"foo!(&k");"#, &ExpandError::BindingError("".into()));
1666} 1698}
diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs
index 2335d99b3..0d277a586 100644
--- a/crates/ra_parser/src/grammar/expressions/atom.rs
+++ b/crates/ra_parser/src/grammar/expressions/atom.rs
@@ -230,14 +230,20 @@ fn lambda_expr(p: &mut Parser) -> CompletedMarker {
230 p.eat(T![async]); 230 p.eat(T![async]);
231 p.eat(T![move]); 231 p.eat(T![move]);
232 params::param_list_closure(p); 232 params::param_list_closure(p);
233 if opt_fn_ret_type(p) && !p.at(T!['{']) { 233 if opt_fn_ret_type(p) {
234 p.error("expected `{`"); 234 if p.at(T!['{']) {
235 } 235 // test lambda_ret_block
236 236 // fn main() { || -> i32 { 92 }(); }
237 if p.at_ts(EXPR_FIRST) { 237 block_expr(p, None);
238 expr(p); 238 } else {
239 p.error("expected `{`");
240 }
239 } else { 241 } else {
240 p.error("expected expression"); 242 if p.at_ts(EXPR_FIRST) {
243 expr(p);
244 } else {
245 p.error("expected expression");
246 }
241 } 247 }
242 m.complete(p, LAMBDA_EXPR) 248 m.complete(p, LAMBDA_EXPR)
243} 249}
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 8efc6b368..6fccc2303 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -11,7 +11,7 @@ repository = "https://github.com/rust-analyzer/rust-analyzer"
11doctest = false 11doctest = false
12 12
13[dependencies] 13[dependencies]
14itertools = "0.8.2" 14itertools = "0.9.0"
15rowan = "0.9.1" 15rowan = "0.9.1"
16rustc_lexer = "0.1.0" 16rustc_lexer = "0.1.0"
17rustc-hash = "1.1.0" 17rustc-hash = "1.1.0"
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index ffdbdc767..191123c8e 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -1,6 +1,9 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use std::ops::RangeInclusive; 3use std::{
4 fmt,
5 ops::{self, RangeInclusive},
6};
4 7
5use itertools::Itertools; 8use itertools::Itertools;
6use ra_text_edit::TextEditBuilder; 9use ra_text_edit::TextEditBuilder;
@@ -222,44 +225,110 @@ fn _replace_children(
222 with_children(parent, new_children) 225 with_children(parent, new_children)
223} 226}
224 227
225/// Replaces descendants in the node, according to the mapping. 228#[derive(Default)]
226/// 229pub struct SyntaxRewriter<'a> {
227/// This is a type-unsafe low-level editing API, if you need to use it, prefer 230 f: Option<Box<dyn Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a>>,
228/// to create a type-safe abstraction on top of it instead. 231 //FIXME: add debug_assertions that all elements are in fact from the same file.
229pub fn replace_descendants( 232 replacements: FxHashMap<SyntaxElement, Replacement>,
230 parent: &SyntaxNode,
231 map: impl Fn(&SyntaxElement) -> Option<SyntaxElement>,
232) -> SyntaxNode {
233 _replace_descendants(parent, &map)
234} 233}
235 234
236fn _replace_descendants( 235impl fmt::Debug for SyntaxRewriter<'_> {
237 parent: &SyntaxNode, 236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
238 map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>, 237 f.debug_struct("SyntaxRewriter").field("replacements", &self.replacements).finish()
239) -> SyntaxNode { 238 }
240 // FIXME: this could be made much faster. 239}
241 let new_children = parent.children_with_tokens().map(|it| go(map, it)).collect::<Vec<_>>();
242 return with_children(parent, new_children);
243 240
244 fn go( 241impl<'a> SyntaxRewriter<'a> {
245 map: &dyn Fn(&SyntaxElement) -> Option<SyntaxElement>, 242 pub fn from_fn(f: impl Fn(&SyntaxElement) -> Option<SyntaxElement> + 'a) -> SyntaxRewriter<'a> {
246 element: SyntaxElement, 243 SyntaxRewriter { f: Some(Box::new(f)), replacements: FxHashMap::default() }
247 ) -> NodeOrToken<rowan::GreenNode, rowan::GreenToken> { 244 }
248 if let Some(replacement) = map(&element) { 245 pub fn delete<T: Clone + Into<SyntaxElement>>(&mut self, what: &T) {
246 let what = what.clone().into();
247 let replacement = Replacement::Delete;
248 self.replacements.insert(what, replacement);
249 }
250 pub fn replace<T: Clone + Into<SyntaxElement>>(&mut self, what: &T, with: &T) {
251 let what = what.clone().into();
252 let replacement = Replacement::Single(with.clone().into());
253 self.replacements.insert(what, replacement);
254 }
255 pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) {
256 self.replace(what.syntax(), with.syntax())
257 }
258
259 pub fn rewrite(&self, node: &SyntaxNode) -> SyntaxNode {
260 if self.f.is_none() && self.replacements.is_empty() {
261 return node.clone();
262 }
263 self.rewrite_children(node)
264 }
265
266 pub fn rewrite_ast<N: AstNode>(self, node: &N) -> N {
267 N::cast(self.rewrite(node.syntax())).unwrap()
268 }
269
270 pub fn rewrite_root(&self) -> Option<SyntaxNode> {
271 assert!(self.f.is_none());
272 self.replacements
273 .keys()
274 .map(|element| match element {
275 SyntaxElement::Node(it) => it.clone(),
276 SyntaxElement::Token(it) => it.parent(),
277 })
278 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
279 }
280
281 fn replacement(&self, element: &SyntaxElement) -> Option<Replacement> {
282 if let Some(f) = &self.f {
283 assert!(self.replacements.is_empty());
284 return f(element).map(Replacement::Single);
285 }
286 self.replacements.get(element).cloned()
287 }
288
289 fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode {
290 // FIXME: this could be made much faster.
291 let new_children =
292 node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>();
293 with_children(node, new_children)
294 }
295
296 fn rewrite_self(
297 &self,
298 element: &SyntaxElement,
299 ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> {
300 if let Some(replacement) = self.replacement(&element) {
249 return match replacement { 301 return match replacement {
250 NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), 302 Replacement::Single(NodeOrToken::Node(it)) => {
251 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), 303 Some(NodeOrToken::Node(it.green().clone()))
304 }
305 Replacement::Single(NodeOrToken::Token(it)) => {
306 Some(NodeOrToken::Token(it.green().clone()))
307 }
308 Replacement::Delete => None,
252 }; 309 };
253 } 310 }
254 match element { 311 let res = match element {
255 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), 312 NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()),
256 NodeOrToken::Node(it) => { 313 NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()),
257 NodeOrToken::Node(_replace_descendants(&it, map).green().clone()) 314 };
258 } 315 Some(res)
259 }
260 } 316 }
261} 317}
262 318
319impl<'a> ops::AddAssign for SyntaxRewriter<'_> {
320 fn add_assign(&mut self, rhs: SyntaxRewriter) {
321 assert!(rhs.f.is_none());
322 self.replacements.extend(rhs.replacements)
323 }
324}
325
326#[derive(Clone, Debug)]
327enum Replacement {
328 Delete,
329 Single(SyntaxElement),
330}
331
263fn with_children( 332fn with_children(
264 parent: &SyntaxNode, 333 parent: &SyntaxNode,
265 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, 334 new_children: Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>,
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs
index 68dae008f..f74c9f9c6 100644
--- a/crates/ra_syntax/src/ast/edit.rs
+++ b/crates/ra_syntax/src/ast/edit.rs
@@ -4,7 +4,6 @@
4use std::{iter, ops::RangeInclusive}; 4use std::{iter, ops::RangeInclusive};
5 5
6use arrayvec::ArrayVec; 6use arrayvec::ArrayVec;
7use rustc_hash::FxHashMap;
8 7
9use crate::{ 8use crate::{
10 algo, 9 algo,
@@ -17,6 +16,7 @@ use crate::{
17 SyntaxKind::{ATTR, COMMENT, WHITESPACE}, 16 SyntaxKind::{ATTR, COMMENT, WHITESPACE},
18 SyntaxNode, SyntaxToken, T, 17 SyntaxNode, SyntaxToken, T,
19}; 18};
19use algo::{neighbor, SyntaxRewriter};
20 20
21impl ast::BinExpr { 21impl ast::BinExpr {
22 #[must_use] 22 #[must_use]
@@ -255,6 +255,28 @@ impl ast::UseItem {
255 } 255 }
256 self.clone() 256 self.clone()
257 } 257 }
258
259 pub fn remove(&self) -> SyntaxRewriter<'static> {
260 let mut res = SyntaxRewriter::default();
261 res.delete(self.syntax());
262 let next_ws = self
263 .syntax()
264 .next_sibling_or_token()
265 .and_then(|it| it.into_token())
266 .and_then(ast::Whitespace::cast);
267 if let Some(next_ws) = next_ws {
268 let ws_text = next_ws.syntax().text();
269 if ws_text.starts_with('\n') {
270 let rest = &ws_text[1..];
271 if rest.is_empty() {
272 res.delete(next_ws.syntax())
273 } else {
274 res.replace(next_ws.syntax(), &make::tokens::whitespace(rest));
275 }
276 }
277 }
278 res
279 }
258} 280}
259 281
260impl ast::UseTree { 282impl ast::UseTree {
@@ -293,14 +315,30 @@ impl ast::UseTree {
293 Some(res) 315 Some(res)
294 } 316 }
295 } 317 }
318
319 pub fn remove(&self) -> SyntaxRewriter<'static> {
320 let mut res = SyntaxRewriter::default();
321 res.delete(self.syntax());
322 for &dir in [Direction::Next, Direction::Prev].iter() {
323 if let Some(nb) = neighbor(self, dir) {
324 self.syntax()
325 .siblings_with_tokens(dir)
326 .skip(1)
327 .take_while(|it| it.as_node() != Some(nb.syntax()))
328 .for_each(|el| res.delete(&el));
329 return res;
330 }
331 }
332 res
333 }
296} 334}
297 335
298#[must_use] 336#[must_use]
299pub fn strip_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N { 337pub fn remove_attrs_and_docs<N: ast::AttrsOwner>(node: &N) -> N {
300 N::cast(strip_attrs_and_docs_inner(node.syntax().clone())).unwrap() 338 N::cast(remove_attrs_and_docs_inner(node.syntax().clone())).unwrap()
301} 339}
302 340
303fn strip_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode { 341fn remove_attrs_and_docs_inner(mut node: SyntaxNode) -> SyntaxNode {
304 while let Some(start) = 342 while let Some(start) =
305 node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT) 343 node.children_with_tokens().find(|it| it.kind() == ATTR || it.kind() == COMMENT)
306 { 344 {
@@ -343,28 +381,24 @@ impl IndentLevel {
343 } 381 }
344 382
345 fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode { 383 fn _increase_indent(self, node: SyntaxNode) -> SyntaxNode {
346 let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node 384 let mut rewriter = SyntaxRewriter::default();
347 .descendants_with_tokens() 385 node.descendants_with_tokens()
348 .filter_map(|el| el.into_token()) 386 .filter_map(|el| el.into_token())
349 .filter_map(ast::Whitespace::cast) 387 .filter_map(ast::Whitespace::cast)
350 .filter(|ws| { 388 .filter(|ws| {
351 let text = ws.syntax().text(); 389 let text = ws.syntax().text();
352 text.contains('\n') 390 text.contains('\n')
353 }) 391 })
354 .map(|ws| { 392 .for_each(|ws| {
355 ( 393 let new_ws = make::tokens::whitespace(&format!(
356 ws.syntax().clone().into(), 394 "{}{:width$}",
357 make::tokens::whitespace(&format!( 395 ws.syntax().text(),
358 "{}{:width$}", 396 "",
359 ws.syntax().text(), 397 width = self.0 as usize * 4
360 "", 398 ));
361 width = self.0 as usize * 4 399 rewriter.replace(ws.syntax(), &new_ws)
362 )) 400 });
363 .into(), 401 rewriter.rewrite(&node)
364 )
365 })
366 .collect();
367 algo::replace_descendants(&node, |n| replacements.get(n).cloned())
368 } 402 }
369 403
370 pub fn decrease_indent<N: AstNode>(self, node: N) -> N { 404 pub fn decrease_indent<N: AstNode>(self, node: N) -> N {
@@ -372,27 +406,21 @@ impl IndentLevel {
372 } 406 }
373 407
374 fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode { 408 fn _decrease_indent(self, node: SyntaxNode) -> SyntaxNode {
375 let replacements: FxHashMap<SyntaxElement, SyntaxElement> = node 409 let mut rewriter = SyntaxRewriter::default();
376 .descendants_with_tokens() 410 node.descendants_with_tokens()
377 .filter_map(|el| el.into_token()) 411 .filter_map(|el| el.into_token())
378 .filter_map(ast::Whitespace::cast) 412 .filter_map(ast::Whitespace::cast)
379 .filter(|ws| { 413 .filter(|ws| {
380 let text = ws.syntax().text(); 414 let text = ws.syntax().text();
381 text.contains('\n') 415 text.contains('\n')
382 }) 416 })
383 .map(|ws| { 417 .for_each(|ws| {
384 ( 418 let new_ws = make::tokens::whitespace(
385 ws.syntax().clone().into(), 419 &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"),
386 make::tokens::whitespace( 420 );
387 &ws.syntax() 421 rewriter.replace(ws.syntax(), &new_ws)
388 .text() 422 });
389 .replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), 423 rewriter.rewrite(&node)
390 )
391 .into(),
392 )
393 })
394 .collect();
395 algo::replace_descendants(&node, |n| replacements.get(n).cloned())
396 } 424 }
397} 425}
398 426
@@ -442,12 +470,11 @@ pub trait AstNodeEdit: AstNode + Sized {
442 &self, 470 &self,
443 replacement_map: impl IntoIterator<Item = (D, D)>, 471 replacement_map: impl IntoIterator<Item = (D, D)>,
444 ) -> Self { 472 ) -> Self {
445 let map = replacement_map 473 let mut rewriter = SyntaxRewriter::default();
446 .into_iter() 474 for (from, to) in replacement_map {
447 .map(|(from, to)| (from.syntax().clone().into(), to.syntax().clone().into())) 475 rewriter.replace(from.syntax(), to.syntax())
448 .collect::<FxHashMap<SyntaxElement, _>>(); 476 }
449 let new_syntax = algo::replace_descendants(self.syntax(), |n| map.get(n).cloned()); 477 rewriter.rewrite_ast(self)
450 Self::cast(new_syntax).unwrap()
451 } 478 }
452} 479}
453 480
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 9257ccd1a..1145b69e8 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -87,6 +87,9 @@ pub fn block_from_expr(e: ast::Expr) -> ast::Block {
87pub fn expr_unit() -> ast::Expr { 87pub fn expr_unit() -> ast::Expr {
88 expr_from_text("()") 88 expr_from_text("()")
89} 89}
90pub fn expr_empty_block() -> ast::Expr {
91 expr_from_text("{}")
92}
90pub fn expr_unimplemented() -> ast::Expr { 93pub fn expr_unimplemented() -> ast::Expr {
91 expr_from_text("unimplemented!()") 94 expr_from_text("unimplemented!()")
92} 95}
diff --git a/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt b/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt
index e0edf6a2d..0ffbd25aa 100644
--- a/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt
+++ b/crates/ra_syntax/test_data/parser/err/0010_unsafe_lambda_block.txt
@@ -12,8 +12,8 @@ SOURCE_FILE@[0; 42)
12 BLOCK@[10; 41) 12 BLOCK@[10; 41)
13 L_CURLY@[10; 11) "{" 13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 16) "\n " 14 WHITESPACE@[11; 16) "\n "
15 EXPR_STMT@[16; 39) 15 EXPR_STMT@[16; 24)
16 LAMBDA_EXPR@[16; 38) 16 LAMBDA_EXPR@[16; 24)
17 PARAM_LIST@[16; 18) 17 PARAM_LIST@[16; 18)
18 PIPE@[16; 17) "|" 18 PIPE@[16; 17) "|"
19 PIPE@[17; 18) "|" 19 PIPE@[17; 18) "|"
@@ -24,20 +24,22 @@ SOURCE_FILE@[0; 42)
24 TUPLE_TYPE@[22; 24) 24 TUPLE_TYPE@[22; 24)
25 L_PAREN@[22; 23) "(" 25 L_PAREN@[22; 23) "("
26 R_PAREN@[23; 24) ")" 26 R_PAREN@[23; 24) ")"
27 WHITESPACE@[24; 25) " " 27 WHITESPACE@[24; 25) " "
28 BLOCK_EXPR@[25; 38) 28 EXPR_STMT@[25; 39)
29 UNSAFE_KW@[25; 31) "unsafe" 29 BLOCK_EXPR@[25; 38)
30 WHITESPACE@[31; 32) " " 30 UNSAFE_KW@[25; 31) "unsafe"
31 BLOCK@[32; 38) 31 WHITESPACE@[31; 32) " "
32 L_CURLY@[32; 33) "{" 32 BLOCK@[32; 38)
33 WHITESPACE@[33; 34) " " 33 L_CURLY@[32; 33) "{"
34 TUPLE_EXPR@[34; 36) 34 WHITESPACE@[33; 34) " "
35 L_PAREN@[34; 35) "(" 35 TUPLE_EXPR@[34; 36)
36 R_PAREN@[35; 36) ")" 36 L_PAREN@[34; 35) "("
37 WHITESPACE@[36; 37) " " 37 R_PAREN@[35; 36) ")"
38 R_CURLY@[37; 38) "}" 38 WHITESPACE@[36; 37) " "
39 R_CURLY@[37; 38) "}"
39 SEMI@[38; 39) ";" 40 SEMI@[38; 39) ";"
40 WHITESPACE@[39; 40) "\n" 41 WHITESPACE@[39; 40) "\n"
41 R_CURLY@[40; 41) "}" 42 R_CURLY@[40; 41) "}"
42 WHITESPACE@[41; 42) "\n" 43 WHITESPACE@[41; 42) "\n"
43error [24; 24): expected `{` 44error [24; 24): expected `{`
45error [24; 24): expected SEMI
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.rs b/crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.rs
new file mode 100644
index 000000000..061118d3a
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.rs
@@ -0,0 +1 @@
fn main() { || -> i32 { 92 }(); }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.txt b/crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.txt
new file mode 100644
index 000000000..ba8779094
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0158_lambda_ret_block.txt
@@ -0,0 +1,45 @@
1SOURCE_FILE@[0; 34)
2 FN_DEF@[0; 33)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK_EXPR@[10; 33)
12 BLOCK@[10; 33)
13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 12) " "
15 EXPR_STMT@[12; 31)
16 CALL_EXPR@[12; 30)
17 LAMBDA_EXPR@[12; 28)
18 PARAM_LIST@[12; 14)
19 PIPE@[12; 13) "|"
20 PIPE@[13; 14) "|"
21 WHITESPACE@[14; 15) " "
22 RET_TYPE@[15; 21)
23 THIN_ARROW@[15; 17) "->"
24 WHITESPACE@[17; 18) " "
25 PATH_TYPE@[18; 21)
26 PATH@[18; 21)
27 PATH_SEGMENT@[18; 21)
28 NAME_REF@[18; 21)
29 IDENT@[18; 21) "i32"
30 WHITESPACE@[21; 22) " "
31 BLOCK_EXPR@[22; 28)
32 BLOCK@[22; 28)
33 L_CURLY@[22; 23) "{"
34 WHITESPACE@[23; 24) " "
35 LITERAL@[24; 26)
36 INT_NUMBER@[24; 26) "92"
37 WHITESPACE@[26; 27) " "
38 R_CURLY@[27; 28) "}"
39 ARG_LIST@[28; 30)
40 L_PAREN@[28; 29) "("
41 R_PAREN@[29; 30) ")"
42 SEMI@[30; 31) ";"
43 WHITESPACE@[31; 32) " "
44 R_CURLY@[32; 33) "}"
45 WHITESPACE@[33; 34) "\n"
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index d44f0ef1d..e071e9b8d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -17,7 +17,7 @@ anyhow = "1.0.26"
17crossbeam-channel = "0.4.0" 17crossbeam-channel = "0.4.0"
18env_logger = { version = "0.7.1", default-features = false } 18env_logger = { version = "0.7.1", default-features = false }
19globset = "0.4.4" 19globset = "0.4.4"
20itertools = "0.8.2" 20itertools = "0.9.0"
21jod-thread = "0.1.0" 21jod-thread = "0.1.0"
22log = "0.4.8" 22log = "0.4.8"
23lsp-types = { version = "0.73.0", features = ["proposed"] } 23lsp-types = { version = "0.73.0", features = ["proposed"] }
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 103b2b53c..628ed107e 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -34,6 +34,8 @@ pub struct ServerConfig {
34 pub inlay_hints_type: bool, 34 pub inlay_hints_type: bool,
35 #[serde(deserialize_with = "nullable_bool_true")] 35 #[serde(deserialize_with = "nullable_bool_true")]
36 pub inlay_hints_parameter: bool, 36 pub inlay_hints_parameter: bool,
37 #[serde(deserialize_with = "nullable_bool_true")]
38 pub inlay_hints_chaining: bool,
37 pub inlay_hints_max_length: Option<usize>, 39 pub inlay_hints_max_length: Option<usize>,
38 40
39 pub cargo_watch_enable: bool, 41 pub cargo_watch_enable: bool,
@@ -66,6 +68,7 @@ impl Default for ServerConfig {
66 lru_capacity: None, 68 lru_capacity: None,
67 inlay_hints_type: true, 69 inlay_hints_type: true,
68 inlay_hints_parameter: true, 70 inlay_hints_parameter: true,
71 inlay_hints_chaining: true,
69 inlay_hints_max_length: None, 72 inlay_hints_max_length: None,
70 cargo_watch_enable: true, 73 cargo_watch_enable: true,
71 cargo_watch_args: Vec::new(), 74 cargo_watch_args: Vec::new(),
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs
index fd4657d7e..6edc03fe0 100644
--- a/crates/rust-analyzer/src/conv.rs
+++ b/crates/rust-analyzer/src/conv.rs
@@ -332,6 +332,7 @@ impl ConvWith<&LineIndex> for InlayHint {
332 kind: match self.kind { 332 kind: match self.kind {
333 InlayKind::ParameterHint => req::InlayKind::ParameterHint, 333 InlayKind::ParameterHint => req::InlayKind::ParameterHint,
334 InlayKind::TypeHint => req::InlayKind::TypeHint, 334 InlayKind::TypeHint => req::InlayKind::TypeHint,
335 InlayKind::ChainingHint => req::InlayKind::ChainingHint,
335 }, 336 },
336 } 337 }
337 } 338 }
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index a8a5894d2..7825b0077 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -183,6 +183,7 @@ pub fn main_loop(
183 inlay_hints: InlayHintsOptions { 183 inlay_hints: InlayHintsOptions {
184 type_hints: config.inlay_hints_type, 184 type_hints: config.inlay_hints_type,
185 parameter_hints: config.inlay_hints_parameter, 185 parameter_hints: config.inlay_hints_parameter,
186 chaining_hints: config.inlay_hints_chaining,
186 max_length: config.inlay_hints_max_length, 187 max_length: config.inlay_hints_max_length,
187 }, 188 },
188 cargo_watch: CheckOptions { 189 cargo_watch: CheckOptions {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 1cc2f6571..1033d6de9 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -734,19 +734,29 @@ pub fn handle_code_action(
734 res.push(fix.action.clone()); 734 res.push(fix.action.clone());
735 } 735 }
736 736
737 let mut grouped_assists: FxHashMap<String, Vec<Assist>> = FxHashMap::default(); 737 let mut grouped_assists: FxHashMap<String, (usize, Vec<Assist>)> = FxHashMap::default();
738 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() { 738 for assist in world.analysis().assists(FileRange { file_id, range })?.into_iter() {
739 match &assist.group_label { 739 match &assist.group_label {
740 Some(label) => grouped_assists.entry(label.to_owned()).or_default().push(assist), 740 Some(label) => grouped_assists
741 None => res.push(create_single_code_action(assist, &world)?.into()), 741 .entry(label.to_owned())
742 .or_insert_with(|| {
743 let idx = res.len();
744 let dummy = Command::new(String::new(), String::new(), None);
745 res.push(dummy.into());
746 (idx, Vec::new())
747 })
748 .1
749 .push(assist),
750 None => {
751 res.push(create_single_code_action(assist, &world)?.into());
752 }
742 } 753 }
743 } 754 }
744 755
745 for (group_label, assists) in grouped_assists { 756 for (group_label, (idx, assists)) in grouped_assists {
746 if assists.len() == 1 { 757 if assists.len() == 1 {
747 res.push( 758 res[idx] =
748 create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into(), 759 create_single_code_action(assists.into_iter().next().unwrap(), &world)?.into();
749 );
750 } else { 760 } else {
751 let title = group_label; 761 let title = group_label;
752 762
@@ -760,17 +770,15 @@ pub fn handle_code_action(
760 command: "rust-analyzer.selectAndApplySourceChange".to_string(), 770 command: "rust-analyzer.selectAndApplySourceChange".to_string(),
761 arguments: Some(vec![serde_json::Value::Array(arguments)]), 771 arguments: Some(vec![serde_json::Value::Array(arguments)]),
762 }); 772 });
763 res.push( 773 res[idx] = CodeAction {
764 CodeAction { 774 title,
765 title, 775 kind: None,
766 kind: None, 776 diagnostics: None,
767 diagnostics: None, 777 edit: None,
768 edit: None, 778 command,
769 command, 779 is_preferred: None,
770 is_preferred: None, 780 }
771 } 781 .into();
772 .into(),
773 );
774 } 782 }
775 } 783 }
776 784
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 9e27d3f1c..8557294f6 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -200,6 +200,7 @@ pub struct InlayHintsParams {
200pub enum InlayKind { 200pub enum InlayKind {
201 TypeHint, 201 TypeHint,
202 ParameterHint, 202 ParameterHint,
203 ChainingHint,
203} 204}
204 205
205#[derive(Debug, Deserialize, Serialize)] 206#[derive(Debug, Deserialize, Serialize)]