aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs50
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/adt.rs21
-rw-r--r--crates/hir_def/src/attr.rs2
-rw-r--r--crates/hir_def/src/body.rs11
-rw-r--r--crates/hir_def/src/body/lower.rs157
-rw-r--r--crates/hir_def/src/child_by_source.rs50
-rw-r--r--crates/hir_def/src/data.rs4
-rw-r--r--crates/hir_def/src/generics.rs4
-rw-r--r--crates/hir_def/src/item_scope.rs31
-rw-r--r--crates/hir_def/src/lib.rs56
-rw-r--r--crates/hir_def/src/nameres/collector.rs29
-rw-r--r--crates/hir_def/src/nameres/path_resolution.rs16
-rw-r--r--crates/hir_def/src/resolver.rs29
-rw-r--r--crates/hir_expand/Cargo.toml6
-rw-r--r--crates/hir_expand/src/builtin_derive.rs66
-rw-r--r--crates/hir_expand/src/builtin_macro.rs92
-rw-r--r--crates/hir_expand/src/name.rs1
-rw-r--r--crates/hir_expand/src/quote.rs5
-rw-r--r--crates/hir_ty/Cargo.toml2
-rw-r--r--crates/hir_ty/src/diagnostics.rs29
-rw-r--r--crates/hir_ty/src/diagnostics/decl_check.rs11
-rw-r--r--crates/hir_ty/src/display.rs4
-rw-r--r--crates/hir_ty/src/infer/path.rs2
-rw-r--r--crates/hir_ty/src/lower.rs4
-rw-r--r--crates/hir_ty/src/method_resolution.rs4
-rw-r--r--crates/hir_ty/src/tests.rs20
-rw-r--r--crates/hir_ty/src/tests/traits.rs33
-rw-r--r--crates/hir_ty/src/traits/chalk.rs4
-rw-r--r--crates/hir_ty/src/utils.rs2
-rw-r--r--crates/ide/Cargo.toml2
-rw-r--r--crates/ide/src/display/short_label.rs6
-rw-r--r--crates/ide/src/hover.rs62
-rw-r--r--crates/ide/src/lib.rs10
-rw-r--r--crates/ide/src/references/rename.rs55
-rw-r--r--crates/ide/src/ssr.rs259
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs5
-rw-r--r--crates/ide_assists/Cargo.toml2
-rw-r--r--crates/ide_assists/src/handlers/add_turbo_fish.rs33
-rw-r--r--crates/ide_assists/src/handlers/auto_import.rs14
-rw-r--r--crates/ide_assists/src/handlers/qualify_path.rs18
-rw-r--r--crates/ide_completion/Cargo.toml2
-rw-r--r--crates/ide_completion/src/completions/attribute.rs35
-rw-r--r--crates/ide_completion/src/completions/fn_param.rs7
-rw-r--r--crates/ide_completion/src/completions/keyword.rs44
-rw-r--r--crates/ide_completion/src/completions/mod_.rs6
-rw-r--r--crates/ide_completion/src/completions/postfix.rs7
-rw-r--r--crates/ide_completion/src/completions/record.rs15
-rw-r--r--crates/ide_completion/src/completions/snippet.rs25
-rw-r--r--crates/ide_completion/src/completions/trait_impl.rs32
-rw-r--r--crates/ide_completion/src/item.rs211
-rw-r--r--crates/ide_completion/src/lib.rs2
-rw-r--r--crates/ide_completion/src/render.rs234
-rw-r--r--crates/ide_completion/src/render/builder_ext.rs6
-rw-r--r--crates/ide_completion/src/render/const_.rs10
-rw-r--r--crates/ide_completion/src/render/enum_variant.rs21
-rw-r--r--crates/ide_completion/src/render/function.rs13
-rw-r--r--crates/ide_completion/src/render/macro_.rs26
-rw-r--r--crates/ide_completion/src/render/pattern.rs18
-rw-r--r--crates/ide_completion/src/render/type_alias.rs10
-rw-r--r--crates/ide_db/Cargo.toml2
-rw-r--r--crates/ide_ssr/Cargo.toml2
-rw-r--r--crates/ide_ssr/src/from_comment.rs32
-rw-r--r--crates/ide_ssr/src/lib.rs11
-rw-r--r--crates/mbe/Cargo.toml2
-rw-r--r--crates/parser/src/grammar.rs2
-rw-r--r--crates/proc_macro_api/Cargo.toml3
-rw-r--r--crates/proc_macro_api/src/lib.rs22
-rw-r--r--crates/proc_macro_api/src/version.rs132
-rw-r--r--crates/proc_macro_srv/Cargo.toml2
-rw-r--r--crates/rust-analyzer/src/config.rs3
-rw-r--r--crates/rust-analyzer/src/handlers.rs33
-rw-r--r--crates/rust-analyzer/src/to_proto.rs94
-rw-r--r--crates/syntax/Cargo.toml2
74 files changed, 1433 insertions, 846 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index d5a3d9034..58adc8fd3 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -305,7 +305,6 @@ impl ModuleDef {
305 ModuleDef::Module(it) => it.name(db), 305 ModuleDef::Module(it) => it.name(db),
306 ModuleDef::Const(it) => it.name(db), 306 ModuleDef::Const(it) => it.name(db),
307 ModuleDef::Static(it) => it.name(db), 307 ModuleDef::Static(it) => it.name(db),
308
309 ModuleDef::BuiltinType(it) => Some(it.name()), 308 ModuleDef::BuiltinType(it) => Some(it.name()),
310 } 309 }
311 } 310 }
@@ -535,7 +534,7 @@ pub struct Struct {
535 534
536impl Struct { 535impl Struct {
537 pub fn module(self, db: &dyn HirDatabase) -> Module { 536 pub fn module(self, db: &dyn HirDatabase) -> Module {
538 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } 537 Module { id: self.id.lookup(db.upcast()).container }
539 } 538 }
540 539
541 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { 540 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
@@ -556,11 +555,7 @@ impl Struct {
556 } 555 }
557 556
558 pub fn ty(self, db: &dyn HirDatabase) -> Type { 557 pub fn ty(self, db: &dyn HirDatabase) -> Type {
559 Type::from_def( 558 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
560 db,
561 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
562 self.id,
563 )
564 } 559 }
565 560
566 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { 561 pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
@@ -587,15 +582,11 @@ impl Union {
587 } 582 }
588 583
589 pub fn module(self, db: &dyn HirDatabase) -> Module { 584 pub fn module(self, db: &dyn HirDatabase) -> Module {
590 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } 585 Module { id: self.id.lookup(db.upcast()).container }
591 } 586 }
592 587
593 pub fn ty(self, db: &dyn HirDatabase) -> Type { 588 pub fn ty(self, db: &dyn HirDatabase) -> Type {
594 Type::from_def( 589 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
595 db,
596 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
597 self.id,
598 )
599 } 590 }
600 591
601 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> { 592 pub fn fields(self, db: &dyn HirDatabase) -> Vec<Field> {
@@ -619,7 +610,7 @@ pub struct Enum {
619 610
620impl Enum { 611impl Enum {
621 pub fn module(self, db: &dyn HirDatabase) -> Module { 612 pub fn module(self, db: &dyn HirDatabase) -> Module {
622 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } 613 Module { id: self.id.lookup(db.upcast()).container }
623 } 614 }
624 615
625 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { 616 pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> {
@@ -635,11 +626,7 @@ impl Enum {
635 } 626 }
636 627
637 pub fn ty(self, db: &dyn HirDatabase) -> Type { 628 pub fn ty(self, db: &dyn HirDatabase) -> Type {
638 Type::from_def( 629 Type::from_def(db, self.id.lookup(db.upcast()).container.krate(), self.id)
639 db,
640 self.id.lookup(db.upcast()).container.module(db.upcast()).krate(),
641 self.id,
642 )
643 } 630 }
644} 631}
645 632
@@ -1001,7 +988,7 @@ pub struct Trait {
1001 988
1002impl Trait { 989impl Trait {
1003 pub fn module(self, db: &dyn HirDatabase) -> Module { 990 pub fn module(self, db: &dyn HirDatabase) -> Module {
1004 Module { id: self.id.lookup(db.upcast()).container.module(db.upcast()) } 991 Module { id: self.id.lookup(db.upcast()).container }
1005 } 992 }
1006 993
1007 pub fn name(self, db: &dyn HirDatabase) -> Name { 994 pub fn name(self, db: &dyn HirDatabase) -> Name {
@@ -1157,7 +1144,7 @@ where
1157{ 1144{
1158 match id.lookup(db.upcast()).container { 1145 match id.lookup(db.upcast()).container {
1159 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))), 1146 AssocContainerId::TraitId(_) | AssocContainerId::ImplId(_) => Some(ctor(DEF::from(id))),
1160 AssocContainerId::ContainerId(_) => None, 1147 AssocContainerId::ModuleId(_) => None,
1161 } 1148 }
1162} 1149}
1163 1150
@@ -1185,7 +1172,7 @@ impl AssocItem {
1185 match container { 1172 match container {
1186 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()), 1173 AssocContainerId::TraitId(id) => AssocItemContainer::Trait(id.into()),
1187 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()), 1174 AssocContainerId::ImplId(id) => AssocItemContainer::Impl(id.into()),
1188 AssocContainerId::ContainerId(_) => panic!("invalid AssocItem"), 1175 AssocContainerId::ModuleId(_) => panic!("invalid AssocItem"),
1189 } 1176 }
1190 } 1177 }
1191 1178
@@ -1296,13 +1283,7 @@ impl Local {
1296 1283
1297 pub fn is_mut(self, db: &dyn HirDatabase) -> bool { 1284 pub fn is_mut(self, db: &dyn HirDatabase) -> bool {
1298 let body = db.body(self.parent.into()); 1285 let body = db.body(self.parent.into());
1299 match &body[self.pat_id] { 1286 matches!(&body[self.pat_id], Pat::Bind { mode: BindingAnnotation::Mutable, .. })
1300 Pat::Bind { mode, .. } => match mode {
1301 BindingAnnotation::Mutable | BindingAnnotation::RefMut => true,
1302 _ => false,
1303 },
1304 _ => false,
1305 }
1306 } 1287 }
1307 1288
1308 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { 1289 pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
@@ -1516,7 +1497,7 @@ impl Impl {
1516 pub fn target_ty(self, db: &dyn HirDatabase) -> Type { 1497 pub fn target_ty(self, db: &dyn HirDatabase) -> Type {
1517 let impl_data = db.impl_data(self.id); 1498 let impl_data = db.impl_data(self.id);
1518 let resolver = self.id.resolver(db.upcast()); 1499 let resolver = self.id.resolver(db.upcast());
1519 let krate = self.id.lookup(db.upcast()).container.module(db.upcast()).krate(); 1500 let krate = self.id.lookup(db.upcast()).container.krate();
1520 let ctx = hir_ty::TyLoweringContext::new(db, &resolver); 1501 let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
1521 let ty = Ty::from_hir(&ctx, &impl_data.target_type); 1502 let ty = Ty::from_hir(&ctx, &impl_data.target_type);
1522 Type::new_with_resolver_inner(db, krate, &resolver, ty) 1503 Type::new_with_resolver_inner(db, krate, &resolver, ty)
@@ -1531,7 +1512,7 @@ impl Impl {
1531 } 1512 }
1532 1513
1533 pub fn module(self, db: &dyn HirDatabase) -> Module { 1514 pub fn module(self, db: &dyn HirDatabase) -> Module {
1534 self.id.lookup(db.upcast()).container.module(db.upcast()).into() 1515 self.id.lookup(db.upcast()).container.into()
1535 } 1516 }
1536 1517
1537 pub fn krate(self, db: &dyn HirDatabase) -> Crate { 1518 pub fn krate(self, db: &dyn HirDatabase) -> Crate {
@@ -1614,10 +1595,9 @@ impl Type {
1614 } 1595 }
1615 1596
1616 pub fn remove_ref(&self) -> Option<Type> { 1597 pub fn remove_ref(&self) -> Option<Type> {
1617 if let Ty::Ref(.., substs) = &self.ty.value { 1598 match &self.ty.value {
1618 Some(self.derived(substs[0].clone())) 1599 Ty::Ref(.., substs) => Some(self.derived(substs[0].clone())),
1619 } else { 1600 _ => None,
1620 None
1621 } 1601 }
1622 } 1602 }
1623 1603
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index 2f07b6d01..c2d99280f 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -28,10 +28,10 @@ base_db = { path = "../base_db", version = "0.0.0" }
28syntax = { path = "../syntax", version = "0.0.0" } 28syntax = { path = "../syntax", version = "0.0.0" }
29profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
30hir_expand = { path = "../hir_expand", version = "0.0.0" } 30hir_expand = { path = "../hir_expand", version = "0.0.0" }
31test_utils = { path = "../test_utils", version = "0.0.0" }
32mbe = { path = "../mbe", version = "0.0.0" } 31mbe = { path = "../mbe", version = "0.0.0" }
33cfg = { path = "../cfg", version = "0.0.0" } 32cfg = { path = "../cfg", version = "0.0.0" }
34tt = { path = "../tt", version = "0.0.0" } 33tt = { path = "../tt", version = "0.0.0" }
35 34
36[dev-dependencies] 35[dev-dependencies]
36test_utils = { path = "../test_utils" }
37expect-test = "1.1" 37expect-test = "1.1"
diff --git a/crates/hir_def/src/adt.rs b/crates/hir_def/src/adt.rs
index ed36c3109..efbde17d8 100644
--- a/crates/hir_def/src/adt.rs
+++ b/crates/hir_def/src/adt.rs
@@ -21,8 +21,7 @@ use crate::{
21 trace::Trace, 21 trace::Trace,
22 type_ref::TypeRef, 22 type_ref::TypeRef,
23 visibility::RawVisibility, 23 visibility::RawVisibility,
24 EnumId, HasModule, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, 24 EnumId, LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StructId, UnionId, VariantId,
25 VariantId,
26}; 25};
27use cfg::CfgOptions; 26use cfg::CfgOptions;
28 27
@@ -92,10 +91,10 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> {
92impl StructData { 91impl StructData {
93 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { 92 pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> {
94 let loc = id.lookup(db); 93 let loc = id.lookup(db);
95 let krate = loc.container.module(db).krate; 94 let krate = loc.container.krate;
96 let item_tree = db.item_tree(loc.id.file_id); 95 let item_tree = db.item_tree(loc.id.file_id);
97 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 96 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
98 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 97 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
99 98
100 let strukt = &item_tree[loc.id.value]; 99 let strukt = &item_tree[loc.id.value];
101 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None); 100 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &strukt.fields, None);
@@ -107,10 +106,10 @@ impl StructData {
107 } 106 }
108 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { 107 pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> {
109 let loc = id.lookup(db); 108 let loc = id.lookup(db);
110 let krate = loc.container.module(db).krate; 109 let krate = loc.container.krate;
111 let item_tree = db.item_tree(loc.id.file_id); 110 let item_tree = db.item_tree(loc.id.file_id);
112 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into()); 111 let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
113 let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); 112 let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
114 113
115 let union = &item_tree[loc.id.value]; 114 let union = &item_tree[loc.id.value];
116 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None); 115 let variant_data = lower_fields(db, krate, &item_tree, &cfg_options, &union.fields, None);
@@ -126,7 +125,7 @@ impl StructData {
126impl EnumData { 125impl EnumData {
127 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> { 126 pub(crate) fn enum_data_query(db: &dyn DefDatabase, e: EnumId) -> Arc<EnumData> {
128 let loc = e.lookup(db); 127 let loc = e.lookup(db);
129 let krate = loc.container.module(db).krate; 128 let krate = loc.container.krate;
130 let item_tree = db.item_tree(loc.id.file_id); 129 let item_tree = db.item_tree(loc.id.file_id);
131 let cfg_options = db.crate_graph()[krate].cfg_options.clone(); 130 let cfg_options = db.crate_graph()[krate].cfg_options.clone();
132 131
@@ -168,7 +167,7 @@ impl HasChildSource<LocalEnumVariantId> for EnumId {
168 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> { 167 ) -> InFile<ArenaMap<LocalEnumVariantId, Self::Value>> {
169 let src = self.lookup(db).source(db); 168 let src = self.lookup(db).source(db);
170 let mut trace = Trace::new_for_map(); 169 let mut trace = Trace::new_for_map();
171 lower_enum(db, &mut trace, &src, self.lookup(db).container.module(db)); 170 lower_enum(db, &mut trace, &src, self.lookup(db).container);
172 src.with_value(trace.into_map()) 171 src.with_value(trace.into_map())
173 } 172 }
174} 173}
@@ -238,10 +237,10 @@ impl HasChildSource<LocalFieldId> for VariantId {
238 // I don't really like the fact that we call into parent source 237 // I don't really like the fact that we call into parent source
239 // here, this might add to more queries then necessary. 238 // here, this might add to more queries then necessary.
240 let src = it.parent.child_source(db); 239 let src = it.parent.child_source(db);
241 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container.module(db)) 240 (src.map(|map| map[it.local_id].kind()), it.parent.lookup(db).container)
242 } 241 }
243 VariantId::StructId(it) => { 242 VariantId::StructId(it) => {
244 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container.module(db)) 243 (it.lookup(db).source(db).map(|it| it.kind()), it.lookup(db).container)
245 } 244 }
246 VariantId::UnionId(it) => ( 245 VariantId::UnionId(it) => (
247 it.lookup(db).source(db).map(|it| { 246 it.lookup(db).source(db).map(|it| {
@@ -249,7 +248,7 @@ impl HasChildSource<LocalFieldId> for VariantId {
249 .map(ast::StructKind::Record) 248 .map(ast::StructKind::Record)
250 .unwrap_or(ast::StructKind::Unit) 249 .unwrap_or(ast::StructKind::Unit)
251 }), 250 }),
252 it.lookup(db).container.module(db), 251 it.lookup(db).container,
253 ), 252 ),
254 }; 253 };
255 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate); 254 let mut expander = CfgExpander::new(db, src.file_id, module_id.krate);
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index b716d5f6e..97cdbbb9e 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -267,7 +267,7 @@ impl Attrs {
267 db: &dyn DefDatabase, 267 db: &dyn DefDatabase,
268 e: EnumId, 268 e: EnumId,
269 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> { 269 ) -> Arc<ArenaMap<LocalEnumVariantId, Attrs>> {
270 let krate = e.lookup(db).container.module(db).krate; 270 let krate = e.lookup(db).container.krate;
271 let src = e.child_source(db); 271 let src = e.child_source(db);
272 let mut res = ArenaMap::default(); 272 let mut res = ArenaMap::default();
273 273
diff --git a/crates/hir_def/src/body.rs b/crates/hir_def/src/body.rs
index b1a3fe1cb..19c4eb521 100644
--- a/crates/hir_def/src/body.rs
+++ b/crates/hir_def/src/body.rs
@@ -28,11 +28,10 @@ use crate::{
28 db::DefDatabase, 28 db::DefDatabase,
29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, 29 expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
30 item_scope::BuiltinShadowMode, 30 item_scope::BuiltinShadowMode,
31 item_scope::ItemScope,
32 nameres::DefMap, 31 nameres::DefMap,
33 path::{ModPath, Path}, 32 path::{ModPath, Path},
34 src::HasSource, 33 src::HasSource,
35 AsMacroCall, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, 34 AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId,
36}; 35};
37 36
38/// A subset of Expander that only deals with cfg attributes. We only need it to 37/// A subset of Expander that only deals with cfg attributes. We only need it to
@@ -226,7 +225,8 @@ pub struct Body {
226 pub params: Vec<PatId>, 225 pub params: Vec<PatId>,
227 /// The `ExprId` of the actual body expression. 226 /// The `ExprId` of the actual body expression.
228 pub body_expr: ExprId, 227 pub body_expr: ExprId,
229 pub item_scope: ItemScope, 228 /// Block expressions in this body that may contain inner items.
229 pub block_scopes: Vec<BlockId>,
230 _c: Count<Self>, 230 _c: Count<Self>,
231} 231}
232 232
@@ -295,7 +295,7 @@ impl Body {
295 } 295 }
296 }; 296 };
297 let expander = Expander::new(db, file_id, module); 297 let expander = Expander::new(db, file_id, module);
298 let (body, source_map) = Body::new(db, def, expander, params, body); 298 let (body, source_map) = Body::new(db, expander, params, body);
299 (Arc::new(body), Arc::new(source_map)) 299 (Arc::new(body), Arc::new(source_map))
300 } 300 }
301 301
@@ -305,12 +305,11 @@ impl Body {
305 305
306 fn new( 306 fn new(
307 db: &dyn DefDatabase, 307 db: &dyn DefDatabase,
308 def: DefWithBodyId,
309 expander: Expander, 308 expander: Expander,
310 params: Option<ast::ParamList>, 309 params: Option<ast::ParamList>,
311 body: Option<ast::Expr>, 310 body: Option<ast::Expr>,
312 ) -> (Body, BodySourceMap) { 311 ) -> (Body, BodySourceMap) {
313 lower::lower(db, def, expander, params, body) 312 lower::lower(db, expander, params, body)
314 } 313 }
315} 314}
316 315
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index d4abe819d..8c8eb8007 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -1,17 +1,16 @@
1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` 1//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
2//! representation. 2//! representation.
3 3
4use std::{any::type_name, mem, sync::Arc}; 4use std::mem;
5 5
6use either::Either; 6use either::Either;
7use hir_expand::{ 7use hir_expand::{
8 hygiene::Hygiene, 8 hygiene::Hygiene,
9 name::{name, AsName, Name}, 9 name::{name, AsName, Name},
10 ExpandError, HirFileId, MacroDefId, MacroDefKind, 10 ExpandError, HirFileId,
11}; 11};
12use la_arena::Arena; 12use la_arena::Arena;
13use profile::Count; 13use profile::Count;
14use rustc_hash::FxHashMap;
15use syntax::{ 14use syntax::{
16 ast::{ 15 ast::{
17 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner, 16 self, ArgListOwner, ArrayExprKind, AstChildren, LiteralKind, LoopBodyOwner, NameOwner,
@@ -32,11 +31,9 @@ use crate::{
32 Statement, 31 Statement,
33 }, 32 },
34 item_scope::BuiltinShadowMode, 33 item_scope::BuiltinShadowMode,
35 item_tree::{ItemTree, ItemTreeId, ItemTreeNode},
36 path::{GenericArgs, Path}, 34 path::{GenericArgs, Path},
37 type_ref::{Mutability, Rawness, TypeRef}, 35 type_ref::{Mutability, Rawness, TypeRef},
38 AdtId, BlockLoc, ConstLoc, ContainerId, DefWithBodyId, EnumLoc, FunctionLoc, Intern, 36 AdtId, BlockLoc, ModuleDefId,
39 ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
40}; 37};
41 38
42use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; 39use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource};
@@ -60,15 +57,12 @@ impl LowerCtx {
60 57
61pub(super) fn lower( 58pub(super) fn lower(
62 db: &dyn DefDatabase, 59 db: &dyn DefDatabase,
63 def: DefWithBodyId,
64 expander: Expander, 60 expander: Expander,
65 params: Option<ast::ParamList>, 61 params: Option<ast::ParamList>,
66 body: Option<ast::Expr>, 62 body: Option<ast::Expr>,
67) -> (Body, BodySourceMap) { 63) -> (Body, BodySourceMap) {
68 let item_tree = db.item_tree(expander.current_file_id);
69 ExprCollector { 64 ExprCollector {
70 db, 65 db,
71 def,
72 source_map: BodySourceMap::default(), 66 source_map: BodySourceMap::default(),
73 body: Body { 67 body: Body {
74 exprs: Arena::default(), 68 exprs: Arena::default(),
@@ -76,14 +70,9 @@ pub(super) fn lower(
76 labels: Arena::default(), 70 labels: Arena::default(),
77 params: Vec::new(), 71 params: Vec::new(),
78 body_expr: dummy_expr_id(), 72 body_expr: dummy_expr_id(),
79 item_scope: Default::default(), 73 block_scopes: Vec::new(),
80 _c: Count::new(), 74 _c: Count::new(),
81 }, 75 },
82 item_trees: {
83 let mut map = FxHashMap::default();
84 map.insert(expander.current_file_id, item_tree);
85 map
86 },
87 expander, 76 expander,
88 } 77 }
89 .collect(params, body) 78 .collect(params, body)
@@ -91,12 +80,9 @@ pub(super) fn lower(
91 80
92struct ExprCollector<'a> { 81struct ExprCollector<'a> {
93 db: &'a dyn DefDatabase, 82 db: &'a dyn DefDatabase,
94 def: DefWithBodyId,
95 expander: Expander, 83 expander: Expander,
96 body: Body, 84 body: Body,
97 source_map: BodySourceMap, 85 source_map: BodySourceMap,
98
99 item_trees: FxHashMap<HirFileId, Arc<ItemTree>>,
100} 86}
101 87
102impl ExprCollector<'_> { 88impl ExprCollector<'_> {
@@ -593,9 +579,6 @@ impl ExprCollector<'_> {
593 } else { 579 } else {
594 self.source_map.expansions.insert(macro_call, self.expander.current_file_id); 580 self.source_map.expansions.insert(macro_call, self.expander.current_file_id);
595 581
596 let item_tree = self.db.item_tree(self.expander.current_file_id);
597 self.item_trees.insert(self.expander.current_file_id, item_tree);
598
599 let id = collector(self, Some(expansion)); 582 let id = collector(self, Some(expansion));
600 self.expander.exit(self.db, mark); 583 self.expander.exit(self.db, mark);
601 id 584 id
@@ -605,32 +588,6 @@ impl ExprCollector<'_> {
605 } 588 }
606 } 589 }
607 590
608 fn find_inner_item<N: ItemTreeNode>(&self, ast: &N::Source) -> Option<ItemTreeId<N>> {
609 let id = self.expander.ast_id(ast);
610 let tree = &self.item_trees[&id.file_id];
611
612 // FIXME: This probably breaks with `use` items, since they produce multiple item tree nodes
613
614 // Root file (non-macro).
615 let item_tree_id = tree
616 .all_inner_items()
617 .chain(tree.top_level_items().iter().copied())
618 .filter_map(|mod_item| mod_item.downcast::<N>())
619 .find(|tree_id| tree[*tree_id].ast_id().upcast() == id.value.upcast())
620 .or_else(|| {
621 log::debug!(
622 "couldn't find inner {} item for {:?} (AST: `{}` - {:?})",
623 type_name::<N>(),
624 id,
625 ast.syntax(),
626 ast.syntax(),
627 );
628 None
629 })?;
630
631 Some(ItemTreeId::new(id.file_id, item_tree_id))
632 }
633
634 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId { 591 fn collect_expr_opt(&mut self, expr: Option<ast::Expr>) -> ExprId {
635 if let Some(expr) = expr { 592 if let Some(expr) = expr {
636 self.collect_expr(expr) 593 self.collect_expr(expr)
@@ -662,7 +619,6 @@ impl ExprCollector<'_> {
662 match expansion { 619 match expansion {
663 Some(expansion) => { 620 Some(expansion) => {
664 let statements: ast::MacroStmts = expansion; 621 let statements: ast::MacroStmts = expansion;
665 this.collect_stmts_items(statements.statements());
666 622
667 statements.statements().for_each(|stmt| { 623 statements.statements().for_each(|stmt| {
668 if let Some(mut r) = this.collect_stmt(stmt) { 624 if let Some(mut r) = this.collect_stmt(stmt) {
@@ -700,6 +656,8 @@ impl ExprCollector<'_> {
700 let block_loc = 656 let block_loc =
701 BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) }; 657 BlockLoc { ast_id, module: self.expander.def_map.module_id(self.expander.module) };
702 let block_id = self.db.intern_block(block_loc); 658 let block_id = self.db.intern_block(block_loc);
659 self.body.block_scopes.push(block_id);
660
703 let opt_def_map = self.db.block_def_map(block_id); 661 let opt_def_map = self.db.block_def_map(block_id);
704 let has_def_map = opt_def_map.is_some(); 662 let has_def_map = opt_def_map.is_some();
705 let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone()); 663 let def_map = opt_def_map.unwrap_or_else(|| self.expander.def_map.clone());
@@ -707,7 +665,6 @@ impl ExprCollector<'_> {
707 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); 665 let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
708 let prev_local_module = mem::replace(&mut self.expander.module, module); 666 let prev_local_module = mem::replace(&mut self.expander.module, module);
709 667
710 self.collect_stmts_items(block.statements());
711 let statements = 668 let statements =
712 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect(); 669 block.statements().filter_map(|s| self.collect_stmt(s)).flatten().collect();
713 let tail = block.tail_expr().map(|e| self.collect_expr(e)); 670 let tail = block.tail_expr().map(|e| self.collect_expr(e));
@@ -722,108 +679,6 @@ impl ExprCollector<'_> {
722 expr_id 679 expr_id
723 } 680 }
724 681
725 fn collect_stmts_items(&mut self, stmts: ast::AstChildren<ast::Stmt>) {
726 let container = ContainerId::DefWithBodyId(self.def);
727
728 let items = stmts
729 .filter_map(|stmt| match stmt {
730 ast::Stmt::Item(it) => Some(it),
731 ast::Stmt::LetStmt(_) | ast::Stmt::ExprStmt(_) => None,
732 })
733 .filter_map(|item| {
734 let (def, name): (ModuleDefId, Option<ast::Name>) = match item {
735 ast::Item::Fn(def) => {
736 let id = self.find_inner_item(&def)?;
737 (
738 FunctionLoc { container: container.into(), id }.intern(self.db).into(),
739 def.name(),
740 )
741 }
742 ast::Item::TypeAlias(def) => {
743 let id = self.find_inner_item(&def)?;
744 (
745 TypeAliasLoc { container: container.into(), id }.intern(self.db).into(),
746 def.name(),
747 )
748 }
749 ast::Item::Const(def) => {
750 let id = self.find_inner_item(&def)?;
751 (
752 ConstLoc { container: container.into(), id }.intern(self.db).into(),
753 def.name(),
754 )
755 }
756 ast::Item::Static(def) => {
757 let id = self.find_inner_item(&def)?;
758 (StaticLoc { container, id }.intern(self.db).into(), def.name())
759 }
760 ast::Item::Struct(def) => {
761 let id = self.find_inner_item(&def)?;
762 (StructLoc { container, id }.intern(self.db).into(), def.name())
763 }
764 ast::Item::Enum(def) => {
765 let id = self.find_inner_item(&def)?;
766 (EnumLoc { container, id }.intern(self.db).into(), def.name())
767 }
768 ast::Item::Union(def) => {
769 let id = self.find_inner_item(&def)?;
770 (UnionLoc { container, id }.intern(self.db).into(), def.name())
771 }
772 ast::Item::Trait(def) => {
773 let id = self.find_inner_item(&def)?;
774 (TraitLoc { container, id }.intern(self.db).into(), def.name())
775 }
776 ast::Item::ExternBlock(_) => return None, // FIXME: collect from extern blocks
777 ast::Item::Impl(_)
778 | ast::Item::Use(_)
779 | ast::Item::ExternCrate(_)
780 | ast::Item::Module(_)
781 | ast::Item::MacroCall(_) => return None,
782 ast::Item::MacroRules(def) => {
783 return Some(Either::Right(ast::Macro::from(def)));
784 }
785 ast::Item::MacroDef(def) => {
786 return Some(Either::Right(ast::Macro::from(def)));
787 }
788 };
789
790 Some(Either::Left((def, name)))
791 })
792 .collect::<Vec<_>>();
793
794 for either in items {
795 match either {
796 Either::Left((def, name)) => {
797 self.body.item_scope.define_def(def);
798 if let Some(name) = name {
799 let vis = crate::visibility::Visibility::Public; // FIXME determine correctly
800 let has_constructor = match def {
801 ModuleDefId::AdtId(AdtId::StructId(s)) => {
802 self.db.struct_data(s).variant_data.kind() != StructKind::Record
803 }
804 _ => true,
805 };
806 self.body.item_scope.push_res(
807 name.as_name(),
808 crate::per_ns::PerNs::from_def(def, vis, has_constructor),
809 );
810 }
811 }
812 Either::Right(e) => {
813 let mac = MacroDefId {
814 krate: self.expander.def_map.krate(),
815 ast_id: Some(self.expander.ast_id(&e)),
816 kind: MacroDefKind::Declarative,
817 local_inner: false,
818 };
819 if let Some(name) = e.name() {
820 self.body.item_scope.define_legacy_macro(name.as_name(), mac);
821 }
822 }
823 }
824 }
825 }
826
827 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId { 682 fn collect_block_opt(&mut self, expr: Option<ast::BlockExpr>) -> ExprId {
828 if let Some(block) = expr { 683 if let Some(block) = expr {
829 self.collect_block(block) 684 self.collect_block(block)
diff --git a/crates/hir_def/src/child_by_source.rs b/crates/hir_def/src/child_by_source.rs
index 75c2d756b..2a331dcaf 100644
--- a/crates/hir_def/src/child_by_source.rs
+++ b/crates/hir_def/src/child_by_source.rs
@@ -17,13 +17,16 @@ use crate::{
17}; 17};
18 18
19pub trait ChildBySource { 19pub trait ChildBySource {
20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap;
21}
22
23impl ChildBySource for TraitId {
24 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 20 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
25 let mut res = DynMap::default(); 21 let mut res = DynMap::default();
22 self.child_by_source_to(db, &mut res);
23 res
24 }
25 fn child_by_source_to(&self, db: &dyn DefDatabase, map: &mut DynMap);
26}
26 27
28impl ChildBySource for TraitId {
29 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
27 let data = db.trait_data(*self); 30 let data = db.trait_data(*self);
28 for (_name, item) in data.items.iter() { 31 for (_name, item) in data.items.iter() {
29 match *item { 32 match *item {
@@ -41,15 +44,11 @@ impl ChildBySource for TraitId {
41 } 44 }
42 } 45 }
43 } 46 }
44
45 res
46 } 47 }
47} 48}
48 49
49impl ChildBySource for ImplId { 50impl ChildBySource for ImplId {
50 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 51 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
51 let mut res = DynMap::default();
52
53 let data = db.impl_data(*self); 52 let data = db.impl_data(*self);
54 for &item in data.items.iter() { 53 for &item in data.items.iter() {
55 match item { 54 match item {
@@ -67,25 +66,21 @@ impl ChildBySource for ImplId {
67 } 66 }
68 } 67 }
69 } 68 }
70
71 res
72 } 69 }
73} 70}
74 71
75impl ChildBySource for ModuleId { 72impl ChildBySource for ModuleId {
76 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 73 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
77 let def_map = self.def_map(db); 74 let def_map = self.def_map(db);
78 let module_data = &def_map[self.local_id]; 75 let module_data = &def_map[self.local_id];
79 module_data.scope.child_by_source(db) 76 module_data.scope.child_by_source_to(db, res);
80 } 77 }
81} 78}
82 79
83impl ChildBySource for ItemScope { 80impl ChildBySource for ItemScope {
84 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 81 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
85 let mut res = DynMap::default(); 82 self.declarations().for_each(|item| add_module_def(db, res, item));
86 self.declarations().for_each(|item| add_module_def(db, &mut res, item)); 83 self.impls().for_each(|imp| add_impl(db, res, imp));
87 self.impls().for_each(|imp| add_impl(db, &mut res, imp));
88 return res;
89 84
90 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) { 85 fn add_module_def(db: &dyn DefDatabase, map: &mut DynMap, item: ModuleDefId) {
91 match item { 86 match item {
@@ -134,9 +129,7 @@ impl ChildBySource for ItemScope {
134} 129}
135 130
136impl ChildBySource for VariantId { 131impl ChildBySource for VariantId {
137 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 132 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
138 let mut res = DynMap::default();
139
140 let arena_map = self.child_source(db); 133 let arena_map = self.child_source(db);
141 let arena_map = arena_map.as_ref(); 134 let arena_map = arena_map.as_ref();
142 for (local_id, source) in arena_map.value.iter() { 135 for (local_id, source) in arena_map.value.iter() {
@@ -150,28 +143,27 @@ impl ChildBySource for VariantId {
150 } 143 }
151 } 144 }
152 } 145 }
153 res
154 } 146 }
155} 147}
156 148
157impl ChildBySource for EnumId { 149impl ChildBySource for EnumId {
158 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 150 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
159 let mut res = DynMap::default();
160
161 let arena_map = self.child_source(db); 151 let arena_map = self.child_source(db);
162 let arena_map = arena_map.as_ref(); 152 let arena_map = arena_map.as_ref();
163 for (local_id, source) in arena_map.value.iter() { 153 for (local_id, source) in arena_map.value.iter() {
164 let id = EnumVariantId { parent: *self, local_id }; 154 let id = EnumVariantId { parent: *self, local_id };
165 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id) 155 res[keys::VARIANT].insert(arena_map.with_value(source.clone()), id)
166 } 156 }
167
168 res
169 } 157 }
170} 158}
171 159
172impl ChildBySource for DefWithBodyId { 160impl ChildBySource for DefWithBodyId {
173 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 161 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
174 let body = db.body(*self); 162 let body = db.body(*self);
175 body.item_scope.child_by_source(db) 163 for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
164 // All block expressions are merged into the same map, because they logically all add
165 // inner items to the containing `DefWithBodyId`.
166 def_map[def_map.root()].scope.child_by_source_to(db, res);
167 }
176 } 168 }
177} 169}
diff --git a/crates/hir_def/src/data.rs b/crates/hir_def/src/data.rs
index d3380e0f4..aea53d527 100644
--- a/crates/hir_def/src/data.rs
+++ b/crates/hir_def/src/data.rs
@@ -97,7 +97,7 @@ impl TraitData {
97 let tr_def = &item_tree[tr_loc.id.value]; 97 let tr_def = &item_tree[tr_loc.id.value];
98 let name = tr_def.name.clone(); 98 let name = tr_def.name.clone();
99 let auto = tr_def.auto; 99 let auto = tr_def.auto;
100 let module_id = tr_loc.container.module(db); 100 let module_id = tr_loc.container;
101 let container = AssocContainerId::TraitId(tr); 101 let container = AssocContainerId::TraitId(tr);
102 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id); 102 let mut expander = Expander::new(db, tr_loc.id.file_id, module_id);
103 103
@@ -147,7 +147,7 @@ impl ImplData {
147 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone()); 147 let target_trait = impl_def.target_trait.map(|id| item_tree[id].clone());
148 let target_type = item_tree[impl_def.target_type].clone(); 148 let target_type = item_tree[impl_def.target_type].clone();
149 let is_negative = impl_def.is_negative; 149 let is_negative = impl_def.is_negative;
150 let module_id = impl_loc.container.module(db); 150 let module_id = impl_loc.container;
151 let container = AssocContainerId::ImplId(id); 151 let container = AssocContainerId::ImplId(id);
152 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id); 152 let mut expander = Expander::new(db, impl_loc.id.file_id, module_id);
153 153
diff --git a/crates/hir_def/src/generics.rs b/crates/hir_def/src/generics.rs
index 3ace3be1f..a056ab797 100644
--- a/crates/hir_def/src/generics.rs
+++ b/crates/hir_def/src/generics.rs
@@ -421,8 +421,7 @@ impl HasChildSource<LocalConstParamId> for GenericDefId {
421} 421}
422 422
423impl ChildBySource for GenericDefId { 423impl ChildBySource for GenericDefId {
424 fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { 424 fn child_by_source_to(&self, db: &dyn DefDatabase, res: &mut DynMap) {
425 let mut res = DynMap::default();
426 let (_, sm) = GenericParams::new(db, *self); 425 let (_, sm) = GenericParams::new(db, *self);
427 426
428 let sm = sm.as_ref(); 427 let sm = sm.as_ref();
@@ -440,6 +439,5 @@ impl ChildBySource for GenericDefId {
440 let id = ConstParamId { parent: *self, local_id }; 439 let id = ConstParamId { parent: *self, local_id };
441 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id); 440 res[keys::CONST_PARAM].insert(sm.with_value(src.clone()), id);
442 } 441 }
443 res
444 } 442 }
445} 443}
diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs
index 919933813..aafd73b60 100644
--- a/crates/hir_def/src/item_scope.rs
+++ b/crates/hir_def/src/item_scope.rs
@@ -168,37 +168,6 @@ impl ItemScope {
168 self.unnamed_trait_imports.insert(tr, vis); 168 self.unnamed_trait_imports.insert(tr, vis);
169 } 169 }
170 170
171 pub(crate) fn push_res(&mut self, name: Name, def: PerNs) -> bool {
172 let mut changed = false;
173
174 if let Some(types) = def.types {
175 self.types.entry(name.clone()).or_insert_with(|| {
176 changed = true;
177 types
178 });
179 }
180 if let Some(values) = def.values {
181 self.values.entry(name.clone()).or_insert_with(|| {
182 changed = true;
183 values
184 });
185 }
186 if let Some(macros) = def.macros {
187 self.macros.entry(name.clone()).or_insert_with(|| {
188 changed = true;
189 macros
190 });
191 }
192
193 if def.is_none() {
194 if self.unresolved.insert(name) {
195 changed = true;
196 }
197 }
198
199 changed
200 }
201
202 pub(crate) fn push_res_with_import( 171 pub(crate) fn push_res_with_import(
203 &mut self, 172 &mut self,
204 glob_imports: &mut PerNsGlobImports, 173 glob_imports: &mut PerNsGlobImports,
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs
index 4498d94bb..6d11c5be4 100644
--- a/crates/hir_def/src/lib.rs
+++ b/crates/hir_def/src/lib.rs
@@ -108,7 +108,7 @@ pub type LocalModuleId = Idx<nameres::ModuleData>;
108 108
109#[derive(Debug)] 109#[derive(Debug)]
110pub struct ItemLoc<N: ItemTreeNode> { 110pub struct ItemLoc<N: ItemTreeNode> {
111 pub container: ContainerId, 111 pub container: ModuleId,
112 pub id: ItemTreeId<N>, 112 pub id: ItemTreeId<N>,
113} 113}
114 114
@@ -279,18 +279,12 @@ pub struct ConstParamId {
279pub type LocalConstParamId = Idx<generics::ConstParamData>; 279pub type LocalConstParamId = Idx<generics::ConstParamData>;
280 280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 281#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
282pub enum ContainerId {
283 ModuleId(ModuleId),
284 DefWithBodyId(DefWithBodyId),
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
288pub enum AssocContainerId { 282pub enum AssocContainerId {
289 ContainerId(ContainerId), 283 ModuleId(ModuleId),
290 ImplId(ImplId), 284 ImplId(ImplId),
291 TraitId(TraitId), 285 TraitId(TraitId),
292} 286}
293impl_from!(ContainerId for AssocContainerId); 287impl_from!(ModuleId for AssocContainerId);
294 288
295/// A Data Type 289/// A Data Type
296#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] 290#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
@@ -447,21 +441,12 @@ pub trait HasModule {
447 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId; 441 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId;
448} 442}
449 443
450impl HasModule for ContainerId {
451 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
452 match *self {
453 ContainerId::ModuleId(it) => it,
454 ContainerId::DefWithBodyId(it) => it.module(db),
455 }
456 }
457}
458
459impl HasModule for AssocContainerId { 444impl HasModule for AssocContainerId {
460 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 445 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
461 match *self { 446 match *self {
462 AssocContainerId::ContainerId(it) => it.module(db), 447 AssocContainerId::ModuleId(it) => it,
463 AssocContainerId::ImplId(it) => it.lookup(db).container.module(db), 448 AssocContainerId::ImplId(it) => it.lookup(db).container,
464 AssocContainerId::TraitId(it) => it.lookup(db).container.module(db), 449 AssocContainerId::TraitId(it) => it.lookup(db).container,
465 } 450 }
466 } 451 }
467} 452}
@@ -479,16 +464,15 @@ impl HasModule for AdtId {
479 AdtId::UnionId(it) => it.lookup(db).container, 464 AdtId::UnionId(it) => it.lookup(db).container,
480 AdtId::EnumId(it) => it.lookup(db).container, 465 AdtId::EnumId(it) => it.lookup(db).container,
481 } 466 }
482 .module(db)
483 } 467 }
484} 468}
485 469
486impl HasModule for VariantId { 470impl HasModule for VariantId {
487 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 471 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
488 match self { 472 match self {
489 VariantId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), 473 VariantId::EnumVariantId(it) => it.parent.lookup(db).container,
490 VariantId::StructId(it) => it.lookup(db).container.module(db), 474 VariantId::StructId(it) => it.lookup(db).container,
491 VariantId::UnionId(it) => it.lookup(db).container.module(db), 475 VariantId::UnionId(it) => it.lookup(db).container,
492 } 476 }
493 } 477 }
494} 478}
@@ -518,18 +502,18 @@ impl HasModule for GenericDefId {
518 match self { 502 match self {
519 GenericDefId::FunctionId(it) => it.lookup(db).module(db), 503 GenericDefId::FunctionId(it) => it.lookup(db).module(db),
520 GenericDefId::AdtId(it) => it.module(db), 504 GenericDefId::AdtId(it) => it.module(db),
521 GenericDefId::TraitId(it) => it.lookup(db).container.module(db), 505 GenericDefId::TraitId(it) => it.lookup(db).container,
522 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db), 506 GenericDefId::TypeAliasId(it) => it.lookup(db).module(db),
523 GenericDefId::ImplId(it) => it.lookup(db).container.module(db), 507 GenericDefId::ImplId(it) => it.lookup(db).container,
524 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db), 508 GenericDefId::EnumVariantId(it) => it.parent.lookup(db).container,
525 GenericDefId::ConstId(it) => it.lookup(db).module(db), 509 GenericDefId::ConstId(it) => it.lookup(db).module(db),
526 } 510 }
527 } 511 }
528} 512}
529 513
530impl HasModule for StaticLoc { 514impl HasModule for StaticLoc {
531 fn module(&self, db: &dyn db::DefDatabase) -> ModuleId { 515 fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
532 self.container.module(db) 516 self.container
533 } 517 }
534} 518}
535 519
@@ -542,10 +526,10 @@ impl ModuleDefId {
542 ModuleDefId::ModuleId(id) => *id, 526 ModuleDefId::ModuleId(id) => *id,
543 ModuleDefId::FunctionId(id) => id.lookup(db).module(db), 527 ModuleDefId::FunctionId(id) => id.lookup(db).module(db),
544 ModuleDefId::AdtId(id) => id.module(db), 528 ModuleDefId::AdtId(id) => id.module(db),
545 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db), 529 ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container,
546 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db), 530 ModuleDefId::ConstId(id) => id.lookup(db).container.module(db),
547 ModuleDefId::StaticId(id) => id.lookup(db).container.module(db), 531 ModuleDefId::StaticId(id) => id.lookup(db).container,
548 ModuleDefId::TraitId(id) => id.lookup(db).container.module(db), 532 ModuleDefId::TraitId(id) => id.lookup(db).container,
549 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db), 533 ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db),
550 ModuleDefId::BuiltinType(_) => return None, 534 ModuleDefId::BuiltinType(_) => return None,
551 }) 535 })
@@ -559,12 +543,12 @@ impl AttrDefId {
559 AttrDefId::FieldId(it) => it.parent.module(db).krate, 543 AttrDefId::FieldId(it) => it.parent.module(db).krate,
560 AttrDefId::AdtId(it) => it.module(db).krate, 544 AttrDefId::AdtId(it) => it.module(db).krate,
561 AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate, 545 AttrDefId::FunctionId(it) => it.lookup(db).module(db).krate,
562 AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.module(db).krate, 546 AttrDefId::EnumVariantId(it) => it.parent.lookup(db).container.krate,
563 AttrDefId::StaticId(it) => it.lookup(db).module(db).krate, 547 AttrDefId::StaticId(it) => it.lookup(db).module(db).krate,
564 AttrDefId::ConstId(it) => it.lookup(db).module(db).krate, 548 AttrDefId::ConstId(it) => it.lookup(db).module(db).krate,
565 AttrDefId::TraitId(it) => it.lookup(db).container.module(db).krate, 549 AttrDefId::TraitId(it) => it.lookup(db).container.krate,
566 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate, 550 AttrDefId::TypeAliasId(it) => it.lookup(db).module(db).krate,
567 AttrDefId::ImplId(it) => it.lookup(db).container.module(db).krate, 551 AttrDefId::ImplId(it) => it.lookup(db).container.krate,
568 AttrDefId::GenericParamId(it) => { 552 AttrDefId::GenericParamId(it) => {
569 match it { 553 match it {
570 GenericParamId::TypeParamId(it) => it.parent, 554 GenericParamId::TypeParamId(it) => it.parent,
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index 3bb69d935..9ed48c506 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -37,9 +37,9 @@ use crate::{
37 path::{ImportAlias, ModPath, PathKind}, 37 path::{ImportAlias, ModPath, PathKind},
38 per_ns::PerNs, 38 per_ns::PerNs,
39 visibility::{RawVisibility, Visibility}, 39 visibility::{RawVisibility, Visibility},
40 AdtId, AstId, AstIdWithPath, ConstLoc, ContainerId, EnumLoc, EnumVariantId, FunctionLoc, 40 AdtId, AstId, AstIdWithPath, ConstLoc, EnumLoc, EnumVariantId, FunctionLoc, ImplLoc, Intern,
41 ImplLoc, Intern, LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, 41 LocalModuleId, ModuleDefId, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc,
42 UnionLoc, UnresolvedMacro, 42 UnresolvedMacro,
43}; 43};
44 44
45const GLOB_RECURSION_LIMIT: usize = 100; 45const GLOB_RECURSION_LIMIT: usize = 100;
@@ -1042,7 +1042,6 @@ impl ModCollector<'_, '_> {
1042 } 1042 }
1043 } 1043 }
1044 let module = self.def_collector.def_map.module_id(self.module_id); 1044 let module = self.def_collector.def_map.module_id(self.module_id);
1045 let container = ContainerId::ModuleId(module);
1046 1045
1047 let mut def = None; 1046 let mut def = None;
1048 match item { 1047 match item {
@@ -1109,9 +1108,9 @@ impl ModCollector<'_, '_> {
1109 } 1108 }
1110 ModItem::Impl(imp) => { 1109 ModItem::Impl(imp) => {
1111 let module = self.def_collector.def_map.module_id(self.module_id); 1110 let module = self.def_collector.def_map.module_id(self.module_id);
1112 let container = ContainerId::ModuleId(module); 1111 let impl_id =
1113 let impl_id = ImplLoc { container, id: ItemTreeId::new(self.file_id, imp) } 1112 ImplLoc { container: module, id: ItemTreeId::new(self.file_id, imp) }
1114 .intern(self.def_collector.db); 1113 .intern(self.def_collector.db);
1115 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id) 1114 self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
1116 } 1115 }
1117 ModItem::Function(id) => { 1116 ModItem::Function(id) => {
@@ -1121,7 +1120,7 @@ impl ModCollector<'_, '_> {
1121 1120
1122 def = Some(DefData { 1121 def = Some(DefData {
1123 id: FunctionLoc { 1122 id: FunctionLoc {
1124 container: container.into(), 1123 container: module.into(),
1125 id: ItemTreeId::new(self.file_id, id), 1124 id: ItemTreeId::new(self.file_id, id),
1126 } 1125 }
1127 .intern(self.def_collector.db) 1126 .intern(self.def_collector.db)
@@ -1140,7 +1139,7 @@ impl ModCollector<'_, '_> {
1140 self.collect_derives(&attrs, it.ast_id.upcast()); 1139 self.collect_derives(&attrs, it.ast_id.upcast());
1141 1140
1142 def = Some(DefData { 1141 def = Some(DefData {
1143 id: StructLoc { container, id: ItemTreeId::new(self.file_id, id) } 1142 id: StructLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1144 .intern(self.def_collector.db) 1143 .intern(self.def_collector.db)
1145 .into(), 1144 .into(),
1146 name: &it.name, 1145 name: &it.name,
@@ -1157,7 +1156,7 @@ impl ModCollector<'_, '_> {
1157 self.collect_derives(&attrs, it.ast_id.upcast()); 1156 self.collect_derives(&attrs, it.ast_id.upcast());
1158 1157
1159 def = Some(DefData { 1158 def = Some(DefData {
1160 id: UnionLoc { container, id: ItemTreeId::new(self.file_id, id) } 1159 id: UnionLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1161 .intern(self.def_collector.db) 1160 .intern(self.def_collector.db)
1162 .into(), 1161 .into(),
1163 name: &it.name, 1162 name: &it.name,
@@ -1174,7 +1173,7 @@ impl ModCollector<'_, '_> {
1174 self.collect_derives(&attrs, it.ast_id.upcast()); 1173 self.collect_derives(&attrs, it.ast_id.upcast());
1175 1174
1176 def = Some(DefData { 1175 def = Some(DefData {
1177 id: EnumLoc { container, id: ItemTreeId::new(self.file_id, id) } 1176 id: EnumLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1178 .intern(self.def_collector.db) 1177 .intern(self.def_collector.db)
1179 .into(), 1178 .into(),
1180 name: &it.name, 1179 name: &it.name,
@@ -1188,7 +1187,7 @@ impl ModCollector<'_, '_> {
1188 if let Some(name) = &it.name { 1187 if let Some(name) = &it.name {
1189 def = Some(DefData { 1188 def = Some(DefData {
1190 id: ConstLoc { 1189 id: ConstLoc {
1191 container: container.into(), 1190 container: module.into(),
1192 id: ItemTreeId::new(self.file_id, id), 1191 id: ItemTreeId::new(self.file_id, id),
1193 } 1192 }
1194 .intern(self.def_collector.db) 1193 .intern(self.def_collector.db)
@@ -1203,7 +1202,7 @@ impl ModCollector<'_, '_> {
1203 let it = &self.item_tree[id]; 1202 let it = &self.item_tree[id];
1204 1203
1205 def = Some(DefData { 1204 def = Some(DefData {
1206 id: StaticLoc { container, id: ItemTreeId::new(self.file_id, id) } 1205 id: StaticLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1207 .intern(self.def_collector.db) 1206 .intern(self.def_collector.db)
1208 .into(), 1207 .into(),
1209 name: &it.name, 1208 name: &it.name,
@@ -1215,7 +1214,7 @@ impl ModCollector<'_, '_> {
1215 let it = &self.item_tree[id]; 1214 let it = &self.item_tree[id];
1216 1215
1217 def = Some(DefData { 1216 def = Some(DefData {
1218 id: TraitLoc { container, id: ItemTreeId::new(self.file_id, id) } 1217 id: TraitLoc { container: module, id: ItemTreeId::new(self.file_id, id) }
1219 .intern(self.def_collector.db) 1218 .intern(self.def_collector.db)
1220 .into(), 1219 .into(),
1221 name: &it.name, 1220 name: &it.name,
@@ -1228,7 +1227,7 @@ impl ModCollector<'_, '_> {
1228 1227
1229 def = Some(DefData { 1228 def = Some(DefData {
1230 id: TypeAliasLoc { 1229 id: TypeAliasLoc {
1231 container: container.into(), 1230 container: module.into(),
1232 id: ItemTreeId::new(self.file_id, id), 1231 id: ItemTreeId::new(self.file_id, id),
1233 } 1232 }
1234 .intern(self.def_collector.db) 1233 .intern(self.def_collector.db)
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs
index 8258dcffb..db459b1ed 100644
--- a/crates/hir_def/src/nameres/path_resolution.rs
+++ b/crates/hir_def/src/nameres/path_resolution.rs
@@ -156,7 +156,7 @@ impl DefMap {
156 } 156 }
157 } 157 }
158 158
159 pub(super) fn resolve_path_fp_with_macro_single( 159 fn resolve_path_fp_with_macro_single(
160 &self, 160 &self,
161 db: &dyn DefDatabase, 161 db: &dyn DefDatabase,
162 mode: ResolveMode, 162 mode: ResolveMode,
@@ -384,10 +384,16 @@ impl DefMap {
384 } 384 }
385 } 385 }
386 }; 386 };
387 let from_extern_prelude = self 387 // Give precedence to names in outer `DefMap`s over the extern prelude; only check prelude
388 .extern_prelude 388 // from the crate DefMap.
389 .get(name) 389 let from_extern_prelude = match self.block {
390 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)); 390 Some(_) => PerNs::none(),
391 None => self
392 .extern_prelude
393 .get(name)
394 .map_or(PerNs::none(), |&it| PerNs::types(it, Visibility::Public)),
395 };
396
391 let from_prelude = self.resolve_in_prelude(db, name); 397 let from_prelude = self.resolve_in_prelude(db, name);
392 398
393 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) 399 from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude)
diff --git a/crates/hir_def/src/resolver.rs b/crates/hir_def/src/resolver.rs
index 77ff21739..42736171e 100644
--- a/crates/hir_def/src/resolver.rs
+++ b/crates/hir_def/src/resolver.rs
@@ -19,10 +19,10 @@ use crate::{
19 path::{ModPath, PathKind}, 19 path::{ModPath, PathKind},
20 per_ns::PerNs, 20 per_ns::PerNs,
21 visibility::{RawVisibility, Visibility}, 21 visibility::{RawVisibility, Visibility},
22 AdtId, AssocContainerId, ConstId, ConstParamId, ContainerId, DefWithBodyId, EnumId, 22 AdtId, AssocContainerId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId,
23 EnumVariantId, FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, 23 FunctionId, GenericDefId, GenericParamId, HasModule, ImplId, LifetimeParamId, LocalModuleId,
24 LocalModuleId, Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, 24 Lookup, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId,
25 TypeParamId, VariantId, 25 VariantId,
26}; 26};
27 27
28#[derive(Debug, Clone, Default)] 28#[derive(Debug, Clone, Default)]
@@ -342,6 +342,16 @@ impl Resolver {
342 traits.extend(prelude_def_map[prelude.local_id].scope.traits()); 342 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
343 } 343 }
344 traits.extend(m.def_map[m.module_id].scope.traits()); 344 traits.extend(m.def_map[m.module_id].scope.traits());
345
346 // Add all traits that are in scope because of the containing DefMaps
347 m.def_map.with_ancestor_maps(db, m.module_id, &mut |def_map, module| {
348 if let Some(prelude) = def_map.prelude() {
349 let prelude_def_map = prelude.def_map(db);
350 traits.extend(prelude_def_map[prelude.local_id].scope.traits());
351 }
352 traits.extend(def_map[module].scope.traits());
353 None::<()>
354 });
345 } 355 }
346 } 356 }
347 traits 357 traits
@@ -678,19 +688,10 @@ impl HasResolver for DefWithBodyId {
678 } 688 }
679} 689}
680 690
681impl HasResolver for ContainerId {
682 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
683 match self {
684 ContainerId::ModuleId(it) => it.resolver(db),
685 ContainerId::DefWithBodyId(it) => it.module(db).resolver(db),
686 }
687 }
688}
689
690impl HasResolver for AssocContainerId { 691impl HasResolver for AssocContainerId {
691 fn resolver(self, db: &dyn DefDatabase) -> Resolver { 692 fn resolver(self, db: &dyn DefDatabase) -> Resolver {
692 match self { 693 match self {
693 AssocContainerId::ContainerId(it) => it.resolver(db), 694 AssocContainerId::ModuleId(it) => it.resolver(db),
694 AssocContainerId::TraitId(it) => it.resolver(db), 695 AssocContainerId::TraitId(it) => it.resolver(db),
695 AssocContainerId::ImplId(it) => it.resolver(db), 696 AssocContainerId::ImplId(it) => it.resolver(db),
696 } 697 }
diff --git a/crates/hir_expand/Cargo.toml b/crates/hir_expand/Cargo.toml
index 5271110d2..f649ab925 100644
--- a/crates/hir_expand/Cargo.toml
+++ b/crates/hir_expand/Cargo.toml
@@ -16,9 +16,13 @@ rustc-hash = "1.0.0"
16la-arena = { version = "0.2.0", path = "../../lib/arena" } 16la-arena = { version = "0.2.0", path = "../../lib/arena" }
17 17
18base_db = { path = "../base_db", version = "0.0.0" } 18base_db = { path = "../base_db", version = "0.0.0" }
19cfg = { path = "../cfg", version = "0.0.0" }
19syntax = { path = "../syntax", version = "0.0.0" } 20syntax = { path = "../syntax", version = "0.0.0" }
20parser = { path = "../parser", version = "0.0.0" } 21parser = { path = "../parser", version = "0.0.0" }
21profile = { path = "../profile", version = "0.0.0" } 22profile = { path = "../profile", version = "0.0.0" }
22tt = { path = "../tt", version = "0.0.0" } 23tt = { path = "../tt", version = "0.0.0" }
23mbe = { path = "../mbe", version = "0.0.0" } 24mbe = { path = "../mbe", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" } 25
26[dev-dependencies]
27test_utils = { path = "../test_utils" }
28expect-test = "1.1"
diff --git a/crates/hir_expand/src/builtin_derive.rs b/crates/hir_expand/src/builtin_derive.rs
index b7f1aae8f..dfdb9cf59 100644
--- a/crates/hir_expand/src/builtin_derive.rs
+++ b/crates/hir_expand/src/builtin_derive.rs
@@ -267,13 +267,14 @@ fn partial_ord_expand(
267#[cfg(test)] 267#[cfg(test)]
268mod tests { 268mod tests {
269 use base_db::{fixture::WithFixture, CrateId, SourceDatabase}; 269 use base_db::{fixture::WithFixture, CrateId, SourceDatabase};
270 use expect_test::{expect, Expect};
270 use name::{known, Name}; 271 use name::{known, Name};
271 272
272 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc}; 273 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
273 274
274 use super::*; 275 use super::*;
275 276
276 fn expand_builtin_derive(s: &str, name: Name) -> String { 277 fn expand_builtin_derive(ra_fixture: &str, name: Name) -> String {
277 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap(); 278 let expander = BuiltinDeriveExpander::find_by_name(&name).unwrap();
278 let fixture = format!( 279 let fixture = format!(
279 r#"//- /main.rs crate:main deps:core 280 r#"//- /main.rs crate:main deps:core
@@ -282,7 +283,7 @@ $0
282//- /lib.rs crate:core 283//- /lib.rs crate:core
283// empty 284// empty
284"#, 285"#,
285 s 286 ra_fixture
286 ); 287 );
287 288
288 let (db, file_pos) = TestDB::with_position(&fixture); 289 let (db, file_pos) = TestDB::with_position(&fixture);
@@ -314,66 +315,57 @@ $0
314 parsed.text().to_string() 315 parsed.text().to_string()
315 } 316 }
316 317
318 fn check_derive(ra_fixture: &str, name: Name, expected: Expect) {
319 let expanded = expand_builtin_derive(ra_fixture, name);
320 expected.assert_eq(&expanded);
321 }
322
317 #[test] 323 #[test]
318 fn test_copy_expand_simple() { 324 fn test_copy_expand_simple() {
319 let expanded = expand_builtin_derive( 325 check_derive(
320 r#" 326 r#"
321 #[derive(Copy)] 327 #[derive(Copy)]
322 struct Foo; 328 struct Foo;
323"#, 329 "#,
324 known::Copy, 330 known::Copy,
331 expect![["impl< >core::marker::CopyforFoo< >{}"]],
325 ); 332 );
326
327 assert_eq!(expanded, "impl< >core::marker::CopyforFoo< >{}");
328 } 333 }
329 334
330 #[test] 335 #[test]
331 fn test_copy_expand_with_type_params() { 336 fn test_copy_expand_with_type_params() {
332 let expanded = expand_builtin_derive( 337 check_derive(
333 r#" 338 r#"
334 #[derive(Copy)] 339 #[derive(Copy)]
335 struct Foo<A, B>; 340 struct Foo<A, B>;
336"#, 341 "#,
337 known::Copy, 342 known::Copy,
338 ); 343 expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
339
340 assert_eq!(
341 expanded,
342 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
343 ); 344 );
344 } 345 }
345 346
346 #[test] 347 #[test]
347 fn test_copy_expand_with_lifetimes() { 348 fn test_copy_expand_with_lifetimes() {
348 let expanded = expand_builtin_derive( 349 check_derive(
349 r#" 350 r#"
350 #[derive(Copy)] 351 #[derive(Copy)]
351 struct Foo<A, B, 'a, 'b>; 352 struct Foo<A, B, 'a, 'b>;
352"#, 353 "#,
353 known::Copy, 354 known::Copy,
354 ); 355 // We currently just ignore lifetimes
355 356 expect![["impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"]],
356 // We currently just ignore lifetimes
357
358 assert_eq!(
359 expanded,
360 "impl<T0:core::marker::Copy,T1:core::marker::Copy>core::marker::CopyforFoo<T0,T1>{}"
361 ); 357 );
362 } 358 }
363 359
364 #[test] 360 #[test]
365 fn test_clone_expand() { 361 fn test_clone_expand() {
366 let expanded = expand_builtin_derive( 362 check_derive(
367 r#" 363 r#"
368 #[derive(Clone)] 364 #[derive(Clone)]
369 struct Foo<A, B>; 365 struct Foo<A, B>;
370"#, 366 "#,
371 known::Clone, 367 known::Clone,
372 ); 368 expect![["impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"]],
373
374 assert_eq!(
375 expanded,
376 "impl<T0:core::clone::Clone,T1:core::clone::Clone>core::clone::CloneforFoo<T0,T1>{}"
377 ); 369 );
378 } 370 }
379} 371}
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index eb57ea7d6..2a79c892b 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -5,6 +5,7 @@ use crate::{
5}; 5};
6 6
7use base_db::{AnchoredPath, FileId}; 7use base_db::{AnchoredPath, FileId};
8use cfg::CfgExpr;
8use either::Either; 9use either::Either;
9use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; 10use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
10use parser::FragmentKind; 11use parser::FragmentKind;
@@ -97,6 +98,7 @@ register_builtin! {
97 (format_args_nl, FormatArgsNl) => format_args_expand, 98 (format_args_nl, FormatArgsNl) => format_args_expand,
98 (llvm_asm, LlvmAsm) => asm_expand, 99 (llvm_asm, LlvmAsm) => asm_expand,
99 (asm, Asm) => asm_expand, 100 (asm, Asm) => asm_expand,
101 (cfg, Cfg) => cfg_expand,
100 102
101 EAGER: 103 EAGER:
102 (compile_error, CompileError) => compile_error_expand, 104 (compile_error, CompileError) => compile_error_expand,
@@ -258,6 +260,18 @@ fn asm_expand(
258 ExpandResult::ok(expanded) 260 ExpandResult::ok(expanded)
259} 261}
260 262
263fn cfg_expand(
264 db: &dyn AstDatabase,
265 id: LazyMacroId,
266 tt: &tt::Subtree,
267) -> ExpandResult<tt::Subtree> {
268 let loc = db.lookup_intern_macro(id);
269 let expr = CfgExpr::parse(tt);
270 let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
271 let expanded = if enabled { quote!(true) } else { quote!(false) };
272 ExpandResult::ok(expanded)
273}
274
261fn unquote_str(lit: &tt::Literal) -> Option<String> { 275fn unquote_str(lit: &tt::Literal) -> Option<String> {
262 let lit = ast::make::tokens::literal(&lit.to_string()); 276 let lit = ast::make::tokens::literal(&lit.to_string());
263 let token = ast::String::cast(lit)?; 277 let token = ast::String::cast(lit)?;
@@ -477,6 +491,7 @@ mod tests {
477 MacroCallLoc, 491 MacroCallLoc,
478 }; 492 };
479 use base_db::{fixture::WithFixture, SourceDatabase}; 493 use base_db::{fixture::WithFixture, SourceDatabase};
494 use expect_test::{expect, Expect};
480 use std::sync::Arc; 495 use std::sync::Arc;
481 use syntax::ast::NameOwner; 496 use syntax::ast::NameOwner;
482 497
@@ -560,87 +575,86 @@ mod tests {
560 db.parse_or_expand(file_id).unwrap().to_string() 575 db.parse_or_expand(file_id).unwrap().to_string()
561 } 576 }
562 577
578 fn check_expansion(ra_fixture: &str, expect: Expect) {
579 let expansion = expand_builtin_macro(ra_fixture);
580 expect.assert_eq(&expansion);
581 }
582
563 #[test] 583 #[test]
564 fn test_column_expand() { 584 fn test_column_expand() {
565 let expanded = expand_builtin_macro( 585 check_expansion(
566 r#" 586 r#"
567 #[rustc_builtin_macro] 587 #[rustc_builtin_macro]
568 macro_rules! column {() => {}} 588 macro_rules! column {() => {}}
569 column!() 589 column!()
570 "#, 590 "#,
591 expect![["0"]],
571 ); 592 );
572
573 assert_eq!(expanded, "0");
574 } 593 }
575 594
576 #[test] 595 #[test]
577 fn test_line_expand() { 596 fn test_line_expand() {
578 let expanded = expand_builtin_macro( 597 check_expansion(
579 r#" 598 r#"
580 #[rustc_builtin_macro] 599 #[rustc_builtin_macro]
581 macro_rules! line {() => {}} 600 macro_rules! line {() => {}}
582 line!() 601 line!()
583 "#, 602 "#,
603 expect![["0"]],
584 ); 604 );
585
586 assert_eq!(expanded, "0");
587 } 605 }
588 606
589 #[test] 607 #[test]
590 fn test_stringify_expand() { 608 fn test_stringify_expand() {
591 let expanded = expand_builtin_macro( 609 check_expansion(
592 r#" 610 r#"
593 #[rustc_builtin_macro] 611 #[rustc_builtin_macro]
594 macro_rules! stringify {() => {}} 612 macro_rules! stringify {() => {}}
595 stringify!(a b c) 613 stringify!(a b c)
596 "#, 614 "#,
615 expect![["\"a b c\""]],
597 ); 616 );
598
599 assert_eq!(expanded, "\"a b c\"");
600 } 617 }
601 618
602 #[test] 619 #[test]
603 fn test_env_expand() { 620 fn test_env_expand() {
604 let expanded = expand_builtin_macro( 621 check_expansion(
605 r#" 622 r#"
606 #[rustc_builtin_macro] 623 #[rustc_builtin_macro]
607 macro_rules! env {() => {}} 624 macro_rules! env {() => {}}
608 env!("TEST_ENV_VAR") 625 env!("TEST_ENV_VAR")
609 "#, 626 "#,
627 expect![["\"__RA_UNIMPLEMENTED__\""]],
610 ); 628 );
611
612 assert_eq!(expanded, "\"__RA_UNIMPLEMENTED__\"");
613 } 629 }
614 630
615 #[test] 631 #[test]
616 fn test_option_env_expand() { 632 fn test_option_env_expand() {
617 let expanded = expand_builtin_macro( 633 check_expansion(
618 r#" 634 r#"
619 #[rustc_builtin_macro] 635 #[rustc_builtin_macro]
620 macro_rules! option_env {() => {}} 636 macro_rules! option_env {() => {}}
621 option_env!("TEST_ENV_VAR") 637 option_env!("TEST_ENV_VAR")
622 "#, 638 "#,
639 expect![["std::option::Option::None:: < &str>"]],
623 ); 640 );
624
625 assert_eq!(expanded, "std::option::Option::None:: < &str>");
626 } 641 }
627 642
628 #[test] 643 #[test]
629 fn test_file_expand() { 644 fn test_file_expand() {
630 let expanded = expand_builtin_macro( 645 check_expansion(
631 r#" 646 r#"
632 #[rustc_builtin_macro] 647 #[rustc_builtin_macro]
633 macro_rules! file {() => {}} 648 macro_rules! file {() => {}}
634 file!() 649 file!()
635 "#, 650 "#,
651 expect![[r#""""#]],
636 ); 652 );
637
638 assert_eq!(expanded, "\"\"");
639 } 653 }
640 654
641 #[test] 655 #[test]
642 fn test_assert_expand() { 656 fn test_assert_expand() {
643 let expanded = expand_builtin_macro( 657 check_expansion(
644 r#" 658 r#"
645 #[rustc_builtin_macro] 659 #[rustc_builtin_macro]
646 macro_rules! assert { 660 macro_rules! assert {
@@ -649,14 +663,13 @@ mod tests {
649 } 663 }
650 assert!(true, "{} {:?}", arg1(a, b, c), arg2); 664 assert!(true, "{} {:?}", arg1(a, b, c), arg2);
651 "#, 665 "#,
666 expect![["{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}"]],
652 ); 667 );
653
654 assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}");
655 } 668 }
656 669
657 #[test] 670 #[test]
658 fn test_compile_error_expand() { 671 fn test_compile_error_expand() {
659 let expanded = expand_builtin_macro( 672 check_expansion(
660 r#" 673 r#"
661 #[rustc_builtin_macro] 674 #[rustc_builtin_macro]
662 macro_rules! compile_error { 675 macro_rules! compile_error {
@@ -665,15 +678,14 @@ mod tests {
665 } 678 }
666 compile_error!("error!"); 679 compile_error!("error!");
667 "#, 680 "#,
681 // This expands to nothing (since it's in item position), but emits an error.
682 expect![[""]],
668 ); 683 );
669
670 // This expands to nothing (since it's in item position), but emits an error.
671 assert_eq!(expanded, "");
672 } 684 }
673 685
674 #[test] 686 #[test]
675 fn test_format_args_expand() { 687 fn test_format_args_expand() {
676 let expanded = expand_builtin_macro( 688 check_expansion(
677 r#" 689 r#"
678 #[rustc_builtin_macro] 690 #[rustc_builtin_macro]
679 macro_rules! format_args { 691 macro_rules! format_args {
@@ -682,17 +694,15 @@ mod tests {
682 } 694 }
683 format_args!("{} {:?}", arg1(a, b, c), arg2); 695 format_args!("{} {:?}", arg1(a, b, c), arg2);
684 "#, 696 "#,
685 ); 697 expect![[
686 698 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
687 assert_eq!( 699 ]],
688 expanded,
689 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
690 ); 700 );
691 } 701 }
692 702
693 #[test] 703 #[test]
694 fn test_format_args_expand_with_comma_exprs() { 704 fn test_format_args_expand_with_comma_exprs() {
695 let expanded = expand_builtin_macro( 705 check_expansion(
696 r#" 706 r#"
697 #[rustc_builtin_macro] 707 #[rustc_builtin_macro]
698 macro_rules! format_args { 708 macro_rules! format_args {
@@ -701,17 +711,15 @@ mod tests {
701 } 711 }
702 format_args!("{} {:?}", a::<A,B>(), b); 712 format_args!("{} {:?}", a::<A,B>(), b);
703 "#, 713 "#,
704 ); 714 expect![[
705 715 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"#
706 assert_eq!( 716 ]],
707 expanded,
708 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"#
709 ); 717 );
710 } 718 }
711 719
712 #[test] 720 #[test]
713 fn test_include_bytes_expand() { 721 fn test_include_bytes_expand() {
714 let expanded = expand_builtin_macro( 722 check_expansion(
715 r#" 723 r#"
716 #[rustc_builtin_macro] 724 #[rustc_builtin_macro]
717 macro_rules! include_bytes { 725 macro_rules! include_bytes {
@@ -720,21 +728,19 @@ mod tests {
720 } 728 }
721 include_bytes("foo"); 729 include_bytes("foo");
722 "#, 730 "#,
731 expect![[r#"b"""#]],
723 ); 732 );
724
725 assert_eq!(expanded, r#"b"""#);
726 } 733 }
727 734
728 #[test] 735 #[test]
729 fn test_concat_expand() { 736 fn test_concat_expand() {
730 let expanded = expand_builtin_macro( 737 check_expansion(
731 r##" 738 r##"
732 #[rustc_builtin_macro] 739 #[rustc_builtin_macro]
733 macro_rules! concat {} 740 macro_rules! concat {}
734 concat!("foo", "r", 0, r#"bar"#, false); 741 concat!("foo", "r", 0, r#"bar"#, false);
735 "##, 742 "##,
743 expect![[r#""foor0barfalse""#]],
736 ); 744 );
737
738 assert_eq!(expanded, r#""foor0barfalse""#);
739 } 745 }
740} 746}
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs
index c94fb580a..e833e032c 100644
--- a/crates/hir_expand/src/name.rs
+++ b/crates/hir_expand/src/name.rs
@@ -154,6 +154,7 @@ pub mod known {
154 macro_rules, 154 macro_rules,
155 derive, 155 derive,
156 doc, 156 doc,
157 cfg,
157 cfg_attr, 158 cfg_attr,
158 // Components of known path (value or mod name) 159 // Components of known path (value or mod name)
159 std, 160 std,
diff --git a/crates/hir_expand/src/quote.rs b/crates/hir_expand/src/quote.rs
index 219bc2097..08bc5aa49 100644
--- a/crates/hir_expand/src/quote.rs
+++ b/crates/hir_expand/src/quote.rs
@@ -188,8 +188,9 @@ macro_rules! impl_to_to_tokentrees {
188 188
189impl_to_to_tokentrees! { 189impl_to_to_tokentrees! {
190 u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} }; 190 u32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
191 usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; 191 usize => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
192 i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()}}; 192 i32 => self { tt::Literal{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
193 bool => self { tt::Ident{text: self.to_string().into(), id: tt::TokenId::unspecified()} };
193 tt::Leaf => self { self }; 194 tt::Leaf => self { self };
194 tt::Literal => self { self }; 195 tt::Literal => self { self };
195 tt::Ident => self { self }; 196 tt::Ident => self { self };
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml
index 6131ebee8..b9c93f56f 100644
--- a/crates/hir_ty/Cargo.toml
+++ b/crates/hir_ty/Cargo.toml
@@ -29,9 +29,9 @@ hir_expand = { path = "../hir_expand", version = "0.0.0" }
29base_db = { path = "../base_db", version = "0.0.0" } 29base_db = { path = "../base_db", version = "0.0.0" }
30profile = { path = "../profile", version = "0.0.0" } 30profile = { path = "../profile", version = "0.0.0" }
31syntax = { path = "../syntax", version = "0.0.0" } 31syntax = { path = "../syntax", version = "0.0.0" }
32test_utils = { path = "../test_utils", version = "0.0.0" }
33 32
34[dev-dependencies] 33[dev-dependencies]
34test_utils = { path = "../test_utils" }
35expect-test = "1.1" 35expect-test = "1.1"
36tracing = "0.1" 36tracing = "0.1"
37tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } 37tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
diff --git a/crates/hir_ty/src/diagnostics.rs b/crates/hir_ty/src/diagnostics.rs
index 6bca7aa0d..86f937e1d 100644
--- a/crates/hir_ty/src/diagnostics.rs
+++ b/crates/hir_ty/src/diagnostics.rs
@@ -706,6 +706,35 @@ fn x(a: S) {
706 } 706 }
707 707
708 #[test] 708 #[test]
709 fn import_extern_crate_clash_with_inner_item() {
710 // This is more of a resolver test, but doesn't really work with the hir_def testsuite.
711
712 check_diagnostics(
713 r#"
714//- /lib.rs crate:lib deps:jwt
715mod permissions;
716
717use permissions::jwt;
718
719fn f() {
720 fn inner() {}
721 jwt::Claims {}; // should resolve to the local one with 0 fields, and not get a diagnostic
722}
723
724//- /permissions.rs
725pub mod jwt {
726 pub struct Claims {}
727}
728
729//- /jwt/lib.rs crate:jwt
730pub struct Claims {
731 field: u8,
732}
733 "#,
734 );
735 }
736
737 #[test]
709 fn break_outside_of_loop() { 738 fn break_outside_of_loop() {
710 check_diagnostics( 739 check_diagnostics(
711 r#" 740 r#"
diff --git a/crates/hir_ty/src/diagnostics/decl_check.rs b/crates/hir_ty/src/diagnostics/decl_check.rs
index e230f9765..3605ca581 100644
--- a/crates/hir_ty/src/diagnostics/decl_check.rs
+++ b/crates/hir_ty/src/diagnostics/decl_check.rs
@@ -99,9 +99,14 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
99 let body = self.db.body(func.into()); 99 let body = self.db.body(func.into());
100 100
101 // Recursively validate inner scope items, such as static variables and constants. 101 // Recursively validate inner scope items, such as static variables and constants.
102 for (item_id, _) in body.item_scope.values() { 102 let db = self.db;
103 let mut validator = DeclValidator::new(self.db, self.krate, self.sink); 103 for block_def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
104 validator.validate_item(item_id); 104 for (_, module) in block_def_map.modules() {
105 for (def_id, _) in module.scope.values() {
106 let mut validator = DeclValidator::new(self.db, self.krate, self.sink);
107 validator.validate_item(def_id);
108 }
109 }
105 } 110 }
106 111
107 // Check whether non-snake case identifiers are allowed for this function. 112 // Check whether non-snake case identifiers are allowed for this function.
diff --git a/crates/hir_ty/src/display.rs b/crates/hir_ty/src/display.rs
index a0882a2a1..ab51cb0a6 100644
--- a/crates/hir_ty/src/display.rs
+++ b/crates/hir_ty/src/display.rs
@@ -6,7 +6,7 @@ use arrayvec::ArrayVec;
6use chalk_ir::Mutability; 6use chalk_ir::Mutability;
7use hir_def::{ 7use hir_def::{
8 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs, 8 db::DefDatabase, find_path, generics::TypeParamProvenance, item_scope::ItemInNs,
9 AssocContainerId, HasModule, Lookup, ModuleId, TraitId, 9 AssocContainerId, Lookup, ModuleId, TraitId,
10}; 10};
11use hir_expand::name::Name; 11use hir_expand::name::Name;
12 12
@@ -611,7 +611,7 @@ impl HirDisplay for CallableSig {
611} 611}
612 612
613fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { 613fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> {
614 let krate = trait_.lookup(db).container.module(db).krate(); 614 let krate = trait_.lookup(db).container.krate();
615 let fn_traits = [ 615 let fn_traits = [
616 db.lang_item(krate, "fn".into()), 616 db.lang_item(krate, "fn".into()),
617 db.lang_item(krate, "fn_mut".into()), 617 db.lang_item(krate, "fn_mut".into()),
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 5d541104e..ae3554bac 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -260,7 +260,7 @@ impl<'a> InferenceContext<'a> {
260 })); 260 }));
261 Some(trait_substs) 261 Some(trait_substs)
262 } 262 }
263 AssocContainerId::ContainerId(_) => None, 263 AssocContainerId::ModuleId(_) => None,
264 }; 264 };
265 265
266 self.write_assoc_resolution(id, item); 266 self.write_assoc_resolution(id, item);
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index b90fdc382..5fa83567b 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -1130,8 +1130,8 @@ impl CallableDefId {
1130 let db = db.upcast(); 1130 let db = db.upcast();
1131 match self { 1131 match self {
1132 CallableDefId::FunctionId(f) => f.lookup(db).module(db), 1132 CallableDefId::FunctionId(f) => f.lookup(db).module(db),
1133 CallableDefId::StructId(s) => s.lookup(db).container.module(db), 1133 CallableDefId::StructId(s) => s.lookup(db).container,
1134 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container.module(db), 1134 CallableDefId::EnumVariantId(e) => e.parent.lookup(db).container,
1135 } 1135 }
1136 .krate() 1136 .krate()
1137 } 1137 }
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 24db33c49..ccc12c075 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -267,7 +267,7 @@ impl Ty {
267 LangItemTarget::ImplDefId(it) => Some(it), 267 LangItemTarget::ImplDefId(it) => Some(it),
268 _ => None, 268 _ => None,
269 }) 269 })
270 .map(|it| it.lookup(db.upcast()).container.module(db.upcast()).krate()) 270 .map(|it| it.lookup(db.upcast()).container.krate())
271 .collect(); 271 .collect();
272 Some(res) 272 Some(res)
273 } 273 }
@@ -715,7 +715,7 @@ fn transform_receiver_ty(
715 .fill_with_unknown() 715 .fill_with_unknown()
716 .build() 716 .build()
717 } 717 }
718 AssocContainerId::ContainerId(_) => unreachable!(), 718 AssocContainerId::ModuleId(_) => unreachable!(),
719 }; 719 };
720 let sig = db.callable_item_signature(function_id.into()); 720 let sig = db.callable_item_signature(function_id.into());
721 Some(sig.value.params()[0].clone().subst_bound_vars(&substs)) 721 Some(sig.value.params()[0].clone().subst_bound_vars(&substs))
diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs
index 7386a4e7b..fc770ea60 100644
--- a/crates/hir_ty/src/tests.rs
+++ b/crates/hir_ty/src/tests.rs
@@ -13,7 +13,7 @@ use std::{env, sync::Arc};
13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; 13use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt};
14use expect_test::Expect; 14use expect_test::Expect;
15use hir_def::{ 15use hir_def::{
16 body::{BodySourceMap, SyntheticSyntax}, 16 body::{Body, BodySourceMap, SyntheticSyntax},
17 child_by_source::ChildBySource, 17 child_by_source::ChildBySource,
18 db::DefDatabase, 18 db::DefDatabase,
19 item_scope::ItemScope, 19 item_scope::ItemScope,
@@ -234,13 +234,13 @@ fn visit_module(
234 let def = it.into(); 234 let def = it.into();
235 cb(def); 235 cb(def);
236 let body = db.body(def); 236 let body = db.body(def);
237 visit_scope(db, crate_def_map, &body.item_scope, cb); 237 visit_body(db, &body, cb);
238 } 238 }
239 AssocItemId::ConstId(it) => { 239 AssocItemId::ConstId(it) => {
240 let def = it.into(); 240 let def = it.into();
241 cb(def); 241 cb(def);
242 let body = db.body(def); 242 let body = db.body(def);
243 visit_scope(db, crate_def_map, &body.item_scope, cb); 243 visit_body(db, &body, cb);
244 } 244 }
245 AssocItemId::TypeAliasId(_) => (), 245 AssocItemId::TypeAliasId(_) => (),
246 } 246 }
@@ -259,19 +259,19 @@ fn visit_module(
259 let def = it.into(); 259 let def = it.into();
260 cb(def); 260 cb(def);
261 let body = db.body(def); 261 let body = db.body(def);
262 visit_scope(db, crate_def_map, &body.item_scope, cb); 262 visit_body(db, &body, cb);
263 } 263 }
264 ModuleDefId::ConstId(it) => { 264 ModuleDefId::ConstId(it) => {
265 let def = it.into(); 265 let def = it.into();
266 cb(def); 266 cb(def);
267 let body = db.body(def); 267 let body = db.body(def);
268 visit_scope(db, crate_def_map, &body.item_scope, cb); 268 visit_body(db, &body, cb);
269 } 269 }
270 ModuleDefId::StaticId(it) => { 270 ModuleDefId::StaticId(it) => {
271 let def = it.into(); 271 let def = it.into();
272 cb(def); 272 cb(def);
273 let body = db.body(def); 273 let body = db.body(def);
274 visit_scope(db, crate_def_map, &body.item_scope, cb); 274 visit_body(db, &body, cb);
275 } 275 }
276 ModuleDefId::TraitId(it) => { 276 ModuleDefId::TraitId(it) => {
277 let trait_data = db.trait_data(it); 277 let trait_data = db.trait_data(it);
@@ -288,6 +288,14 @@ fn visit_module(
288 } 288 }
289 } 289 }
290 } 290 }
291
292 fn visit_body(db: &TestDB, body: &Body, cb: &mut dyn FnMut(DefWithBodyId)) {
293 for def_map in body.block_scopes.iter().filter_map(|block| db.block_def_map(*block)) {
294 for (mod_id, _) in def_map.modules() {
295 visit_module(db, &def_map, mod_id, cb);
296 }
297 }
298 }
291} 299}
292 300
293fn ellipsize(mut text: String, max_len: usize) -> String { 301fn ellipsize(mut text: String, max_len: usize) -> String {
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 528092082..e185b1c0a 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3174,6 +3174,39 @@ fn f() {
3174} 3174}
3175 3175
3176#[test] 3176#[test]
3177fn trait_in_scope_with_inner_item() {
3178 check_infer(
3179 r#"
3180mod m {
3181 pub trait Tr {
3182 fn method(&self) -> u8 { 0 }
3183 }
3184
3185 impl Tr for () {}
3186}
3187
3188use m::Tr;
3189
3190fn f() {
3191 fn inner() {
3192 ().method();
3193 //^^^^^^^^^^^ u8
3194 }
3195}
3196 "#,
3197 expect![[r#"
3198 46..50 'self': &Self
3199 58..63 '{ 0 }': u8
3200 60..61 '0': u8
3201 115..185 '{ ... } }': ()
3202 132..183 '{ ... }': ()
3203 142..144 '()': ()
3204 142..153 '().method()': u8
3205 "#]],
3206 );
3207}
3208
3209#[test]
3177fn inner_use_in_block() { 3210fn inner_use_in_block() {
3178 check_types( 3211 check_types(
3179 r#" 3212 r#"
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs
index 4378a9723..565672b6b 100644
--- a/crates/hir_ty/src/traits/chalk.rs
+++ b/crates/hir_ty/src/traits/chalk.rs
@@ -424,7 +424,7 @@ pub(crate) fn trait_datum_query(
424 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 424 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
425 let flags = rust_ir::TraitFlags { 425 let flags = rust_ir::TraitFlags {
426 auto: trait_data.auto, 426 auto: trait_data.auto,
427 upstream: trait_.lookup(db.upcast()).container.module(db.upcast()).krate() != krate, 427 upstream: trait_.lookup(db.upcast()).container.krate() != krate,
428 non_enumerable: true, 428 non_enumerable: true,
429 coinductive: false, // only relevant for Chalk testing 429 coinductive: false, // only relevant for Chalk testing
430 // FIXME: set these flags correctly 430 // FIXME: set these flags correctly
@@ -548,7 +548,7 @@ fn impl_def_datum(
548 let generic_params = generics(db.upcast(), impl_id.into()); 548 let generic_params = generics(db.upcast(), impl_id.into());
549 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST); 549 let bound_vars = Substs::bound_vars(&generic_params, DebruijnIndex::INNERMOST);
550 let trait_ = trait_ref.trait_; 550 let trait_ = trait_ref.trait_;
551 let impl_type = if impl_id.lookup(db.upcast()).container.module(db.upcast()).krate() == krate { 551 let impl_type = if impl_id.lookup(db.upcast()).container.krate() == krate {
552 rust_ir::ImplType::Local 552 rust_ir::ImplType::Local
553 } else { 553 } else {
554 rust_ir::ImplType::External 554 rust_ir::ImplType::External
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs
index 65b79df0d..7351e4e54 100644
--- a/crates/hir_ty/src/utils.rs
+++ b/crates/hir_ty/src/utils.rs
@@ -259,6 +259,6 @@ fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option<Generic
259 match container { 259 match container {
260 AssocContainerId::ImplId(it) => Some(it.into()), 260 AssocContainerId::ImplId(it) => Some(it.into()),
261 AssocContainerId::TraitId(it) => Some(it.into()), 261 AssocContainerId::TraitId(it) => Some(it.into()),
262 AssocContainerId::ContainerId(_) => None, 262 AssocContainerId::ModuleId(_) => None,
263 } 263 }
264} 264}
diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml
index f7c5efaf3..107bd8432 100644
--- a/crates/ide/Cargo.toml
+++ b/crates/ide/Cargo.toml
@@ -27,7 +27,6 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
27ide_db = { path = "../ide_db", version = "0.0.0" } 27ide_db = { path = "../ide_db", version = "0.0.0" }
28cfg = { path = "../cfg", version = "0.0.0" } 28cfg = { path = "../cfg", version = "0.0.0" }
29profile = { path = "../profile", version = "0.0.0" } 29profile = { path = "../profile", version = "0.0.0" }
30test_utils = { path = "../test_utils", version = "0.0.0" }
31ide_assists = { path = "../ide_assists", version = "0.0.0" } 30ide_assists = { path = "../ide_assists", version = "0.0.0" }
32ide_ssr = { path = "../ide_ssr", version = "0.0.0" } 31ide_ssr = { path = "../ide_ssr", version = "0.0.0" }
33ide_completion = { path = "../ide_completion", version = "0.0.0" } 32ide_completion = { path = "../ide_completion", version = "0.0.0" }
@@ -37,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" }
37hir = { path = "../hir", version = "0.0.0" } 36hir = { path = "../hir", version = "0.0.0" }
38 37
39[dev-dependencies] 38[dev-dependencies]
39test_utils = { path = "../test_utils" }
40expect-test = "1.1" 40expect-test = "1.1"
diff --git a/crates/ide/src/display/short_label.rs b/crates/ide/src/display/short_label.rs
index 84b8883de..2df9266b4 100644
--- a/crates/ide/src/display/short_label.rs
+++ b/crates/ide/src/display/short_label.rs
@@ -71,11 +71,7 @@ impl ShortLabel for ast::TypeAlias {
71 71
72impl ShortLabel for ast::Const { 72impl ShortLabel for ast::Const {
73 fn short_label(&self) -> Option<String> { 73 fn short_label(&self) -> Option<String> {
74 let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; 74 short_label_from_ty(self, self.ty(), "const ")
75 if let Some(expr) = self.body() {
76 format_to!(new_buf, " = {}", expr.syntax());
77 }
78 Some(new_buf)
79 } 75 }
80} 76}
81 77
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 5d1cc2052..ea45086ce 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use hir::{ 2use hir::{
2 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, 3 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource,
3 HirDisplay, Module, ModuleDef, ModuleSource, Semantics, 4 HirDisplay, Module, ModuleDef, ModuleSource, Semantics,
@@ -366,7 +367,7 @@ fn hover_for_definition(
366 .and_then(|fd| hover_for_builtin(fd, it)) 367 .and_then(|fd| hover_for_builtin(fd, it))
367 .or_else(|| Some(Markup::fenced_block(&it.name()))), 368 .or_else(|| Some(Markup::fenced_block(&it.name()))),
368 }, 369 },
369 Definition::Local(it) => Some(Markup::fenced_block(&it.ty(db).display(db))), 370 Definition::Local(it) => hover_for_local(it, db),
370 Definition::SelfType(impl_def) => { 371 Definition::SelfType(impl_def) => {
371 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
372 Adt::Struct(it) => from_def_source(db, it, mod_path), 373 Adt::Struct(it) => from_def_source(db, it, mod_path),
@@ -405,6 +406,29 @@ fn hover_for_definition(
405 } 406 }
406} 407}
407 408
409fn hover_for_local(it: hir::Local, db: &RootDatabase) -> Option<Markup> {
410 let ty = it.ty(db);
411 let ty = ty.display(db);
412 let is_mut = if it.is_mut(db) { "mut " } else { "" };
413 let desc = match it.source(db).value {
414 Either::Left(ident) => {
415 let name = it.name(db).unwrap();
416 let let_kw = if ident
417 .syntax()
418 .parent()
419 .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
420 {
421 "let "
422 } else {
423 ""
424 };
425 format!("{}{}{}: {}", let_kw, is_mut, name, ty)
426 }
427 Either::Right(_) => format!("{}self: {}", is_mut, ty),
428 };
429 hover_markup(None, Some(desc), None)
430}
431
408fn hover_for_keyword( 432fn hover_for_keyword(
409 sema: &Semantics<RootDatabase>, 433 sema: &Semantics<RootDatabase>,
410 links_in_hover: bool, 434 links_in_hover: bool,
@@ -574,7 +598,7 @@ fn main() {
574 *iter* 598 *iter*
575 599
576 ```rust 600 ```rust
577 Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>> 601 let mut iter: Iter<Scan<OtherStruct<OtherStruct<i32>>, |&mut u32, &u32, &mut u32| -> Option<u32>, u32>>
578 ``` 602 ```
579 "#]], 603 "#]],
580 ); 604 );
@@ -798,7 +822,7 @@ fn main() {
798 ``` 822 ```
799 823
800 ```rust 824 ```rust
801 const foo: u32 = 123 825 const foo: u32
802 ``` 826 ```
803 "#]], 827 "#]],
804 ); 828 );
@@ -831,7 +855,7 @@ fn main() {
831 *zz* 855 *zz*
832 856
833 ```rust 857 ```rust
834 Test<i32, u8> 858 let zz: Test<i32, u8>
835 ``` 859 ```
836 "#]], 860 "#]],
837 ); 861 );
@@ -870,7 +894,7 @@ fn main() { let b$0ar = Some(12); }
870 *bar* 894 *bar*
871 895
872 ```rust 896 ```rust
873 Option<i32> 897 let bar: Option<i32>
874 ``` 898 ```
875 "#]], 899 "#]],
876 ); 900 );
@@ -938,7 +962,7 @@ fn main() {
938 *foo* 962 *foo*
939 963
940 ```rust 964 ```rust
941 i32 965 foo: i32
942 ``` 966 ```
943 "#]], 967 "#]],
944 ) 968 )
@@ -952,7 +976,7 @@ fn main() {
952 *foo* 976 *foo*
953 977
954 ```rust 978 ```rust
955 i32 979 foo: i32
956 ``` 980 ```
957 "#]], 981 "#]],
958 ) 982 )
@@ -966,7 +990,7 @@ fn main() {
966 *foo* 990 *foo*
967 991
968 ```rust 992 ```rust
969 i32 993 foo: i32
970 ``` 994 ```
971 "#]], 995 "#]],
972 ) 996 )
@@ -980,7 +1004,7 @@ fn main() {
980 *foo* 1004 *foo*
981 1005
982 ```rust 1006 ```rust
983 i32 1007 foo: i32
984 ``` 1008 ```
985 "#]], 1009 "#]],
986 ) 1010 )
@@ -1000,7 +1024,7 @@ fn main() {
1000 *_x* 1024 *_x*
1001 1025
1002 ```rust 1026 ```rust
1003 impl Deref<Target = u8> + DerefMut<Target = u8> 1027 _x: impl Deref<Target = u8> + DerefMut<Target = u8>
1004 ``` 1028 ```
1005 "#]], 1029 "#]],
1006 ) 1030 )
@@ -1022,7 +1046,7 @@ fn main() { let foo_$0test = Thing::new(); }
1022 *foo_test* 1046 *foo_test*
1023 1047
1024 ```rust 1048 ```rust
1025 Thing 1049 let foo_test: Thing
1026 ``` 1050 ```
1027 "#]], 1051 "#]],
1028 ) 1052 )
@@ -1081,7 +1105,7 @@ fn main() {
1081 ``` 1105 ```
1082 1106
1083 ```rust 1107 ```rust
1084 const C: u32 = 1 1108 const C: u32
1085 ``` 1109 ```
1086 "#]], 1110 "#]],
1087 ) 1111 )
@@ -1182,7 +1206,7 @@ fn y() {
1182 *x* 1206 *x*
1183 1207
1184 ```rust 1208 ```rust
1185 i32 1209 let x: i32
1186 ``` 1210 ```
1187 "#]], 1211 "#]],
1188 ) 1212 )
@@ -1259,7 +1283,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1259 *bar* 1283 *bar*
1260 1284
1261 ```rust 1285 ```rust
1262 u32 1286 bar: u32
1263 ``` 1287 ```
1264 "#]], 1288 "#]],
1265 ); 1289 );
@@ -1277,7 +1301,7 @@ fn foo(bar:u32) { let a = id!(ba$0r); }
1277 *bar* 1301 *bar*
1278 1302
1279 ```rust 1303 ```rust
1280 u32 1304 bar: u32
1281 ``` 1305 ```
1282 "#]], 1306 "#]],
1283 ); 1307 );
@@ -3302,7 +3326,7 @@ fn main() {
3302 *f* 3326 *f*
3303 3327
3304 ```rust 3328 ```rust
3305 &i32 3329 f: &i32
3306 ``` 3330 ```
3307 "#]], 3331 "#]],
3308 ); 3332 );
@@ -3321,7 +3345,7 @@ impl Foo {
3321 *self* 3345 *self*
3322 3346
3323 ```rust 3347 ```rust
3324 &Foo 3348 self: &Foo
3325 ``` 3349 ```
3326 "#]], 3350 "#]],
3327 ); 3351 );
@@ -3341,7 +3365,7 @@ impl Foo {
3341 *self* 3365 *self*
3342 3366
3343 ```rust 3367 ```rust
3344 Arc<Foo> 3368 self: Arc<Foo>
3345 ``` 3369 ```
3346 "#]], 3370 "#]],
3347 ); 3371 );
@@ -3537,7 +3561,7 @@ fn foo() {
3537 ``` 3561 ```
3538 3562
3539 ```rust 3563 ```rust
3540 const FOO: usize = 3 3564 const FOO: usize
3541 ``` 3565 ```
3542 3566
3543 --- 3567 ---
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index f83ed65d5..0a493d2f3 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -41,6 +41,7 @@ mod parent_module;
41mod references; 41mod references;
42mod fn_references; 42mod fn_references;
43mod runnables; 43mod runnables;
44mod ssr;
44mod status; 45mod status;
45mod syntax_highlighting; 46mod syntax_highlighting;
46mod syntax_tree; 47mod syntax_tree;
@@ -51,6 +52,7 @@ mod doc_links;
51use std::sync::Arc; 52use std::sync::Arc;
52 53
53use cfg::CfgOptions; 54use cfg::CfgOptions;
55
54use ide_db::base_db::{ 56use ide_db::base_db::{
55 salsa::{self, ParallelDatabase}, 57 salsa::{self, ParallelDatabase},
56 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath, 58 CheckCanceled, Env, FileLoader, FileSet, SourceDatabase, VfsPath,
@@ -85,7 +87,7 @@ pub use crate::{
85pub use hir::{Documentation, Semantics}; 87pub use hir::{Documentation, Semantics};
86pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; 88pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind};
87pub use ide_completion::{ 89pub use ide_completion::{
88 CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, 90 CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit,
89 InsertTextFormat, 91 InsertTextFormat,
90}; 92};
91pub use ide_db::{ 93pub use ide_db::{
@@ -502,7 +504,11 @@ impl Analysis {
502 resolve: bool, 504 resolve: bool,
503 frange: FileRange, 505 frange: FileRange,
504 ) -> Cancelable<Vec<Assist>> { 506 ) -> Cancelable<Vec<Assist>> {
505 self.with_db(|db| Assist::get(db, config, resolve, frange)) 507 self.with_db(|db| {
508 let mut acc = Assist::get(db, config, resolve, frange);
509 ssr::add_ssr_assist(db, &mut acc, resolve, frange);
510 acc
511 })
506 } 512 }
507 513
508 /// Computes the set of diagnostics for the given file. 514 /// Computes the set of diagnostics for the given file.
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 05c73de88..1e378279d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -21,7 +21,7 @@ use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceCh
21 21
22type RenameResult<T> = Result<T, RenameError>; 22type RenameResult<T> = Result<T, RenameError>;
23#[derive(Debug)] 23#[derive(Debug)]
24pub struct RenameError(pub(crate) String); 24pub struct RenameError(String);
25 25
26impl fmt::Display for RenameError { 26impl fmt::Display for RenameError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -47,16 +47,15 @@ pub(crate) fn prepare_rename(
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
48 let source_file = sema.parse(position.file_id); 48 let source_file = sema.parse(position.file_id);
49 let syntax = source_file.syntax(); 49 let syntax = source_file.syntax();
50 let range = match &sema 50 let name_like = sema
51 .find_node_at_offset_with_descend(&syntax, position.offset) 51 .find_node_at_offset_with_descend(&syntax, position.offset)
52 .ok_or_else(|| format_err!("No references found at position"))? 52 .ok_or_else(|| format_err!("No references found at position"))?;
53 { 53 let node = match &name_like {
54 ast::NameLike::Name(it) => it.syntax(), 54 ast::NameLike::Name(it) => it.syntax(),
55 ast::NameLike::NameRef(it) => it.syntax(), 55 ast::NameLike::NameRef(it) => it.syntax(),
56 ast::NameLike::Lifetime(it) => it.syntax(), 56 ast::NameLike::Lifetime(it) => it.syntax(),
57 } 57 };
58 .text_range(); 58 Ok(RangeInfo::new(sema.original_range(node).range, ()))
59 Ok(RangeInfo::new(range, ()))
60} 59}
61 60
62// Feature: Rename 61// Feature: Rename
@@ -94,6 +93,7 @@ pub(crate) fn rename_with_semantics(
94 } 93 }
95} 94}
96 95
96/// Called by the client when it is about to rename a file.
97pub(crate) fn will_rename_file( 97pub(crate) fn will_rename_file(
98 db: &RootDatabase, 98 db: &RootDatabase,
99 file_id: FileId, 99 file_id: FileId,
@@ -545,6 +545,8 @@ mod tests {
545 545
546 use crate::{fixture, FileId}; 546 use crate::{fixture, FileId};
547 547
548 use super::{RangeInfo, RenameError};
549
548 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 550 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
549 let ra_fixture_after = &trim_indent(ra_fixture_after); 551 let ra_fixture_after = &trim_indent(ra_fixture_after);
550 let (analysis, position) = fixture::position(ra_fixture_before); 552 let (analysis, position) = fixture::position(ra_fixture_before);
@@ -590,6 +592,45 @@ mod tests {
590 expect.assert_debug_eq(&source_change) 592 expect.assert_debug_eq(&source_change)
591 } 593 }
592 594
595 fn check_prepare(ra_fixture: &str, expect: Expect) {
596 let (analysis, position) = fixture::position(ra_fixture);
597 let result = analysis
598 .prepare_rename(position)
599 .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err));
600 match result {
601 Ok(RangeInfo { range, info: () }) => {
602 let source = analysis.file_text(position.file_id).unwrap();
603 expect.assert_eq(&format!("{:?}: {}", range, &source[range]))
604 }
605 Err(RenameError(err)) => expect.assert_eq(&err),
606 };
607 }
608
609 #[test]
610 fn test_prepare_rename_namelikes() {
611 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
612 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
613 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]);
614 }
615
616 #[test]
617 fn test_prepare_rename_in_macro() {
618 check_prepare(
619 r"macro_rules! foo {
620 ($ident:ident) => {
621 pub struct $ident;
622 }
623}
624foo!(Foo$0);",
625 expect![[r#"83..86: Foo"#]],
626 );
627 }
628
629 #[test]
630 fn test_prepare_rename_keyword() {
631 check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
632 }
633
593 #[test] 634 #[test]
594 fn test_rename_to_underscore() { 635 fn test_rename_to_underscore() {
595 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); 636 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
diff --git a/crates/ide/src/ssr.rs b/crates/ide/src/ssr.rs
new file mode 100644
index 000000000..f3638d928
--- /dev/null
+++ b/crates/ide/src/ssr.rs
@@ -0,0 +1,259 @@
1//! This module provides an SSR assist. It is not desirable to include this
2//! assist in ide_assists because that would require the ide_assists crate
3//! depend on the ide_ssr crate.
4
5use ide_assists::{Assist, AssistId, AssistKind, GroupLabel};
6use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase};
7
8pub(crate) fn add_ssr_assist(
9 db: &RootDatabase,
10 base: &mut Vec<Assist>,
11 resolve: bool,
12 frange: FileRange,
13) -> Option<()> {
14 let (match_finder, comment_range) = ide_ssr::ssr_from_comment(db, frange)?;
15
16 let (source_change_for_file, source_change_for_workspace) = if resolve {
17 let edits = match_finder.edits();
18
19 let source_change_for_file = {
20 let text_edit_for_file = edits.get(&frange.file_id).cloned().unwrap_or_default();
21 SourceChange::from_text_edit(frange.file_id, text_edit_for_file)
22 };
23
24 let source_change_for_workspace = SourceChange::from(match_finder.edits());
25
26 (Some(source_change_for_file), Some(source_change_for_workspace))
27 } else {
28 (None, None)
29 };
30
31 let assists = vec![
32 ("Apply SSR in file", source_change_for_file),
33 ("Apply SSR in workspace", source_change_for_workspace),
34 ];
35
36 for (label, source_change) in assists.into_iter() {
37 let assist = Assist {
38 id: AssistId("ssr", AssistKind::RefactorRewrite),
39 label: Label::new(label),
40 group: Some(GroupLabel("Apply SSR".into())),
41 target: comment_range,
42 source_change,
43 };
44
45 base.push(assist);
46 }
47 Some(())
48}
49
50#[cfg(test)]
51mod tests {
52 use std::sync::Arc;
53
54 use expect_test::expect;
55 use ide_assists::Assist;
56 use ide_db::{
57 base_db::{fixture::WithFixture, salsa::Durability, FileRange},
58 symbol_index::SymbolsDatabase,
59 RootDatabase,
60 };
61 use rustc_hash::FxHashSet;
62
63 use super::add_ssr_assist;
64
65 fn get_assists(ra_fixture: &str, resolve: bool) -> Vec<Assist> {
66 let (mut db, file_id, range_or_offset) = RootDatabase::with_range_or_offset(ra_fixture);
67 let mut local_roots = FxHashSet::default();
68 local_roots.insert(ide_db::base_db::fixture::WORKSPACE);
69 db.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
70
71 let mut assists = vec![];
72
73 add_ssr_assist(
74 &db,
75 &mut assists,
76 resolve,
77 FileRange { file_id, range: range_or_offset.into() },
78 );
79
80 assists
81 }
82
83 #[test]
84 fn not_applicable_comment_not_ssr() {
85 let ra_fixture = r#"
86 //- /lib.rs
87
88 // This is foo $0
89 fn foo() {}
90 "#;
91 let resolve = true;
92
93 let assists = get_assists(ra_fixture, resolve);
94
95 assert_eq!(0, assists.len());
96 }
97
98 #[test]
99 fn resolve_edits_true() {
100 let resolve = true;
101 let assists = get_assists(
102 r#"
103 //- /lib.rs
104 mod bar;
105
106 // 2 ==>> 3$0
107 fn foo() { 2 }
108
109 //- /bar.rs
110 fn bar() { 2 }
111 "#,
112 resolve,
113 );
114
115 assert_eq!(2, assists.len());
116 let mut assists = assists.into_iter();
117
118 let apply_in_file_assist = assists.next().unwrap();
119 expect![[r#"
120 Assist {
121 id: AssistId(
122 "ssr",
123 RefactorRewrite,
124 ),
125 label: "Apply SSR in file",
126 group: Some(
127 GroupLabel(
128 "Apply SSR",
129 ),
130 ),
131 target: 10..21,
132 source_change: Some(
133 SourceChange {
134 source_file_edits: {
135 FileId(
136 0,
137 ): TextEdit {
138 indels: [
139 Indel {
140 insert: "3",
141 delete: 33..34,
142 },
143 ],
144 },
145 },
146 file_system_edits: [],
147 is_snippet: false,
148 },
149 ),
150 }
151 "#]]
152 .assert_debug_eq(&apply_in_file_assist);
153
154 let apply_in_workspace_assist = assists.next().unwrap();
155 expect![[r#"
156 Assist {
157 id: AssistId(
158 "ssr",
159 RefactorRewrite,
160 ),
161 label: "Apply SSR in workspace",
162 group: Some(
163 GroupLabel(
164 "Apply SSR",
165 ),
166 ),
167 target: 10..21,
168 source_change: Some(
169 SourceChange {
170 source_file_edits: {
171 FileId(
172 0,
173 ): TextEdit {
174 indels: [
175 Indel {
176 insert: "3",
177 delete: 33..34,
178 },
179 ],
180 },
181 FileId(
182 1,
183 ): TextEdit {
184 indels: [
185 Indel {
186 insert: "3",
187 delete: 11..12,
188 },
189 ],
190 },
191 },
192 file_system_edits: [],
193 is_snippet: false,
194 },
195 ),
196 }
197 "#]]
198 .assert_debug_eq(&apply_in_workspace_assist);
199 }
200
201 #[test]
202 fn resolve_edits_false() {
203 let resolve = false;
204 let assists = get_assists(
205 r#"
206 //- /lib.rs
207 mod bar;
208
209 // 2 ==>> 3$0
210 fn foo() { 2 }
211
212 //- /bar.rs
213 fn bar() { 2 }
214 "#,
215 resolve,
216 );
217
218 assert_eq!(2, assists.len());
219 let mut assists = assists.into_iter();
220
221 let apply_in_file_assist = assists.next().unwrap();
222 expect![[r#"
223 Assist {
224 id: AssistId(
225 "ssr",
226 RefactorRewrite,
227 ),
228 label: "Apply SSR in file",
229 group: Some(
230 GroupLabel(
231 "Apply SSR",
232 ),
233 ),
234 target: 10..21,
235 source_change: None,
236 }
237 "#]]
238 .assert_debug_eq(&apply_in_file_assist);
239
240 let apply_in_workspace_assist = assists.next().unwrap();
241 expect![[r#"
242 Assist {
243 id: AssistId(
244 "ssr",
245 RefactorRewrite,
246 ),
247 label: "Apply SSR in workspace",
248 group: Some(
249 GroupLabel(
250 "Apply SSR",
251 ),
252 ),
253 target: 10..21,
254 source_change: None,
255 }
256 "#]]
257 .assert_debug_eq(&apply_in_workspace_assist);
258 }
259}
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index 24fcbb584..b0cfdd8b7 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -330,10 +330,11 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
330 HlTag::Symbol(SymbolKind::Local) 330 HlTag::Symbol(SymbolKind::Local)
331 }; 331 };
332 let mut h = Highlight::new(tag); 332 let mut h = Highlight::new(tag);
333 if local.is_mut(db) || local.ty(db).is_mutable_reference() { 333 let ty = local.ty(db);
334 if local.is_mut(db) || ty.is_mutable_reference() {
334 h |= HlMod::Mutable; 335 h |= HlMod::Mutable;
335 } 336 }
336 if local.ty(db).as_callable(db).is_some() || local.ty(db).impls_fnonce(db) { 337 if ty.as_callable(db).is_some() || ty.impls_fnonce(db) {
337 h |= HlMod::Callable; 338 h |= HlMod::Callable;
338 } 339 }
339 return h; 340 return h;
diff --git a/crates/ide_assists/Cargo.toml b/crates/ide_assists/Cargo.toml
index 3bf0099a9..dd9aa27c6 100644
--- a/crates/ide_assists/Cargo.toml
+++ b/crates/ide_assists/Cargo.toml
@@ -21,7 +21,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
21profile = { path = "../profile", version = "0.0.0" } 21profile = { path = "../profile", version = "0.0.0" }
22ide_db = { path = "../ide_db", version = "0.0.0" } 22ide_db = { path = "../ide_db", version = "0.0.0" }
23hir = { path = "../hir", version = "0.0.0" } 23hir = { path = "../hir", version = "0.0.0" }
24test_utils = { path = "../test_utils", version = "0.0.0" }
25 24
26[dev-dependencies] 25[dev-dependencies]
26test_utils = { path = "../test_utils" }
27expect-test = "1.1" 27expect-test = "1.1"
diff --git a/crates/ide_assists/src/handlers/add_turbo_fish.rs b/crates/ide_assists/src/handlers/add_turbo_fish.rs
index 3b6efbab4..ee879c151 100644
--- a/crates/ide_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ide_assists/src/handlers/add_turbo_fish.rs
@@ -56,13 +56,20 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
56 if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() { 56 if let Some(let_stmt) = ctx.find_node_at_offset::<ast::LetStmt>() {
57 if let_stmt.colon_token().is_none() { 57 if let_stmt.colon_token().is_none() {
58 let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end(); 58 let type_pos = let_stmt.pat()?.syntax().last_token()?.text_range().end();
59 let semi_pos = let_stmt.syntax().last_token()?.text_range().end();
60
59 acc.add( 61 acc.add(
60 AssistId("add_type_ascription", AssistKind::RefactorRewrite), 62 AssistId("add_type_ascription", AssistKind::RefactorRewrite),
61 "Add `: _` before assignment operator", 63 "Add `: _` before assignment operator",
62 ident.text_range(), 64 ident.text_range(),
63 |builder| match ctx.config.snippet_cap { 65 |builder| {
64 Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"), 66 if let_stmt.semicolon_token().is_none() {
65 None => builder.insert(type_pos, ": _"), 67 builder.insert(semi_pos, ";");
68 }
69 match ctx.config.snippet_cap {
70 Some(cap) => builder.insert_snippet(cap, type_pos, ": ${0:_}"),
71 None => builder.insert(type_pos, ": _"),
72 }
66 }, 73 },
67 )? 74 )?
68 } else { 75 } else {
@@ -265,4 +272,24 @@ fn main() {
265"#, 272"#,
266 ); 273 );
267 } 274 }
275
276 #[test]
277 fn add_type_ascription_append_semicolon() {
278 check_assist_by_label(
279 add_turbo_fish,
280 r#"
281fn make<T>() -> T {}
282fn main() {
283 let x = make$0()
284}
285"#,
286 r#"
287fn make<T>() -> T {}
288fn main() {
289 let x: ${0:_} = make();
290}
291"#,
292 "Add `: _` before assignment operator",
293 );
294 }
268} 295}
diff --git a/crates/ide_assists/src/handlers/auto_import.rs b/crates/ide_assists/src/handlers/auto_import.rs
index 7caee8df0..7019039b9 100644
--- a/crates/ide_assists/src/handlers/auto_import.rs
+++ b/crates/ide_assists/src/handlers/auto_import.rs
@@ -1,7 +1,7 @@
1use ide_db::helpers::{ 1use ide_db::helpers::{
2 import_assets::{ImportAssets, ImportCandidate}, 2 import_assets::{ImportAssets, ImportCandidate},
3 insert_use::{insert_use, ImportScope}, 3 insert_use::{insert_use, ImportScope},
4 item_name, mod_path_to_ast, 4 mod_path_to_ast,
5}; 5};
6use syntax::{ast, AstNode, SyntaxNode}; 6use syntax::{ast, AstNode, SyntaxNode};
7 7
@@ -90,17 +90,13 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
90 } 90 }
91 91
92 let range = ctx.sema.original_range(&syntax_under_caret).range; 92 let range = ctx.sema.original_range(&syntax_under_caret).range;
93 let group = import_group_message(import_assets.import_candidate()); 93 let group_label = group_label(import_assets.import_candidate());
94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?; 94 let scope = ImportScope::find_insert_use_container(&syntax_under_caret, &ctx.sema)?;
95 for import in proposed_imports { 95 for import in proposed_imports {
96 let name = match item_name(ctx.db(), import.original_item) {
97 Some(name) => name,
98 None => continue,
99 };
100 acc.add_group( 96 acc.add_group(
101 &group, 97 &group_label,
102 AssistId("auto_import", AssistKind::QuickFix), 98 AssistId("auto_import", AssistKind::QuickFix),
103 format!("Import `{}`", name), 99 format!("Import `{}`", import.import_path),
104 range, 100 range,
105 |builder| { 101 |builder| {
106 let rewriter = 102 let rewriter =
@@ -126,7 +122,7 @@ pub(super) fn find_importable_node(ctx: &AssistContext) -> Option<(ImportAssets,
126 } 122 }
127} 123}
128 124
129fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { 125fn group_label(import_candidate: &ImportCandidate) -> GroupLabel {
130 let name = match import_candidate { 126 let name = match import_candidate {
131 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()), 127 ImportCandidate::Path(candidate) => format!("Import {}", candidate.name.text()),
132 ImportCandidate::TraitAssocItem(candidate) => { 128 ImportCandidate::TraitAssocItem(candidate) => {
diff --git a/crates/ide_assists/src/handlers/qualify_path.rs b/crates/ide_assists/src/handlers/qualify_path.rs
index 272874ae3..30b23da6c 100644
--- a/crates/ide_assists/src/handlers/qualify_path.rs
+++ b/crates/ide_assists/src/handlers/qualify_path.rs
@@ -3,7 +3,7 @@ use std::iter;
3use hir::AsAssocItem; 3use hir::AsAssocItem;
4use ide_db::helpers::{ 4use ide_db::helpers::{
5 import_assets::{ImportCandidate, LocatedImport}, 5 import_assets::{ImportCandidate, LocatedImport},
6 item_name, mod_path_to_ast, 6 mod_path_to_ast,
7}; 7};
8use ide_db::RootDatabase; 8use ide_db::RootDatabase;
9use syntax::{ 9use syntax::{
@@ -78,7 +78,7 @@ pub(crate) fn qualify_path(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
78 acc.add_group( 78 acc.add_group(
79 &group_label, 79 &group_label,
80 AssistId("qualify_path", AssistKind::QuickFix), 80 AssistId("qualify_path", AssistKind::QuickFix),
81 label(ctx.db(), candidate, &import), 81 label(candidate, &import),
82 range, 82 range,
83 |builder| { 83 |builder| {
84 qualify_candidate.qualify( 84 qualify_candidate.qualify(
@@ -199,21 +199,17 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel {
199 GroupLabel(format!("Qualify {}", name)) 199 GroupLabel(format!("Qualify {}", name))
200} 200}
201 201
202fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String { 202fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String {
203 let display_path = match item_name(db, import.original_item) {
204 Some(display_path) => display_path.to_string(),
205 None => "{unknown}".to_string(),
206 };
207 match candidate { 203 match candidate {
208 ImportCandidate::Path(candidate) => { 204 ImportCandidate::Path(candidate) => {
209 if candidate.qualifier.is_some() { 205 if candidate.qualifier.is_some() {
210 format!("Qualify with `{}`", display_path) 206 format!("Qualify with `{}`", import.import_path)
211 } else { 207 } else {
212 format!("Qualify as `{}`", display_path) 208 format!("Qualify as `{}`", import.import_path)
213 } 209 }
214 } 210 }
215 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", display_path), 211 ImportCandidate::TraitAssocItem(_) => format!("Qualify `{}`", import.import_path),
216 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", display_path), 212 ImportCandidate::TraitMethod(_) => format!("Qualify with cast as `{}`", import.import_path),
217 } 213 }
218} 214}
219 215
diff --git a/crates/ide_completion/Cargo.toml b/crates/ide_completion/Cargo.toml
index 84aa40736..585ecca50 100644
--- a/crates/ide_completion/Cargo.toml
+++ b/crates/ide_completion/Cargo.toml
@@ -22,11 +22,11 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
22base_db = { path = "../base_db", version = "0.0.0" } 22base_db = { path = "../base_db", version = "0.0.0" }
23ide_db = { path = "../ide_db", version = "0.0.0" } 23ide_db = { path = "../ide_db", version = "0.0.0" }
24profile = { path = "../profile", version = "0.0.0" } 24profile = { path = "../profile", version = "0.0.0" }
25test_utils = { path = "../test_utils", version = "0.0.0" }
26 25
27# completions crate should depend only on the top-level `hir` package. if you need 26# completions crate should depend only on the top-level `hir` package. if you need
28# something from some `hir_xxx` subpackage, reexport the API via `hir`. 27# something from some `hir_xxx` subpackage, reexport the API via `hir`.
29hir = { path = "../hir", version = "0.0.0" } 28hir = { path = "../hir", version = "0.0.0" }
30 29
31[dev-dependencies] 30[dev-dependencies]
31test_utils = { path = "../test_utils" }
32expect-test = "1.1" 32expect-test = "1.1"
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index cb05e85fc..e846678b4 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -45,15 +45,15 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
45 CompletionKind::Attribute, 45 CompletionKind::Attribute,
46 ctx.source_range(), 46 ctx.source_range(),
47 attr_completion.label, 47 attr_completion.label,
48 ) 48 );
49 .kind(CompletionItemKind::Attribute); 49 item.kind(CompletionItemKind::Attribute);
50 50
51 if let Some(lookup) = attr_completion.lookup { 51 if let Some(lookup) = attr_completion.lookup {
52 item = item.lookup_by(lookup); 52 item.lookup_by(lookup);
53 } 53 }
54 54
55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) { 55 if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
56 item = item.insert_snippet(cap, snippet); 56 item.insert_snippet(cap, snippet);
57 } 57 }
58 58
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner {
@@ -168,16 +168,20 @@ fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input:
168 ); 168 );
169 let lookup = components.join(", "); 169 let lookup = components.join(", ");
170 let label = components.iter().rev().join(", "); 170 let label = components.iter().rev().join(", ");
171 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label) 171 let mut item =
172 .lookup_by(lookup) 172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 .kind(CompletionItemKind::Attribute) 173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 .add_to(acc) 174 item.add_to(acc);
175 } 175 }
176 176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) { 177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), custom_derive_name) 178 let mut item = CompletionItem::new(
179 .kind(CompletionItemKind::Attribute) 179 CompletionKind::Attribute,
180 .add_to(acc) 180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
181 } 185 }
182 } 186 }
183} 187}
@@ -193,14 +197,13 @@ fn complete_lint(
193 .into_iter() 197 .into_iter()
194 .filter(|completion| !existing_lints.contains(completion.label)) 198 .filter(|completion| !existing_lints.contains(completion.label))
195 { 199 {
196 CompletionItem::new( 200 let mut item = CompletionItem::new(
197 CompletionKind::Attribute, 201 CompletionKind::Attribute,
198 ctx.source_range(), 202 ctx.source_range(),
199 lint_completion.label, 203 lint_completion.label,
200 ) 204 );
201 .kind(CompletionItemKind::Attribute) 205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
202 .detail(lint_completion.description) 206 item.add_to(acc)
203 .add_to(acc)
204 } 207 }
205 } 208 }
206} 209}
diff --git a/crates/ide_completion/src/completions/fn_param.rs b/crates/ide_completion/src/completions/fn_param.rs
index 1bcc8727f..0243dce56 100644
--- a/crates/ide_completion/src/completions/fn_param.rs
+++ b/crates/ide_completion/src/completions/fn_param.rs
@@ -54,10 +54,9 @@ pub(crate) fn complete_fn_param(acc: &mut Completions, ctx: &CompletionContext)
54 } 54 }
55 55
56 params.into_iter().for_each(|(label, lookup)| { 56 params.into_iter().for_each(|(label, lookup)| {
57 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 57 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
58 .kind(CompletionItemKind::Binding) 58 item.kind(CompletionItemKind::Binding).lookup_by(lookup);
59 .lookup_by(lookup) 59 item.add_to(acc)
60 .add_to(acc)
61 }); 60 });
62} 61}
63 62
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 80aa9fb06..b635e0ca3 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -12,21 +12,19 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
12 12
13 if ctx.use_item_syntax.is_some() { 13 if ctx.use_item_syntax.is_some() {
14 if ctx.path_qual.is_none() { 14 if ctx.path_qual.is_none() {
15 CompletionItem::new(CompletionKind::Keyword, source_range, "crate::") 15 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "crate::");
16 .kind(CompletionItemKind::Keyword) 16 item.kind(CompletionItemKind::Keyword).insert_text("crate::");
17 .insert_text("crate::") 17 item.add_to(acc);
18 .add_to(acc);
19 } 18 }
20 CompletionItem::new(CompletionKind::Keyword, source_range, "self") 19 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "self");
21 .kind(CompletionItemKind::Keyword) 20 item.kind(CompletionItemKind::Keyword);
22 .add_to(acc); 21 item.add_to(acc);
23 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier()) 22 if iter::successors(ctx.path_qual.clone(), |p| p.qualifier())
24 .all(|p| p.segment().and_then(|s| s.super_token()).is_some()) 23 .all(|p| p.segment().and_then(|s| s.super_token()).is_some())
25 { 24 {
26 CompletionItem::new(CompletionKind::Keyword, source_range, "super::") 25 let mut item = CompletionItem::new(CompletionKind::Keyword, source_range, "super::");
27 .kind(CompletionItemKind::Keyword) 26 item.kind(CompletionItemKind::Keyword).insert_text("super::");
28 .insert_text("super::") 27 item.add_to(acc);
29 .add_to(acc);
30 } 28 }
31 } 29 }
32 30
@@ -34,11 +32,10 @@ pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
34 if let Some(receiver) = &ctx.dot_receiver { 32 if let Some(receiver) = &ctx.dot_receiver {
35 if let Some(ty) = ctx.sema.type_of_expr(receiver) { 33 if let Some(ty) = ctx.sema.type_of_expr(receiver) {
36 if ty.impls_future(ctx.db) { 34 if ty.impls_future(ctx.db) {
37 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await") 35 let mut item =
38 .kind(CompletionItemKind::Keyword) 36 CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await");
39 .detail("expr.await") 37 item.kind(CompletionItemKind::Keyword).detail("expr.await").insert_text("await");
40 .insert_text("await") 38 item.add_to(acc);
41 .add_to(acc);
42 } 39 }
43 }; 40 };
44 } 41 }
@@ -165,9 +162,10 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
165} 162}
166 163
167fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { 164fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
168 let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 165 let mut item = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw);
169 .kind(CompletionItemKind::Keyword); 166 item.kind(CompletionItemKind::Keyword);
170 let builder = match ctx.config.snippet_cap { 167
168 match ctx.config.snippet_cap {
171 Some(cap) => { 169 Some(cap) => {
172 let tmp; 170 let tmp;
173 let snippet = if snippet.ends_with('}') && ctx.incomplete_let { 171 let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
@@ -177,11 +175,13 @@ fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet
177 } else { 175 } else {
178 snippet 176 snippet
179 }; 177 };
180 builder.insert_snippet(cap, snippet) 178 item.insert_snippet(cap, snippet);
179 }
180 None => {
181 item.insert_text(if snippet.contains('$') { kw } else { snippet });
181 } 182 }
182 None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
183 }; 183 };
184 acc.add(builder.build()); 184 item.add_to(acc);
185} 185}
186 186
187#[cfg(test)] 187#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/mod_.rs b/crates/ide_completion/src/completions/mod_.rs
index 352fc7c77..4f9415736 100644
--- a/crates/ide_completion/src/completions/mod_.rs
+++ b/crates/ide_completion/src/completions/mod_.rs
@@ -80,9 +80,9 @@ pub(crate) fn complete_mod(acc: &mut Completions, ctx: &CompletionContext) -> Op
80 if mod_under_caret.semicolon_token().is_none() { 80 if mod_under_caret.semicolon_token().is_none() {
81 label.push(';'); 81 label.push(';');
82 } 82 }
83 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label) 83 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), &label);
84 .kind(SymbolKind::Module) 84 item.kind(SymbolKind::Module);
85 .add_to(acc) 85 item.add_to(acc)
86 }); 86 });
87 87
88 Some(()) 88 Some(())
diff --git a/crates/ide_completion/src/completions/postfix.rs b/crates/ide_completion/src/completions/postfix.rs
index d45ad7944..ac69b720a 100644
--- a/crates/ide_completion/src/completions/postfix.rs
+++ b/crates/ide_completion/src/completions/postfix.rs
@@ -297,10 +297,9 @@ fn postfix_snippet(
297 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end()); 297 let delete_range = TextRange::new(receiver_range.start(), ctx.source_range().end());
298 TextEdit::replace(delete_range, snippet.to_string()) 298 TextEdit::replace(delete_range, snippet.to_string())
299 }; 299 };
300 CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label) 300 let mut item = CompletionItem::new(CompletionKind::Postfix, ctx.source_range(), label);
301 .detail(detail) 301 item.detail(detail).kind(CompletionItemKind::Snippet).snippet_edit(cap, edit);
302 .kind(CompletionItemKind::Snippet) 302 item
303 .snippet_edit(cap, edit)
304} 303}
305 304
306#[cfg(test)] 305#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/record.rs b/crates/ide_completion/src/completions/record.rs
index 0a7927eb8..2f95b8687 100644
--- a/crates/ide_completion/src/completions/record.rs
+++ b/crates/ide_completion/src/completions/record.rs
@@ -22,16 +22,13 @@ pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) ->
22 let completion_text = completion_text 22 let completion_text = completion_text
23 .strip_prefix(ctx.token.to_string().as_str()) 23 .strip_prefix(ctx.token.to_string().as_str())
24 .unwrap_or(completion_text); 24 .unwrap_or(completion_text);
25 acc.add( 25 let mut item = CompletionItem::new(
26 CompletionItem::new( 26 CompletionKind::Snippet,
27 CompletionKind::Snippet, 27 ctx.source_range(),
28 ctx.source_range(), 28 "..Default::default()",
29 "..Default::default()",
30 )
31 .insert_text(completion_text)
32 .kind(SymbolKind::Field)
33 .build(),
34 ); 29 );
30 item.insert_text(completion_text).kind(SymbolKind::Field);
31 item.add_to(acc);
35 } 32 }
36 33
37 missing_fields 34 missing_fields
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index df17a15c5..7f7830976 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -8,9 +8,9 @@ use crate::{
8}; 8};
9 9
10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { 10fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder {
11 CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label) 11 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), label);
12 .insert_snippet(cap, snippet) 12 item.insert_snippet(cap, snippet).kind(CompletionItemKind::Snippet);
13 .kind(CompletionItemKind::Snippet) 13 item
14} 14}
15 15
16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 16pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
@@ -35,7 +35,7 @@ pub(crate) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte
35 None => return, 35 None => return,
36 }; 36 };
37 37
38 snippet( 38 let mut item = snippet(
39 ctx, 39 ctx,
40 cap, 40 cap,
41 "tmod (Test module)", 41 "tmod (Test module)",
@@ -49,11 +49,11 @@ mod tests {
49 $0 49 $0
50 } 50 }
51}", 51}",
52 ) 52 );
53 .lookup_by("tmod") 53 item.lookup_by("tmod");
54 .add_to(acc); 54 item.add_to(acc);
55 55
56 snippet( 56 let mut item = snippet(
57 ctx, 57 ctx,
58 cap, 58 cap,
59 "tfn (Test function)", 59 "tfn (Test function)",
@@ -62,11 +62,12 @@ mod tests {
62fn ${1:feature}() { 62fn ${1:feature}() {
63 $0 63 $0
64}", 64}",
65 ) 65 );
66 .lookup_by("tfn") 66 item.lookup_by("tfn");
67 .add_to(acc); 67 item.add_to(acc);
68 68
69 snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}").add_to(acc); 69 let item = snippet(ctx, cap, "macro_rules", "macro_rules! $1 {\n\t($2) => {\n\t\t$0\n\t};\n}");
70 item.add_to(acc);
70} 71}
71 72
72#[cfg(test)] 73#[cfg(test)]
diff --git a/crates/ide_completion/src/completions/trait_impl.rs b/crates/ide_completion/src/completions/trait_impl.rs
index b999540b8..5a7361f8e 100644
--- a/crates/ide_completion/src/completions/trait_impl.rs
+++ b/crates/ide_completion/src/completions/trait_impl.rs
@@ -145,9 +145,8 @@ fn add_function_impl(
145 format!("fn {}(..)", fn_name) 145 format!("fn {}(..)", fn_name)
146 }; 146 };
147 147
148 let builder = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label) 148 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), label);
149 .lookup_by(fn_name) 149 item.lookup_by(fn_name).set_documentation(func.docs(ctx.db));
150 .set_documentation(func.docs(ctx.db));
151 150
152 let completion_kind = if func.self_param(ctx.db).is_some() { 151 let completion_kind = if func.self_param(ctx.db).is_some() {
153 CompletionItemKind::Method 152 CompletionItemKind::Method
@@ -161,15 +160,15 @@ fn add_function_impl(
161 match ctx.config.snippet_cap { 160 match ctx.config.snippet_cap {
162 Some(cap) => { 161 Some(cap) => {
163 let snippet = format!("{} {{\n $0\n}}", function_decl); 162 let snippet = format!("{} {{\n $0\n}}", function_decl);
164 builder.snippet_edit(cap, TextEdit::replace(range, snippet)) 163 item.snippet_edit(cap, TextEdit::replace(range, snippet));
165 } 164 }
166 None => { 165 None => {
167 let header = format!("{} {{", function_decl); 166 let header = format!("{} {{", function_decl);
168 builder.text_edit(TextEdit::replace(range, header)) 167 item.text_edit(TextEdit::replace(range, header));
169 } 168 }
170 } 169 };
171 .kind(completion_kind) 170 item.kind(completion_kind);
172 .add_to(acc); 171 item.add_to(acc);
173 } 172 }
174} 173}
175 174
@@ -185,12 +184,12 @@ fn add_type_alias_impl(
185 184
186 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end()); 185 let range = TextRange::new(type_def_node.text_range().start(), ctx.source_range().end());
187 186
188 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 187 let mut item = CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
189 .text_edit(TextEdit::replace(range, snippet)) 188 item.text_edit(TextEdit::replace(range, snippet))
190 .lookup_by(alias_name) 189 .lookup_by(alias_name)
191 .kind(SymbolKind::TypeAlias) 190 .kind(SymbolKind::TypeAlias)
192 .set_documentation(type_alias.docs(ctx.db)) 191 .set_documentation(type_alias.docs(ctx.db));
193 .add_to(acc); 192 item.add_to(acc);
194} 193}
195 194
196fn add_const_impl( 195fn add_const_impl(
@@ -208,12 +207,13 @@ fn add_const_impl(
208 let range = 207 let range =
209 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end()); 208 TextRange::new(const_def_node.text_range().start(), ctx.source_range().end());
210 209
211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone()) 210 let mut item =
212 .text_edit(TextEdit::replace(range, snippet)) 211 CompletionItem::new(CompletionKind::Magic, ctx.source_range(), snippet.clone());
212 item.text_edit(TextEdit::replace(range, snippet))
213 .lookup_by(const_name) 213 .lookup_by(const_name)
214 .kind(SymbolKind::Const) 214 .kind(SymbolKind::Const)
215 .set_documentation(const_.docs(ctx.db)) 215 .set_documentation(const_.docs(ctx.db));
216 .add_to(acc); 216 item.add_to(acc);
217 } 217 }
218 } 218 }
219} 219}
diff --git a/crates/ide_completion/src/item.rs b/crates/ide_completion/src/item.rs
index 9b039e3e5..3febab32b 100644
--- a/crates/ide_completion/src/item.rs
+++ b/crates/ide_completion/src/item.rs
@@ -63,12 +63,18 @@ pub struct CompletionItem {
63 /// after completion. 63 /// after completion.
64 trigger_call_info: bool, 64 trigger_call_info: bool,
65 65
66 /// Score is useful to pre select or display in better order completion items 66 /// We use this to sort completion. Relevance records facts like "do the
67 score: Option<CompletionScore>, 67 /// types align precisely?". We can't sort by relevances directly, they are
68 /// only partially ordered.
69 ///
70 /// Note that Relevance ignores fuzzy match score. We compute Relevance for
71 /// all possible items, and then separately build an ordered completion list
72 /// based on relevance and fuzzy matching with the already typed identifier.
73 relevance: CompletionRelevance,
68 74
69 /// Indicates that a reference or mutable reference to this variable is a 75 /// Indicates that a reference or mutable reference to this variable is a
70 /// possible match. 76 /// possible match.
71 ref_match: Option<(Mutability, CompletionScore)>, 77 ref_match: Option<Mutability>,
72 78
73 /// The import data to add to completion's edits. 79 /// The import data to add to completion's edits.
74 import_to_add: Option<ImportEdit>, 80 import_to_add: Option<ImportEdit>,
@@ -101,8 +107,13 @@ impl fmt::Debug for CompletionItem {
101 if self.deprecated { 107 if self.deprecated {
102 s.field("deprecated", &true); 108 s.field("deprecated", &true);
103 } 109 }
104 if let Some(score) = &self.score { 110
105 s.field("score", score); 111 if self.relevance != CompletionRelevance::default() {
112 s.field("relevance", &self.relevance);
113 }
114
115 if let Some(mutability) = &self.ref_match {
116 s.field("ref_match", &format!("&{}", mutability.as_keyword_for_ref()));
106 } 117 }
107 if self.trigger_call_info { 118 if self.trigger_call_info {
108 s.field("trigger_call_info", &true); 119 s.field("trigger_call_info", &true);
@@ -111,12 +122,59 @@ impl fmt::Debug for CompletionItem {
111 } 122 }
112} 123}
113 124
114#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq)] 125#[derive(Debug, Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default)]
115pub enum CompletionScore { 126pub struct CompletionRelevance {
116 /// If only type match 127 /// This is set in cases like these:
117 TypeMatch, 128 ///
118 /// If type and name match 129 /// ```
119 TypeAndNameMatch, 130 /// fn f(spam: String) {}
131 /// fn main {
132 /// let spam = 92;
133 /// f($0) // name of local matches the name of param
134 /// }
135 /// ```
136 pub exact_name_match: bool,
137 /// This is set in cases like these:
138 ///
139 /// ```
140 /// fn f(spam: String) {}
141 /// fn main {
142 /// let foo = String::new();
143 /// f($0) // type of local matches the type of param
144 /// }
145 /// ```
146 pub exact_type_match: bool,
147}
148
149impl CompletionRelevance {
150 /// Provides a relevance score. Higher values are more relevant.
151 ///
152 /// The absolute value of the relevance score is not meaningful, for
153 /// example a value of 0 doesn't mean "not relevant", rather
154 /// it means "least relevant". The score value should only be used
155 /// for relative ordering.
156 ///
157 /// See is_relevant if you need to make some judgement about score
158 /// in an absolute sense.
159 pub fn score(&self) -> u32 {
160 let mut score = 0;
161
162 if self.exact_name_match {
163 score += 1;
164 }
165 if self.exact_type_match {
166 score += 1;
167 }
168
169 score
170 }
171
172 /// Returns true when the score for this threshold is above
173 /// some threshold such that we think it is especially likely
174 /// to be relevant.
175 pub fn is_relevant(&self) -> bool {
176 self.score() > 0
177 }
120} 178}
121 179
122#[derive(Debug, Clone, Copy, PartialEq, Eq)] 180#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -208,9 +266,9 @@ impl CompletionItem {
208 lookup: None, 266 lookup: None,
209 kind: None, 267 kind: None,
210 text_edit: None, 268 text_edit: None,
211 deprecated: None, 269 deprecated: false,
212 trigger_call_info: None, 270 trigger_call_info: None,
213 score: None, 271 relevance: CompletionRelevance::default(),
214 ref_match: None, 272 ref_match: None,
215 import_to_add: None, 273 import_to_add: None,
216 } 274 }
@@ -253,16 +311,22 @@ impl CompletionItem {
253 self.deprecated 311 self.deprecated
254 } 312 }
255 313
256 pub fn score(&self) -> Option<CompletionScore> { 314 pub fn relevance(&self) -> CompletionRelevance {
257 self.score 315 self.relevance
258 } 316 }
259 317
260 pub fn trigger_call_info(&self) -> bool { 318 pub fn trigger_call_info(&self) -> bool {
261 self.trigger_call_info 319 self.trigger_call_info
262 } 320 }
263 321
264 pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> { 322 pub fn ref_match(&self) -> Option<(Mutability, CompletionRelevance)> {
265 self.ref_match 323 // Relevance of the ref match should be the same as the original
324 // match, but with exact type match set because self.ref_match
325 // is only set if there is an exact type match.
326 let mut relevance = self.relevance;
327 relevance.exact_type_match = true;
328
329 self.ref_match.map(|mutability| (mutability, relevance))
266 } 330 }
267 331
268 pub fn import_to_add(&self) -> Option<&ImportEdit> { 332 pub fn import_to_add(&self) -> Option<&ImportEdit> {
@@ -308,10 +372,10 @@ pub(crate) struct Builder {
308 lookup: Option<String>, 372 lookup: Option<String>,
309 kind: Option<CompletionItemKind>, 373 kind: Option<CompletionItemKind>,
310 text_edit: Option<TextEdit>, 374 text_edit: Option<TextEdit>,
311 deprecated: Option<bool>, 375 deprecated: bool,
312 trigger_call_info: Option<bool>, 376 trigger_call_info: Option<bool>,
313 score: Option<CompletionScore>, 377 relevance: CompletionRelevance,
314 ref_match: Option<(Mutability, CompletionScore)>, 378 ref_match: Option<Mutability>,
315} 379}
316 380
317impl Builder { 381impl Builder {
@@ -355,49 +419,49 @@ impl Builder {
355 lookup, 419 lookup,
356 kind: self.kind, 420 kind: self.kind,
357 completion_kind: self.completion_kind, 421 completion_kind: self.completion_kind,
358 deprecated: self.deprecated.unwrap_or(false), 422 deprecated: self.deprecated,
359 trigger_call_info: self.trigger_call_info.unwrap_or(false), 423 trigger_call_info: self.trigger_call_info.unwrap_or(false),
360 score: self.score, 424 relevance: self.relevance,
361 ref_match: self.ref_match, 425 ref_match: self.ref_match,
362 import_to_add: self.import_to_add, 426 import_to_add: self.import_to_add,
363 } 427 }
364 } 428 }
365 pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder { 429 pub(crate) fn lookup_by(&mut self, lookup: impl Into<String>) -> &mut Builder {
366 self.lookup = Some(lookup.into()); 430 self.lookup = Some(lookup.into());
367 self 431 self
368 } 432 }
369 pub(crate) fn label(mut self, label: impl Into<String>) -> Builder { 433 pub(crate) fn label(&mut self, label: impl Into<String>) -> &mut Builder {
370 self.label = label.into(); 434 self.label = label.into();
371 self 435 self
372 } 436 }
373 pub(crate) fn insert_text(mut self, insert_text: impl Into<String>) -> Builder { 437 pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
374 self.insert_text = Some(insert_text.into()); 438 self.insert_text = Some(insert_text.into());
375 self 439 self
376 } 440 }
377 pub(crate) fn insert_snippet( 441 pub(crate) fn insert_snippet(
378 mut self, 442 &mut self,
379 _cap: SnippetCap, 443 _cap: SnippetCap,
380 snippet: impl Into<String>, 444 snippet: impl Into<String>,
381 ) -> Builder { 445 ) -> &mut Builder {
382 self.insert_text_format = InsertTextFormat::Snippet; 446 self.insert_text_format = InsertTextFormat::Snippet;
383 self.insert_text(snippet) 447 self.insert_text(snippet)
384 } 448 }
385 pub(crate) fn kind(mut self, kind: impl Into<CompletionItemKind>) -> Builder { 449 pub(crate) fn kind(&mut self, kind: impl Into<CompletionItemKind>) -> &mut Builder {
386 self.kind = Some(kind.into()); 450 self.kind = Some(kind.into());
387 self 451 self
388 } 452 }
389 pub(crate) fn text_edit(mut self, edit: TextEdit) -> Builder { 453 pub(crate) fn text_edit(&mut self, edit: TextEdit) -> &mut Builder {
390 self.text_edit = Some(edit); 454 self.text_edit = Some(edit);
391 self 455 self
392 } 456 }
393 pub(crate) fn snippet_edit(mut self, _cap: SnippetCap, edit: TextEdit) -> Builder { 457 pub(crate) fn snippet_edit(&mut self, _cap: SnippetCap, edit: TextEdit) -> &mut Builder {
394 self.insert_text_format = InsertTextFormat::Snippet; 458 self.insert_text_format = InsertTextFormat::Snippet;
395 self.text_edit(edit) 459 self.text_edit(edit)
396 } 460 }
397 pub(crate) fn detail(self, detail: impl Into<String>) -> Builder { 461 pub(crate) fn detail(&mut self, detail: impl Into<String>) -> &mut Builder {
398 self.set_detail(Some(detail)) 462 self.set_detail(Some(detail))
399 } 463 }
400 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 464 pub(crate) fn set_detail(&mut self, detail: Option<impl Into<String>>) -> &mut Builder {
401 self.detail = detail.map(Into::into); 465 self.detail = detail.map(Into::into);
402 if let Some(detail) = &self.detail { 466 if let Some(detail) = &self.detail {
403 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) { 467 if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
@@ -407,34 +471,91 @@ impl Builder {
407 self 471 self
408 } 472 }
409 #[allow(unused)] 473 #[allow(unused)]
410 pub(crate) fn documentation(self, docs: Documentation) -> Builder { 474 pub(crate) fn documentation(&mut self, docs: Documentation) -> &mut Builder {
411 self.set_documentation(Some(docs)) 475 self.set_documentation(Some(docs))
412 } 476 }
413 pub(crate) fn set_documentation(mut self, docs: Option<Documentation>) -> Builder { 477 pub(crate) fn set_documentation(&mut self, docs: Option<Documentation>) -> &mut Builder {
414 self.documentation = docs.map(Into::into); 478 self.documentation = docs.map(Into::into);
415 self 479 self
416 } 480 }
417 pub(crate) fn set_deprecated(mut self, deprecated: bool) -> Builder { 481 pub(crate) fn set_deprecated(&mut self, deprecated: bool) -> &mut Builder {
418 self.deprecated = Some(deprecated); 482 self.deprecated = deprecated;
419 self 483 self
420 } 484 }
421 pub(crate) fn set_score(mut self, score: CompletionScore) -> Builder { 485 pub(crate) fn set_relevance(&mut self, relevance: CompletionRelevance) -> &mut Builder {
422 self.score = Some(score); 486 self.relevance = relevance;
423 self 487 self
424 } 488 }
425 pub(crate) fn trigger_call_info(mut self) -> Builder { 489 pub(crate) fn trigger_call_info(&mut self) -> &mut Builder {
426 self.trigger_call_info = Some(true); 490 self.trigger_call_info = Some(true);
427 self 491 self
428 } 492 }
429 pub(crate) fn add_import(mut self, import_to_add: Option<ImportEdit>) -> Builder { 493 pub(crate) fn add_import(&mut self, import_to_add: Option<ImportEdit>) -> &mut Builder {
430 self.import_to_add = import_to_add; 494 self.import_to_add = import_to_add;
431 self 495 self
432 } 496 }
433 pub(crate) fn set_ref_match( 497 pub(crate) fn ref_match(&mut self, mutability: Mutability) -> &mut Builder {
434 mut self, 498 self.ref_match = Some(mutability);
435 ref_match: Option<(Mutability, CompletionScore)>,
436 ) -> Builder {
437 self.ref_match = ref_match;
438 self 499 self
439 } 500 }
440} 501}
502
503#[cfg(test)]
504mod tests {
505 use itertools::Itertools;
506 use test_utils::assert_eq_text;
507
508 use super::CompletionRelevance;
509
510 /// Check that these are CompletionRelevance are sorted in ascending order
511 /// by their relevance score.
512 ///
513 /// We want to avoid making assertions about the absolute score of any
514 /// item, but we do want to assert whether each is >, <, or == to the
515 /// others.
516 ///
517 /// If provided vec![vec![a], vec![b, c], vec![d]], then this will assert:
518 /// a.score < b.score == c.score < d.score
519 fn check_relevance_score_ordered(expected_relevance_order: Vec<Vec<CompletionRelevance>>) {
520 let expected = format!("{:#?}", &expected_relevance_order);
521
522 let actual_relevance_order = expected_relevance_order
523 .into_iter()
524 .flatten()
525 .map(|r| (r.score(), r))
526 .sorted_by_key(|(score, _r)| *score)
527 .fold(
528 (u32::MIN, vec![vec![]]),
529 |(mut currently_collecting_score, mut out), (score, r)| {
530 if currently_collecting_score == score {
531 out.last_mut().unwrap().push(r);
532 } else {
533 currently_collecting_score = score;
534 out.push(vec![r]);
535 }
536 (currently_collecting_score, out)
537 },
538 )
539 .1;
540
541 let actual = format!("{:#?}", &actual_relevance_order);
542
543 assert_eq_text!(&expected, &actual);
544 }
545
546 #[test]
547 fn relevance_score() {
548 // This test asserts that the relevance score for these items is ascending, and
549 // that any items in the same vec have the same score.
550 let expected_relevance_order = vec![
551 vec![CompletionRelevance::default()],
552 vec![
553 CompletionRelevance { exact_name_match: true, ..CompletionRelevance::default() },
554 CompletionRelevance { exact_type_match: true, ..CompletionRelevance::default() },
555 ],
556 vec![CompletionRelevance { exact_name_match: true, exact_type_match: true }],
557 ];
558
559 check_relevance_score_ordered(expected_relevance_order);
560 }
561}
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index a0c8c374d..263554ecf 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -23,7 +23,7 @@ use crate::{completions::Completions, context::CompletionContext, item::Completi
23 23
24pub use crate::{ 24pub use crate::{
25 config::CompletionConfig, 25 config::CompletionConfig,
26 item::{CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, InsertTextFormat}, 26 item::{CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, InsertTextFormat},
27}; 27};
28 28
29//FIXME: split the following feature into fine-grained features. 29//FIXME: split the following feature into fine-grained features.
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs
index fae5685e2..db31896e5 100644
--- a/crates/ide_completion/src/render.rs
+++ b/crates/ide_completion/src/render.rs
@@ -20,8 +20,8 @@ use ide_db::{
20use syntax::TextRange; 20use syntax::TextRange;
21 21
22use crate::{ 22use crate::{
23 item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, 23 item::{CompletionRelevance, ImportEdit},
24 CompletionScore, 24 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind,
25}; 25};
26 26
27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; 27use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro};
@@ -117,7 +117,7 @@ impl<'a> RenderContext<'a> {
117 node.docs(self.db()) 117 node.docs(self.db())
118 } 118 }
119 119
120 fn active_name_and_type(&self) -> Option<(String, Type)> { 120 fn expected_name_and_type(&self) -> Option<(String, Type)> {
121 if let Some(record_field) = &self.completion.record_field_syntax { 121 if let Some(record_field) = &self.completion.record_field_syntax {
122 cov_mark::hit!(record_field_type_match); 122 cov_mark::hit!(record_field_type_match);
123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; 123 let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?;
@@ -149,24 +149,29 @@ impl<'a> Render<'a> {
149 CompletionKind::Reference, 149 CompletionKind::Reference,
150 self.ctx.source_range(), 150 self.ctx.source_range(),
151 name.to_string(), 151 name.to_string(),
152 ) 152 );
153 .kind(SymbolKind::Field) 153 item.kind(SymbolKind::Field)
154 .detail(ty.display(self.ctx.db()).to_string()) 154 .detail(ty.display(self.ctx.db()).to_string())
155 .set_documentation(field.docs(self.ctx.db())) 155 .set_documentation(field.docs(self.ctx.db()))
156 .set_deprecated(is_deprecated); 156 .set_deprecated(is_deprecated);
157 157
158 if let Some(score) = compute_score(&self.ctx, &ty, &name.to_string()) { 158 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &name.to_string()) {
159 item = item.set_score(score); 159 item.set_relevance(relevance);
160 } 160 }
161 161
162 item.build() 162 item.build()
163 } 163 }
164 164
165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem { 165 fn add_tuple_field(&mut self, field: usize, ty: &Type) -> CompletionItem {
166 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), field.to_string()) 166 let mut item = CompletionItem::new(
167 .kind(SymbolKind::Field) 167 CompletionKind::Reference,
168 .detail(ty.display(self.ctx.db()).to_string()) 168 self.ctx.source_range(),
169 .build() 169 field.to_string(),
170 );
171
172 item.kind(SymbolKind::Field).detail(ty.display(self.ctx.db()).to_string());
173
174 item.build()
170 } 175 }
171 176
172 fn render_resolution( 177 fn render_resolution(
@@ -225,15 +230,13 @@ impl<'a> Render<'a> {
225 CompletionItemKind::SymbolKind(SymbolKind::SelfParam) 230 CompletionItemKind::SymbolKind(SymbolKind::SelfParam)
226 } 231 }
227 ScopeDef::Unknown => { 232 ScopeDef::Unknown => {
228 let item = CompletionItem::new( 233 let mut item = CompletionItem::new(
229 CompletionKind::Reference, 234 CompletionKind::Reference,
230 self.ctx.source_range(), 235 self.ctx.source_range(),
231 local_name, 236 local_name,
232 ) 237 );
233 .kind(CompletionItemKind::UnresolvedReference) 238 item.kind(CompletionItemKind::UnresolvedReference).add_import(import_to_add);
234 .add_import(import_to_add) 239 return Some(item.build());
235 .build();
236 return Some(item);
237 } 240 }
238 }; 241 };
239 242
@@ -242,20 +245,27 @@ impl<'a> Render<'a> {
242 if let ScopeDef::Local(local) = resolution { 245 if let ScopeDef::Local(local) = resolution {
243 let ty = local.ty(self.ctx.db()); 246 let ty = local.ty(self.ctx.db());
244 if !ty.is_unknown() { 247 if !ty.is_unknown() {
245 item = item.detail(ty.display(self.ctx.db()).to_string()); 248 item.detail(ty.display(self.ctx.db()).to_string());
246 } 249 }
247 }; 250 };
248 251
249 let mut ref_match = None;
250 if let ScopeDef::Local(local) = resolution { 252 if let ScopeDef::Local(local) = resolution {
251 if let Some((active_name, active_type)) = self.ctx.active_name_and_type() { 253 let ty = local.ty(self.ctx.db());
252 let ty = local.ty(self.ctx.db()); 254 if let Some(relevance) = compute_relevance(&self.ctx, &ty, &local_name) {
253 if let Some(score) = 255 item.set_relevance(relevance);
254 compute_score_from_active(&active_type, &active_name, &ty, &local_name) 256 }
255 { 257 if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() {
256 item = item.set_score(score); 258 if let Some(ty_without_ref) = expected_type.remove_ref() {
259 if ty_without_ref == ty {
260 cov_mark::hit!(suggest_ref);
261 let mutability = if expected_type.is_mutable_reference() {
262 Mutability::Mut
263 } else {
264 Mutability::Shared
265 };
266 item.ref_match(mutability);
267 }
257 } 268 }
258 ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
259 } 269 }
260 } 270 }
261 271
@@ -274,22 +284,17 @@ impl<'a> Render<'a> {
274 }; 284 };
275 if has_non_default_type_params { 285 if has_non_default_type_params {
276 cov_mark::hit!(inserts_angle_brackets_for_generics); 286 cov_mark::hit!(inserts_angle_brackets_for_generics);
277 item = item 287 item.lookup_by(local_name.clone())
278 .lookup_by(local_name.clone())
279 .label(format!("{}<…>", local_name)) 288 .label(format!("{}<…>", local_name))
280 .insert_snippet(cap, format!("{}<$0>", local_name)); 289 .insert_snippet(cap, format!("{}<$0>", local_name));
281 } 290 }
282 } 291 }
283 } 292 }
284 293 item.kind(kind)
285 Some( 294 .add_import(import_to_add)
286 item.kind(kind) 295 .set_documentation(self.docs(resolution))
287 .add_import(import_to_add) 296 .set_deprecated(self.is_deprecated(resolution));
288 .set_ref_match(ref_match) 297 Some(item.build())
289 .set_documentation(self.docs(resolution))
290 .set_deprecated(self.is_deprecated(resolution))
291 .build(),
292 )
293 } 298 }
294 299
295 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 300 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -317,45 +322,13 @@ impl<'a> Render<'a> {
317 } 322 }
318} 323}
319 324
320fn compute_score_from_active( 325fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionRelevance> {
321 active_type: &Type, 326 let (expected_name, expected_type) = ctx.expected_name_and_type()?;
322 active_name: &str, 327 let mut res = CompletionRelevance::default();
323 ty: &Type, 328 res.exact_type_match = ty == &expected_type;
324 name: &str, 329 res.exact_name_match = name == &expected_name;
325) -> Option<CompletionScore> {
326 // Compute score
327 // For the same type
328 if active_type != ty {
329 return None;
330 }
331
332 let mut res = CompletionScore::TypeMatch;
333
334 // If same type + same name then go top position
335 if active_name == name {
336 res = CompletionScore::TypeAndNameMatch
337 }
338
339 Some(res) 330 Some(res)
340} 331}
341fn refed_type_matches(
342 active_type: &Type,
343 active_name: &str,
344 ty: &Type,
345 name: &str,
346) -> Option<(Mutability, CompletionScore)> {
347 let derefed_active = active_type.remove_ref()?;
348 let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
349 Some((
350 if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
351 score,
352 ))
353}
354
355fn compute_score(ctx: &RenderContext, ty: &Type, name: &str) -> Option<CompletionScore> {
356 let (active_name, active_type) = ctx.active_name_and_type()?;
357 compute_score_from_active(&active_type, &active_name, ty, name)
358}
359 332
360#[cfg(test)] 333#[cfg(test)]
361mod tests { 334mod tests {
@@ -365,7 +338,7 @@ mod tests {
365 338
366 use crate::{ 339 use crate::{
367 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG}, 340 test_utils::{check_edit, do_completion, get_all_items, TEST_CONFIG},
368 CompletionKind, CompletionScore, 341 CompletionKind, CompletionRelevance,
369 }; 342 };
370 343
371 fn check(ra_fixture: &str, expect: Expect) { 344 fn check(ra_fixture: &str, expect: Expect) {
@@ -373,24 +346,27 @@ mod tests {
373 expect.assert_debug_eq(&actual); 346 expect.assert_debug_eq(&actual);
374 } 347 }
375 348
376 fn check_scores(ra_fixture: &str, expect: Expect) { 349 fn check_relevance(ra_fixture: &str, expect: Expect) {
377 fn display_score(score: Option<CompletionScore>) -> &'static str { 350 fn display_relevance(relevance: CompletionRelevance) -> &'static str {
378 match score { 351 match relevance {
379 Some(CompletionScore::TypeMatch) => "[type]", 352 CompletionRelevance { exact_type_match: true, exact_name_match: true } => {
380 Some(CompletionScore::TypeAndNameMatch) => "[type+name]", 353 "[type+name]"
381 None => "[]".into(), 354 }
355 CompletionRelevance { exact_type_match: true, exact_name_match: false } => "[type]",
356 CompletionRelevance { exact_type_match: false, exact_name_match: true } => "[name]",
357 CompletionRelevance { exact_type_match: false, exact_name_match: false } => "[]",
382 } 358 }
383 } 359 }
384 360
385 let mut completions = get_all_items(TEST_CONFIG, ra_fixture); 361 let mut completions = get_all_items(TEST_CONFIG, ra_fixture);
386 completions.sort_by_key(|it| (Reverse(it.score()), it.label().to_string())); 362 completions.sort_by_key(|it| (Reverse(it.relevance()), it.label().to_string()));
387 let actual = completions 363 let actual = completions
388 .into_iter() 364 .into_iter()
389 .filter(|it| it.completion_kind == CompletionKind::Reference) 365 .filter(|it| it.completion_kind == CompletionKind::Reference)
390 .map(|it| { 366 .map(|it| {
391 let tag = it.kind().unwrap().tag(); 367 let tag = it.kind().unwrap().tag();
392 let score = display_score(it.score()); 368 let relevance = display_relevance(it.relevance());
393 format!("{} {} {}\n", tag, it.label(), score) 369 format!("{} {} {}\n", tag, it.label(), relevance)
394 }) 370 })
395 .collect::<String>(); 371 .collect::<String>();
396 expect.assert_eq(&actual); 372 expect.assert_eq(&actual);
@@ -853,9 +829,9 @@ fn foo(xs: Vec<i128>)
853 } 829 }
854 830
855 #[test] 831 #[test]
856 fn active_param_score() { 832 fn active_param_relevance() {
857 cov_mark::check!(active_param_type_match); 833 cov_mark::check!(active_param_type_match);
858 check_scores( 834 check_relevance(
859 r#" 835 r#"
860struct S { foo: i64, bar: u32, baz: u32 } 836struct S { foo: i64, bar: u32, baz: u32 }
861fn test(bar: u32) { } 837fn test(bar: u32) { }
@@ -870,9 +846,9 @@ fn foo(s: S) { test(s.$0) }
870 } 846 }
871 847
872 #[test] 848 #[test]
873 fn record_field_scores() { 849 fn record_field_relevances() {
874 cov_mark::check!(record_field_type_match); 850 cov_mark::check!(record_field_type_match);
875 check_scores( 851 check_relevance(
876 r#" 852 r#"
877struct A { foo: i64, bar: u32, baz: u32 } 853struct A { foo: i64, bar: u32, baz: u32 }
878struct B { x: (), y: f32, bar: u32 } 854struct B { x: (), y: f32, bar: u32 }
@@ -887,8 +863,8 @@ fn foo(a: A) { B { bar: a.$0 }; }
887 } 863 }
888 864
889 #[test] 865 #[test]
890 fn record_field_and_call_scores() { 866 fn record_field_and_call_relevances() {
891 check_scores( 867 check_relevance(
892 r#" 868 r#"
893struct A { foo: i64, bar: u32, baz: u32 } 869struct A { foo: i64, bar: u32, baz: u32 }
894struct B { x: (), y: f32, bar: u32 } 870struct B { x: (), y: f32, bar: u32 }
@@ -901,7 +877,7 @@ fn foo(a: A) { B { bar: f(a.$0) }; }
901 fd baz [] 877 fd baz []
902 "#]], 878 "#]],
903 ); 879 );
904 check_scores( 880 check_relevance(
905 r#" 881 r#"
906struct A { foo: i64, bar: u32, baz: u32 } 882struct A { foo: i64, bar: u32, baz: u32 }
907struct B { x: (), y: f32, bar: u32 } 883struct B { x: (), y: f32, bar: u32 }
@@ -918,7 +894,7 @@ fn foo(a: A) { f(B { bar: a.$0 }); }
918 894
919 #[test] 895 #[test]
920 fn prioritize_exact_ref_match() { 896 fn prioritize_exact_ref_match() {
921 check_scores( 897 check_relevance(
922 r#" 898 r#"
923struct WorldSnapshot { _f: () }; 899struct WorldSnapshot { _f: () };
924fn go(world: &WorldSnapshot) { go(w$0) } 900fn go(world: &WorldSnapshot) { go(w$0) }
@@ -933,7 +909,7 @@ fn go(world: &WorldSnapshot) { go(w$0) }
933 909
934 #[test] 910 #[test]
935 fn too_many_arguments() { 911 fn too_many_arguments() {
936 check_scores( 912 check_relevance(
937 r#" 913 r#"
938struct Foo; 914struct Foo;
939fn f(foo: &Foo) { f(foo, w$0) } 915fn f(foo: &Foo) { f(foo, w$0) }
@@ -945,4 +921,70 @@ fn f(foo: &Foo) { f(foo, w$0) }
945 "#]], 921 "#]],
946 ); 922 );
947 } 923 }
924
925 #[test]
926 fn suggest_ref_mut() {
927 cov_mark::check!(suggest_ref);
928 check(
929 r#"
930struct S;
931fn foo(s: &mut S) {}
932fn main() {
933 let mut s = S;
934 foo($0);
935}
936 "#,
937 expect![[r#"
938 [
939 CompletionItem {
940 label: "S",
941 source_range: 70..70,
942 delete: 70..70,
943 insert: "S",
944 kind: SymbolKind(
945 Struct,
946 ),
947 },
948 CompletionItem {
949 label: "foo(…)",
950 source_range: 70..70,
951 delete: 70..70,
952 insert: "foo(${1:&mut s})$0",
953 kind: SymbolKind(
954 Function,
955 ),
956 lookup: "foo",
957 detail: "-> ()",
958 trigger_call_info: true,
959 },
960 CompletionItem {
961 label: "main()",
962 source_range: 70..70,
963 delete: 70..70,
964 insert: "main()$0",
965 kind: SymbolKind(
966 Function,
967 ),
968 lookup: "main",
969 detail: "-> ()",
970 },
971 CompletionItem {
972 label: "s",
973 source_range: 70..70,
974 delete: 70..70,
975 insert: "s",
976 kind: SymbolKind(
977 Local,
978 ),
979 detail: "S",
980 relevance: CompletionRelevance {
981 exact_name_match: true,
982 exact_type_match: false,
983 },
984 ref_match: "&mut ",
985 },
986 ]
987 "#]],
988 )
989 }
948} 990}
diff --git a/crates/ide_completion/src/render/builder_ext.rs b/crates/ide_completion/src/render/builder_ext.rs
index 95a7596c1..6d062b3b9 100644
--- a/crates/ide_completion/src/render/builder_ext.rs
+++ b/crates/ide_completion/src/render/builder_ext.rs
@@ -52,11 +52,11 @@ impl Builder {
52 } 52 }
53 53
54 pub(super) fn add_call_parens( 54 pub(super) fn add_call_parens(
55 mut self, 55 &mut self,
56 ctx: &CompletionContext, 56 ctx: &CompletionContext,
57 name: String, 57 name: String,
58 params: Params, 58 params: Params,
59 ) -> Builder { 59 ) -> &mut Builder {
60 if !self.should_add_parens(ctx) { 60 if !self.should_add_parens(ctx) {
61 return self; 61 return self;
62 } 62 }
@@ -71,7 +71,7 @@ impl Builder {
71 let (snippet, label) = if params.is_empty() { 71 let (snippet, label) = if params.is_empty() {
72 (format!("{}()$0", name), format!("{}()", name)) 72 (format!("{}()$0", name), format!("{}()", name))
73 } else { 73 } else {
74 self = self.trigger_call_info(); 74 self.trigger_call_info();
75 let snippet = match (ctx.config.add_call_argument_snippets, params) { 75 let snippet = match (ctx.config.add_call_argument_snippets, params) {
76 (true, Params::Named(params)) => { 76 (true, Params::Named(params)) => {
77 let function_params_snippet = 77 let function_params_snippet =
diff --git a/crates/ide_completion/src/render/const_.rs b/crates/ide_completion/src/render/const_.rs
index 5010b642a..8add369e4 100644
--- a/crates/ide_completion/src/render/const_.rs
+++ b/crates/ide_completion/src/render/const_.rs
@@ -36,17 +36,17 @@ impl<'a> ConstRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::Const) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::Const)
41 .set_documentation(self.ctx.docs(self.const_)) 42 .set_documentation(self.ctx.docs(self.const_))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.const_) 44 self.ctx.is_deprecated(self.const_)
44 || self.ctx.is_deprecated_assoc_item(self.const_), 45 || self.ctx.is_deprecated_assoc_item(self.const_),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_completion/src/render/enum_variant.rs b/crates/ide_completion/src/render/enum_variant.rs
index ed055c1fb..e8cfcc0c7 100644
--- a/crates/ide_completion/src/render/enum_variant.rs
+++ b/crates/ide_completion/src/render/enum_variant.rs
@@ -55,27 +55,26 @@ impl<'a> EnumRender<'a> {
55 } 55 }
56 56
57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 57 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
58 let mut builder = CompletionItem::new( 58 let mut item = CompletionItem::new(
59 CompletionKind::Reference, 59 CompletionKind::Reference,
60 self.ctx.source_range(), 60 self.ctx.source_range(),
61 self.qualified_name.clone(), 61 self.qualified_name.clone(),
62 ) 62 );
63 .kind(SymbolKind::Variant) 63 item.kind(SymbolKind::Variant)
64 .set_documentation(self.variant.docs(self.ctx.db())) 64 .set_documentation(self.variant.docs(self.ctx.db()))
65 .set_deprecated(self.ctx.is_deprecated(self.variant)) 65 .set_deprecated(self.ctx.is_deprecated(self.variant))
66 .add_import(import_to_add) 66 .add_import(import_to_add)
67 .detail(self.detail()); 67 .detail(self.detail());
68 68
69 if self.variant_kind == StructKind::Tuple { 69 if self.variant_kind == StructKind::Tuple {
70 cov_mark::hit!(inserts_parens_for_tuple_enums); 70 cov_mark::hit!(inserts_parens_for_tuple_enums);
71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len()); 71 let params = Params::Anonymous(self.variant.fields(self.ctx.db()).len());
72 builder = 72 item.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
73 builder.add_call_parens(self.ctx.completion, self.short_qualified_name, params);
74 } else if self.path.is_some() { 73 } else if self.path.is_some() {
75 builder = builder.lookup_by(self.short_qualified_name); 74 item.lookup_by(self.short_qualified_name);
76 } 75 }
77 76
78 builder.build() 77 item.build()
79 } 78 }
80 79
81 fn detail(&self) -> String { 80 fn detail(&self) -> String {
diff --git a/crates/ide_completion/src/render/function.rs b/crates/ide_completion/src/render/function.rs
index 5931945a8..f4dabe3d1 100644
--- a/crates/ide_completion/src/render/function.rs
+++ b/crates/ide_completion/src/render/function.rs
@@ -41,16 +41,21 @@ impl<'a> FunctionRender<'a> {
41 41
42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem { 42 fn render(self, import_to_add: Option<ImportEdit>) -> CompletionItem {
43 let params = self.params(); 43 let params = self.params();
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 let mut item = CompletionItem::new(
45 .kind(self.kind()) 45 CompletionKind::Reference,
46 self.ctx.source_range(),
47 self.name.clone(),
48 );
49 item.kind(self.kind())
46 .set_documentation(self.ctx.docs(self.func)) 50 .set_documentation(self.ctx.docs(self.func))
47 .set_deprecated( 51 .set_deprecated(
48 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func), 52 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
49 ) 53 )
50 .detail(self.detail()) 54 .detail(self.detail())
51 .add_call_parens(self.ctx.completion, self.name, params) 55 .add_call_parens(self.ctx.completion, self.name, params)
52 .add_import(import_to_add) 56 .add_import(import_to_add);
53 .build() 57
58 item.build()
54 } 59 }
55 60
56 fn detail(&self) -> String { 61 fn detail(&self) -> String {
diff --git a/crates/ide_completion/src/render/macro_.rs b/crates/ide_completion/src/render/macro_.rs
index a6cf3e479..3fa21ba7c 100644
--- a/crates/ide_completion/src/render/macro_.rs
+++ b/crates/ide_completion/src/render/macro_.rs
@@ -39,29 +39,31 @@ impl<'a> MacroRender<'a> {
39 } 39 }
40 40
41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> { 41 fn render(&self, import_to_add: Option<ImportEdit>) -> Option<CompletionItem> {
42 let mut builder = 42 let mut item =
43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label()) 43 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), &self.label());
44 .kind(SymbolKind::Macro) 44 item.kind(SymbolKind::Macro)
45 .set_documentation(self.docs.clone()) 45 .set_documentation(self.docs.clone())
46 .set_deprecated(self.ctx.is_deprecated(self.macro_)) 46 .set_deprecated(self.ctx.is_deprecated(self.macro_))
47 .add_import(import_to_add) 47 .add_import(import_to_add)
48 .set_detail(self.detail()); 48 .set_detail(self.detail());
49 49
50 let needs_bang = self.needs_bang(); 50 let needs_bang = self.needs_bang();
51 builder = match self.ctx.snippet_cap() { 51 match self.ctx.snippet_cap() {
52 Some(cap) if needs_bang => { 52 Some(cap) if needs_bang => {
53 let snippet = self.snippet(); 53 let snippet = self.snippet();
54 let lookup = self.lookup(); 54 let lookup = self.lookup();
55 builder.insert_snippet(cap, snippet).lookup_by(lookup) 55 item.insert_snippet(cap, snippet).lookup_by(lookup);
56 }
57 None if needs_bang => {
58 item.insert_text(self.banged_name());
56 } 59 }
57 None if needs_bang => builder.insert_text(self.banged_name()),
58 _ => { 60 _ => {
59 cov_mark::hit!(dont_insert_macro_call_parens_unncessary); 61 cov_mark::hit!(dont_insert_macro_call_parens_unncessary);
60 builder.insert_text(&self.name) 62 item.insert_text(&self.name);
61 } 63 }
62 }; 64 };
63 65
64 Some(builder.build()) 66 Some(item.build())
65 } 67 }
66 68
67 fn needs_bang(&self) -> bool { 69 fn needs_bang(&self) -> bool {
diff --git a/crates/ide_completion/src/render/pattern.rs b/crates/ide_completion/src/render/pattern.rs
index 465dfe00c..ca2926125 100644
--- a/crates/ide_completion/src/render/pattern.rs
+++ b/crates/ide_completion/src/render/pattern.rs
@@ -69,19 +69,19 @@ fn build_completion(
69 ctx: RenderContext<'_>, 69 ctx: RenderContext<'_>,
70 name: String, 70 name: String,
71 pat: String, 71 pat: String,
72 item: impl HasAttrs + Copy, 72 def: impl HasAttrs + Copy,
73) -> CompletionItem { 73) -> CompletionItem {
74 let completion = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name) 74 let mut item = CompletionItem::new(CompletionKind::Snippet, ctx.source_range(), name);
75 .kind(CompletionItemKind::Binding) 75 item.kind(CompletionItemKind::Binding)
76 .set_documentation(ctx.docs(item)) 76 .set_documentation(ctx.docs(def))
77 .set_deprecated(ctx.is_deprecated(item)) 77 .set_deprecated(ctx.is_deprecated(def))
78 .detail(&pat); 78 .detail(&pat);
79 let completion = if let Some(snippet_cap) = ctx.snippet_cap() { 79 if let Some(snippet_cap) = ctx.snippet_cap() {
80 completion.insert_snippet(snippet_cap, pat) 80 item.insert_snippet(snippet_cap, pat);
81 } else { 81 } else {
82 completion.insert_text(pat) 82 item.insert_text(pat);
83 }; 83 };
84 completion.build() 84 item.build()
85} 85}
86 86
87fn render_pat( 87fn render_pat(
diff --git a/crates/ide_completion/src/render/type_alias.rs b/crates/ide_completion/src/render/type_alias.rs
index bd97c3692..e47b4c745 100644
--- a/crates/ide_completion/src/render/type_alias.rs
+++ b/crates/ide_completion/src/render/type_alias.rs
@@ -36,17 +36,17 @@ impl<'a> TypeAliasRender<'a> {
36 let name = self.name()?; 36 let name = self.name()?;
37 let detail = self.detail(); 37 let detail = self.detail();
38 38
39 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 39 let mut item =
40 .kind(SymbolKind::TypeAlias) 40 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name);
41 item.kind(SymbolKind::TypeAlias)
41 .set_documentation(self.ctx.docs(self.type_alias)) 42 .set_documentation(self.ctx.docs(self.type_alias))
42 .set_deprecated( 43 .set_deprecated(
43 self.ctx.is_deprecated(self.type_alias) 44 self.ctx.is_deprecated(self.type_alias)
44 || self.ctx.is_deprecated_assoc_item(self.type_alias), 45 || self.ctx.is_deprecated_assoc_item(self.type_alias),
45 ) 46 )
46 .detail(detail) 47 .detail(detail);
47 .build();
48 48
49 Some(item) 49 Some(item.build())
50 } 50 }
51 51
52 fn name(&self) -> Option<String> { 52 fn name(&self) -> Option<String> {
diff --git a/crates/ide_db/Cargo.toml b/crates/ide_db/Cargo.toml
index 1f855c621..1f7a90d20 100644
--- a/crates/ide_db/Cargo.toml
+++ b/crates/ide_db/Cargo.toml
@@ -24,10 +24,10 @@ syntax = { path = "../syntax", version = "0.0.0" }
24text_edit = { path = "../text_edit", version = "0.0.0" } 24text_edit = { path = "../text_edit", version = "0.0.0" }
25base_db = { path = "../base_db", version = "0.0.0" } 25base_db = { path = "../base_db", version = "0.0.0" }
26profile = { path = "../profile", version = "0.0.0" } 26profile = { path = "../profile", version = "0.0.0" }
27test_utils = { path = "../test_utils", version = "0.0.0" }
28# ide should depend only on the top-level `hir` package. if you need 27# ide should depend only on the top-level `hir` package. if you need
29# something from some `hir_xxx` subpackage, reexport the API via `hir`. 28# something from some `hir_xxx` subpackage, reexport the API via `hir`.
30hir = { path = "../hir", version = "0.0.0" } 29hir = { path = "../hir", version = "0.0.0" }
31 30
32[dev-dependencies] 31[dev-dependencies]
32test_utils = { path = "../test_utils" }
33expect-test = "1.1" 33expect-test = "1.1"
diff --git a/crates/ide_ssr/Cargo.toml b/crates/ide_ssr/Cargo.toml
index 315691f40..8c31df13a 100644
--- a/crates/ide_ssr/Cargo.toml
+++ b/crates/ide_ssr/Cargo.toml
@@ -19,7 +19,7 @@ text_edit = { path = "../text_edit", version = "0.0.0" }
19syntax = { path = "../syntax", version = "0.0.0" } 19syntax = { path = "../syntax", version = "0.0.0" }
20ide_db = { path = "../ide_db", version = "0.0.0" } 20ide_db = { path = "../ide_db", version = "0.0.0" }
21hir = { path = "../hir", version = "0.0.0" } 21hir = { path = "../hir", version = "0.0.0" }
22test_utils = { path = "../test_utils", version = "0.0.0" }
23 22
24[dev-dependencies] 23[dev-dependencies]
24test_utils = { path = "../test_utils" }
25expect-test = "1.1" 25expect-test = "1.1"
diff --git a/crates/ide_ssr/src/from_comment.rs b/crates/ide_ssr/src/from_comment.rs
new file mode 100644
index 000000000..f1b312284
--- /dev/null
+++ b/crates/ide_ssr/src/from_comment.rs
@@ -0,0 +1,32 @@
1//! This module allows building an SSR MatchFinder by parsing the SSR rule
2//! from a comment.
3
4use ide_db::{
5 base_db::{FilePosition, FileRange, SourceDatabase},
6 RootDatabase,
7};
8use syntax::{
9 ast::{self, AstNode, AstToken},
10 TextRange,
11};
12
13use crate::MatchFinder;
14
15/// Attempts to build an SSR MatchFinder from a comment at the given file
16/// range. If successful, returns the MatchFinder and a TextRange covering
17/// comment.
18pub fn ssr_from_comment(db: &RootDatabase, frange: FileRange) -> Option<(MatchFinder, TextRange)> {
19 let comment = {
20 let file = db.parse(frange.file_id);
21 file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast)
22 }?;
23 let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap();
24 let ssr_rule = comment_text_without_prefix.parse().ok()?;
25
26 let lookup_context = FilePosition { file_id: frange.file_id, offset: frange.range.start() };
27
28 let mut match_finder = MatchFinder::in_context(db, lookup_context, vec![]);
29 match_finder.add_rule(ssr_rule).ok()?;
30
31 Some((match_finder, comment.syntax().text_range()))
32}
diff --git a/crates/ide_ssr/src/lib.rs b/crates/ide_ssr/src/lib.rs
index a97fc8bca..00585f448 100644
--- a/crates/ide_ssr/src/lib.rs
+++ b/crates/ide_ssr/src/lib.rs
@@ -57,7 +57,17 @@
57// 57//
58// | VS Code | **Rust Analyzer: Structural Search Replace** 58// | VS Code | **Rust Analyzer: Structural Search Replace**
59// |=== 59// |===
60//
61// Also available as an assist, by writing a comment containing the structural
62// search and replace rule. You will only see the assist if the comment can
63// be parsed as a valid structural search and replace rule.
64//
65// ```rust
66// // Place the cursor on the line below to see the assist 💡.
67// // foo($a, $b) ==>> ($a).foo($b)
68// ```
60 69
70mod from_comment;
61mod matching; 71mod matching;
62mod nester; 72mod nester;
63mod parsing; 73mod parsing;
@@ -71,6 +81,7 @@ mod tests;
71 81
72use crate::errors::bail; 82use crate::errors::bail;
73pub use crate::errors::SsrError; 83pub use crate::errors::SsrError;
84pub use crate::from_comment::ssr_from_comment;
74pub use crate::matching::Match; 85pub use crate::matching::Match;
75use crate::matching::MatchFailureReason; 86use crate::matching::MatchFailureReason;
76use hir::Semantics; 87use hir::Semantics;
diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml
index c7d5e39fa..139214207 100644
--- a/crates/mbe/Cargo.toml
+++ b/crates/mbe/Cargo.toml
@@ -18,8 +18,8 @@ log = "0.4.8"
18syntax = { path = "../syntax", version = "0.0.0" } 18syntax = { path = "../syntax", version = "0.0.0" }
19parser = { path = "../parser", version = "0.0.0" } 19parser = { path = "../parser", version = "0.0.0" }
20tt = { path = "../tt", version = "0.0.0" } 20tt = { path = "../tt", version = "0.0.0" }
21test_utils = { path = "../test_utils", version = "0.0.0" }
22stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
23 22
24[dev-dependencies] 23[dev-dependencies]
25profile = { path = "../profile" } 24profile = { path = "../profile" }
25test_utils = { path = "../test_utils" }
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 6159d064c..6c0e22722 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -18,7 +18,7 @@
18//! // fn foo() {} 18//! // fn foo() {}
19//! ``` 19//! ```
20//! 20//!
21//! After adding a new inline-test, run `cargo xtask codegen` to 21//! After adding a new inline-test, run `cargo test -p xtask` to
22//! extract it as a standalone text-fixture into 22//! extract it as a standalone text-fixture into
23//! `crates/syntax/test_data/parser/`, and run `cargo test` once to 23//! `crates/syntax/test_data/parser/`, and run `cargo test` once to
24//! create the "gold" value. 24//! create the "gold" value.
diff --git a/crates/proc_macro_api/Cargo.toml b/crates/proc_macro_api/Cargo.toml
index f09726223..16fd56c7e 100644
--- a/crates/proc_macro_api/Cargo.toml
+++ b/crates/proc_macro_api/Cargo.toml
@@ -19,3 +19,6 @@ jod-thread = "0.1.1"
19tt = { path = "../tt", version = "0.0.0" } 19tt = { path = "../tt", version = "0.0.0" }
20base_db = { path = "../base_db", version = "0.0.0" } 20base_db = { path = "../base_db", version = "0.0.0" }
21stdx = { path = "../stdx", version = "0.0.0" } 21stdx = { path = "../stdx", version = "0.0.0" }
22snap = "1"
23object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] }
24memmap = "0.7.0"
diff --git a/crates/proc_macro_api/src/lib.rs b/crates/proc_macro_api/src/lib.rs
index 2ea456fb0..941d0fe9e 100644
--- a/crates/proc_macro_api/src/lib.rs
+++ b/crates/proc_macro_api/src/lib.rs
@@ -5,10 +5,12 @@
5//! is used to provide basic infrastructure for communication between two 5//! is used to provide basic infrastructure for communication between two
6//! processes: Client (RA itself), Server (the external program) 6//! processes: Client (RA itself), Server (the external program)
7 7
8mod rpc;
9mod process;
10pub mod msg; 8pub mod msg;
9mod process;
10mod rpc;
11mod version;
11 12
13use base_db::{Env, ProcMacro};
12use std::{ 14use std::{
13 ffi::OsStr, 15 ffi::OsStr,
14 io, 16 io,
@@ -16,7 +18,6 @@ use std::{
16 sync::Arc, 18 sync::Arc,
17}; 19};
18 20
19use base_db::{Env, ProcMacro};
20use tt::{SmolStr, Subtree}; 21use tt::{SmolStr, Subtree};
21 22
22use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; 23use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread};
@@ -75,6 +76,21 @@ impl ProcMacroClient {
75 } 76 }
76 77
77 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> { 78 pub fn by_dylib_path(&self, dylib_path: &Path) -> Vec<ProcMacro> {
79 match version::read_info(dylib_path) {
80 Ok(info) => {
81 if info.version.0 < 1 || info.version.1 < 47 {
82 eprintln!("proc-macro {} built by {:#?} is not supported by Rust Analyzer, please update your rust version.", dylib_path.to_string_lossy(), info);
83 }
84 }
85 Err(err) => {
86 eprintln!(
87 "proc-macro {} failed to find the given version. Reason: {}",
88 dylib_path.to_string_lossy(),
89 err
90 );
91 }
92 }
93
78 let macros = match self.process.find_proc_macros(dylib_path) { 94 let macros = match self.process.find_proc_macros(dylib_path) {
79 Err(err) => { 95 Err(err) => {
80 eprintln!("Failed to find proc macros. Error: {:#?}", err); 96 eprintln!("Failed to find proc macros. Error: {:#?}", err);
diff --git a/crates/proc_macro_api/src/version.rs b/crates/proc_macro_api/src/version.rs
new file mode 100644
index 000000000..11a7fb59a
--- /dev/null
+++ b/crates/proc_macro_api/src/version.rs
@@ -0,0 +1,132 @@
1//! Reading proc-macro rustc version information from binary data
2
3use std::{
4 fs::File,
5 io::{self, Read},
6 path::Path,
7};
8
9use memmap::Mmap;
10use object::read::{File as BinaryFile, Object, ObjectSection};
11use snap::read::FrameDecoder as SnapDecoder;
12
13#[derive(Debug)]
14pub(crate) struct RustCInfo {
15 pub(crate) version: (usize, usize, usize),
16 pub(crate) channel: String,
17 pub(crate) commit: String,
18 pub(crate) date: String,
19}
20
21pub(crate) fn read_info(dylib_path: &Path) -> io::Result<RustCInfo> {
22 macro_rules! err {
23 ($e:literal) => {
24 io::Error::new(io::ErrorKind::InvalidData, $e)
25 };
26 }
27
28 let ver_str = read_version(dylib_path)?;
29 let mut items = ver_str.split_whitespace();
30 let tag = items.next().ok_or(err!("version format error"))?;
31 if tag != "rustc" {
32 return Err(err!("version format error (No rustc tag)"));
33 }
34
35 let version_part = items.next().ok_or(err!("no version string"))?;
36 let mut version_parts = version_part.split("-");
37 let version = version_parts.next().ok_or(err!("no version"))?;
38 let channel = version_parts.next().unwrap_or_default().to_string();
39
40 let commit = items.next().ok_or(err!("no commit info"))?;
41 // remove (
42 if commit.len() == 0 {
43 return Err(err!("commit format error"));
44 }
45 let commit = commit[1..].to_string();
46 let date = items.next().ok_or(err!("no date info"))?;
47 // remove )
48 if date.len() == 0 {
49 return Err(err!("date format error"));
50 }
51 let date = date[0..date.len() - 2].to_string();
52
53 let version_numbers = version
54 .split(".")
55 .map(|it| it.parse::<usize>())
56 .collect::<Result<Vec<_>, _>>()
57 .map_err(|_| err!("version number error"))?;
58
59 if version_numbers.len() != 3 {
60 return Err(err!("version number format error"));
61 }
62 let version = (version_numbers[0], version_numbers[1], version_numbers[2]);
63
64 Ok(RustCInfo { version, channel, commit, date })
65}
66
67/// This is used inside read_version() to locate the ".rustc" section
68/// from a proc macro crate's binary file.
69fn read_section<'a>(dylib_binary: &'a [u8], section_name: &str) -> io::Result<&'a [u8]> {
70 BinaryFile::parse(dylib_binary)
71 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
72 .section_by_name(section_name)
73 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "section read error"))?
74 .data()
75 .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
76}
77
78/// Check the version of rustc that was used to compile a proc macro crate's
79///
80/// binary file.
81/// A proc macro crate binary's ".rustc" section has following byte layout:
82/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes
83/// * ff060000 734e6150 is followed, it's the snappy format magic bytes,
84/// means bytes from here(including this sequence) are compressed in
85/// snappy compression format. Version info is inside here, so decompress
86/// this.
87/// The bytes you get after decompressing the snappy format portion has
88/// following layout:
89/// * [b'r',b'u',b's',b't',0,0,0,5] is the first 8 bytes(again)
90/// * [crate root bytes] next 4 bytes is to store crate root position,
91/// according to rustc's source code comment
92/// * [length byte] next 1 byte tells us how many bytes we should read next
93/// for the version string's utf8 bytes
94/// * [version string bytes encoded in utf8] <- GET THIS BOI
95/// * [some more bytes that we don really care but still there] :-)
96/// Check this issue for more about the bytes layout:
97/// https://github.com/rust-analyzer/rust-analyzer/issues/6174
98fn read_version(dylib_path: &Path) -> io::Result<String> {
99 let dylib_file = File::open(dylib_path)?;
100 let dylib_mmaped = unsafe { Mmap::map(&dylib_file) }?;
101
102 let dot_rustc = read_section(&dylib_mmaped, ".rustc")?;
103
104 let header = &dot_rustc[..8];
105 const EXPECTED_HEADER: [u8; 8] = [b'r', b'u', b's', b't', 0, 0, 0, 5];
106 // check if header is valid
107 if header != EXPECTED_HEADER {
108 return Err(io::Error::new(
109 io::ErrorKind::InvalidData,
110 format!("only metadata version 5 is supported, section header was: {:?}", header),
111 ));
112 }
113
114 let snappy_portion = &dot_rustc[8..];
115
116 let mut snappy_decoder = SnapDecoder::new(snappy_portion);
117
118 // the bytes before version string bytes, so this basically is:
119 // 8 bytes for [b'r',b'u',b's',b't',0,0,0,5]
120 // 4 bytes for [crate root bytes]
121 // 1 byte for length of version string
122 // so 13 bytes in total, and we should check the 13th byte
123 // to know the length
124 let mut bytes_before_version = [0u8; 13];
125 snappy_decoder.read_exact(&mut bytes_before_version)?;
126 let length = bytes_before_version[12]; // what? can't use -1 indexing?
127
128 let mut version_string_utf8 = vec![0u8; length as usize];
129 snappy_decoder.read_exact(&mut version_string_utf8)?;
130 let version_string = String::from_utf8(version_string_utf8);
131 version_string.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
132}
diff --git a/crates/proc_macro_srv/Cargo.toml b/crates/proc_macro_srv/Cargo.toml
index 4c1b3036a..63b3f1532 100644
--- a/crates/proc_macro_srv/Cargo.toml
+++ b/crates/proc_macro_srv/Cargo.toml
@@ -17,9 +17,9 @@ memmap2 = "0.2.0"
17tt = { path = "../tt", version = "0.0.0" } 17tt = { path = "../tt", version = "0.0.0" }
18mbe = { path = "../mbe", version = "0.0.0" } 18mbe = { path = "../mbe", version = "0.0.0" }
19proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } 19proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" }
20test_utils = { path = "../test_utils", version = "0.0.0" }
21 20
22[dev-dependencies] 21[dev-dependencies]
22test_utils = { path = "../test_utils" }
23cargo_metadata = "0.13" 23cargo_metadata = "0.13"
24 24
25# used as proc macro test targets 25# used as proc macro test targets
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 25df13554..8af7871ac 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -395,6 +395,9 @@ impl Config {
395 pub fn work_done_progress(&self) -> bool { 395 pub fn work_done_progress(&self) -> bool {
396 try_or!(self.caps.window.as_ref()?.work_done_progress?, false) 396 try_or!(self.caps.window.as_ref()?.work_done_progress?, false)
397 } 397 }
398 pub fn will_rename(&self) -> bool {
399 try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false)
400 }
398 pub fn code_action_resolve(&self) -> bool { 401 pub fn code_action_resolve(&self) -> bool {
399 try_or!( 402 try_or!(
400 self.caps 403 self.caps
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 2c4c339cb..6cc433cb8 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -799,8 +799,18 @@ pub(crate) fn handle_rename(
799 let _p = profile::span("handle_rename"); 799 let _p = profile::span("handle_rename");
800 let position = from_proto::file_position(&snap, params.text_document_position)?; 800 let position = from_proto::file_position(&snap, params.text_document_position)?;
801 801
802 let change = 802 let mut change =
803 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?; 803 snap.analysis.rename(position, &*params.new_name)?.map_err(to_proto::rename_error)?;
804
805 // this is kind of a hack to prevent double edits from happening when moving files
806 // When a module gets renamed by renaming the mod declaration this causes the file to move
807 // which in turn will trigger a WillRenameFiles request to the server for which we reply with a
808 // a second identical set of renames, the client will then apply both edits causing incorrect edits
809 // with this we only emit source_file_edits in the WillRenameFiles response which will do the rename instead
810 // See https://github.com/microsoft/vscode-languageserver-node/issues/752 for more info
811 if !change.file_system_edits.is_empty() && snap.config.will_rename() {
812 change.source_file_edits.clear();
813 }
804 let workspace_edit = to_proto::workspace_edit(&snap, change)?; 814 let workspace_edit = to_proto::workspace_edit(&snap, change)?;
805 Ok(Some(workspace_edit)) 815 Ok(Some(workspace_edit))
806} 816}
@@ -1134,20 +1144,13 @@ pub(crate) fn handle_document_highlight(
1134 None 1144 None
1135 }; 1145 };
1136 1146
1137 let res = refs 1147 let file_refs = refs.references.get(&position.file_id).map_or(&[][..], Vec::as_slice);
1138 .references 1148 let mut res = Vec::with_capacity(file_refs.len() + 1);
1139 .get(&position.file_id) 1149 res.extend(decl);
1140 .map(|file_refs| { 1150 res.extend(file_refs.iter().map(|&(range, access)| DocumentHighlight {
1141 file_refs 1151 range: to_proto::range(&line_index, range),
1142 .into_iter() 1152 kind: access.map(to_proto::document_highlight_kind),
1143 .map(|&(range, access)| DocumentHighlight { 1153 }));
1144 range: to_proto::range(&line_index, range),
1145 kind: access.map(to_proto::document_highlight_kind),
1146 })
1147 .chain(decl)
1148 .collect()
1149 })
1150 .unwrap_or_default();
1151 Ok(Some(res)) 1154 Ok(Some(res))
1152} 1155}
1153 1156
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 4235eb6dd..1a8cdadad 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -6,9 +6,10 @@ use std::{
6 6
7use ide::{ 7use ide::{
8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, 8 Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind,
9 Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlPunct, 9 CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind,
10 HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat, Markup, NavigationTarget, 10 Highlight, HlMod, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, InsertTextFormat,
11 ReferenceAccess, RenameError, Runnable, Severity, SourceChange, TextEdit, TextRange, TextSize, 11 Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, SourceChange,
12 TextEdit, TextRange, TextSize,
12}; 13};
13use ide_db::SymbolKind; 14use ide_db::SymbolKind;
14use itertools::Itertools; 15use itertools::Itertools;
@@ -173,20 +174,14 @@ pub(crate) fn snippet_text_edit_vec(
173 174
174pub(crate) fn completion_item( 175pub(crate) fn completion_item(
175 line_index: &LineIndex, 176 line_index: &LineIndex,
176 completion_item: CompletionItem, 177 item: CompletionItem,
177) -> Vec<lsp_types::CompletionItem> { 178) -> Vec<lsp_types::CompletionItem> {
178 fn set_score(res: &mut lsp_types::CompletionItem, label: &str) {
179 res.preselect = Some(true);
180 // HACK: sort preselect items first
181 res.sort_text = Some(format!(" {}", label));
182 }
183
184 let mut additional_text_edits = Vec::new(); 179 let mut additional_text_edits = Vec::new();
185 let mut text_edit = None; 180 let mut text_edit = None;
186 // LSP does not allow arbitrary edits in completion, so we have to do a 181 // LSP does not allow arbitrary edits in completion, so we have to do a
187 // non-trivial mapping here. 182 // non-trivial mapping here.
188 let source_range = completion_item.source_range(); 183 let source_range = item.source_range();
189 for indel in completion_item.text_edit().iter() { 184 for indel in item.text_edit().iter() {
190 if indel.delete.contains_range(source_range) { 185 if indel.delete.contains_range(source_range) {
191 text_edit = Some(if indel.delete == source_range { 186 text_edit = Some(if indel.delete == source_range {
192 self::text_edit(line_index, indel.clone()) 187 self::text_edit(line_index, indel.clone())
@@ -207,46 +202,61 @@ pub(crate) fn completion_item(
207 } 202 }
208 let text_edit = text_edit.unwrap(); 203 let text_edit = text_edit.unwrap();
209 204
210 let mut res = lsp_types::CompletionItem { 205 let mut lsp_item = lsp_types::CompletionItem {
211 label: completion_item.label().to_string(), 206 label: item.label().to_string(),
212 detail: completion_item.detail().map(|it| it.to_string()), 207 detail: item.detail().map(|it| it.to_string()),
213 filter_text: Some(completion_item.lookup().to_string()), 208 filter_text: Some(item.lookup().to_string()),
214 kind: completion_item.kind().map(completion_item_kind), 209 kind: item.kind().map(completion_item_kind),
215 text_edit: Some(text_edit.into()), 210 text_edit: Some(text_edit.into()),
216 additional_text_edits: Some(additional_text_edits), 211 additional_text_edits: Some(additional_text_edits),
217 documentation: completion_item.documentation().map(documentation), 212 documentation: item.documentation().map(documentation),
218 deprecated: Some(completion_item.deprecated()), 213 deprecated: Some(item.deprecated()),
219 ..Default::default() 214 ..Default::default()
220 }; 215 };
221 216
222 if completion_item.score().is_some() { 217 fn set_score(res: &mut lsp_types::CompletionItem, relevance: CompletionRelevance) {
223 set_score(&mut res, completion_item.label()); 218 if relevance.is_relevant() {
219 res.preselect = Some(true);
220 }
221 // The relevance needs to be inverted to come up with a sort score
222 // because the client will sort ascending.
223 let sort_score = relevance.score() ^ 0xFF_FF_FF_FF;
224 // Zero pad the string to ensure values can be properly sorted
225 // by the client. Hex format is used because it is easier to
226 // visually compare very large values, which the sort text
227 // tends to be since it is the opposite of the score.
228 res.sort_text = Some(format!("{:08x}", sort_score));
224 } 229 }
225 230
226 if completion_item.deprecated() { 231 set_score(&mut lsp_item, item.relevance());
227 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) 232
233 if item.deprecated() {
234 lsp_item.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
228 } 235 }
229 236
230 if completion_item.trigger_call_info() { 237 if item.trigger_call_info() {
231 res.command = Some(command::trigger_parameter_hints()); 238 lsp_item.command = Some(command::trigger_parameter_hints());
232 } 239 }
233 240
234 let mut all_results = match completion_item.ref_match() { 241 let mut res = match item.ref_match() {
235 Some(ref_match) => { 242 Some((mutability, relevance)) => {
236 let mut refed = res.clone(); 243 let mut lsp_item_with_ref = lsp_item.clone();
237 let (mutability, _score) = ref_match; 244 set_score(&mut lsp_item_with_ref, relevance);
238 let label = format!("&{}{}", mutability.as_keyword_for_ref(), refed.label); 245 lsp_item_with_ref.label =
239 set_score(&mut refed, &label); 246 format!("&{}{}", mutability.as_keyword_for_ref(), lsp_item_with_ref.label);
240 refed.label = label; 247 if let Some(lsp_types::CompletionTextEdit::Edit(it)) = &mut lsp_item_with_ref.text_edit
241 vec![res, refed] 248 {
249 it.new_text = format!("&{}{}", mutability.as_keyword_for_ref(), it.new_text);
250 }
251 vec![lsp_item_with_ref, lsp_item]
242 } 252 }
243 None => vec![res], 253 None => vec![lsp_item],
244 }; 254 };
245 255
246 for mut r in all_results.iter_mut() { 256 for lsp_item in res.iter_mut() {
247 r.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); 257 lsp_item.insert_text_format = Some(insert_text_format(item.insert_text_format()));
248 } 258 }
249 all_results 259 res
250} 260}
251 261
252pub(crate) fn signature_help( 262pub(crate) fn signature_help(
@@ -1105,13 +1115,15 @@ mod tests {
1105 expect_test::expect![[r#" 1115 expect_test::expect![[r#"
1106 [ 1116 [
1107 ( 1117 (
1108 "arg", 1118 "&arg",
1109 None, 1119 Some(
1120 "fffffffd",
1121 ),
1110 ), 1122 ),
1111 ( 1123 (
1112 "&arg", 1124 "arg",
1113 Some( 1125 Some(
1114 " &arg", 1126 "fffffffe",
1115 ), 1127 ),
1116 ), 1128 ),
1117 ] 1129 ]
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml
index 33bde099b..05fca5dc4 100644
--- a/crates/syntax/Cargo.toml
+++ b/crates/syntax/Cargo.toml
@@ -14,7 +14,7 @@ doctest = false
14cov-mark = "1.1" 14cov-mark = "1.1"
15itertools = "0.10.0" 15itertools = "0.10.0"
16rowan = "0.12.2" 16rowan = "0.12.2"
17rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } 17rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" }
18rustc-hash = "1.1.0" 18rustc-hash = "1.1.0"
19arrayvec = "0.5.1" 19arrayvec = "0.5.1"
20once_cell = "1.3.1" 20once_cell = "1.3.1"