aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir_def/src/item_tree.rs3
-rw-r--r--crates/hir_def/src/item_tree/lower.rs3
-rw-r--r--crates/hir_def/src/lib.rs12
-rw-r--r--crates/hir_def/src/nameres.rs2
-rw-r--r--crates/hir_def/src/nameres/collector.rs20
-rw-r--r--crates/hir_def/src/nameres/tests/incremental.rs6
-rw-r--r--crates/hir_expand/src/builtin_macro.rs5
-rw-r--r--crates/hir_expand/src/db.rs71
-rw-r--r--crates/hir_expand/src/eager.rs7
-rw-r--r--crates/hir_expand/src/lib.rs68
-rw-r--r--crates/hir_expand/src/quote.rs4
-rw-r--r--crates/hir_ty/src/chalk_ext.rs36
-rw-r--r--crates/ide/src/join_lines.rs69
-rw-r--r--crates/ide/src/references.rs64
-rw-r--r--crates/ide/src/references/rename.rs17
-rw-r--r--crates/ide/src/typing.rs150
-rw-r--r--crates/ide/src/typing/on_enter.rs115
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs2
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs4
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs3
-rw-r--r--crates/ide_assists/src/handlers/extract_function.rs62
-rw-r--r--crates/ide_assists/src/handlers/extract_variable.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_function.rs2
-rw-r--r--crates/ide_assists/src/handlers/introduce_named_lifetime.rs46
-rw-r--r--crates/ide_assists/src/handlers/merge_imports.rs4
-rw-r--r--crates/ide_assists/src/handlers/move_bounds.rs6
-rw-r--r--crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs8
-rw-r--r--crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs120
-rw-r--r--crates/ide_assists/src/handlers/replace_let_with_if_let.rs21
-rw-r--r--crates/ide_assists/src/handlers/replace_unwrap_with_match.rs21
-rw-r--r--crates/ide_assists/src/handlers/wrap_return_type_in_result.rs4
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs2
-rw-r--r--crates/ide_assists/src/utils.rs4
-rw-r--r--crates/ide_assists/src/utils/suggest_name.rs15
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/test_utils.rs2
-rw-r--r--crates/ide_db/src/helpers/insert_use/tests.rs84
-rw-r--r--crates/ide_db/src/helpers/merge_imports.rs16
-rw-r--r--crates/ide_db/src/search.rs177
-rw-r--r--crates/ide_db/src/ty_filter.rs6
-rw-r--r--crates/project_model/src/cargo_workspace.rs67
-rw-r--r--crates/project_model/src/rustc_cfg.rs45
-rw-r--r--crates/project_model/src/workspace.rs7
-rw-r--r--crates/rust-analyzer/src/config.rs20
-rw-r--r--crates/rust-analyzer/src/integrated_benchmarks.rs4
-rw-r--r--crates/syntax/src/ast/edit_in_place.rs19
-rw-r--r--crates/syntax/src/ast/make.rs111
49 files changed, 1015 insertions, 529 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index ac23e385e..c9ef4b420 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2071,6 +2071,10 @@ impl Type {
2071 Some(adt.into()) 2071 Some(adt.into())
2072 } 2072 }
2073 2073
2074 pub fn as_builtin(&self) -> Option<BuiltinType> {
2075 self.ty.as_builtin().map(|inner| BuiltinType { inner })
2076 }
2077
2074 pub fn as_dyn_trait(&self) -> Option<Trait> { 2078 pub fn as_dyn_trait(&self) -> Option<Trait> {
2075 self.ty.dyn_trait().map(Into::into) 2079 self.ty.dyn_trait().map(Into::into)
2076 } 2080 }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 8d13c7e04..cad8a7479 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -18,7 +18,7 @@ use hir_expand::{
18 ast_id_map::FileAstId, 18 ast_id_map::FileAstId,
19 hygiene::Hygiene, 19 hygiene::Hygiene,
20 name::{name, AsName, Name}, 20 name::{name, AsName, Name},
21 HirFileId, InFile, 21 FragmentKind, HirFileId, InFile,
22}; 22};
23use la_arena::{Arena, Idx, RawIdx}; 23use la_arena::{Arena, Idx, RawIdx};
24use profile::Count; 24use profile::Count;
@@ -656,6 +656,7 @@ pub struct MacroCall {
656 /// Path to the called macro. 656 /// Path to the called macro.
657 pub path: Interned<ModPath>, 657 pub path: Interned<ModPath>,
658 pub ast_id: FileAstId<ast::MacroCall>, 658 pub ast_id: FileAstId<ast::MacroCall>,
659 pub fragment: FragmentKind,
659} 660}
660 661
661#[derive(Debug, Clone, Eq, PartialEq)] 662#[derive(Debug, Clone, Eq, PartialEq)]
diff --git a/crates/hir_def/src/item_tree/lower.rs b/crates/hir_def/src/item_tree/lower.rs
index 5743b3386..fe348091d 100644
--- a/crates/hir_def/src/item_tree/lower.rs
+++ b/crates/hir_def/src/item_tree/lower.rs
@@ -624,7 +624,8 @@ impl<'a> Ctx<'a> {
624 fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> { 624 fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
625 let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?); 625 let path = Interned::new(ModPath::from_src(self.db, m.path()?, &self.hygiene)?);
626 let ast_id = self.source_ast_id_map.ast_id(m); 626 let ast_id = self.source_ast_id_map.ast_id(m);
627 let res = MacroCall { path, ast_id }; 627 let fragment = hir_expand::to_fragment_kind(m);
628 let res = MacroCall { path, ast_id, fragment };
628 Some(id(self.data().macro_calls.alloc(res))) 629 Some(id(self.data().macro_calls.alloc(res)))
629 } 630 }
630 631
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index da46f16f7..e96ca953f 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -62,7 +62,8 @@ use hir_expand::{
62 ast_id_map::FileAstId, 62 ast_id_map::FileAstId,
63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink}, 63 eager::{expand_eager_macro, ErrorEmitted, ErrorSink},
64 hygiene::Hygiene, 64 hygiene::Hygiene,
65 AstId, AttrId, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 65 AstId, AttrId, FragmentKind, HirFileId, InFile, MacroCallId, MacroCallKind, MacroDefId,
66 MacroDefKind,
66}; 67};
67use la_arena::Idx; 68use la_arena::Idx;
68use nameres::DefMap; 69use nameres::DefMap;
@@ -652,6 +653,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
652 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 653 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
653 mut error_sink: &mut dyn FnMut(mbe::ExpandError), 654 mut error_sink: &mut dyn FnMut(mbe::ExpandError),
654 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { 655 ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> {
656 let fragment = hir_expand::to_fragment_kind(self.value);
655 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 657 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
656 let h = Hygiene::new(db.upcast(), self.file_id); 658 let h = Hygiene::new(db.upcast(), self.file_id);
657 let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h)); 659 let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &h));
@@ -667,6 +669,7 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
667 669
668 macro_call_as_call_id( 670 macro_call_as_call_id(
669 &AstIdWithPath::new(ast_id.file_id, ast_id.value, path), 671 &AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
672 fragment,
670 db, 673 db,
671 krate, 674 krate,
672 resolver, 675 resolver,
@@ -695,6 +698,7 @@ pub struct UnresolvedMacro {
695 698
696fn macro_call_as_call_id( 699fn macro_call_as_call_id(
697 call: &AstIdWithPath<ast::MacroCall>, 700 call: &AstIdWithPath<ast::MacroCall>,
701 fragment: FragmentKind,
698 db: &dyn db::DefDatabase, 702 db: &dyn db::DefDatabase,
699 krate: CrateId, 703 krate: CrateId,
700 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 704 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
@@ -718,7 +722,11 @@ fn macro_call_as_call_id(
718 .map(MacroCallId::from) 722 .map(MacroCallId::from)
719 } else { 723 } else {
720 Ok(def 724 Ok(def
721 .as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike { ast_id: call.ast_id }) 725 .as_lazy_macro(
726 db.upcast(),
727 krate,
728 MacroCallKind::FnLike { ast_id: call.ast_id, fragment },
729 )
722 .into()) 730 .into())
723 }; 731 };
724 Ok(res) 732 Ok(res)
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index 1bc72ec1f..249af6fc8 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -629,7 +629,7 @@ mod diagnostics {
629 DiagnosticKind::UnresolvedProcMacro { ast } => { 629 DiagnosticKind::UnresolvedProcMacro { ast } => {
630 let mut precise_location = None; 630 let mut precise_location = None;
631 let (file, ast, name) = match ast { 631 let (file, ast, name) = match ast {
632 MacroCallKind::FnLike { ast_id } => { 632 MacroCallKind::FnLike { ast_id, .. } => {
633 let node = ast_id.to_node(db.upcast()); 633 let node = ast_id.to_node(db.upcast());
634 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None) 634 (ast_id.file_id, SyntaxNodePtr::from(AstPtr::new(&node)), None)
635 } 635 }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 05ceb1efb..e89136ed1 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -13,7 +13,7 @@ use hir_expand::{
13 builtin_macro::find_builtin_macro, 13 builtin_macro::find_builtin_macro,
14 name::{AsName, Name}, 14 name::{AsName, Name},
15 proc_macro::ProcMacroExpander, 15 proc_macro::ProcMacroExpander,
16 AttrId, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 16 AttrId, FragmentKind, HirFileId, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
17}; 17};
18use hir_expand::{InFile, MacroCallLoc}; 18use hir_expand::{InFile, MacroCallLoc};
19use rustc_hash::{FxHashMap, FxHashSet}; 19use rustc_hash::{FxHashMap, FxHashSet};
@@ -215,7 +215,7 @@ struct MacroDirective {
215 215
216#[derive(Clone, Debug, Eq, PartialEq)] 216#[derive(Clone, Debug, Eq, PartialEq)]
217enum MacroDirectiveKind { 217enum MacroDirectiveKind {
218 FnLike { ast_id: AstIdWithPath<ast::MacroCall> }, 218 FnLike { ast_id: AstIdWithPath<ast::MacroCall>, fragment: FragmentKind },
219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId }, 219 Derive { ast_id: AstIdWithPath<ast::Item>, derive_attr: AttrId },
220} 220}
221 221
@@ -807,9 +807,10 @@ impl DefCollector<'_> {
807 let mut res = ReachedFixedPoint::Yes; 807 let mut res = ReachedFixedPoint::Yes;
808 macros.retain(|directive| { 808 macros.retain(|directive| {
809 match &directive.kind { 809 match &directive.kind {
810 MacroDirectiveKind::FnLike { ast_id } => { 810 MacroDirectiveKind::FnLike { ast_id, fragment } => {
811 match macro_call_as_call_id( 811 match macro_call_as_call_id(
812 ast_id, 812 ast_id,
813 *fragment,
813 self.db, 814 self.db,
814 self.def_map.krate, 815 self.def_map.krate,
815 |path| { 816 |path| {
@@ -926,8 +927,9 @@ impl DefCollector<'_> {
926 927
927 for directive in &self.unexpanded_macros { 928 for directive in &self.unexpanded_macros {
928 match &directive.kind { 929 match &directive.kind {
929 MacroDirectiveKind::FnLike { ast_id, .. } => match macro_call_as_call_id( 930 MacroDirectiveKind::FnLike { ast_id, fragment } => match macro_call_as_call_id(
930 ast_id, 931 ast_id,
932 *fragment,
931 self.db, 933 self.db,
932 self.def_map.krate, 934 self.def_map.krate,
933 |path| { 935 |path| {
@@ -1496,6 +1498,7 @@ impl ModCollector<'_, '_> {
1496 let mut error = None; 1498 let mut error = None;
1497 match macro_call_as_call_id( 1499 match macro_call_as_call_id(
1498 &ast_id, 1500 &ast_id,
1501 mac.fragment,
1499 self.def_collector.db, 1502 self.def_collector.db,
1500 self.def_collector.def_map.krate, 1503 self.def_collector.def_map.krate,
1501 |path| { 1504 |path| {
@@ -1524,9 +1527,14 @@ impl ModCollector<'_, '_> {
1524 } 1527 }
1525 Ok(Err(_)) => { 1528 Ok(Err(_)) => {
1526 // Built-in macro failed eager expansion. 1529 // Built-in macro failed eager expansion.
1530
1531 // FIXME: don't parse the file here
1532 let fragment = hir_expand::to_fragment_kind(
1533 &ast_id.ast_id.to_node(self.def_collector.db.upcast()),
1534 );
1527 self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error( 1535 self.def_collector.def_map.diagnostics.push(DefDiagnostic::macro_error(
1528 self.module_id, 1536 self.module_id,
1529 MacroCallKind::FnLike { ast_id: ast_id.ast_id }, 1537 MacroCallKind::FnLike { ast_id: ast_id.ast_id, fragment },
1530 error.unwrap().to_string(), 1538 error.unwrap().to_string(),
1531 )); 1539 ));
1532 return; 1540 return;
@@ -1543,7 +1551,7 @@ impl ModCollector<'_, '_> {
1543 self.def_collector.unexpanded_macros.push(MacroDirective { 1551 self.def_collector.unexpanded_macros.push(MacroDirective {
1544 module_id: self.module_id, 1552 module_id: self.module_id,
1545 depth: self.macro_depth + 1, 1553 depth: self.macro_depth + 1,
1546 kind: MacroDirectiveKind::FnLike { ast_id }, 1554 kind: MacroDirectiveKind::FnLike { ast_id, fragment: mac.fragment },
1547 }); 1555 });
1548 } 1556 }
1549 1557
diff --git a/crates/hir_def/src/nameres/tests/incremental.rs b/crates/hir_def/src/nameres/tests/incremental.rs
index 227ecd162..d884a6eb4 100644
--- a/crates/hir_def/src/nameres/tests/incremental.rs
+++ b/crates/hir_def/src/nameres/tests/incremental.rs
@@ -137,6 +137,9 @@ m!(Z);
137 }); 137 });
138 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); 138 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
139 assert_eq!(n_recalculated_item_trees, 6); 139 assert_eq!(n_recalculated_item_trees, 6);
140 let n_reparsed_macros =
141 events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
142 assert_eq!(n_reparsed_macros, 3);
140 } 143 }
141 144
142 let new_text = r#" 145 let new_text = r#"
@@ -155,5 +158,8 @@ m!(Z);
155 }); 158 });
156 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count(); 159 let n_recalculated_item_trees = events.iter().filter(|it| it.contains("item_tree")).count();
157 assert_eq!(n_recalculated_item_trees, 1); 160 assert_eq!(n_recalculated_item_trees, 1);
161 let n_reparsed_macros =
162 events.iter().filter(|it| it.contains("parse_macro_expansion")).count();
163 assert_eq!(n_reparsed_macros, 0);
158 } 164 }
159} 165}
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 179de61f9..af9802144 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -578,6 +578,7 @@ mod tests {
578 krate, 578 krate,
579 kind: MacroCallKind::FnLike { 579 kind: MacroCallKind::FnLike {
580 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call)), 580 ast_id: AstId::new(file_id.into(), ast_id_map.ast_id(&macro_call)),
581 fragment: FragmentKind::Expr,
581 }, 582 },
582 }; 583 };
583 584
@@ -788,9 +789,9 @@ mod tests {
788 r##" 789 r##"
789 #[rustc_builtin_macro] 790 #[rustc_builtin_macro]
790 macro_rules! concat {} 791 macro_rules! concat {}
791 concat!("foo", "r", 0, r#"bar"#, false); 792 concat!("foo", "r", 0, r#"bar"#, "\n", false);
792 "##, 793 "##,
793 expect![[r#""foor0barfalse""#]], 794 expect![[r#""foor0bar\nfalse""#]],
794 ); 795 );
795 } 796 }
796} 797}
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index d61f4b31a..6647e57e7 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -8,9 +8,7 @@ use parser::FragmentKind;
8use syntax::{ 8use syntax::{
9 algo::diff, 9 algo::diff,
10 ast::{self, NameOwner}, 10 ast::{self, NameOwner},
11 AstNode, GreenNode, Parse, 11 AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken,
12 SyntaxKind::*,
13 SyntaxNode, SyntaxToken,
14}; 12};
15 13
16use crate::{ 14use crate::{
@@ -160,7 +158,7 @@ pub fn expand_hypothetical(
160 158
161 let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt); 159 let hypothetical_expansion = macro_def.expand(db, lazy_id, &tt);
162 160
163 let fragment_kind = to_fragment_kind(db, actual_macro_call); 161 let fragment_kind = macro_fragment_kind(db, actual_macro_call);
164 162
165 let (node, tmap_2) = 163 let (node, tmap_2) =
166 mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?; 164 mbe::token_tree_to_syntax_node(&hypothetical_expansion.value, fragment_kind).ok()?;
@@ -226,7 +224,7 @@ fn parse_macro_expansion(
226 None => return ExpandResult { value: None, err: result.err }, 224 None => return ExpandResult { value: None, err: result.err },
227 }; 225 };
228 226
229 let fragment_kind = to_fragment_kind(db, macro_file.macro_call_id); 227 let fragment_kind = macro_fragment_kind(db, macro_file.macro_call_id);
230 228
231 log::debug!("expanded = {}", tt.as_debug_string()); 229 log::debug!("expanded = {}", tt.as_debug_string());
232 log::debug!("kind = {:?}", fragment_kind); 230 log::debug!("kind = {:?}", fragment_kind);
@@ -427,62 +425,15 @@ fn hygiene_frame(db: &dyn AstDatabase, file_id: HirFileId) -> Arc<HygieneFrame>
427 Arc::new(HygieneFrame::new(db, file_id)) 425 Arc::new(HygieneFrame::new(db, file_id))
428} 426}
429 427
430/// Given a `MacroCallId`, return what `FragmentKind` it belongs to. 428fn macro_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
431/// FIXME: Not completed 429 match id {
432fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind { 430 MacroCallId::LazyMacro(id) => {
433 let lazy_id = match id { 431 let loc: MacroCallLoc = db.lookup_intern_macro(id);
434 MacroCallId::LazyMacro(id) => id, 432 loc.kind.fragment_kind()
435 MacroCallId::EagerMacro(id) => {
436 return db.lookup_intern_eager_expansion(id).fragment;
437 }
438 };
439 let syn = db.lookup_intern_macro(lazy_id).kind.node(db).value;
440
441 let parent = match syn.parent() {
442 Some(it) => it,
443 None => return FragmentKind::Statements,
444 };
445
446 match parent.kind() {
447 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
448 MACRO_STMTS => FragmentKind::Statements,
449 MACRO_PAT => FragmentKind::Pattern,
450 MACRO_TYPE => FragmentKind::Type,
451 ITEM_LIST => FragmentKind::Items,
452 LET_STMT => {
453 // FIXME: Handle LHS Pattern
454 FragmentKind::Expr
455 } 433 }
456 EXPR_STMT => FragmentKind::Statements, 434 MacroCallId::EagerMacro(id) => {
457 BLOCK_EXPR => FragmentKind::Statements, 435 let loc: EagerCallLoc = db.lookup_intern_eager_expansion(id);
458 ARG_LIST => FragmentKind::Expr, 436 loc.fragment
459 TRY_EXPR => FragmentKind::Expr,
460 TUPLE_EXPR => FragmentKind::Expr,
461 PAREN_EXPR => FragmentKind::Expr,
462 ARRAY_EXPR => FragmentKind::Expr,
463 FOR_EXPR => FragmentKind::Expr,
464 PATH_EXPR => FragmentKind::Expr,
465 CLOSURE_EXPR => FragmentKind::Expr,
466 CONDITION => FragmentKind::Expr,
467 BREAK_EXPR => FragmentKind::Expr,
468 RETURN_EXPR => FragmentKind::Expr,
469 MATCH_EXPR => FragmentKind::Expr,
470 MATCH_ARM => FragmentKind::Expr,
471 MATCH_GUARD => FragmentKind::Expr,
472 RECORD_EXPR_FIELD => FragmentKind::Expr,
473 CALL_EXPR => FragmentKind::Expr,
474 INDEX_EXPR => FragmentKind::Expr,
475 METHOD_CALL_EXPR => FragmentKind::Expr,
476 FIELD_EXPR => FragmentKind::Expr,
477 AWAIT_EXPR => FragmentKind::Expr,
478 CAST_EXPR => FragmentKind::Expr,
479 REF_EXPR => FragmentKind::Expr,
480 PREFIX_EXPR => FragmentKind::Expr,
481 RANGE_EXPR => FragmentKind::Expr,
482 BIN_EXPR => FragmentKind::Expr,
483 _ => {
484 // Unknown , Just guess it is `Items`
485 FragmentKind::Items
486 } 437 }
487 } 438 }
488} 439}
diff --git a/crates/hir_expand/src/eager.rs b/crates/hir_expand/src/eager.rs
index f12132f84..85491fe8b 100644
--- a/crates/hir_expand/src/eager.rs
+++ b/crates/hir_expand/src/eager.rs
@@ -175,8 +175,13 @@ fn lazy_expand(
175) -> ExpandResult<Option<InFile<SyntaxNode>>> { 175) -> ExpandResult<Option<InFile<SyntaxNode>>> {
176 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 176 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
177 177
178 let fragment = crate::to_fragment_kind(&macro_call.value);
178 let id: MacroCallId = def 179 let id: MacroCallId = def
179 .as_lazy_macro(db, krate, MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id) }) 180 .as_lazy_macro(
181 db,
182 krate,
183 MacroCallKind::FnLike { ast_id: macro_call.with_value(ast_id), fragment },
184 )
180 .into(); 185 .into();
181 186
182 let err = db.macro_expand_error(id); 187 let err = db.macro_expand_error(id);
diff --git a/crates/hir_expand/src/lib.rs b/crates/hir_expand/src/lib.rs
index 0402640de..80ab3aeee 100644
--- a/crates/hir_expand/src/lib.rs
+++ b/crates/hir_expand/src/lib.rs
@@ -16,7 +16,9 @@ pub mod quote;
16pub mod eager; 16pub mod eager;
17 17
18use either::Either; 18use either::Either;
19
19pub use mbe::{ExpandError, ExpandResult}; 20pub use mbe::{ExpandError, ExpandResult};
21pub use parser::FragmentKind;
20 22
21use std::hash::Hash; 23use std::hash::Hash;
22use std::sync::Arc; 24use std::sync::Arc;
@@ -290,7 +292,7 @@ pub struct MacroCallLoc {
290 292
291#[derive(Debug, Clone, PartialEq, Eq, Hash)] 293#[derive(Debug, Clone, PartialEq, Eq, Hash)]
292pub enum MacroCallKind { 294pub enum MacroCallKind {
293 FnLike { ast_id: AstId<ast::MacroCall> }, 295 FnLike { ast_id: AstId<ast::MacroCall>, fragment: FragmentKind },
294 Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId }, 296 Derive { ast_id: AstId<ast::Item>, derive_name: String, derive_attr: AttrId },
295} 297}
296 298
@@ -324,6 +326,13 @@ impl MacroCallKind {
324 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()), 326 MacroCallKind::Derive { ast_id, .. } => Some(ast_id.to_node(db).syntax().clone()),
325 } 327 }
326 } 328 }
329
330 fn fragment_kind(&self) -> FragmentKind {
331 match self {
332 MacroCallKind::FnLike { fragment, .. } => *fragment,
333 MacroCallKind::Derive { .. } => FragmentKind::Items,
334 }
335 }
327} 336}
328 337
329impl MacroCallId { 338impl MacroCallId {
@@ -357,7 +366,6 @@ pub struct ExpansionInfo {
357} 366}
358 367
359pub use mbe::Origin; 368pub use mbe::Origin;
360use parser::FragmentKind;
361 369
362impl ExpansionInfo { 370impl ExpansionInfo {
363 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { 371 pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
@@ -562,3 +570,59 @@ impl<N: AstNode> InFile<N> {
562 self.with_value(self.value.syntax()) 570 self.with_value(self.value.syntax())
563 } 571 }
564} 572}
573
574/// Given a `MacroCallId`, return what `FragmentKind` it belongs to.
575/// FIXME: Not completed
576pub fn to_fragment_kind(call: &ast::MacroCall) -> FragmentKind {
577 use syntax::SyntaxKind::*;
578
579 let syn = call.syntax();
580
581 let parent = match syn.parent() {
582 Some(it) => it,
583 None => return FragmentKind::Statements,
584 };
585
586 match parent.kind() {
587 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
588 MACRO_STMTS => FragmentKind::Statements,
589 MACRO_PAT => FragmentKind::Pattern,
590 MACRO_TYPE => FragmentKind::Type,
591 ITEM_LIST => FragmentKind::Items,
592 LET_STMT => {
593 // FIXME: Handle LHS Pattern
594 FragmentKind::Expr
595 }
596 EXPR_STMT => FragmentKind::Statements,
597 BLOCK_EXPR => FragmentKind::Statements,
598 ARG_LIST => FragmentKind::Expr,
599 TRY_EXPR => FragmentKind::Expr,
600 TUPLE_EXPR => FragmentKind::Expr,
601 PAREN_EXPR => FragmentKind::Expr,
602 ARRAY_EXPR => FragmentKind::Expr,
603 FOR_EXPR => FragmentKind::Expr,
604 PATH_EXPR => FragmentKind::Expr,
605 CLOSURE_EXPR => FragmentKind::Expr,
606 CONDITION => FragmentKind::Expr,
607 BREAK_EXPR => FragmentKind::Expr,
608 RETURN_EXPR => FragmentKind::Expr,
609 MATCH_EXPR => FragmentKind::Expr,
610 MATCH_ARM => FragmentKind::Expr,
611 MATCH_GUARD => FragmentKind::Expr,
612 RECORD_EXPR_FIELD => FragmentKind::Expr,
613 CALL_EXPR => FragmentKind::Expr,
614 INDEX_EXPR => FragmentKind::Expr,
615 METHOD_CALL_EXPR => FragmentKind::Expr,
616 FIELD_EXPR => FragmentKind::Expr,
617 AWAIT_EXPR => FragmentKind::Expr,
618 CAST_EXPR => FragmentKind::Expr,
619 REF_EXPR => FragmentKind::Expr,
620 PREFIX_EXPR => FragmentKind::Expr,
621 RANGE_EXPR => FragmentKind::Expr,
622 BIN_EXPR => FragmentKind::Expr,
623 _ => {
624 // Unknown , Just guess it is `Items`
625 FragmentKind::Items
626 }
627 }
628}
diff --git a/crates/hir_expand/src/quote.rs b/crates/hir_expand/src/quote.rs
index c82487ef0..230a59964 100644
--- a/crates/hir_expand/src/quote.rs
+++ b/crates/hir_expand/src/quote.rs
@@ -196,8 +196,8 @@ impl_to_to_tokentrees! {
196 tt::Literal => self { self }; 196 tt::Literal => self { self };
197 tt::Ident => self { self }; 197 tt::Ident => self { self };
198 tt::Punct => self { self }; 198 tt::Punct => self { self };
199 &str => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}}; 199 &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}};
200 String => self { tt::Literal{text: format!("{:?}", self.escape_default().to_string()).into(), id: tt::TokenId::unspecified()}} 200 String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}
201} 201}
202 202
203#[cfg(test)] 203#[cfg(test)]
diff --git a/crates/hir_ty/src/chalk_ext.rs b/crates/hir_ty/src/chalk_ext.rs
index 8c4542956..5232a7d80 100644
--- a/crates/hir_ty/src/chalk_ext.rs
+++ b/crates/hir_ty/src/chalk_ext.rs
@@ -1,8 +1,10 @@
1//! Various extensions traits for Chalk types. 1//! Various extensions traits for Chalk types.
2 2
3use chalk_ir::Mutability; 3use chalk_ir::{FloatTy, IntTy, Mutability, Scalar, UintTy};
4use hir_def::{ 4use hir_def::{
5 type_ref::Rawness, AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId, 5 builtin_type::{BuiltinFloat, BuiltinInt, BuiltinType, BuiltinUint},
6 type_ref::Rawness,
7 AssocContainerId, FunctionId, GenericDefId, HasModule, Lookup, TraitId,
6}; 8};
7 9
8use crate::{ 10use crate::{
@@ -18,6 +20,7 @@ pub trait TyExt {
18 fn is_unknown(&self) -> bool; 20 fn is_unknown(&self) -> bool;
19 21
20 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>; 22 fn as_adt(&self) -> Option<(hir_def::AdtId, &Substitution)>;
23 fn as_builtin(&self) -> Option<BuiltinType>;
21 fn as_tuple(&self) -> Option<&Substitution>; 24 fn as_tuple(&self) -> Option<&Substitution>;
22 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>; 25 fn as_fn_def(&self, db: &dyn HirDatabase) -> Option<FunctionId>;
23 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>; 26 fn as_reference(&self) -> Option<(&Ty, Lifetime, Mutability)>;
@@ -59,6 +62,35 @@ impl TyExt for Ty {
59 } 62 }
60 } 63 }
61 64
65 fn as_builtin(&self) -> Option<BuiltinType> {
66 match self.kind(&Interner) {
67 TyKind::Str => Some(BuiltinType::Str),
68 TyKind::Scalar(Scalar::Bool) => Some(BuiltinType::Bool),
69 TyKind::Scalar(Scalar::Char) => Some(BuiltinType::Char),
70 TyKind::Scalar(Scalar::Float(fty)) => Some(BuiltinType::Float(match fty {
71 FloatTy::F64 => BuiltinFloat::F64,
72 FloatTy::F32 => BuiltinFloat::F32,
73 })),
74 TyKind::Scalar(Scalar::Int(ity)) => Some(BuiltinType::Int(match ity {
75 IntTy::Isize => BuiltinInt::Isize,
76 IntTy::I8 => BuiltinInt::I8,
77 IntTy::I16 => BuiltinInt::I16,
78 IntTy::I32 => BuiltinInt::I32,
79 IntTy::I64 => BuiltinInt::I64,
80 IntTy::I128 => BuiltinInt::I128,
81 })),
82 TyKind::Scalar(Scalar::Uint(ity)) => Some(BuiltinType::Uint(match ity {
83 UintTy::Usize => BuiltinUint::Usize,
84 UintTy::U8 => BuiltinUint::U8,
85 UintTy::U16 => BuiltinUint::U16,
86 UintTy::U32 => BuiltinUint::U32,
87 UintTy::U64 => BuiltinUint::U64,
88 UintTy::U128 => BuiltinUint::U128,
89 })),
90 _ => None,
91 }
92 }
93
62 fn as_tuple(&self) -> Option<&Substitution> { 94 fn as_tuple(&self) -> Option<&Substitution> {
63 match self.kind(&Interner) { 95 match self.kind(&Interner) {
64 TyKind::Tuple(_, substs) => Some(substs), 96 TyKind::Tuple(_, substs) => Some(substs),
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index fe2a349e6..61dcbb399 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -1,3 +1,5 @@
1use std::convert::TryFrom;
2
1use ide_assists::utils::extract_trivial_expression; 3use ide_assists::utils::extract_trivial_expression;
2use itertools::Itertools; 4use itertools::Itertools;
3use syntax::{ 5use syntax::{
@@ -65,14 +67,6 @@ fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextR
65 67
66fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { 68fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) {
67 if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { 69 if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
68 let mut string_open_quote = false;
69 if let Some(string) = ast::String::cast(token.clone()) {
70 if let Some(range) = string.open_quote_text_range() {
71 cov_mark::hit!(join_string_literal);
72 string_open_quote = range.end() == offset;
73 }
74 }
75
76 let n_spaces_after_line_break = { 70 let n_spaces_after_line_break = {
77 let suff = &token.text()[TextRange::new( 71 let suff = &token.text()[TextRange::new(
78 offset - token.text_range().start() + TextSize::of('\n'), 72 offset - token.text_range().start() + TextSize::of('\n'),
@@ -81,8 +75,23 @@ fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextS
81 suff.bytes().take_while(|&b| b == b' ').count() 75 suff.bytes().take_while(|&b| b == b' ').count()
82 }; 76 };
83 77
78 let mut no_space = false;
79 if let Some(string) = ast::String::cast(token.clone()) {
80 if let Some(range) = string.open_quote_text_range() {
81 cov_mark::hit!(join_string_literal_open_quote);
82 no_space |= range.end() == offset;
83 }
84 if let Some(range) = string.close_quote_text_range() {
85 cov_mark::hit!(join_string_literal_close_quote);
86 no_space |= range.start()
87 == offset
88 + TextSize::of('\n')
89 + TextSize::try_from(n_spaces_after_line_break).unwrap();
90 }
91 }
92
84 let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into()); 93 let range = TextRange::at(offset, ((n_spaces_after_line_break + 1) as u32).into());
85 let replace_with = if string_open_quote { "" } else { " " }; 94 let replace_with = if no_space { "" } else { " " };
86 edit.replace(range, replace_with.to_string()); 95 edit.replace(range, replace_with.to_string());
87 return; 96 return;
88 } 97 }
@@ -797,22 +806,54 @@ fn foo() {
797 806
798 #[test] 807 #[test]
799 fn join_string_literal() { 808 fn join_string_literal() {
800 cov_mark::check!(join_string_literal); 809 {
801 check_join_lines( 810 cov_mark::check!(join_string_literal_open_quote);
802 r#" 811 check_join_lines(
812 r#"
803fn main() { 813fn main() {
804 $0" 814 $0"
805hello 815hello
806"; 816";
807} 817}
808"#, 818"#,
809 r#" 819 r#"
810fn main() { 820fn main() {
811 $0"hello 821 $0"hello
812"; 822";
813} 823}
814"#, 824"#,
815 ); 825 );
826 }
827
828 {
829 cov_mark::check!(join_string_literal_close_quote);
830 check_join_lines(
831 r#"
832fn main() {
833 $0"hello
834";
835}
836"#,
837 r#"
838fn main() {
839 $0"hello";
840}
841"#,
842 );
843 check_join_lines(
844 r#"
845fn main() {
846 $0r"hello
847 ";
848}
849"#,
850 r#"
851fn main() {
852 $0r"hello";
853}
854"#,
855 );
856 }
816 857
817 check_join_lines( 858 check_join_lines(
818 r#" 859 r#"
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 11ca7ec6b..ae492a264 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -65,7 +65,7 @@ pub(crate) fn find_all_refs(
65 (find_def(&sema, &syntax, position)?, false) 65 (find_def(&sema, &syntax, position)?, false)
66 }; 66 };
67 67
68 let mut usages = def.usages(sema).set_scope(search_scope).all(); 68 let mut usages = def.usages(sema).set_scope(search_scope).include_self_refs().all();
69 if is_literal_search { 69 if is_literal_search {
70 // filter for constructor-literals 70 // filter for constructor-literals
71 let refs = usages.references.values_mut(); 71 let refs = usages.references.values_mut();
@@ -1163,22 +1163,76 @@ fn foo<const FOO$0: usize>() -> usize {
1163 } 1163 }
1164 1164
1165 #[test] 1165 #[test]
1166 fn test_find_self_ty_in_trait_def() { 1166 fn test_trait() {
1167 check( 1167 check(
1168 r#" 1168 r#"
1169trait Foo { 1169trait Foo$0 where Self: {}
1170 fn f() -> Self$0; 1170
1171impl Foo for () {}
1172"#,
1173 expect![[r#"
1174 Foo Trait FileId(0) 0..24 6..9
1175
1176 FileId(0) 31..34
1177 "#]],
1178 );
1179 }
1180
1181 #[test]
1182 fn test_trait_self() {
1183 check(
1184 r#"
1185trait Foo where Self$0 {
1186 fn f() -> Self;
1171} 1187}
1188
1189impl Foo for () {}
1172"#, 1190"#,
1173 expect![[r#" 1191 expect![[r#"
1174 Self TypeParam FileId(0) 6..9 6..9 1192 Self TypeParam FileId(0) 6..9 6..9
1175 1193
1176 FileId(0) 26..30 1194 FileId(0) 16..20
1195 FileId(0) 37..41
1177 "#]], 1196 "#]],
1178 ); 1197 );
1179 } 1198 }
1180 1199
1181 #[test] 1200 #[test]
1201 fn test_self_ty() {
1202 check(
1203 r#"
1204 struct $0Foo;
1205
1206 impl Foo where Self: {
1207 fn f() -> Self;
1208 }
1209 "#,
1210 expect![[r#"
1211 Foo Struct FileId(0) 0..11 7..10
1212
1213 FileId(0) 18..21
1214 FileId(0) 28..32
1215 FileId(0) 50..54
1216 "#]],
1217 );
1218 check(
1219 r#"
1220struct Foo;
1221
1222impl Foo where Self: {
1223 fn f() -> Self$0;
1224}
1225"#,
1226 expect![[r#"
1227 impl Impl FileId(0) 13..57 18..21
1228
1229 FileId(0) 18..21
1230 FileId(0) 28..32
1231 FileId(0) 50..54
1232 "#]],
1233 );
1234 }
1235 #[test]
1182 fn test_self_variant_with_payload() { 1236 fn test_self_variant_with_payload() {
1183 check( 1237 check(
1184 r#" 1238 r#"
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 175e7a31d..2bf953305 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1888,4 +1888,21 @@ impl Foo {
1888 "error: Cannot rename `Self`", 1888 "error: Cannot rename `Self`",
1889 ); 1889 );
1890 } 1890 }
1891
1892 #[test]
1893 fn test_rename_ignores_self_ty() {
1894 check(
1895 "Fo0",
1896 r#"
1897struct $0Foo;
1898
1899impl Foo where Self: {}
1900"#,
1901 r#"
1902struct Fo0;
1903
1904impl Fo0 where Self: {}
1905"#,
1906 );
1907 }
1891} 1908}
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 82c732390..4ad49eca0 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -88,7 +88,7 @@ fn on_char_typed_inner(
88} 88}
89 89
90/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a 90/// Inserts a closing `}` when the user types an opening `{`, wrapping an existing expression in a
91/// block. 91/// block, or a part of a `use` item.
92fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> { 92fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<TextEdit> {
93 if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) { 93 if !stdx::always!(file.tree().syntax().text().char_at(offset) == Some('{')) {
94 return None; 94 return None;
@@ -99,30 +99,59 @@ fn on_opening_brace_typed(file: &Parse<SourceFile>, offset: TextSize) -> Option<
99 // Remove the `{` to get a better parse tree, and reparse 99 // Remove the `{` to get a better parse tree, and reparse
100 let file = file.reparse(&Indel::delete(brace_token.text_range())); 100 let file = file.reparse(&Indel::delete(brace_token.text_range()));
101 101
102 let mut expr: ast::Expr = find_node_at_offset(file.tree().syntax(), offset)?; 102 if let Some(edit) = brace_expr(&file.tree(), offset) {
103 if expr.syntax().text_range().start() != offset { 103 return Some(edit);
104 return None;
105 } 104 }
106 105
107 // Enclose the outermost expression starting at `offset` 106 if let Some(edit) = brace_use_path(&file.tree(), offset) {
108 while let Some(parent) = expr.syntax().parent() { 107 return Some(edit);
109 if parent.text_range().start() != expr.syntax().text_range().start() { 108 }
110 break;
111 }
112 109
113 match ast::Expr::cast(parent) { 110 return None;
114 Some(parent) => expr = parent, 111
115 None => break, 112 fn brace_use_path(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
113 let segment: ast::PathSegment = find_node_at_offset(file.syntax(), offset)?;
114 if segment.syntax().text_range().start() != offset {
115 return None;
116 } 116 }
117 }
118 117
119 // If it's a statement in a block, we don't know how many statements should be included 118 let tree: ast::UseTree = find_node_at_offset(file.syntax(), offset)?;
120 if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) { 119
121 return None; 120 Some(TextEdit::insert(
121 tree.syntax().text_range().end() + TextSize::of("{"),
122 "}".to_string(),
123 ))
122 } 124 }
123 125
124 // Insert `}` right after the expression. 126 fn brace_expr(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
125 Some(TextEdit::insert(expr.syntax().text_range().end() + TextSize::of("{"), "}".to_string())) 127 let mut expr: ast::Expr = find_node_at_offset(file.syntax(), offset)?;
128 if expr.syntax().text_range().start() != offset {
129 return None;
130 }
131
132 // Enclose the outermost expression starting at `offset`
133 while let Some(parent) = expr.syntax().parent() {
134 if parent.text_range().start() != expr.syntax().text_range().start() {
135 break;
136 }
137
138 match ast::Expr::cast(parent) {
139 Some(parent) => expr = parent,
140 None => break,
141 }
142 }
143
144 // If it's a statement in a block, we don't know how many statements should be included
145 if ast::ExprStmt::can_cast(expr.syntax().parent()?.kind()) {
146 return None;
147 }
148
149 // Insert `}` right after the expression.
150 Some(TextEdit::insert(
151 expr.syntax().text_range().end() + TextSize::of("{"),
152 "}".to_string(),
153 ))
154 }
126} 155}
127 156
128/// Returns an edit which should be applied after `=` was typed. Primarily, 157/// Returns an edit which should be applied after `=` was typed. Primarily,
@@ -440,7 +469,7 @@ fn foo() -> { 92 }
440 } 469 }
441 470
442 #[test] 471 #[test]
443 fn adds_closing_brace() { 472 fn adds_closing_brace_for_expr() {
444 type_char( 473 type_char(
445 '{', 474 '{',
446 r#" 475 r#"
@@ -519,4 +548,87 @@ fn f() {
519 "#, 548 "#,
520 ); 549 );
521 } 550 }
551
552 #[test]
553 fn adds_closing_brace_for_use_tree() {
554 type_char(
555 '{',
556 r#"
557use some::$0Path;
558 "#,
559 r#"
560use some::{Path};
561 "#,
562 );
563 type_char(
564 '{',
565 r#"
566use some::{Path, $0Other};
567 "#,
568 r#"
569use some::{Path, {Other}};
570 "#,
571 );
572 type_char(
573 '{',
574 r#"
575use some::{$0Path, Other};
576 "#,
577 r#"
578use some::{{Path}, Other};
579 "#,
580 );
581 type_char(
582 '{',
583 r#"
584use some::path::$0to::Item;
585 "#,
586 r#"
587use some::path::{to::Item};
588 "#,
589 );
590 type_char(
591 '{',
592 r#"
593use some::$0path::to::Item;
594 "#,
595 r#"
596use some::{path::to::Item};
597 "#,
598 );
599 type_char(
600 '{',
601 r#"
602use $0some::path::to::Item;
603 "#,
604 r#"
605use {some::path::to::Item};
606 "#,
607 );
608 type_char(
609 '{',
610 r#"
611use some::path::$0to::{Item};
612 "#,
613 r#"
614use some::path::{to::{Item}};
615 "#,
616 );
617 type_char(
618 '{',
619 r#"
620use $0Thing as _;
621 "#,
622 r#"
623use {Thing as _};
624 "#,
625 );
626
627 type_char_noop(
628 '{',
629 r#"
630use some::pa$0th::to::Item;
631 "#,
632 );
633 }
522} 634}
diff --git a/crates/ide/src/typing/on_enter.rs b/crates/ide/src/typing/on_enter.rs
index 7d2db201a..81c4d95b1 100644
--- a/crates/ide/src/typing/on_enter.rs
+++ b/crates/ide/src/typing/on_enter.rs
@@ -54,6 +54,14 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option<Text
54 cov_mark::hit!(indent_block_contents); 54 cov_mark::hit!(indent_block_contents);
55 return Some(edit); 55 return Some(edit);
56 } 56 }
57
58 // Typing enter after the `{` of a use tree list.
59 if let Some(edit) = find_node_at_offset(file.syntax(), position.offset - TextSize::of('{'))
60 .and_then(|list| on_enter_in_use_tree_list(list, position))
61 {
62 cov_mark::hit!(indent_block_contents);
63 return Some(edit);
64 }
57 } 65 }
58 66
59 None 67 None
@@ -111,6 +119,21 @@ fn on_enter_in_block(block: ast::BlockExpr, position: FilePosition) -> Option<Te
111 Some(edit) 119 Some(edit)
112} 120}
113 121
122fn on_enter_in_use_tree_list(list: ast::UseTreeList, position: FilePosition) -> Option<TextEdit> {
123 if list.syntax().text().contains_char('\n') {
124 return None;
125 }
126
127 let indent = IndentLevel::from_node(list.syntax());
128 let mut edit = TextEdit::insert(position.offset, format!("\n{}$0", indent + 1));
129 edit.union(TextEdit::insert(
130 list.r_curly_token()?.text_range().start(),
131 format!("\n{}", indent),
132 ))
133 .ok()?;
134 Some(edit)
135}
136
114fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> { 137fn block_contents(block: &ast::BlockExpr) -> Option<SyntaxNode> {
115 let mut node = block.tail_expr().map(|e| e.syntax().clone()); 138 let mut node = block.tail_expr().map(|e| e.syntax().clone());
116 139
@@ -484,4 +507,96 @@ fn f() {$0
484 "#, 507 "#,
485 ); 508 );
486 } 509 }
510
511 #[test]
512 fn indents_use_tree_list() {
513 do_check(
514 r#"
515use crate::{$0};
516 "#,
517 r#"
518use crate::{
519 $0
520};
521 "#,
522 );
523 do_check(
524 r#"
525use crate::{$0Object, path::to::OtherThing};
526 "#,
527 r#"
528use crate::{
529 $0Object, path::to::OtherThing
530};
531 "#,
532 );
533 do_check(
534 r#"
535use {crate::{$0Object, path::to::OtherThing}};
536 "#,
537 r#"
538use {crate::{
539 $0Object, path::to::OtherThing
540}};
541 "#,
542 );
543 do_check(
544 r#"
545use {
546 crate::{$0Object, path::to::OtherThing}
547};
548 "#,
549 r#"
550use {
551 crate::{
552 $0Object, path::to::OtherThing
553 }
554};
555 "#,
556 );
557 }
558
559 #[test]
560 fn does_not_indent_use_tree_list_when_not_at_curly_brace() {
561 do_check_noop(
562 r#"
563use path::{Thing$0};
564 "#,
565 );
566 }
567
568 #[test]
569 fn does_not_indent_use_tree_list_without_curly_braces() {
570 do_check_noop(
571 r#"
572use path::Thing$0;
573 "#,
574 );
575 do_check_noop(
576 r#"
577use path::$0Thing;
578 "#,
579 );
580 do_check_noop(
581 r#"
582use path::Thing$0};
583 "#,
584 );
585 do_check_noop(
586 r#"
587use path::{$0Thing;
588 "#,
589 );
590 }
591
592 #[test]
593 fn does_not_indent_multiline_use_tree_list() {
594 do_check_noop(
595 r#"
596use path::{$0
597 Thing
598};
599 "#,
600 );
601 }
487} 602}
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index b5b5ada5e..70949ca35 100644
--- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -107,7 +107,7 @@ fn edit_struct_references(
107 names: &[ast::Name], 107 names: &[ast::Name],
108) { 108) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); 109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt)));
110 let usages = strukt_def.usages(&ctx.sema).include_self_kw_refs(true).all(); 110 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111 111
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { 112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
113 match_ast! { 113 match_ast! {
diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs
index c66f8c05d..5eb6a57f0 100644
--- a/crates/ide_assists/src/handlers/early_return.rs
+++ b/crates/ide_assists/src/handlers/early_return.rs
@@ -130,9 +130,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
130 once(make::ident_pat(make::name("it")).into()), 130 once(make::ident_pat(make::name("it")).into()),
131 ); 131 );
132 let expr = { 132 let expr = {
133 let name_ref = make::name_ref("it"); 133 let path = make::ext::ident_path("it");
134 let segment = make::path_segment(name_ref);
135 let path = make::path_unqualified(segment);
136 make::expr_path(path) 134 make::expr_path(path)
137 }; 135 };
138 make::match_arm(once(pat.into()), expr) 136 make::match_arm(once(pat.into()), expr)
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs
index e3095f26b..da8d245e5 100644
--- a/crates/ide_assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide_assists/src/handlers/expand_glob_import.rs
@@ -65,8 +65,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
65 65
66 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs); 66 let names_to_import = find_names_to_import(ctx, refs_in_target, imported_defs);
67 let expanded = make::use_tree_list(names_to_import.iter().map(|n| { 67 let expanded = make::use_tree_list(names_to_import.iter().map(|n| {
68 let path = 68 let path = make::ext::ident_path(&n.to_string());
69 make::path_unqualified(make::path_segment(make::name_ref(&n.to_string())));
70 make::use_tree(path, None, None, false) 69 make::use_tree(path, None, None, false)
71 })) 70 }))
72 .clone_for_update(); 71 .clone_for_update();
diff --git a/crates/ide_assists/src/handlers/extract_function.rs b/crates/ide_assists/src/handlers/extract_function.rs
index 4116985ae..6311afc1f 100644
--- a/crates/ide_assists/src/handlers/extract_function.rs
+++ b/crates/ide_assists/src/handlers/extract_function.rs
@@ -956,10 +956,10 @@ fn format_replacement(ctx: &AssistContext, fun: &Function, indent: IndentLevel)
956 let args = fun.params.iter().map(|param| param.to_arg(ctx)); 956 let args = fun.params.iter().map(|param| param.to_arg(ctx));
957 let args = make::arg_list(args); 957 let args = make::arg_list(args);
958 let call_expr = if fun.self_param.is_some() { 958 let call_expr = if fun.self_param.is_some() {
959 let self_arg = make::expr_path(make_path_from_text("self")); 959 let self_arg = make::expr_path(make::ext::ident_path("self"));
960 make::expr_method_call(self_arg, &fun.name, args) 960 make::expr_method_call(self_arg, &fun.name, args)
961 } else { 961 } else {
962 let func = make::expr_path(make_path_from_text(&fun.name)); 962 let func = make::expr_path(make::ext::ident_path(&fun.name));
963 make::expr_call(func, args) 963 make::expr_call(func, args)
964 }; 964 };
965 965
@@ -1054,11 +1054,11 @@ impl FlowHandler {
1054 make::expr_if(condition, block, None) 1054 make::expr_if(condition, block, None)
1055 } 1055 }
1056 FlowHandler::IfOption { action } => { 1056 FlowHandler::IfOption { action } => {
1057 let path = make_path_from_text("Some"); 1057 let path = make::ext::ident_path("Some");
1058 let value_pat = make::ident_pat(make::name("value")); 1058 let value_pat = make::ident_pat(make::name("value"));
1059 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1059 let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1060 let cond = make::condition(call_expr, Some(pattern.into())); 1060 let cond = make::condition(call_expr, Some(pattern.into()));
1061 let value = make::expr_path(make_path_from_text("value")); 1061 let value = make::expr_path(make::ext::ident_path("value"));
1062 let action_expr = action.make_result_handler(Some(value)); 1062 let action_expr = action.make_result_handler(Some(value));
1063 let action_stmt = make::expr_stmt(action_expr); 1063 let action_stmt = make::expr_stmt(action_expr);
1064 let then = make::block_expr(iter::once(action_stmt.into()), None); 1064 let then = make::block_expr(iter::once(action_stmt.into()), None);
@@ -1068,14 +1068,14 @@ impl FlowHandler {
1068 let some_name = "value"; 1068 let some_name = "value";
1069 1069
1070 let some_arm = { 1070 let some_arm = {
1071 let path = make_path_from_text("Some"); 1071 let path = make::ext::ident_path("Some");
1072 let value_pat = make::ident_pat(make::name(some_name)); 1072 let value_pat = make::ident_pat(make::name(some_name));
1073 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1073 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1074 let value = make::expr_path(make_path_from_text(some_name)); 1074 let value = make::expr_path(make::ext::ident_path(some_name));
1075 make::match_arm(iter::once(pat.into()), value) 1075 make::match_arm(iter::once(pat.into()), value)
1076 }; 1076 };
1077 let none_arm = { 1077 let none_arm = {
1078 let path = make_path_from_text("None"); 1078 let path = make::ext::ident_path("None");
1079 let pat = make::path_pat(path); 1079 let pat = make::path_pat(path);
1080 make::match_arm(iter::once(pat), none.make_result_handler(None)) 1080 make::match_arm(iter::once(pat), none.make_result_handler(None))
1081 }; 1081 };
@@ -1087,17 +1087,17 @@ impl FlowHandler {
1087 let err_name = "value"; 1087 let err_name = "value";
1088 1088
1089 let ok_arm = { 1089 let ok_arm = {
1090 let path = make_path_from_text("Ok"); 1090 let path = make::ext::ident_path("Ok");
1091 let value_pat = make::ident_pat(make::name(ok_name)); 1091 let value_pat = make::ident_pat(make::name(ok_name));
1092 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1092 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1093 let value = make::expr_path(make_path_from_text(ok_name)); 1093 let value = make::expr_path(make::ext::ident_path(ok_name));
1094 make::match_arm(iter::once(pat.into()), value) 1094 make::match_arm(iter::once(pat.into()), value)
1095 }; 1095 };
1096 let err_arm = { 1096 let err_arm = {
1097 let path = make_path_from_text("Err"); 1097 let path = make::ext::ident_path("Err");
1098 let value_pat = make::ident_pat(make::name(err_name)); 1098 let value_pat = make::ident_pat(make::name(err_name));
1099 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into())); 1099 let pat = make::tuple_struct_pat(path, iter::once(value_pat.into()));
1100 let value = make::expr_path(make_path_from_text(err_name)); 1100 let value = make::expr_path(make::ext::ident_path(err_name));
1101 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value))) 1101 make::match_arm(iter::once(pat.into()), err.make_result_handler(Some(value)))
1102 }; 1102 };
1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]); 1103 let arms = make::match_arm_list(vec![ok_arm, err_arm]);
@@ -1107,13 +1107,9 @@ impl FlowHandler {
1107 } 1107 }
1108} 1108}
1109 1109
1110fn make_path_from_text(text: &str) -> ast::Path {
1111 make::path_unqualified(make::path_segment(make::name_ref(text)))
1112}
1113
1114fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr { 1110fn path_expr_from_local(ctx: &AssistContext, var: Local) -> ast::Expr {
1115 let name = var.name(ctx.db()).unwrap().to_string(); 1111 let name = var.name(ctx.db()).unwrap().to_string();
1116 make::expr_path(make_path_from_text(&name)) 1112 make::expr_path(make::ext::ident_path(&name))
1117} 1113}
1118 1114
1119fn format_function( 1115fn format_function(
@@ -1179,7 +1175,7 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1179 fun_ty.make_ty(ctx, module) 1175 fun_ty.make_ty(ctx, module)
1180 } 1176 }
1181 FlowHandler::Try { kind: TryKind::Option } => { 1177 FlowHandler::Try { kind: TryKind::Option } => {
1182 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module))) 1178 make::ext::ty_option(fun_ty.make_ty(ctx, module))
1183 } 1179 }
1184 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => { 1180 FlowHandler::Try { kind: TryKind::Result { ty: parent_ret_ty } } => {
1185 let handler_ty = parent_ret_ty 1181 let handler_ty = parent_ret_ty
@@ -1187,29 +1183,21 @@ fn make_ret_ty(ctx: &AssistContext, module: hir::Module, fun: &Function) -> Opti
1187 .nth(1) 1183 .nth(1)
1188 .map(|ty| make_ty(&ty, ctx, module)) 1184 .map(|ty| make_ty(&ty, ctx, module))
1189 .unwrap_or_else(make::ty_unit); 1185 .unwrap_or_else(make::ty_unit);
1190 make::ty_generic( 1186 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1191 make::name_ref("Result"),
1192 vec![fun_ty.make_ty(ctx, module), handler_ty],
1193 )
1194 } 1187 }
1195 FlowHandler::If { .. } => make::ty("bool"), 1188 FlowHandler::If { .. } => make::ext::ty_bool(),
1196 FlowHandler::IfOption { action } => { 1189 FlowHandler::IfOption { action } => {
1197 let handler_ty = action 1190 let handler_ty = action
1198 .expr_ty(ctx) 1191 .expr_ty(ctx)
1199 .map(|ty| make_ty(&ty, ctx, module)) 1192 .map(|ty| make_ty(&ty, ctx, module))
1200 .unwrap_or_else(make::ty_unit); 1193 .unwrap_or_else(make::ty_unit);
1201 make::ty_generic(make::name_ref("Option"), iter::once(handler_ty)) 1194 make::ext::ty_option(handler_ty)
1202 }
1203 FlowHandler::MatchOption { .. } => {
1204 make::ty_generic(make::name_ref("Option"), iter::once(fun_ty.make_ty(ctx, module)))
1205 } 1195 }
1196 FlowHandler::MatchOption { .. } => make::ext::ty_option(fun_ty.make_ty(ctx, module)),
1206 FlowHandler::MatchResult { err } => { 1197 FlowHandler::MatchResult { err } => {
1207 let handler_ty = 1198 let handler_ty =
1208 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit); 1199 err.expr_ty(ctx).map(|ty| make_ty(&ty, ctx, module)).unwrap_or_else(make::ty_unit);
1209 make::ty_generic( 1200 make::ext::ty_result(fun_ty.make_ty(ctx, module), handler_ty)
1210 make::name_ref("Result"),
1211 vec![fun_ty.make_ty(ctx, module), handler_ty],
1212 )
1213 } 1201 }
1214 }; 1202 };
1215 Some(make::ret_type(ret_ty)) 1203 Some(make::ret_type(ret_ty))
@@ -1296,7 +1284,7 @@ fn make_body(
1296 TryKind::Option => "Some", 1284 TryKind::Option => "Some",
1297 TryKind::Result { .. } => "Ok", 1285 TryKind::Result { .. } => "Ok",
1298 }; 1286 };
1299 let func = make::expr_path(make_path_from_text(constructor)); 1287 let func = make::expr_path(make::ext::ident_path(constructor));
1300 let args = make::arg_list(iter::once(tail_expr)); 1288 let args = make::arg_list(iter::once(tail_expr));
1301 make::expr_call(func, args) 1289 make::expr_call(func, args)
1302 }) 1290 })
@@ -1306,16 +1294,16 @@ fn make_body(
1306 with_tail_expr(block, lit_false.into()) 1294 with_tail_expr(block, lit_false.into())
1307 } 1295 }
1308 FlowHandler::IfOption { .. } => { 1296 FlowHandler::IfOption { .. } => {
1309 let none = make::expr_path(make_path_from_text("None")); 1297 let none = make::expr_path(make::ext::ident_path("None"));
1310 with_tail_expr(block, none) 1298 with_tail_expr(block, none)
1311 } 1299 }
1312 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| { 1300 FlowHandler::MatchOption { .. } => map_tail_expr(block, |tail_expr| {
1313 let some = make::expr_path(make_path_from_text("Some")); 1301 let some = make::expr_path(make::ext::ident_path("Some"));
1314 let args = make::arg_list(iter::once(tail_expr)); 1302 let args = make::arg_list(iter::once(tail_expr));
1315 make::expr_call(some, args) 1303 make::expr_call(some, args)
1316 }), 1304 }),
1317 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| { 1305 FlowHandler::MatchResult { .. } => map_tail_expr(block, |tail_expr| {
1318 let ok = make::expr_path(make_path_from_text("Ok")); 1306 let ok = make::expr_path(make::ext::ident_path("Ok"));
1319 let args = make::arg_list(iter::once(tail_expr)); 1307 let args = make::arg_list(iter::once(tail_expr));
1320 make::expr_call(ok, args) 1308 make::expr_call(ok, args)
1321 }), 1309 }),
@@ -1483,13 +1471,13 @@ fn make_rewritten_flow(handler: &FlowHandler, arg_expr: Option<ast::Expr>) -> Op
1483 FlowHandler::IfOption { .. } => { 1471 FlowHandler::IfOption { .. } => {
1484 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); 1472 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
1485 let args = make::arg_list(iter::once(expr)); 1473 let args = make::arg_list(iter::once(expr));
1486 make::expr_call(make::expr_path(make_path_from_text("Some")), args) 1474 make::expr_call(make::expr_path(make::ext::ident_path("Some")), args)
1487 } 1475 }
1488 FlowHandler::MatchOption { .. } => make::expr_path(make_path_from_text("None")), 1476 FlowHandler::MatchOption { .. } => make::expr_path(make::ext::ident_path("None")),
1489 FlowHandler::MatchResult { .. } => { 1477 FlowHandler::MatchResult { .. } => {
1490 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new())); 1478 let expr = arg_expr.unwrap_or_else(|| make::expr_tuple(Vec::new()));
1491 let args = make::arg_list(iter::once(expr)); 1479 let args = make::arg_list(iter::once(expr));
1492 make::expr_call(make::expr_path(make_path_from_text("Err")), args) 1480 make::expr_call(make::expr_path(make::ext::ident_path("Err")), args)
1493 } 1481 }
1494 }; 1482 };
1495 Some(make::expr_return(Some(value)).clone_for_update()) 1483 Some(make::expr_return(Some(value)).clone_for_update())
diff --git a/crates/ide_assists/src/handlers/extract_variable.rs b/crates/ide_assists/src/handlers/extract_variable.rs
index 136b9a55b..ae084c86c 100644
--- a/crates/ide_assists/src/handlers/extract_variable.rs
+++ b/crates/ide_assists/src/handlers/extract_variable.rs
@@ -54,7 +54,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
54 54
55 let var_name = match &field_shorthand { 55 let var_name = match &field_shorthand {
56 Some(it) => it.to_string(), 56 Some(it) => it.to_string(),
57 None => suggest_name::variable(&to_extract, &ctx.sema), 57 None => suggest_name::for_variable(&to_extract, &ctx.sema),
58 }; 58 };
59 let expr_range = match &field_shorthand { 59 let expr_range = match &field_shorthand {
60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()), 60 Some(it) => it.syntax().text_range().cover(to_extract.syntax().text_range()),
diff --git a/crates/ide_assists/src/handlers/generate_function.rs b/crates/ide_assists/src/handlers/generate_function.rs
index 6f95b1a07..bc9fc524b 100644
--- a/crates/ide_assists/src/handlers/generate_function.rs
+++ b/crates/ide_assists/src/handlers/generate_function.rs
@@ -175,7 +175,7 @@ impl FunctionBuilder {
175 } 175 }
176 176
177 fn render(self) -> FunctionTemplate { 177 fn render(self) -> FunctionTemplate {
178 let placeholder_expr = make::expr_todo(); 178 let placeholder_expr = make::ext::expr_todo();
179 let fn_body = make::block_expr(vec![], Some(placeholder_expr)); 179 let fn_body = make::block_expr(vec![], Some(placeholder_expr));
180 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None }; 180 let visibility = if self.needs_pub { Some(make::visibility_pub_crate()) } else { None };
181 let mut fn_def = make::fn_( 181 let mut fn_def = make::fn_(
diff --git a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
index 9f4f71d6c..68bc15120 100644
--- a/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ide_assists/src/handlers/introduce_named_lifetime.rs
@@ -89,14 +89,12 @@ fn generate_fn_def_assist(
89 let loc_needing_lifetime = 89 let loc_needing_lifetime =
90 loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position()); 90 loc_needing_lifetime.and_then(|it| it.make_mut(builder).to_position());
91 91
92 add_lifetime_param(fn_def.get_or_create_generic_param_list(), new_lifetime_param); 92 fn_def.get_or_create_generic_param_list().add_generic_param(
93 ted::replace( 93 make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
94 lifetime.syntax(),
95 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
96 ); 94 );
97 loc_needing_lifetime.map(|position| { 95 ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
98 ted::insert(position, make_ast_lifetime(new_lifetime_param).clone_for_update().syntax()) 96 loc_needing_lifetime
99 }); 97 .map(|position| ted::insert(position, new_lifetime_param.clone_for_update().syntax()));
100 }) 98 })
101} 99}
102 100
@@ -112,11 +110,10 @@ fn generate_impl_def_assist(
112 let impl_def = builder.make_ast_mut(impl_def); 110 let impl_def = builder.make_ast_mut(impl_def);
113 let lifetime = builder.make_ast_mut(lifetime); 111 let lifetime = builder.make_ast_mut(lifetime);
114 112
115 add_lifetime_param(impl_def.get_or_create_generic_param_list(), new_lifetime_param); 113 impl_def.get_or_create_generic_param_list().add_generic_param(
116 ted::replace( 114 make::lifetime_param(new_lifetime_param.clone()).clone_for_update().into(),
117 lifetime.syntax(),
118 make_ast_lifetime(new_lifetime_param).clone_for_update().syntax(),
119 ); 115 );
116 ted::replace(lifetime.syntax(), new_lifetime_param.clone_for_update().syntax());
120 }) 117 })
121} 118}
122 119
@@ -124,31 +121,16 @@ fn generate_impl_def_assist(
124/// which is not in the list 121/// which is not in the list
125fn generate_unique_lifetime_param_name( 122fn generate_unique_lifetime_param_name(
126 existing_type_param_list: Option<ast::GenericParamList>, 123 existing_type_param_list: Option<ast::GenericParamList>,
127) -> Option<char> { 124) -> Option<ast::Lifetime> {
128 match existing_type_param_list { 125 match existing_type_param_list {
129 Some(type_params) => { 126 Some(type_params) => {
130 let used_lifetime_params: FxHashSet<_> = type_params 127 let used_lifetime_params: FxHashSet<_> =
131 .lifetime_params() 128 type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect();
132 .map(|p| p.syntax().text().to_string()[1..].to_owned()) 129 ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it))
133 .collect();
134 (b'a'..=b'z').map(char::from).find(|c| !used_lifetime_params.contains(&c.to_string()))
135 } 130 }
136 None => Some('a'), 131 None => Some("'a".to_string()),
137 } 132 }
138} 133 .map(|it| make::lifetime(&it))
139
140fn add_lifetime_param(type_params: ast::GenericParamList, new_lifetime_param: char) {
141 let generic_param =
142 make::generic_param(format!("'{}", new_lifetime_param), None).clone_for_update();
143 type_params.add_generic_param(generic_param);
144}
145
146fn make_ast_lifetime(new_lifetime_param: char) -> ast::Lifetime {
147 make::generic_param(format!("'{}", new_lifetime_param), None)
148 .syntax()
149 .descendants()
150 .find_map(ast::Lifetime::cast)
151 .unwrap()
152} 134}
153 135
154enum NeedsLifetime { 136enum NeedsLifetime {
diff --git a/crates/ide_assists/src/handlers/merge_imports.rs b/crates/ide_assists/src/handlers/merge_imports.rs
index add7b8e37..3cd090737 100644
--- a/crates/ide_assists/src/handlers/merge_imports.rs
+++ b/crates/ide_assists/src/handlers/merge_imports.rs
@@ -27,14 +27,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
27 if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) { 27 if let Some(use_item) = tree.syntax().parent().and_then(ast::Use::cast) {
28 let (merged, to_remove) = 28 let (merged, to_remove) =
29 next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| { 29 next_prev().filter_map(|dir| neighbor(&use_item, dir)).find_map(|use_item2| {
30 try_merge_imports(&use_item, &use_item2, MergeBehavior::Full).zip(Some(use_item2)) 30 try_merge_imports(&use_item, &use_item2, MergeBehavior::Crate).zip(Some(use_item2))
31 })?; 31 })?;
32 32
33 imports = Some((use_item, merged, to_remove)); 33 imports = Some((use_item, merged, to_remove));
34 } else { 34 } else {
35 let (merged, to_remove) = 35 let (merged, to_remove) =
36 next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| { 36 next_prev().filter_map(|dir| neighbor(&tree, dir)).find_map(|use_tree| {
37 try_merge_trees(&tree, &use_tree, MergeBehavior::Full).zip(Some(use_tree)) 37 try_merge_trees(&tree, &use_tree, MergeBehavior::Crate).zip(Some(use_tree))
38 })?; 38 })?;
39 39
40 uses = Some((tree.clone(), merged, to_remove)) 40 uses = Some((tree.clone(), merged, to_remove))
diff --git a/crates/ide_assists/src/handlers/move_bounds.rs b/crates/ide_assists/src/handlers/move_bounds.rs
index 011a28d44..fa3f76609 100644
--- a/crates/ide_assists/src/handlers/move_bounds.rs
+++ b/crates/ide_assists/src/handlers/move_bounds.rs
@@ -63,11 +63,7 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
63} 63}
64 64
65fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 65fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
66 let path = { 66 let path = make::ext::ident_path(&param.name()?.syntax().to_string());
67 let name_ref = make::name_ref(&param.name()?.syntax().to_string());
68 let segment = make::path_segment(name_ref);
69 make::path_unqualified(segment)
70 };
71 let predicate = make::where_pred(path, param.type_bound_list()?.bounds()); 67 let predicate = make::where_pred(path, param.type_bound_list()?.bounds());
72 Some(predicate.clone_for_update()) 68 Some(predicate.clone_for_update())
73} 69}
diff --git a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
index 694d897d1..10d9cec31 100644
--- a/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -84,7 +84,7 @@ pub(crate) fn replace_derive_with_manual_impl(
84 add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?; 84 add_assist(acc, ctx, &attr, &args, &trait_path, Some(trait_), &adt)?;
85 } 85 }
86 if no_traits_found { 86 if no_traits_found {
87 let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_name))); 87 let trait_path = make::ext::ident_path(trait_name);
88 add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?; 88 add_assist(acc, ctx, &attr, &args, &trait_path, None, &adt)?;
89 } 89 }
90 Some(()) 90 Some(())
@@ -159,10 +159,8 @@ fn impl_def_from_trait(
159 if trait_items.is_empty() { 159 if trait_items.is_empty() {
160 return None; 160 return None;
161 } 161 }
162 let impl_def = make::impl_trait( 162 let impl_def =
163 trait_path.clone(), 163 make::impl_trait(trait_path.clone(), make::ext::ident_path(&annotated_name.text()));
164 make::path_unqualified(make::path_segment(make::name_ref(&annotated_name.text()))),
165 );
166 let (impl_def, first_assoc_item) = 164 let (impl_def, first_assoc_item) =
167 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope); 165 add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
168 Some((impl_def, first_assoc_item)) 166 Some((impl_def, first_assoc_item))
diff --git a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
index ff25b61ea..15420aedf 100644
--- a/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
+++ b/crates/ide_assists/src/handlers/replace_impl_trait_with_generic.rs
@@ -1,6 +1,9 @@
1use syntax::ast::{self, edit::AstNodeEdit, make, AstNode, GenericParamsOwner}; 1use syntax::{
2 ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode},
3 ted,
4};
2 5
3use crate::{AssistContext, AssistId, AssistKind, Assists}; 6use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists};
4 7
5// Assist: replace_impl_trait_with_generic 8// Assist: replace_impl_trait_with_generic
6// 9//
@@ -17,30 +20,29 @@ pub(crate) fn replace_impl_trait_with_generic(
17 acc: &mut Assists, 20 acc: &mut Assists,
18 ctx: &AssistContext, 21 ctx: &AssistContext,
19) -> Option<()> { 22) -> Option<()> {
20 let type_impl_trait = ctx.find_node_at_offset::<ast::ImplTraitType>()?; 23 let impl_trait_type = ctx.find_node_at_offset::<ast::ImplTraitType>()?;
21 let type_param = type_impl_trait.syntax().parent().and_then(ast::Param::cast)?; 24 let param = impl_trait_type.syntax().parent().and_then(ast::Param::cast)?;
22 let type_fn = type_param.syntax().ancestors().find_map(ast::Fn::cast)?; 25 let fn_ = param.syntax().ancestors().find_map(ast::Fn::cast)?;
23 26
24 let impl_trait_ty = type_impl_trait.type_bound_list()?; 27 let type_bound_list = impl_trait_type.type_bound_list()?;
25 28
26 let target = type_fn.syntax().text_range(); 29 let target = fn_.syntax().text_range();
27 acc.add( 30 acc.add(
28 AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite), 31 AssistId("replace_impl_trait_with_generic", AssistKind::RefactorRewrite),
29 "Replace impl trait with generic", 32 "Replace impl trait with generic",
30 target, 33 target,
31 |edit| { 34 |edit| {
32 let generic_letter = impl_trait_ty.to_string().chars().next().unwrap().to_string(); 35 let impl_trait_type = edit.make_ast_mut(impl_trait_type);
36 let fn_ = edit.make_ast_mut(fn_);
33 37
34 let generic_param_list = type_fn 38 let type_param_name = suggest_name::for_generic_parameter(&impl_trait_type);
35 .generic_param_list()
36 .unwrap_or_else(|| make::generic_param_list(None))
37 .append_param(make::generic_param(generic_letter.clone(), Some(impl_trait_ty)));
38 39
39 let new_type_fn = type_fn 40 let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list))
40 .replace_descendant::<ast::Type>(type_impl_trait.into(), make::ty(&generic_letter)) 41 .clone_for_update();
41 .with_generic_param_list(generic_param_list); 42 let new_ty = make::ty(&type_param_name).clone_for_update();
42 43
43 edit.replace_ast(type_fn.clone(), new_type_fn); 44 ted::replace(impl_trait_type.syntax(), new_ty.syntax());
45 fn_.get_or_create_generic_param_list().add_generic_param(type_param.into())
44 }, 46 },
45 ) 47 )
46} 48}
@@ -55,12 +57,8 @@ mod tests {
55 fn replace_impl_trait_with_generic_params() { 57 fn replace_impl_trait_with_generic_params() {
56 check_assist( 58 check_assist(
57 replace_impl_trait_with_generic, 59 replace_impl_trait_with_generic,
58 r#" 60 r#"fn foo<G>(bar: $0impl Bar) {}"#,
59 fn foo<G>(bar: $0impl Bar) {} 61 r#"fn foo<G, B: Bar>(bar: B) {}"#,
60 "#,
61 r#"
62 fn foo<G, B: Bar>(bar: B) {}
63 "#,
64 ); 62 );
65 } 63 }
66 64
@@ -68,12 +66,8 @@ mod tests {
68 fn replace_impl_trait_without_generic_params() { 66 fn replace_impl_trait_without_generic_params() {
69 check_assist( 67 check_assist(
70 replace_impl_trait_with_generic, 68 replace_impl_trait_with_generic,
71 r#" 69 r#"fn foo(bar: $0impl Bar) {}"#,
72 fn foo(bar: $0impl Bar) {} 70 r#"fn foo<B: Bar>(bar: B) {}"#,
73 "#,
74 r#"
75 fn foo<B: Bar>(bar: B) {}
76 "#,
77 ); 71 );
78 } 72 }
79 73
@@ -81,12 +75,8 @@ mod tests {
81 fn replace_two_impl_trait_with_generic_params() { 75 fn replace_two_impl_trait_with_generic_params() {
82 check_assist( 76 check_assist(
83 replace_impl_trait_with_generic, 77 replace_impl_trait_with_generic,
84 r#" 78 r#"fn foo<G>(foo: impl Foo, bar: $0impl Bar) {}"#,
85 fn foo<G>(foo: impl Foo, bar: $0impl Bar) {} 79 r#"fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}"#,
86 "#,
87 r#"
88 fn foo<G, B: Bar>(foo: impl Foo, bar: B) {}
89 "#,
90 ); 80 );
91 } 81 }
92 82
@@ -94,12 +84,8 @@ mod tests {
94 fn replace_impl_trait_with_empty_generic_params() { 84 fn replace_impl_trait_with_empty_generic_params() {
95 check_assist( 85 check_assist(
96 replace_impl_trait_with_generic, 86 replace_impl_trait_with_generic,
97 r#" 87 r#"fn foo<>(bar: $0impl Bar) {}"#,
98 fn foo<>(bar: $0impl Bar) {} 88 r#"fn foo<B: Bar>(bar: B) {}"#,
99 "#,
100 r#"
101 fn foo<B: Bar>(bar: B) {}
102 "#,
103 ); 89 );
104 } 90 }
105 91
@@ -108,13 +94,13 @@ mod tests {
108 check_assist( 94 check_assist(
109 replace_impl_trait_with_generic, 95 replace_impl_trait_with_generic,
110 r#" 96 r#"
111 fn foo< 97fn foo<
112 >(bar: $0impl Bar) {} 98>(bar: $0impl Bar) {}
113 "#, 99"#,
114 r#" 100 r#"
115 fn foo<B: Bar 101fn foo<B: Bar
116 >(bar: B) {} 102>(bar: B) {}
117 "#, 103"#,
118 ); 104 );
119 } 105 }
120 106
@@ -123,12 +109,8 @@ mod tests {
123 fn replace_impl_trait_with_exist_generic_letter() { 109 fn replace_impl_trait_with_exist_generic_letter() {
124 check_assist( 110 check_assist(
125 replace_impl_trait_with_generic, 111 replace_impl_trait_with_generic,
126 r#" 112 r#"fn foo<B>(bar: $0impl Bar) {}"#,
127 fn foo<B>(bar: $0impl Bar) {} 113 r#"fn foo<B, C: Bar>(bar: C) {}"#,
128 "#,
129 r#"
130 fn foo<B, C: Bar>(bar: C) {}
131 "#,
132 ); 114 );
133 } 115 }
134 116
@@ -137,19 +119,19 @@ mod tests {
137 check_assist( 119 check_assist(
138 replace_impl_trait_with_generic, 120 replace_impl_trait_with_generic,
139 r#" 121 r#"
140 fn foo< 122fn foo<
141 G: Foo, 123 G: Foo,
142 F, 124 F,
143 H, 125 H,
144 >(bar: $0impl Bar) {} 126>(bar: $0impl Bar) {}
145 "#, 127"#,
146 r#" 128 r#"
147 fn foo< 129fn foo<
148 G: Foo, 130 G: Foo,
149 F, 131 F,
150 H, B: Bar 132 H, B: Bar,
151 >(bar: B) {} 133>(bar: B) {}
152 "#, 134"#,
153 ); 135 );
154 } 136 }
155 137
@@ -157,12 +139,8 @@ mod tests {
157 fn replace_impl_trait_multiple() { 139 fn replace_impl_trait_multiple() {
158 check_assist( 140 check_assist(
159 replace_impl_trait_with_generic, 141 replace_impl_trait_with_generic,
160 r#" 142 r#"fn foo(bar: $0impl Foo + Bar) {}"#,
161 fn foo(bar: $0impl Foo + Bar) {} 143 r#"fn foo<F: Foo + Bar>(bar: F) {}"#,
162 "#,
163 r#"
164 fn foo<F: Foo + Bar>(bar: F) {}
165 "#,
166 ); 144 );
167 } 145 }
168} 146}
diff --git a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
index be7e724b5..1ad0fa816 100644
--- a/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ide_assists/src/handlers/replace_let_with_if_let.rs
@@ -50,22 +50,19 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
50 "Replace with if-let", 50 "Replace with if-let",
51 target, 51 target,
52 |edit| { 52 |edit| {
53 let with_placeholder: ast::Pat = match happy_variant { 53 let pat = match happy_variant {
54 None => make::wildcard_pat().into(), 54 None => original_pat,
55 Some(var_name) => make::tuple_struct_pat( 55 Some(var_name) => {
56 make::path_unqualified(make::path_segment(make::name_ref(var_name))), 56 make::tuple_struct_pat(make::ext::ident_path(var_name), once(original_pat))
57 once(make::wildcard_pat().into()), 57 .into()
58 ) 58 }
59 .into(),
60 }; 59 };
60
61 let block = 61 let block =
62 make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); 62 make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
63 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block, None); 63 let if_ = make::expr_if(make::condition(init, Some(pat)), block, None);
64 let stmt = make::expr_stmt(if_); 64 let stmt = make::expr_stmt(if_);
65 65
66 let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap();
67 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
68
69 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); 66 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
70 }, 67 },
71 ) 68 )
diff --git a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
index a986a6ae8..0fec961b4 100644
--- a/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ide_assists/src/handlers/replace_unwrap_with_match.rs
@@ -32,7 +32,7 @@ use ide_db::ty_filter::TryEnum;
32// fn main() { 32// fn main() {
33// let x: Result<i32, i32> = Result::Ok(92); 33// let x: Result<i32, i32> = Result::Ok(92);
34// let y = match x { 34// let y = match x {
35// Ok(a) => a, 35// Ok(it) => it,
36// $0_ => unreachable!(), 36// $0_ => unreachable!(),
37// }; 37// };
38// } 38// }
@@ -52,16 +52,17 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
52 "Replace unwrap with match", 52 "Replace unwrap with match",
53 target, 53 target,
54 |builder| { 54 |builder| {
55 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 55 let ok_path = make::ext::ident_path(happy_variant);
56 let it = make::ident_pat(make::name("a")).into(); 56 let it = make::ident_pat(make::name("it")).into();
57 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); 57 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
58 58
59 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); 59 let bind_path = make::ext::ident_path("it");
60 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); 60 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
61 61
62 let unreachable_call = make::expr_unreachable(); 62 let err_arm = make::match_arm(
63 let err_arm = 63 iter::once(make::wildcard_pat().into()),
64 make::match_arm(iter::once(make::wildcard_pat().into()), unreachable_call); 64 make::ext::expr_unreachable(),
65 );
65 66
66 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); 67 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
67 let match_expr = make::expr_match(caller.clone(), match_arm_list) 68 let match_expr = make::expr_match(caller.clone(), match_arm_list)
@@ -110,7 +111,7 @@ fn i<T>(a: T) -> T { a }
110fn main() { 111fn main() {
111 let x: Result<i32, i32> = Result::Ok(92); 112 let x: Result<i32, i32> = Result::Ok(92);
112 let y = match i(x) { 113 let y = match i(x) {
113 Ok(a) => a, 114 Ok(it) => it,
114 $0_ => unreachable!(), 115 $0_ => unreachable!(),
115 }; 116 };
116} 117}
@@ -136,7 +137,7 @@ fn i<T>(a: T) -> T { a }
136fn main() { 137fn main() {
137 let x = Option::Some(92); 138 let x = Option::Some(92);
138 let y = match i(x) { 139 let y = match i(x) {
139 Some(a) => a, 140 Some(it) => it,
140 $0_ => unreachable!(), 141 $0_ => unreachable!(),
141 }; 142 };
142} 143}
@@ -162,7 +163,7 @@ fn i<T>(a: T) -> T { a }
162fn main() { 163fn main() {
163 let x: Result<i32, i32> = Result::Ok(92); 164 let x: Result<i32, i32> = Result::Ok(92);
164 let y = match i(x) { 165 let y = match i(x) {
165 Ok(a) => a, 166 Ok(it) => it,
166 $0_ => unreachable!(), 167 $0_ => unreachable!(),
167 }.count_zeroes(); 168 }.count_zeroes();
168} 169}
diff --git a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
index e838630ea..2f1da82c7 100644
--- a/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
+++ b/crates/ide_assists/src/handlers/wrap_return_type_in_result.rs
@@ -54,9 +54,7 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext)
54 54
55 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { 55 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
56 let ok_wrapped = make::expr_call( 56 let ok_wrapped = make::expr_call(
57 make::expr_path(make::path_unqualified(make::path_segment(make::name_ref( 57 make::expr_path(make::ext::ident_path("Ok")),
58 "Ok",
59 )))),
60 make::arg_list(iter::once(ret_expr_arg.clone())), 58 make::arg_list(iter::once(ret_expr_arg.clone())),
61 ); 59 );
62 builder.replace_ast(ret_expr_arg, ok_wrapped); 60 builder.replace_ast(ret_expr_arg, ok_wrapped);
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 9c2847998..0d3969c36 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -21,7 +21,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig {
21 snippet_cap: SnippetCap::new(true), 21 snippet_cap: SnippetCap::new(true),
22 allowed: None, 22 allowed: None,
23 insert_use: InsertUseConfig { 23 insert_use: InsertUseConfig {
24 merge: Some(MergeBehavior::Full), 24 merge: Some(MergeBehavior::Crate),
25 prefix_kind: hir::PrefixKind::Plain, 25 prefix_kind: hir::PrefixKind::Plain,
26 group: true, 26 group: true,
27 }, 27 },
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 59bcef8fb..49c1a9776 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -1531,7 +1531,7 @@ enum Result<T, E> { Ok(T), Err(E) }
1531fn main() { 1531fn main() {
1532 let x: Result<i32, i32> = Result::Ok(92); 1532 let x: Result<i32, i32> = Result::Ok(92);
1533 let y = match x { 1533 let y = match x {
1534 Ok(a) => a, 1534 Ok(it) => it,
1535 $0_ => unreachable!(), 1535 $0_ => unreachable!(),
1536 }; 1536 };
1537} 1537}
diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs
index 5a90ad715..0dcf20c61 100644
--- a/crates/ide_assists/src/utils.rs
+++ b/crates/ide_assists/src/utils.rs
@@ -159,8 +159,8 @@ pub fn add_trait_assoc_items_to_impl(
159 match fn_def.body() { 159 match fn_def.body() {
160 Some(_) => fn_def, 160 Some(_) => fn_def,
161 None => { 161 None => {
162 let body = 162 let body = make::block_expr(None, Some(make::ext::expr_todo()))
163 make::block_expr(None, Some(make::expr_todo())).indent(edit::IndentLevel(1)); 163 .indent(edit::IndentLevel(1));
164 fn_def.with_body(body) 164 fn_def.with_body(body)
165 } 165 }
166 } 166 }
diff --git a/crates/ide_assists/src/utils/suggest_name.rs b/crates/ide_assists/src/utils/suggest_name.rs
index deafcd630..b3aabeab3 100644
--- a/crates/ide_assists/src/utils/suggest_name.rs
+++ b/crates/ide_assists/src/utils/suggest_name.rs
@@ -6,7 +6,7 @@ use itertools::Itertools;
6use stdx::to_lower_snake_case; 6use stdx::to_lower_snake_case;
7use syntax::{ 7use syntax::{
8 ast::{self, NameOwner}, 8 ast::{self, NameOwner},
9 match_ast, AstNode, 9 match_ast, AstNode, SmolStr,
10}; 10};
11 11
12/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` 12/// Trait names, that will be ignored when in `impl Trait` and `dyn Trait`
@@ -57,6 +57,14 @@ const USELESS_METHODS: &[&str] = &[
57 "iter_mut", 57 "iter_mut",
58]; 58];
59 59
60pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr {
61 let c = ty
62 .type_bound_list()
63 .and_then(|bounds| bounds.syntax().text().char_at(0.into()))
64 .unwrap_or('T');
65 c.encode_utf8(&mut [0; 4]).into()
66}
67
60/// Suggest name of variable for given expression 68/// Suggest name of variable for given expression
61/// 69///
62/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name. 70/// **NOTE**: it is caller's responsibility to guarantee uniqueness of the name.
@@ -75,7 +83,8 @@ const USELESS_METHODS: &[&str] = &[
75/// It also applies heuristics to filter out less informative names 83/// It also applies heuristics to filter out less informative names
76/// 84///
77/// Currently it sticks to the first name found. 85/// Currently it sticks to the first name found.
78pub(crate) fn variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { 86// FIXME: Microoptimize and return a `SmolStr` here.
87pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String {
79 // `from_param` does not benifit from stripping 88 // `from_param` does not benifit from stripping
80 // it need the largest context possible 89 // it need the largest context possible
81 // so we check firstmost 90 // so we check firstmost
@@ -276,7 +285,7 @@ mod tests {
276 frange.range, 285 frange.range,
277 "selection is not an expression(yet contained in one)" 286 "selection is not an expression(yet contained in one)"
278 ); 287 );
279 let name = variable(&expr, &sema); 288 let name = for_variable(&expr, &sema);
280 assert_eq!(&name, expected); 289 assert_eq!(&name, expected);
281 } 290 }
282 291
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index e32633565..645349215 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -80,7 +80,7 @@ pub use crate::{
80// 80//
81// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities. 81// And the auto import completions, enabled with the `rust-analyzer.completion.autoimport.enable` setting and the corresponding LSP client capabilities.
82// Those are the additional completion options with automatic `use` import and options from all project importable items, 82// Those are the additional completion options with automatic `use` import and options from all project importable items,
83// fuzzy matched agains the completion imput. 83// fuzzy matched against the completion input.
84// 84//
85// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[] 85// image::https://user-images.githubusercontent.com/48062697/113020667-b72ab880-917a-11eb-8778-716cf26a0eb3.gif[]
86 86
diff --git a/crates/ide_completion/src/test_utils.rs b/crates/ide_completion/src/test_utils.rs
index c9857ec5f..939fb2d47 100644
--- a/crates/ide_completion/src/test_utils.rs
+++ b/crates/ide_completion/src/test_utils.rs
@@ -20,7 +20,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
20 add_call_argument_snippets: true, 20 add_call_argument_snippets: true,
21 snippet_cap: SnippetCap::new(true), 21 snippet_cap: SnippetCap::new(true),
22 insert_use: InsertUseConfig { 22 insert_use: InsertUseConfig {
23 merge: Some(MergeBehavior::Full), 23 merge: Some(MergeBehavior::Crate),
24 prefix_kind: PrefixKind::Plain, 24 prefix_kind: PrefixKind::Plain,
25 group: true, 25 group: true,
26 }, 26 },
diff --git a/crates/ide_db/src/helpers/insert_use/tests.rs b/crates/ide_db/src/helpers/insert_use/tests.rs
index 048c213e2..248227d29 100644
--- a/crates/ide_db/src/helpers/insert_use/tests.rs
+++ b/crates/ide_db/src/helpers/insert_use/tests.rs
@@ -44,7 +44,7 @@ fn insert_not_group_empty() {
44 44
45#[test] 45#[test]
46fn insert_existing() { 46fn insert_existing() {
47 check_full("std::fs", "use std::fs;", "use std::fs;") 47 check_crate("std::fs", "use std::fs;", "use std::fs;")
48} 48}
49 49
50#[test] 50#[test]
@@ -249,7 +249,7 @@ use self::fmt;",
249 249
250#[test] 250#[test]
251fn insert_no_imports() { 251fn insert_no_imports() {
252 check_full( 252 check_crate(
253 "foo::bar", 253 "foo::bar",
254 "fn main() {}", 254 "fn main() {}",
255 r"use foo::bar; 255 r"use foo::bar;
@@ -263,7 +263,7 @@ fn insert_empty_file() {
263 cov_mark::check!(insert_group_empty_file); 263 cov_mark::check!(insert_group_empty_file);
264 // empty files will get two trailing newlines 264 // empty files will get two trailing newlines
265 // this is due to the test case insert_no_imports above 265 // this is due to the test case insert_no_imports above
266 check_full( 266 check_crate(
267 "foo::bar", 267 "foo::bar",
268 "", 268 "",
269 r"use foo::bar; 269 r"use foo::bar;
@@ -290,7 +290,7 @@ fn insert_empty_module() {
290#[test] 290#[test]
291fn insert_after_inner_attr() { 291fn insert_after_inner_attr() {
292 cov_mark::check!(insert_group_empty_inner_attr); 292 cov_mark::check!(insert_group_empty_inner_attr);
293 check_full( 293 check_crate(
294 "foo::bar", 294 "foo::bar",
295 r"#![allow(unused_imports)]", 295 r"#![allow(unused_imports)]",
296 r"#![allow(unused_imports)] 296 r"#![allow(unused_imports)]
@@ -301,7 +301,7 @@ use foo::bar;",
301 301
302#[test] 302#[test]
303fn insert_after_inner_attr2() { 303fn insert_after_inner_attr2() {
304 check_full( 304 check_crate(
305 "foo::bar", 305 "foo::bar",
306 r"#![allow(unused_imports)] 306 r"#![allow(unused_imports)]
307 307
@@ -371,12 +371,12 @@ fn main() {}"#,
371 371
372#[test] 372#[test]
373fn merge_groups() { 373fn merge_groups() {
374 check_last("std::io", r"use std::fmt;", r"use std::{fmt, io};") 374 check_module("std::io", r"use std::fmt;", r"use std::{fmt, io};")
375} 375}
376 376
377#[test] 377#[test]
378fn merge_groups_last() { 378fn merge_groups_last() {
379 check_last( 379 check_module(
380 "std::io", 380 "std::io",
381 r"use std::fmt::{Result, Display};", 381 r"use std::fmt::{Result, Display};",
382 r"use std::fmt::{Result, Display}; 382 r"use std::fmt::{Result, Display};
@@ -386,12 +386,12 @@ use std::io;",
386 386
387#[test] 387#[test]
388fn merge_last_into_self() { 388fn merge_last_into_self() {
389 check_last("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};"); 389 check_module("foo::bar::baz", r"use foo::bar;", r"use foo::bar::{self, baz};");
390} 390}
391 391
392#[test] 392#[test]
393fn merge_groups_full() { 393fn merge_groups_full() {
394 check_full( 394 check_crate(
395 "std::io", 395 "std::io",
396 r"use std::fmt::{Result, Display};", 396 r"use std::fmt::{Result, Display};",
397 r"use std::{fmt::{Result, Display}, io};", 397 r"use std::{fmt::{Result, Display}, io};",
@@ -400,17 +400,21 @@ fn merge_groups_full() {
400 400
401#[test] 401#[test]
402fn merge_groups_long_full() { 402fn merge_groups_long_full() {
403 check_full("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") 403 check_crate("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};")
404} 404}
405 405
406#[test] 406#[test]
407fn merge_groups_long_last() { 407fn merge_groups_long_last() {
408 check_last("std::foo::bar::Baz", r"use std::foo::bar::Qux;", r"use std::foo::bar::{Baz, Qux};") 408 check_module(
409 "std::foo::bar::Baz",
410 r"use std::foo::bar::Qux;",
411 r"use std::foo::bar::{Baz, Qux};",
412 )
409} 413}
410 414
411#[test] 415#[test]
412fn merge_groups_long_full_list() { 416fn merge_groups_long_full_list() {
413 check_full( 417 check_crate(
414 "std::foo::bar::Baz", 418 "std::foo::bar::Baz",
415 r"use std::foo::bar::{Qux, Quux};", 419 r"use std::foo::bar::{Qux, Quux};",
416 r"use std::foo::bar::{Baz, Quux, Qux};", 420 r"use std::foo::bar::{Baz, Quux, Qux};",
@@ -419,7 +423,7 @@ fn merge_groups_long_full_list() {
419 423
420#[test] 424#[test]
421fn merge_groups_long_last_list() { 425fn merge_groups_long_last_list() {
422 check_last( 426 check_module(
423 "std::foo::bar::Baz", 427 "std::foo::bar::Baz",
424 r"use std::foo::bar::{Qux, Quux};", 428 r"use std::foo::bar::{Qux, Quux};",
425 r"use std::foo::bar::{Baz, Quux, Qux};", 429 r"use std::foo::bar::{Baz, Quux, Qux};",
@@ -428,7 +432,7 @@ fn merge_groups_long_last_list() {
428 432
429#[test] 433#[test]
430fn merge_groups_long_full_nested() { 434fn merge_groups_long_full_nested() {
431 check_full( 435 check_crate(
432 "std::foo::bar::Baz", 436 "std::foo::bar::Baz",
433 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", 437 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
434 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};", 438 r"use std::foo::bar::{Baz, Qux, quux::{Fez, Fizz}};",
@@ -437,7 +441,7 @@ fn merge_groups_long_full_nested() {
437 441
438#[test] 442#[test]
439fn merge_groups_long_last_nested() { 443fn merge_groups_long_last_nested() {
440 check_last( 444 check_module(
441 "std::foo::bar::Baz", 445 "std::foo::bar::Baz",
442 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", 446 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
443 r"use std::foo::bar::Baz; 447 r"use std::foo::bar::Baz;
@@ -447,7 +451,7 @@ use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
447 451
448#[test] 452#[test]
449fn merge_groups_full_nested_deep() { 453fn merge_groups_full_nested_deep() {
450 check_full( 454 check_crate(
451 "std::foo::bar::quux::Baz", 455 "std::foo::bar::quux::Baz",
452 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};", 456 r"use std::foo::bar::{Qux, quux::{Fez, Fizz}};",
453 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};", 457 r"use std::foo::bar::{Qux, quux::{Baz, Fez, Fizz}};",
@@ -456,7 +460,7 @@ fn merge_groups_full_nested_deep() {
456 460
457#[test] 461#[test]
458fn merge_groups_full_nested_long() { 462fn merge_groups_full_nested_long() {
459 check_full( 463 check_crate(
460 "std::foo::bar::Baz", 464 "std::foo::bar::Baz",
461 r"use std::{foo::bar::Qux};", 465 r"use std::{foo::bar::Qux};",
462 r"use std::{foo::bar::{Baz, Qux}};", 466 r"use std::{foo::bar::{Baz, Qux}};",
@@ -465,7 +469,7 @@ fn merge_groups_full_nested_long() {
465 469
466#[test] 470#[test]
467fn merge_groups_last_nested_long() { 471fn merge_groups_last_nested_long() {
468 check_full( 472 check_crate(
469 "std::foo::bar::Baz", 473 "std::foo::bar::Baz",
470 r"use std::{foo::bar::Qux};", 474 r"use std::{foo::bar::Qux};",
471 r"use std::{foo::bar::{Baz, Qux}};", 475 r"use std::{foo::bar::{Baz, Qux}};",
@@ -474,7 +478,7 @@ fn merge_groups_last_nested_long() {
474 478
475#[test] 479#[test]
476fn merge_groups_skip_pub() { 480fn merge_groups_skip_pub() {
477 check_full( 481 check_crate(
478 "std::io", 482 "std::io",
479 r"pub use std::fmt::{Result, Display};", 483 r"pub use std::fmt::{Result, Display};",
480 r"pub use std::fmt::{Result, Display}; 484 r"pub use std::fmt::{Result, Display};
@@ -484,7 +488,7 @@ use std::io;",
484 488
485#[test] 489#[test]
486fn merge_groups_skip_pub_crate() { 490fn merge_groups_skip_pub_crate() {
487 check_full( 491 check_crate(
488 "std::io", 492 "std::io",
489 r"pub(crate) use std::fmt::{Result, Display};", 493 r"pub(crate) use std::fmt::{Result, Display};",
490 r"pub(crate) use std::fmt::{Result, Display}; 494 r"pub(crate) use std::fmt::{Result, Display};
@@ -494,7 +498,7 @@ use std::io;",
494 498
495#[test] 499#[test]
496fn merge_groups_skip_attributed() { 500fn merge_groups_skip_attributed() {
497 check_full( 501 check_crate(
498 "std::io", 502 "std::io",
499 r#" 503 r#"
500#[cfg(feature = "gated")] use std::fmt::{Result, Display}; 504#[cfg(feature = "gated")] use std::fmt::{Result, Display};
@@ -509,7 +513,7 @@ use std::io;
509#[test] 513#[test]
510#[ignore] // FIXME: Support this 514#[ignore] // FIXME: Support this
511fn split_out_merge() { 515fn split_out_merge() {
512 check_last( 516 check_module(
513 "std::fmt::Result", 517 "std::fmt::Result",
514 r"use std::{fmt, io};", 518 r"use std::{fmt, io};",
515 r"use std::fmt::{self, Result}; 519 r"use std::fmt::{self, Result};
@@ -519,29 +523,33 @@ use std::io;",
519 523
520#[test] 524#[test]
521fn merge_into_module_import() { 525fn merge_into_module_import() {
522 check_full("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};") 526 check_crate("std::fmt::Result", r"use std::{fmt, io};", r"use std::{fmt::{self, Result}, io};")
523} 527}
524 528
525#[test] 529#[test]
526fn merge_groups_self() { 530fn merge_groups_self() {
527 check_full("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};") 531 check_crate("std::fmt::Debug", r"use std::fmt;", r"use std::fmt::{self, Debug};")
528} 532}
529 533
530#[test] 534#[test]
531fn merge_mod_into_glob() { 535fn merge_mod_into_glob() {
532 check_full("token::TokenKind", r"use token::TokenKind::*;", r"use token::TokenKind::{*, self};") 536 check_crate(
537 "token::TokenKind",
538 r"use token::TokenKind::*;",
539 r"use token::TokenKind::{*, self};",
540 )
533 // FIXME: have it emit `use token::TokenKind::{self, *}`? 541 // FIXME: have it emit `use token::TokenKind::{self, *}`?
534} 542}
535 543
536#[test] 544#[test]
537fn merge_self_glob() { 545fn merge_self_glob() {
538 check_full("self", r"use self::*;", r"use self::{*, self};") 546 check_crate("self", r"use self::*;", r"use self::{*, self};")
539 // FIXME: have it emit `use {self, *}`? 547 // FIXME: have it emit `use {self, *}`?
540} 548}
541 549
542#[test] 550#[test]
543fn merge_glob_nested() { 551fn merge_glob_nested() {
544 check_full( 552 check_crate(
545 "foo::bar::quux::Fez", 553 "foo::bar::quux::Fez",
546 r"use foo::bar::{Baz, quux::*};", 554 r"use foo::bar::{Baz, quux::*};",
547 r"use foo::bar::{Baz, quux::{self::*, Fez}};", 555 r"use foo::bar::{Baz, quux::{self::*, Fez}};",
@@ -550,7 +558,7 @@ fn merge_glob_nested() {
550 558
551#[test] 559#[test]
552fn merge_nested_considers_first_segments() { 560fn merge_nested_considers_first_segments() {
553 check_full( 561 check_crate(
554 "hir_ty::display::write_bounds_like_dyn_trait", 562 "hir_ty::display::write_bounds_like_dyn_trait",
555 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};", 563 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter}, method_resolution};",
556 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};", 564 r"use hir_ty::{autoderef, display::{HirDisplayError, HirFormatter, write_bounds_like_dyn_trait}, method_resolution};",
@@ -559,7 +567,7 @@ fn merge_nested_considers_first_segments() {
559 567
560#[test] 568#[test]
561fn skip_merge_last_too_long() { 569fn skip_merge_last_too_long() {
562 check_last( 570 check_module(
563 "foo::bar", 571 "foo::bar",
564 r"use foo::bar::baz::Qux;", 572 r"use foo::bar::baz::Qux;",
565 r"use foo::bar; 573 r"use foo::bar;
@@ -569,7 +577,7 @@ use foo::bar::baz::Qux;",
569 577
570#[test] 578#[test]
571fn skip_merge_last_too_long2() { 579fn skip_merge_last_too_long2() {
572 check_last( 580 check_module(
573 "foo::bar::baz::Qux", 581 "foo::bar::baz::Qux",
574 r"use foo::bar;", 582 r"use foo::bar;",
575 r"use foo::bar; 583 r"use foo::bar;
@@ -592,7 +600,7 @@ fn merge_last_fail() {
592 check_merge_only_fail( 600 check_merge_only_fail(
593 r"use foo::bar::{baz::{Qux, Fez}};", 601 r"use foo::bar::{baz::{Qux, Fez}};",
594 r"use foo::bar::{baaz::{Quux, Feez}};", 602 r"use foo::bar::{baaz::{Quux, Feez}};",
595 MergeBehavior::Last, 603 MergeBehavior::Module,
596 ); 604 );
597} 605}
598 606
@@ -601,7 +609,7 @@ fn merge_last_fail1() {
601 check_merge_only_fail( 609 check_merge_only_fail(
602 r"use foo::bar::{baz::{Qux, Fez}};", 610 r"use foo::bar::{baz::{Qux, Fez}};",
603 r"use foo::bar::baaz::{Quux, Feez};", 611 r"use foo::bar::baaz::{Quux, Feez};",
604 MergeBehavior::Last, 612 MergeBehavior::Module,
605 ); 613 );
606} 614}
607 615
@@ -610,7 +618,7 @@ fn merge_last_fail2() {
610 check_merge_only_fail( 618 check_merge_only_fail(
611 r"use foo::bar::baz::{Qux, Fez};", 619 r"use foo::bar::baz::{Qux, Fez};",
612 r"use foo::bar::{baaz::{Quux, Feez}};", 620 r"use foo::bar::{baaz::{Quux, Feez}};",
613 MergeBehavior::Last, 621 MergeBehavior::Module,
614 ); 622 );
615} 623}
616 624
@@ -619,7 +627,7 @@ fn merge_last_fail3() {
619 check_merge_only_fail( 627 check_merge_only_fail(
620 r"use foo::bar::baz::{Qux, Fez};", 628 r"use foo::bar::baz::{Qux, Fez};",
621 r"use foo::bar::baaz::{Quux, Feez};", 629 r"use foo::bar::baaz::{Quux, Feez};",
622 MergeBehavior::Last, 630 MergeBehavior::Module,
623 ); 631 );
624} 632}
625 633
@@ -648,12 +656,12 @@ fn check(
648 assert_eq_text!(ra_fixture_after, &result); 656 assert_eq_text!(ra_fixture_after, &result);
649} 657}
650 658
651fn check_full(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 659fn check_crate(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
652 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Full), false, true) 660 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Crate), false, true)
653} 661}
654 662
655fn check_last(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 663fn check_module(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
656 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Last), false, true) 664 check(path, ra_fixture_before, ra_fixture_after, Some(MergeBehavior::Module), false, true)
657} 665}
658 666
659fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 667fn check_none(path: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
diff --git a/crates/ide_db/src/helpers/merge_imports.rs b/crates/ide_db/src/helpers/merge_imports.rs
index 3f5bbef7f..af2a51a4d 100644
--- a/crates/ide_db/src/helpers/merge_imports.rs
+++ b/crates/ide_db/src/helpers/merge_imports.rs
@@ -9,19 +9,19 @@ use syntax::ast::{
9/// What type of merges are allowed. 9/// What type of merges are allowed.
10#[derive(Copy, Clone, Debug, PartialEq, Eq)] 10#[derive(Copy, Clone, Debug, PartialEq, Eq)]
11pub enum MergeBehavior { 11pub enum MergeBehavior {
12 /// Merge everything together creating deeply nested imports. 12 /// Merge imports from the same crate into a single use statement.
13 Full, 13 Crate,
14 /// Only merge the last import level, doesn't allow import nesting. 14 /// Merge imports from the same module into a single use statement.
15 Last, 15 Module,
16} 16}
17 17
18impl MergeBehavior { 18impl MergeBehavior {
19 #[inline] 19 #[inline]
20 fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool { 20 fn is_tree_allowed(&self, tree: &ast::UseTree) -> bool {
21 match self { 21 match self {
22 MergeBehavior::Full => true, 22 MergeBehavior::Crate => true,
23 // only simple single segment paths are allowed 23 // only simple single segment paths are allowed
24 MergeBehavior::Last => { 24 MergeBehavior::Module => {
25 tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1) 25 tree.use_tree_list().is_none() && tree.path().map(path_len) <= Some(1)
26 } 26 }
27 } 27 }
@@ -137,7 +137,7 @@ fn recursive_merge(
137 None, 137 None,
138 false, 138 false,
139 ); 139 );
140 use_trees.insert(idx, make::glob_use_tree()); 140 use_trees.insert(idx, make::use_tree_glob());
141 continue; 141 continue;
142 } 142 }
143 143
@@ -153,7 +153,7 @@ fn recursive_merge(
153 } 153 }
154 } 154 }
155 Err(_) 155 Err(_)
156 if merge == MergeBehavior::Last 156 if merge == MergeBehavior::Module
157 && use_trees.len() > 0 157 && use_trees.len() > 0
158 && rhs_t.use_tree_list().is_some() => 158 && rhs_t.use_tree_list().is_some() =>
159 { 159 {
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 8f899ea56..67840602b 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -233,6 +233,13 @@ impl Definition {
233 }; 233 };
234 } 234 }
235 235
236 if let Definition::SelfType(impl_) = self {
237 return match impl_.source(db).map(|src| src.value.syntax().text_range()) {
238 Some(range) => SearchScope::file_range(FileRange { file_id, range }),
239 None => SearchScope::single_file(file_id),
240 };
241 }
242
236 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self { 243 if let Definition::GenericParam(hir::GenericParam::LifetimeParam(param)) = self {
237 let range = match param.parent(db) { 244 let range = match param.parent(db) {
238 hir::GenericDef::Function(it) => { 245 hir::GenericDef::Function(it) => {
@@ -297,7 +304,7 @@ impl Definition {
297 } 304 }
298 305
299 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> { 306 pub fn usages<'a>(&'a self, sema: &'a Semantics<RootDatabase>) -> FindUsages<'a> {
300 FindUsages { def: self, sema, scope: None, include_self_kw_refs: false } 307 FindUsages { def: self, sema, scope: None, include_self_kw_refs: None }
301 } 308 }
302} 309}
303 310
@@ -305,12 +312,13 @@ pub struct FindUsages<'a> {
305 def: &'a Definition, 312 def: &'a Definition,
306 sema: &'a Semantics<'a, RootDatabase>, 313 sema: &'a Semantics<'a, RootDatabase>,
307 scope: Option<SearchScope>, 314 scope: Option<SearchScope>,
308 include_self_kw_refs: bool, 315 include_self_kw_refs: Option<hir::Type>,
309} 316}
310 317
311impl<'a> FindUsages<'a> { 318impl<'a> FindUsages<'a> {
312 pub fn include_self_kw_refs(mut self, include: bool) -> FindUsages<'a> { 319 /// Enable searching for `Self` when the definition is a type.
313 self.include_self_kw_refs = include; 320 pub fn include_self_refs(mut self) -> FindUsages<'a> {
321 self.include_self_kw_refs = def_to_ty(self.sema, self.def);
314 self 322 self
315 } 323 }
316 324
@@ -354,13 +362,18 @@ impl<'a> FindUsages<'a> {
354 } 362 }
355 }; 363 };
356 364
357 let name = match self.def.name(sema.db) { 365 let name = self.def.name(sema.db).or_else(|| {
358 Some(it) => it.to_string(), 366 self.include_self_kw_refs.as_ref().and_then(|ty| {
367 ty.as_adt()
368 .map(|adt| adt.name(self.sema.db))
369 .or_else(|| ty.as_builtin().map(|builtin| builtin.name()))
370 })
371 });
372 let name = match name {
373 Some(name) => name.to_string(),
359 None => return, 374 None => return,
360 }; 375 };
361 376 let name = name.as_str();
362 let pat = name.as_str();
363 let search_for_self = self.include_self_kw_refs;
364 377
365 for (file_id, search_range) in search_scope { 378 for (file_id, search_range) in search_scope {
366 let text = sema.db.file_text(file_id); 379 let text = sema.db.file_text(file_id);
@@ -369,51 +382,63 @@ impl<'a> FindUsages<'a> {
369 382
370 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); 383 let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
371 384
372 let mut handle_match = |idx: usize| -> bool { 385 for (idx, _) in text.match_indices(name) {
373 let offset: TextSize = idx.try_into().unwrap(); 386 let offset: TextSize = idx.try_into().unwrap();
374 if !search_range.contains_inclusive(offset) { 387 if !search_range.contains_inclusive(offset) {
375 return false; 388 continue;
376 } 389 }
377 390
378 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) { 391 if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
379 match name { 392 if match name {
380 ast::NameLike::NameRef(name_ref) => { 393 ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink),
381 if self.found_name_ref(&name_ref, sink) { 394 ast::NameLike::Name(name) => self.found_name(&name, sink),
382 return true; 395 ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink),
383 } 396 } {
384 } 397 return;
385 ast::NameLike::Name(name) => {
386 if self.found_name(&name, sink) {
387 return true;
388 }
389 }
390 ast::NameLike::Lifetime(lifetime) => {
391 if self.found_lifetime(&lifetime, sink) {
392 return true;
393 }
394 }
395 } 398 }
396 } 399 }
397
398 return false;
399 };
400
401 for (idx, _) in text.match_indices(pat) {
402 if handle_match(idx) {
403 return;
404 }
405 } 400 }
406 401 if let Some(self_ty) = &self.include_self_kw_refs {
407 if search_for_self {
408 for (idx, _) in text.match_indices("Self") { 402 for (idx, _) in text.match_indices("Self") {
409 if handle_match(idx) { 403 let offset: TextSize = idx.try_into().unwrap();
410 return; 404 if !search_range.contains_inclusive(offset) {
405 continue;
406 }
407
408 if let Some(ast::NameLike::NameRef(name_ref)) =
409 sema.find_node_at_offset_with_descend(&tree, offset)
410 {
411 if self.found_self_ty_name_ref(&self_ty, &name_ref, sink) {
412 return;
413 }
411 } 414 }
412 } 415 }
413 } 416 }
414 } 417 }
415 } 418 }
416 419
420 fn found_self_ty_name_ref(
421 &self,
422 self_ty: &hir::Type,
423 name_ref: &ast::NameRef,
424 sink: &mut dyn FnMut(FileId, FileReference) -> bool,
425 ) -> bool {
426 match NameRefClass::classify(self.sema, &name_ref) {
427 Some(NameRefClass::Definition(Definition::SelfType(impl_)))
428 if impl_.self_ty(self.sema.db) == *self_ty =>
429 {
430 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
431 let reference = FileReference {
432 range,
433 name: ast::NameLike::NameRef(name_ref.clone()),
434 access: None,
435 };
436 sink(file_id, reference)
437 }
438 _ => false,
439 }
440 }
441
417 fn found_lifetime( 442 fn found_lifetime(
418 &self, 443 &self,
419 lifetime: &ast::Lifetime, 444 lifetime: &ast::Lifetime,
@@ -429,7 +454,7 @@ impl<'a> FindUsages<'a> {
429 }; 454 };
430 sink(file_id, reference) 455 sink(file_id, reference)
431 } 456 }
432 _ => false, // not a usage 457 _ => false,
433 } 458 }
434 } 459 }
435 460
@@ -448,42 +473,35 @@ impl<'a> FindUsages<'a> {
448 }; 473 };
449 sink(file_id, reference) 474 sink(file_id, reference)
450 } 475 }
451 Some(NameRefClass::Definition(Definition::SelfType(impl_))) => { 476 Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => {
452 let ty = impl_.self_ty(self.sema.db); 477 if self.include_self_kw_refs == def_to_ty(self.sema, &def) {
453 478 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
454 if let Some(adt) = ty.as_adt() { 479 let reference = FileReference {
455 if &Definition::ModuleDef(ModuleDef::Adt(adt)) == self.def { 480 range,
456 let FileRange { file_id, range } = 481 name: ast::NameLike::NameRef(name_ref.clone()),
457 self.sema.original_range(name_ref.syntax()); 482 access: reference_access(&def, &name_ref),
458 let reference = FileReference { 483 };
459 range, 484 sink(file_id, reference)
460 name: ast::NameLike::NameRef(name_ref.clone()), 485 } else {
461 access: None, 486 false
462 };
463 return sink(file_id, reference);
464 }
465 } 487 }
466
467 false
468 } 488 }
469 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { 489 Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
470 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); 490 let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
471 let reference = match self.def { 491 let access = match self.def {
472 Definition::Field(_) if &field == self.def => FileReference { 492 Definition::Field(_) if &field == self.def => {
473 range, 493 reference_access(&field, &name_ref)
474 name: ast::NameLike::NameRef(name_ref.clone()), 494 }
475 access: reference_access(&field, &name_ref), 495 Definition::Local(l) if &local == l => {
476 }, 496 reference_access(&Definition::Local(local), &name_ref)
477 Definition::Local(l) if &local == l => FileReference { 497 }
478 range, 498 _ => return false,
479 name: ast::NameLike::NameRef(name_ref.clone()),
480 access: reference_access(&Definition::Local(local), &name_ref),
481 },
482 _ => return false, // not a usage
483 }; 499 };
500 let reference =
501 FileReference { range, name: ast::NameLike::NameRef(name_ref.clone()), access };
484 sink(file_id, reference) 502 sink(file_id, reference)
485 } 503 }
486 _ => false, // not a usage 504 _ => false,
487 } 505 }
488 } 506 }
489 507
@@ -513,11 +531,30 @@ impl<'a> FindUsages<'a> {
513 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None }; 531 FileReference { range, name: ast::NameLike::Name(name.clone()), access: None };
514 sink(file_id, reference) 532 sink(file_id, reference)
515 } 533 }
516 _ => false, // not a usage 534 _ => false,
517 } 535 }
518 } 536 }
519} 537}
520 538
539fn def_to_ty(sema: &Semantics<RootDatabase>, def: &Definition) -> Option<hir::Type> {
540 match def {
541 Definition::ModuleDef(def) => match def {
542 ModuleDef::Adt(adt) => Some(adt.ty(sema.db)),
543 ModuleDef::TypeAlias(it) => Some(it.ty(sema.db)),
544 ModuleDef::BuiltinType(it) => {
545 let graph = sema.db.crate_graph();
546 let krate = graph.iter().next()?;
547 let root_file = graph[krate].root_file_id;
548 let module = sema.to_module_def(root_file)?;
549 Some(it.ty(sema.db, module))
550 }
551 _ => None,
552 },
553 Definition::SelfType(it) => Some(it.self_ty(sema.db)),
554 _ => None,
555 }
556}
557
521fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { 558fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
522 // Only Locals and Fields have accesses for now. 559 // Only Locals and Fields have accesses for now.
523 if !matches!(def, Definition::Local(_) | Definition::Field(_)) { 560 if !matches!(def, Definition::Local(_) | Definition::Field(_)) {
diff --git a/crates/ide_db/src/ty_filter.rs b/crates/ide_db/src/ty_filter.rs
index 988ecd060..00678bf3e 100644
--- a/crates/ide_db/src/ty_filter.rs
+++ b/crates/ide_db/src/ty_filter.rs
@@ -43,7 +43,7 @@ impl TryEnum {
43 pub fn sad_pattern(self) -> ast::Pat { 43 pub fn sad_pattern(self) -> ast::Pat {
44 match self { 44 match self {
45 TryEnum::Result => make::tuple_struct_pat( 45 TryEnum::Result => make::tuple_struct_pat(
46 make::path_unqualified(make::path_segment(make::name_ref("Err"))), 46 make::ext::ident_path("Err"),
47 iter::once(make::wildcard_pat().into()), 47 iter::once(make::wildcard_pat().into()),
48 ) 48 )
49 .into(), 49 .into(),
@@ -54,12 +54,12 @@ impl TryEnum {
54 pub fn happy_pattern(self) -> ast::Pat { 54 pub fn happy_pattern(self) -> ast::Pat {
55 match self { 55 match self {
56 TryEnum::Result => make::tuple_struct_pat( 56 TryEnum::Result => make::tuple_struct_pat(
57 make::path_unqualified(make::path_segment(make::name_ref("Ok"))), 57 make::ext::ident_path("Ok"),
58 iter::once(make::wildcard_pat().into()), 58 iter::once(make::wildcard_pat().into()),
59 ) 59 )
60 .into(), 60 .into(),
61 TryEnum::Option => make::tuple_struct_pat( 61 TryEnum::Option => make::tuple_struct_pat(
62 make::path_unqualified(make::path_segment(make::name_ref("Some"))), 62 make::ext::ident_path("Some"),
63 iter::once(make::wildcard_pat().into()), 63 iter::once(make::wildcard_pat().into()),
64 ) 64 )
65 .into(), 65 .into(),
diff --git a/crates/project_model/src/cargo_workspace.rs b/crates/project_model/src/cargo_workspace.rs
index bc6e20341..b18699b77 100644
--- a/crates/project_model/src/cargo_workspace.rs
+++ b/crates/project_model/src/cargo_workspace.rs
@@ -201,31 +201,12 @@ impl CargoWorkspace {
201 if let Some(parent) = cargo_toml.parent() { 201 if let Some(parent) = cargo_toml.parent() {
202 meta.current_dir(parent.to_path_buf()); 202 meta.current_dir(parent.to_path_buf());
203 } 203 }
204 let target = if let Some(target) = config.target.as_ref() { 204 let target = if let Some(target) = &config.target {
205 Some(target.clone()) 205 Some(target.clone())
206 } else if let stdout @ Some(_) = cargo_config_build_target(cargo_toml) {
207 stdout
206 } else { 208 } else {
207 // cargo metadata defaults to giving information for _all_ targets. 209 rustc_discover_host_triple(cargo_toml)
208 // In the absence of a preference from the user, we use the host platform.
209 let mut rustc = Command::new(toolchain::rustc());
210 rustc.current_dir(cargo_toml.parent().unwrap()).arg("-vV");
211 log::debug!("Discovering host platform by {:?}", rustc);
212 match utf8_stdout(rustc) {
213 Ok(stdout) => {
214 let field = "host: ";
215 let target = stdout.lines().find_map(|l| l.strip_prefix(field));
216 if let Some(target) = target {
217 Some(target.to_string())
218 } else {
219 // If we fail to resolve the host platform, it's not the end of the world.
220 log::info!("rustc -vV did not report host platform, got:\n{}", stdout);
221 None
222 }
223 }
224 Err(e) => {
225 log::warn!("Failed to discover host platform: {}", e);
226 None
227 }
228 }
229 }; 210 };
230 if let Some(target) = target { 211 if let Some(target) = target {
231 meta.other_options(vec![String::from("--filter-platform"), target]); 212 meta.other_options(vec![String::from("--filter-platform"), target]);
@@ -368,3 +349,43 @@ impl CargoWorkspace {
368 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 349 self.packages.iter().filter(|(_, v)| v.name == name).count() == 1
369 } 350 }
370} 351}
352
353fn rustc_discover_host_triple(cargo_toml: &AbsPath) -> Option<String> {
354 let mut rustc = Command::new(toolchain::rustc());
355 rustc.current_dir(cargo_toml.parent().unwrap()).arg("-vV");
356 log::debug!("Discovering host platform by {:?}", rustc);
357 match utf8_stdout(rustc) {
358 Ok(stdout) => {
359 let field = "host: ";
360 let target = stdout.lines().find_map(|l| l.strip_prefix(field));
361 if let Some(target) = target {
362 Some(target.to_string())
363 } else {
364 // If we fail to resolve the host platform, it's not the end of the world.
365 log::info!("rustc -vV did not report host platform, got:\n{}", stdout);
366 None
367 }
368 }
369 Err(e) => {
370 log::warn!("Failed to discover host platform: {}", e);
371 None
372 }
373 }
374}
375
376fn cargo_config_build_target(cargo_toml: &AbsPath) -> Option<String> {
377 let mut cargo_config = Command::new(toolchain::cargo());
378 cargo_config
379 .current_dir(cargo_toml.parent().unwrap())
380 .args(&["-Z", "unstable-options", "config", "get", "build.target"])
381 .env("RUSTC_BOOTSTRAP", "1");
382 // if successful we receive `build.target = "target-triple"`
383 log::debug!("Discovering cargo config target by {:?}", cargo_config);
384 match utf8_stdout(cargo_config) {
385 Ok(stdout) => stdout
386 .strip_prefix("build.target = \"")
387 .and_then(|stdout| stdout.strip_suffix('"'))
388 .map(ToOwned::to_owned),
389 Err(_) => None,
390 }
391}
diff --git a/crates/project_model/src/rustc_cfg.rs b/crates/project_model/src/rustc_cfg.rs
index 312708575..012eab256 100644
--- a/crates/project_model/src/rustc_cfg.rs
+++ b/crates/project_model/src/rustc_cfg.rs
@@ -2,9 +2,12 @@
2 2
3use std::process::Command; 3use std::process::Command;
4 4
5use anyhow::Result;
6use paths::AbsPath;
7
5use crate::{cfg_flag::CfgFlag, utf8_stdout}; 8use crate::{cfg_flag::CfgFlag, utf8_stdout};
6 9
7pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> { 10pub(crate) fn get(cargo_toml: Option<&AbsPath>, target: Option<&str>) -> Vec<CfgFlag> {
8 let _p = profile::span("rustc_cfg::get"); 11 let _p = profile::span("rustc_cfg::get");
9 let mut res = Vec::with_capacity(6 * 2 + 1); 12 let mut res = Vec::with_capacity(6 * 2 + 1);
10 13
@@ -16,19 +19,39 @@ pub(crate) fn get(target: Option<&str>) -> Vec<CfgFlag> {
16 } 19 }
17 } 20 }
18 21
19 let rustc_cfgs = { 22 match get_rust_cfgs(cargo_toml, target) {
20 let mut cmd = Command::new(toolchain::rustc());
21 cmd.args(&["--print", "cfg", "-O"]);
22 if let Some(target) = target {
23 cmd.args(&["--target", target]);
24 }
25 utf8_stdout(cmd)
26 };
27
28 match rustc_cfgs {
29 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())), 23 Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
30 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 24 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
31 } 25 }
32 26
33 res 27 res
34} 28}
29
30fn get_rust_cfgs(cargo_toml: Option<&AbsPath>, target: Option<&str>) -> Result<String> {
31 let cargo_rust_cfgs = match cargo_toml {
32 Some(cargo_toml) => {
33 let mut cargo_config = Command::new(toolchain::cargo());
34 cargo_config
35 .current_dir(cargo_toml.parent().unwrap())
36 .args(&["-Z", "unstable-options", "rustc", "--print", "cfg"])
37 .env("RUSTC_BOOTSTRAP", "1");
38 if let Some(target) = target {
39 cargo_config.args(&["--target", target]);
40 }
41 utf8_stdout(cargo_config).ok()
42 }
43 None => None,
44 };
45 match cargo_rust_cfgs {
46 Some(stdout) => Ok(stdout),
47 None => {
48 // using unstable cargo features failed, fall back to using plain rustc
49 let mut cmd = Command::new(toolchain::rustc());
50 cmd.args(&["--print", "cfg", "-O"]);
51 if let Some(target) = target {
52 cmd.args(&["--target", target]);
53 }
54 utf8_stdout(cmd)
55 }
56 }
57}
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index 2fcd0f8fa..84c702fdf 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -143,7 +143,8 @@ impl ProjectWorkspace {
143 } else { 143 } else {
144 None 144 None
145 }; 145 };
146 let rustc_cfg = rustc_cfg::get(config.target.as_deref()); 146
147 let rustc_cfg = rustc_cfg::get(Some(&cargo_toml), config.target.as_deref());
147 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg } 148 ProjectWorkspace::Cargo { cargo, sysroot, rustc, rustc_cfg }
148 } 149 }
149 }; 150 };
@@ -159,7 +160,7 @@ impl ProjectWorkspace {
159 Some(path) => Some(Sysroot::load(path)?), 160 Some(path) => Some(Sysroot::load(path)?),
160 None => None, 161 None => None,
161 }; 162 };
162 let rustc_cfg = rustc_cfg::get(target); 163 let rustc_cfg = rustc_cfg::get(None, target);
163 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) 164 Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
164 } 165 }
165 166
@@ -310,7 +311,7 @@ fn project_json_to_crate_graph(
310 311
311 let target_cfgs = match krate.target.as_deref() { 312 let target_cfgs = match krate.target.as_deref() {
312 Some(target) => { 313 Some(target) => {
313 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(Some(target))) 314 cfg_cache.entry(target).or_insert_with(|| rustc_cfg::get(None, Some(target)))
314 } 315 }
315 None => &rustc_cfg, 316 None => &rustc_cfg,
316 }; 317 };
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 8879a9161..d83670bda 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -36,7 +36,7 @@ config_data! {
36 struct ConfigData { 36 struct ConfigData {
37 /// The strategy to use when inserting new imports or merging imports. 37 /// The strategy to use when inserting new imports or merging imports.
38 assist_importMergeBehavior | 38 assist_importMergeBehavior |
39 assist_importMergeBehaviour: MergeBehaviorDef = "\"full\"", 39 assist_importMergeBehaviour: MergeBehaviorDef = "\"crate\"",
40 /// The path structure for newly inserted paths to use. 40 /// The path structure for newly inserted paths to use.
41 assist_importPrefix: ImportPrefixDef = "\"plain\"", 41 assist_importPrefix: ImportPrefixDef = "\"plain\"",
42 /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines. 42 /// Group inserted imports by the [following order](https://rust-analyzer.github.io/manual.html#auto-import). Groups are separated by newlines.
@@ -604,8 +604,8 @@ impl Config {
604 InsertUseConfig { 604 InsertUseConfig {
605 merge: match self.data.assist_importMergeBehavior { 605 merge: match self.data.assist_importMergeBehavior {
606 MergeBehaviorDef::None => None, 606 MergeBehaviorDef::None => None,
607 MergeBehaviorDef::Full => Some(MergeBehavior::Full), 607 MergeBehaviorDef::Crate => Some(MergeBehavior::Crate),
608 MergeBehaviorDef::Last => Some(MergeBehavior::Last), 608 MergeBehaviorDef::Module => Some(MergeBehavior::Module),
609 }, 609 },
610 prefix_kind: match self.data.assist_importPrefix { 610 prefix_kind: match self.data.assist_importPrefix {
611 ImportPrefixDef::Plain => PrefixKind::Plain, 611 ImportPrefixDef::Plain => PrefixKind::Plain,
@@ -709,8 +709,10 @@ enum ManifestOrProjectJson {
709#[serde(rename_all = "snake_case")] 709#[serde(rename_all = "snake_case")]
710enum MergeBehaviorDef { 710enum MergeBehaviorDef {
711 None, 711 None,
712 Full, 712 #[serde(alias = "full")]
713 Last, 713 Crate,
714 #[serde(alias = "last")]
715 Module,
714} 716}
715 717
716#[derive(Deserialize, Debug, Clone)] 718#[derive(Deserialize, Debug, Clone)]
@@ -867,11 +869,11 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
867 }, 869 },
868 "MergeBehaviorDef" => set! { 870 "MergeBehaviorDef" => set! {
869 "type": "string", 871 "type": "string",
870 "enum": ["none", "full", "last"], 872 "enum": ["none", "crate", "module"],
871 "enumDescriptions": [ 873 "enumDescriptions": [
872 "No merging", 874 "Do not merge imports at all.",
873 "Merge all layers of the import trees", 875 "Merge imports from the same crate into a single `use` statement.",
874 "Only merge the last layer of the import trees" 876 "Merge imports from the same module into a single `use` statement."
875 ], 877 ],
876 }, 878 },
877 "ImportPrefixDef" => set! { 879 "ImportPrefixDef" => set! {
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs
index 3dcbe397a..56de9681c 100644
--- a/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -133,7 +133,7 @@ fn integrated_completion_benchmark() {
133 add_call_argument_snippets: true, 133 add_call_argument_snippets: true,
134 snippet_cap: SnippetCap::new(true), 134 snippet_cap: SnippetCap::new(true),
135 insert_use: InsertUseConfig { 135 insert_use: InsertUseConfig {
136 merge: Some(MergeBehavior::Full), 136 merge: Some(MergeBehavior::Crate),
137 prefix_kind: hir::PrefixKind::ByCrate, 137 prefix_kind: hir::PrefixKind::ByCrate,
138 group: true, 138 group: true,
139 }, 139 },
@@ -166,7 +166,7 @@ fn integrated_completion_benchmark() {
166 add_call_argument_snippets: true, 166 add_call_argument_snippets: true,
167 snippet_cap: SnippetCap::new(true), 167 snippet_cap: SnippetCap::new(true),
168 insert_use: InsertUseConfig { 168 insert_use: InsertUseConfig {
169 merge: Some(MergeBehavior::Full), 169 merge: Some(MergeBehavior::Crate),
170 prefix_kind: hir::PrefixKind::ByCrate, 170 prefix_kind: hir::PrefixKind::ByCrate,
171 group: true, 171 group: true,
172 }, 172 },
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 04f97f368..168355555 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -195,18 +195,13 @@ impl ast::GenericParamList {
195 pub fn add_generic_param(&self, generic_param: ast::GenericParam) { 195 pub fn add_generic_param(&self, generic_param: ast::GenericParam) {
196 match self.generic_params().last() { 196 match self.generic_params().last() {
197 Some(last_param) => { 197 Some(last_param) => {
198 let mut elems = Vec::new(); 198 let position = Position::after(last_param.syntax());
199 if !last_param 199 let elements = vec![
200 .syntax() 200 make::token(T![,]).into(),
201 .siblings_with_tokens(Direction::Next) 201 make::tokens::single_space().into(),
202 .any(|it| it.kind() == T![,]) 202 generic_param.syntax().clone().into(),
203 { 203 ];
204 elems.push(make::token(T![,]).into()); 204 ted::insert_all(position, elements);
205 elems.push(make::tokens::single_space().into());
206 };
207 elems.push(generic_param.syntax().clone().into());
208 let after_last_param = Position::after(last_param.syntax());
209 ted::insert_all(after_last_param, elems);
210 } 205 }
211 None => { 206 None => {
212 let after_l_angle = Position::after(self.l_angle_token().unwrap()); 207 let after_l_angle = Position::after(self.l_angle_token().unwrap());
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 5a6687397..1998ad1f6 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -3,25 +3,55 @@
3//! 3//!
4//! Note that all functions here intended to be stupid constructors, which just 4//! Note that all functions here intended to be stupid constructors, which just
5//! assemble a finish node from immediate children. If you want to do something 5//! assemble a finish node from immediate children. If you want to do something
6//! smarter than that, it probably doesn't belong in this module. 6//! smarter than that, it belongs to the `ext` submodule.
7//! 7//!
8//! Keep in mind that `from_text` functions should be kept private. The public 8//! Keep in mind that `from_text` functions should be kept private. The public
9//! API should require to assemble every node piecewise. The trick of 9//! API should require to assemble every node piecewise. The trick of
10//! `parse(format!())` we use internally is an implementation detail -- long 10//! `parse(format!())` we use internally is an implementation detail -- long
11//! term, it will be replaced with direct tree manipulation. 11//! term, it will be replaced with direct tree manipulation.
12use itertools::Itertools; 12use itertools::Itertools;
13use stdx::format_to; 13use stdx::{format_to, never};
14 14
15use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; 15use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken};
16 16
17/// While the parent module defines basic atomic "constructors", the `ext`
18/// module defines shortcuts for common things.
19///
20/// It's named `ext` rather than `shortcuts` just to keep it short.
21pub mod ext {
22 use super::*;
23
24 pub fn ident_path(ident: &str) -> ast::Path {
25 path_unqualified(path_segment(name_ref(ident)))
26 }
27
28 pub fn expr_unreachable() -> ast::Expr {
29 expr_from_text("unreachable!()")
30 }
31 pub fn expr_todo() -> ast::Expr {
32 expr_from_text("todo!()")
33 }
34 pub fn empty_block_expr() -> ast::BlockExpr {
35 block_expr(None, None)
36 }
37
38 pub fn ty_bool() -> ast::Type {
39 ty_path(ident_path("bool"))
40 }
41 pub fn ty_option(t: ast::Type) -> ast::Type {
42 ty_from_text(&format!("Option<{}>", t))
43 }
44 pub fn ty_result(t: ast::Type, e: ast::Type) -> ast::Type {
45 ty_from_text(&format!("Result<{}, {}>", t, e))
46 }
47}
48
17pub fn name(text: &str) -> ast::Name { 49pub fn name(text: &str) -> ast::Name {
18 ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text)) 50 ast_from_text(&format!("mod {}{};", raw_ident_esc(text), text))
19} 51}
20
21pub fn name_ref(text: &str) -> ast::NameRef { 52pub fn name_ref(text: &str) -> ast::NameRef {
22 ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text)) 53 ast_from_text(&format!("fn f() {{ {}{}; }}", raw_ident_esc(text), text))
23} 54}
24
25fn raw_ident_esc(ident: &str) -> &'static str { 55fn raw_ident_esc(ident: &str) -> &'static str {
26 let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some(); 56 let is_keyword = parser::SyntaxKind::from_keyword(ident).is_some();
27 if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") { 57 if is_keyword && !matches!(ident, "self" | "crate" | "super" | "Self") {
@@ -31,13 +61,23 @@ fn raw_ident_esc(ident: &str) -> &'static str {
31 } 61 }
32} 62}
33 63
64pub fn lifetime(text: &str) -> ast::Lifetime {
65 let mut text = text;
66 let tmp;
67 if never!(!text.starts_with('\'')) {
68 tmp = format!("'{}", text);
69 text = &tmp;
70 }
71 ast_from_text(&format!("fn f<{}>() {{ }}", text))
72}
73
34// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la 74// FIXME: replace stringly-typed constructor with a family of typed ctors, a-la
35// `expr_xxx`. 75// `expr_xxx`.
36pub fn ty(text: &str) -> ast::Type { 76pub fn ty(text: &str) -> ast::Type {
37 ast_from_text(&format!("fn f() -> {} {{}}", text)) 77 ty_from_text(text)
38} 78}
39pub fn ty_unit() -> ast::Type { 79pub fn ty_unit() -> ast::Type {
40 ty("()") 80 ty_from_text("()")
41} 81}
42pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { 82pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
43 let mut count: usize = 0; 83 let mut count: usize = 0;
@@ -46,15 +86,16 @@ pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
46 contents.push(','); 86 contents.push(',');
47 } 87 }
48 88
49 ty(&format!("({})", contents)) 89 ty_from_text(&format!("({})", contents))
50}
51// FIXME: handle path to type
52pub fn ty_generic(name: ast::NameRef, types: impl IntoIterator<Item = ast::Type>) -> ast::Type {
53 let contents = types.into_iter().join(", ");
54 ty(&format!("{}<{}>", name, contents))
55} 90}
56pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type { 91pub fn ty_ref(target: ast::Type, exclusive: bool) -> ast::Type {
57 ty(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) }) 92 ty_from_text(&if exclusive { format!("&mut {}", target) } else { format!("&{}", target) })
93}
94pub fn ty_path(path: ast::Path) -> ast::Type {
95 ty_from_text(&path.to_string())
96}
97fn ty_from_text(text: &str) -> ast::Type {
98 ast_from_text(&format!("type _T = {};", text))
58} 99}
59 100
60pub fn assoc_item_list() -> ast::AssocItemList { 101pub fn assoc_item_list() -> ast::AssocItemList {
@@ -88,7 +129,7 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
88pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 129pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
89 ast_from_text(&format!("{}::{}", qual, segment)) 130 ast_from_text(&format!("{}::{}", qual, segment))
90} 131}
91 132// FIXME: path concatenation operation doesn't make sense as AST op.
92pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path { 133pub fn path_concat(first: ast::Path, second: ast::Path) -> ast::Path {
93 ast_from_text(&format!("{}::{}", first, second)) 134 ast_from_text(&format!("{}::{}", first, second))
94} 135}
@@ -104,15 +145,14 @@ pub fn path_from_segments(
104 format!("use {};", segments) 145 format!("use {};", segments)
105 }) 146 })
106} 147}
107 148// FIXME: should not be pub
108pub fn path_from_text(text: &str) -> ast::Path { 149pub fn path_from_text(text: &str) -> ast::Path {
109 ast_from_text(&format!("fn main() {{ let test = {}; }}", text)) 150 ast_from_text(&format!("fn main() {{ let test = {}; }}", text))
110} 151}
111 152
112pub fn glob_use_tree() -> ast::UseTree { 153pub fn use_tree_glob() -> ast::UseTree {
113 ast_from_text("use *;") 154 ast_from_text("use *;")
114} 155}
115
116pub fn use_tree( 156pub fn use_tree(
117 path: ast::Path, 157 path: ast::Path,
118 use_tree_list: Option<ast::UseTreeList>, 158 use_tree_list: Option<ast::UseTreeList>,
@@ -207,15 +247,6 @@ pub fn expr_literal(text: &str) -> ast::Literal {
207pub fn expr_empty_block() -> ast::Expr { 247pub fn expr_empty_block() -> ast::Expr {
208 expr_from_text("{}") 248 expr_from_text("{}")
209} 249}
210pub fn expr_unimplemented() -> ast::Expr {
211 expr_from_text("unimplemented!()")
212}
213pub fn expr_unreachable() -> ast::Expr {
214 expr_from_text("unreachable!()")
215}
216pub fn expr_todo() -> ast::Expr {
217 expr_from_text("todo!()")
218}
219pub fn expr_path(path: ast::Path) -> ast::Expr { 250pub fn expr_path(path: ast::Path) -> ast::Expr {
220 expr_from_text(&path.to_string()) 251 expr_from_text(&path.to_string())
221} 252}
@@ -444,17 +475,6 @@ pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
444 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi)) 475 ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
445} 476}
446 477
447pub fn token(kind: SyntaxKind) -> SyntaxToken {
448 tokens::SOURCE_FILE
449 .tree()
450 .syntax()
451 .clone_for_update()
452 .descendants_with_tokens()
453 .filter_map(|it| it.into_token())
454 .find(|it| it.kind() == kind)
455 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
456}
457
458pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param { 478pub fn param(pat: ast::Pat, ty: ast::Type) -> ast::Param {
459 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty)) 479 ast_from_text(&format!("fn f({}: {}) {{ }}", pat, ty))
460} 480}
@@ -476,7 +496,7 @@ pub fn param_list(
476 ast_from_text(&list) 496 ast_from_text(&list)
477} 497}
478 498
479pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::GenericParam { 499pub fn type_param(name: ast::Name, ty: Option<ast::TypeBoundList>) -> ast::TypeParam {
480 let bound = match ty { 500 let bound = match ty {
481 Some(it) => format!(": {}", it), 501 Some(it) => format!(": {}", it),
482 None => String::new(), 502 None => String::new(),
@@ -484,6 +504,10 @@ pub fn generic_param(name: String, ty: Option<ast::TypeBoundList>) -> ast::Gener
484 ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound)) 504 ast_from_text(&format!("fn f<{}{}>() {{ }}", name, bound))
485} 505}
486 506
507pub fn lifetime_param(lifetime: ast::Lifetime) -> ast::LifetimeParam {
508 ast_from_text(&format!("fn f<{}>() {{ }}", lifetime))
509}
510
487pub fn generic_param_list( 511pub fn generic_param_list(
488 pats: impl IntoIterator<Item = ast::GenericParam>, 512 pats: impl IntoIterator<Item = ast::GenericParam>,
489) -> ast::GenericParamList { 513) -> ast::GenericParamList {
@@ -588,6 +612,17 @@ fn unroot(n: SyntaxNode) -> SyntaxNode {
588 SyntaxNode::new_root(n.green().into()) 612 SyntaxNode::new_root(n.green().into())
589} 613}
590 614
615pub fn token(kind: SyntaxKind) -> SyntaxToken {
616 tokens::SOURCE_FILE
617 .tree()
618 .syntax()
619 .clone_for_update()
620 .descendants_with_tokens()
621 .filter_map(|it| it.into_token())
622 .find(|it| it.kind() == kind)
623 .unwrap_or_else(|| panic!("unhandled token: {:?}", kind))
624}
625
591pub mod tokens { 626pub mod tokens {
592 use once_cell::sync::Lazy; 627 use once_cell::sync::Lazy;
593 628