diff options
Diffstat (limited to 'crates')
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 | ||
536 | impl Struct { | 535 | impl 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 | ||
620 | impl Enum { | 611 | impl 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 | ||
1002 | impl Trait { | 989 | impl 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" } | |||
28 | syntax = { path = "../syntax", version = "0.0.0" } | 28 | syntax = { path = "../syntax", version = "0.0.0" } |
29 | profile = { path = "../profile", version = "0.0.0" } | 29 | profile = { path = "../profile", version = "0.0.0" } |
30 | hir_expand = { path = "../hir_expand", version = "0.0.0" } | 30 | hir_expand = { path = "../hir_expand", version = "0.0.0" } |
31 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
32 | mbe = { path = "../mbe", version = "0.0.0" } | 31 | mbe = { path = "../mbe", version = "0.0.0" } |
33 | cfg = { path = "../cfg", version = "0.0.0" } | 32 | cfg = { path = "../cfg", version = "0.0.0" } |
34 | tt = { path = "../tt", version = "0.0.0" } | 33 | tt = { path = "../tt", version = "0.0.0" } |
35 | 34 | ||
36 | [dev-dependencies] | 35 | [dev-dependencies] |
36 | test_utils = { path = "../test_utils" } | ||
37 | expect-test = "1.1" | 37 | expect-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 | }; |
27 | use cfg::CfgOptions; | 26 | use cfg::CfgOptions; |
28 | 27 | ||
@@ -92,10 +91,10 @@ fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> { | |||
92 | impl StructData { | 91 | impl 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 { | |||
126 | impl EnumData { | 125 | impl 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 | ||
4 | use std::{any::type_name, mem, sync::Arc}; | 4 | use std::mem; |
5 | 5 | ||
6 | use either::Either; | 6 | use either::Either; |
7 | use hir_expand::{ | 7 | use 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 | }; |
12 | use la_arena::Arena; | 12 | use la_arena::Arena; |
13 | use profile::Count; | 13 | use profile::Count; |
14 | use rustc_hash::FxHashMap; | ||
15 | use syntax::{ | 14 | use 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 | ||
42 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; | 39 | use super::{diagnostics::BodyDiagnostic, ExprSource, PatSource}; |
@@ -60,15 +57,12 @@ impl LowerCtx { | |||
60 | 57 | ||
61 | pub(super) fn lower( | 58 | pub(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 | ||
92 | struct ExprCollector<'a> { | 81 | struct 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 | ||
102 | impl ExprCollector<'_> { | 88 | impl 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 | ||
19 | pub trait ChildBySource { | 19 | pub trait ChildBySource { |
20 | fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap; | ||
21 | } | ||
22 | |||
23 | impl 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 | ||
28 | impl 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 | ||
49 | impl ChildBySource for ImplId { | 50 | impl 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 | ||
75 | impl ChildBySource for ModuleId { | 72 | impl 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 | ||
83 | impl ChildBySource for ItemScope { | 80 | impl 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 | ||
136 | impl ChildBySource for VariantId { | 131 | impl 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 | ||
157 | impl ChildBySource for EnumId { | 149 | impl 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 | ||
172 | impl ChildBySource for DefWithBodyId { | 160 | impl 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 | ||
423 | impl ChildBySource for GenericDefId { | 423 | impl 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)] |
110 | pub struct ItemLoc<N: ItemTreeNode> { | 110 | pub 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 { | |||
279 | pub type LocalConstParamId = Idx<generics::ConstParamData>; | 279 | pub type LocalConstParamId = Idx<generics::ConstParamData>; |
280 | 280 | ||
281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 281 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
282 | pub enum ContainerId { | ||
283 | ModuleId(ModuleId), | ||
284 | DefWithBodyId(DefWithBodyId), | ||
285 | } | ||
286 | |||
287 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
288 | pub enum AssocContainerId { | 282 | pub enum AssocContainerId { |
289 | ContainerId(ContainerId), | 283 | ModuleId(ModuleId), |
290 | ImplId(ImplId), | 284 | ImplId(ImplId), |
291 | TraitId(TraitId), | 285 | TraitId(TraitId), |
292 | } | 286 | } |
293 | impl_from!(ContainerId for AssocContainerId); | 287 | impl_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 | ||
450 | impl 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 | |||
459 | impl HasModule for AssocContainerId { | 444 | impl 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 | ||
486 | impl HasModule for VariantId { | 470 | impl 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 | ||
530 | impl HasModule for StaticLoc { | 514 | impl 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 | ||
45 | const GLOB_RECURSION_LIMIT: usize = 100; | 45 | const 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 | ||
681 | impl 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 | |||
690 | impl HasResolver for AssocContainerId { | 691 | impl 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" | |||
16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } | 16 | la-arena = { version = "0.2.0", path = "../../lib/arena" } |
17 | 17 | ||
18 | base_db = { path = "../base_db", version = "0.0.0" } | 18 | base_db = { path = "../base_db", version = "0.0.0" } |
19 | cfg = { path = "../cfg", version = "0.0.0" } | ||
19 | syntax = { path = "../syntax", version = "0.0.0" } | 20 | syntax = { path = "../syntax", version = "0.0.0" } |
20 | parser = { path = "../parser", version = "0.0.0" } | 21 | parser = { path = "../parser", version = "0.0.0" } |
21 | profile = { path = "../profile", version = "0.0.0" } | 22 | profile = { path = "../profile", version = "0.0.0" } |
22 | tt = { path = "../tt", version = "0.0.0" } | 23 | tt = { path = "../tt", version = "0.0.0" } |
23 | mbe = { path = "../mbe", version = "0.0.0" } | 24 | mbe = { path = "../mbe", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | 25 | |
26 | [dev-dependencies] | ||
27 | test_utils = { path = "../test_utils" } | ||
28 | expect-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)] |
268 | mod tests { | 268 | mod 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 | ||
7 | use base_db::{AnchoredPath, FileId}; | 7 | use base_db::{AnchoredPath, FileId}; |
8 | use cfg::CfgExpr; | ||
8 | use either::Either; | 9 | use either::Either; |
9 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; | 10 | use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult}; |
10 | use parser::FragmentKind; | 11 | use 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 | ||
263 | fn 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 | |||
261 | fn unquote_str(lit: &tt::Literal) -> Option<String> { | 275 | fn 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 | ||
189 | impl_to_to_tokentrees! { | 189 | impl_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" } | |||
29 | base_db = { path = "../base_db", version = "0.0.0" } | 29 | base_db = { path = "../base_db", version = "0.0.0" } |
30 | profile = { path = "../profile", version = "0.0.0" } | 30 | profile = { path = "../profile", version = "0.0.0" } |
31 | syntax = { path = "../syntax", version = "0.0.0" } | 31 | syntax = { path = "../syntax", version = "0.0.0" } |
32 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
33 | 32 | ||
34 | [dev-dependencies] | 33 | [dev-dependencies] |
34 | test_utils = { path = "../test_utils" } | ||
35 | expect-test = "1.1" | 35 | expect-test = "1.1" |
36 | tracing = "0.1" | 36 | tracing = "0.1" |
37 | tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] } | 37 | tracing-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 | ||
715 | mod permissions; | ||
716 | |||
717 | use permissions::jwt; | ||
718 | |||
719 | fn 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 | ||
725 | pub mod jwt { | ||
726 | pub struct Claims {} | ||
727 | } | ||
728 | |||
729 | //- /jwt/lib.rs crate:jwt | ||
730 | pub 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; | |||
6 | use chalk_ir::Mutability; | 6 | use chalk_ir::Mutability; |
7 | use hir_def::{ | 7 | use 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 | }; |
11 | use hir_expand::name::Name; | 11 | use hir_expand::name::Name; |
12 | 12 | ||
@@ -611,7 +611,7 @@ impl HirDisplay for CallableSig { | |||
611 | } | 611 | } |
612 | 612 | ||
613 | fn fn_traits(db: &dyn DefDatabase, trait_: TraitId) -> impl Iterator<Item = TraitId> { | 613 | fn 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}; | |||
13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; | 13 | use base_db::{fixture::WithFixture, FileRange, SourceDatabase, SourceDatabaseExt}; |
14 | use expect_test::Expect; | 14 | use expect_test::Expect; |
15 | use hir_def::{ | 15 | use 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 | ||
293 | fn ellipsize(mut text: String, max_len: usize) -> String { | 301 | fn 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] |
3177 | fn trait_in_scope_with_inner_item() { | ||
3178 | check_infer( | ||
3179 | r#" | ||
3180 | mod m { | ||
3181 | pub trait Tr { | ||
3182 | fn method(&self) -> u8 { 0 } | ||
3183 | } | ||
3184 | |||
3185 | impl Tr for () {} | ||
3186 | } | ||
3187 | |||
3188 | use m::Tr; | ||
3189 | |||
3190 | fn 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] | ||
3177 | fn inner_use_in_block() { | 3210 | fn 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" } | |||
27 | ide_db = { path = "../ide_db", version = "0.0.0" } | 27 | ide_db = { path = "../ide_db", version = "0.0.0" } |
28 | cfg = { path = "../cfg", version = "0.0.0" } | 28 | cfg = { path = "../cfg", version = "0.0.0" } |
29 | profile = { path = "../profile", version = "0.0.0" } | 29 | profile = { path = "../profile", version = "0.0.0" } |
30 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
31 | ide_assists = { path = "../ide_assists", version = "0.0.0" } | 30 | ide_assists = { path = "../ide_assists", version = "0.0.0" } |
32 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } | 31 | ide_ssr = { path = "../ide_ssr", version = "0.0.0" } |
33 | ide_completion = { path = "../ide_completion", version = "0.0.0" } | 32 | ide_completion = { path = "../ide_completion", version = "0.0.0" } |
@@ -37,4 +36,5 @@ ide_completion = { path = "../ide_completion", version = "0.0.0" } | |||
37 | hir = { path = "../hir", version = "0.0.0" } | 36 | hir = { path = "../hir", version = "0.0.0" } |
38 | 37 | ||
39 | [dev-dependencies] | 38 | [dev-dependencies] |
39 | test_utils = { path = "../test_utils" } | ||
40 | expect-test = "1.1" | 40 | expect-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 | ||
72 | impl ShortLabel for ast::Const { | 72 | impl 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 @@ | |||
1 | use either::Either; | ||
1 | use hir::{ | 2 | use 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 | ||
409 | fn 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 | |||
408 | fn hover_for_keyword( | 432 | fn 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; | |||
41 | mod references; | 41 | mod references; |
42 | mod fn_references; | 42 | mod fn_references; |
43 | mod runnables; | 43 | mod runnables; |
44 | mod ssr; | ||
44 | mod status; | 45 | mod status; |
45 | mod syntax_highlighting; | 46 | mod syntax_highlighting; |
46 | mod syntax_tree; | 47 | mod syntax_tree; |
@@ -51,6 +52,7 @@ mod doc_links; | |||
51 | use std::sync::Arc; | 52 | use std::sync::Arc; |
52 | 53 | ||
53 | use cfg::CfgOptions; | 54 | use cfg::CfgOptions; |
55 | |||
54 | use ide_db::base_db::{ | 56 | use 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::{ | |||
85 | pub use hir::{Documentation, Semantics}; | 87 | pub use hir::{Documentation, Semantics}; |
86 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; | 88 | pub use ide_assists::{Assist, AssistConfig, AssistId, AssistKind}; |
87 | pub use ide_completion::{ | 89 | pub use ide_completion::{ |
88 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionScore, ImportEdit, | 90 | CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance, ImportEdit, |
89 | InsertTextFormat, | 91 | InsertTextFormat, |
90 | }; | 92 | }; |
91 | pub use ide_db::{ | 93 | pub 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 | ||
22 | type RenameResult<T> = Result<T, RenameError>; | 22 | type RenameResult<T> = Result<T, RenameError>; |
23 | #[derive(Debug)] | 23 | #[derive(Debug)] |
24 | pub struct RenameError(pub(crate) String); | 24 | pub struct RenameError(String); |
25 | 25 | ||
26 | impl fmt::Display for RenameError { | 26 | impl 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. | ||
97 | pub(crate) fn will_rename_file( | 97 | pub(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 | } | ||
624 | foo!(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 | |||
5 | use ide_assists::{Assist, AssistId, AssistKind, GroupLabel}; | ||
6 | use ide_db::{base_db::FileRange, label::Label, source_change::SourceChange, RootDatabase}; | ||
7 | |||
8 | pub(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)] | ||
51 | mod 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" } | |||
21 | profile = { path = "../profile", version = "0.0.0" } | 21 | profile = { path = "../profile", version = "0.0.0" } |
22 | ide_db = { path = "../ide_db", version = "0.0.0" } | 22 | ide_db = { path = "../ide_db", version = "0.0.0" } |
23 | hir = { path = "../hir", version = "0.0.0" } | 23 | hir = { path = "../hir", version = "0.0.0" } |
24 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
25 | 24 | ||
26 | [dev-dependencies] | 25 | [dev-dependencies] |
26 | test_utils = { path = "../test_utils" } | ||
27 | expect-test = "1.1" | 27 | expect-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#" | ||
281 | fn make<T>() -> T {} | ||
282 | fn main() { | ||
283 | let x = make$0() | ||
284 | } | ||
285 | "#, | ||
286 | r#" | ||
287 | fn make<T>() -> T {} | ||
288 | fn 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 @@ | |||
1 | use ide_db::helpers::{ | 1 | use 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 | }; |
6 | use syntax::{ast, AstNode, SyntaxNode}; | 6 | use 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 | ||
129 | fn import_group_message(import_candidate: &ImportCandidate) -> GroupLabel { | 125 | fn 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; | |||
3 | use hir::AsAssocItem; | 3 | use hir::AsAssocItem; |
4 | use ide_db::helpers::{ | 4 | use 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 | }; |
8 | use ide_db::RootDatabase; | 8 | use ide_db::RootDatabase; |
9 | use syntax::{ | 9 | use 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 | ||
202 | fn label(db: &RootDatabase, candidate: &ImportCandidate, import: &LocatedImport) -> String { | 202 | fn 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" } | |||
22 | base_db = { path = "../base_db", version = "0.0.0" } | 22 | base_db = { path = "../base_db", version = "0.0.0" } |
23 | ide_db = { path = "../ide_db", version = "0.0.0" } | 23 | ide_db = { path = "../ide_db", version = "0.0.0" } |
24 | profile = { path = "../profile", version = "0.0.0" } | 24 | profile = { path = "../profile", version = "0.0.0" } |
25 | test_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`. |
29 | hir = { path = "../hir", version = "0.0.0" } | 28 | hir = { path = "../hir", version = "0.0.0" } |
30 | 29 | ||
31 | [dev-dependencies] | 30 | [dev-dependencies] |
31 | test_utils = { path = "../test_utils" } | ||
32 | expect-test = "1.1" | 32 | expect-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 | ||
167 | fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { | 164 | fn 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 | ||
10 | fn snippet(ctx: &CompletionContext, cap: SnippetCap, label: &str, snippet: &str) -> Builder { | 10 | fn 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 | ||
16 | pub(crate) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 16 | pub(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 { | |||
62 | fn ${1:feature}() { | 62 | fn ${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 | ||
196 | fn add_const_impl( | 195 | fn 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)] |
115 | pub enum CompletionScore { | 126 | pub 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 | |||
149 | impl 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 | ||
317 | impl Builder { | 381 | impl 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)] | ||
504 | mod 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 | ||
24 | pub use crate::{ | 24 | pub 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::{ | |||
20 | use syntax::TextRange; | 20 | use syntax::TextRange; |
21 | 21 | ||
22 | use crate::{ | 22 | use crate::{ |
23 | item::ImportEdit, CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, | 23 | item::{CompletionRelevance, ImportEdit}, |
24 | CompletionScore, | 24 | CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, |
25 | }; | 25 | }; |
26 | 26 | ||
27 | use crate::render::{enum_variant::render_variant, function::render_fn, macro_::render_macro}; | 27 | use 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 | ||
320 | fn compute_score_from_active( | 325 | fn 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 | } |
341 | fn 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 | |||
355 | fn 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)] |
361 | mod tests { | 334 | mod 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#" |
860 | struct S { foo: i64, bar: u32, baz: u32 } | 836 | struct S { foo: i64, bar: u32, baz: u32 } |
861 | fn test(bar: u32) { } | 837 | fn 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#" |
877 | struct A { foo: i64, bar: u32, baz: u32 } | 853 | struct A { foo: i64, bar: u32, baz: u32 } |
878 | struct B { x: (), y: f32, bar: u32 } | 854 | struct 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#" |
893 | struct A { foo: i64, bar: u32, baz: u32 } | 869 | struct A { foo: i64, bar: u32, baz: u32 } |
894 | struct B { x: (), y: f32, bar: u32 } | 870 | struct 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#" |
906 | struct A { foo: i64, bar: u32, baz: u32 } | 882 | struct A { foo: i64, bar: u32, baz: u32 } |
907 | struct B { x: (), y: f32, bar: u32 } | 883 | struct 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#" |
923 | struct WorldSnapshot { _f: () }; | 899 | struct WorldSnapshot { _f: () }; |
924 | fn go(world: &WorldSnapshot) { go(w$0) } | 900 | fn 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#" |
938 | struct Foo; | 914 | struct Foo; |
939 | fn f(foo: &Foo) { f(foo, w$0) } | 915 | fn 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#" | ||
930 | struct S; | ||
931 | fn foo(s: &mut S) {} | ||
932 | fn 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 | ||
87 | fn render_pat( | 87 | fn 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" } | |||
24 | text_edit = { path = "../text_edit", version = "0.0.0" } | 24 | text_edit = { path = "../text_edit", version = "0.0.0" } |
25 | base_db = { path = "../base_db", version = "0.0.0" } | 25 | base_db = { path = "../base_db", version = "0.0.0" } |
26 | profile = { path = "../profile", version = "0.0.0" } | 26 | profile = { path = "../profile", version = "0.0.0" } |
27 | test_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`. |
30 | hir = { path = "../hir", version = "0.0.0" } | 29 | hir = { path = "../hir", version = "0.0.0" } |
31 | 30 | ||
32 | [dev-dependencies] | 31 | [dev-dependencies] |
32 | test_utils = { path = "../test_utils" } | ||
33 | expect-test = "1.1" | 33 | expect-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" } | |||
19 | syntax = { path = "../syntax", version = "0.0.0" } | 19 | syntax = { path = "../syntax", version = "0.0.0" } |
20 | ide_db = { path = "../ide_db", version = "0.0.0" } | 20 | ide_db = { path = "../ide_db", version = "0.0.0" } |
21 | hir = { path = "../hir", version = "0.0.0" } | 21 | hir = { path = "../hir", version = "0.0.0" } |
22 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
23 | 22 | ||
24 | [dev-dependencies] | 23 | [dev-dependencies] |
24 | test_utils = { path = "../test_utils" } | ||
25 | expect-test = "1.1" | 25 | expect-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 | |||
4 | use ide_db::{ | ||
5 | base_db::{FilePosition, FileRange, SourceDatabase}, | ||
6 | RootDatabase, | ||
7 | }; | ||
8 | use syntax::{ | ||
9 | ast::{self, AstNode, AstToken}, | ||
10 | TextRange, | ||
11 | }; | ||
12 | |||
13 | use 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. | ||
18 | pub 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 | ||
70 | mod from_comment; | ||
61 | mod matching; | 71 | mod matching; |
62 | mod nester; | 72 | mod nester; |
63 | mod parsing; | 73 | mod parsing; |
@@ -71,6 +81,7 @@ mod tests; | |||
71 | 81 | ||
72 | use crate::errors::bail; | 82 | use crate::errors::bail; |
73 | pub use crate::errors::SsrError; | 83 | pub use crate::errors::SsrError; |
84 | pub use crate::from_comment::ssr_from_comment; | ||
74 | pub use crate::matching::Match; | 85 | pub use crate::matching::Match; |
75 | use crate::matching::MatchFailureReason; | 86 | use crate::matching::MatchFailureReason; |
76 | use hir::Semantics; | 87 | use 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" | |||
18 | syntax = { path = "../syntax", version = "0.0.0" } | 18 | syntax = { path = "../syntax", version = "0.0.0" } |
19 | parser = { path = "../parser", version = "0.0.0" } | 19 | parser = { path = "../parser", version = "0.0.0" } |
20 | tt = { path = "../tt", version = "0.0.0" } | 20 | tt = { path = "../tt", version = "0.0.0" } |
21 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
22 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
23 | 22 | ||
24 | [dev-dependencies] | 23 | [dev-dependencies] |
25 | profile = { path = "../profile" } | 24 | profile = { path = "../profile" } |
25 | test_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" | |||
19 | tt = { path = "../tt", version = "0.0.0" } | 19 | tt = { path = "../tt", version = "0.0.0" } |
20 | base_db = { path = "../base_db", version = "0.0.0" } | 20 | base_db = { path = "../base_db", version = "0.0.0" } |
21 | stdx = { path = "../stdx", version = "0.0.0" } | 21 | stdx = { path = "../stdx", version = "0.0.0" } |
22 | snap = "1" | ||
23 | object = { version = "0.23.0", default-features = false, features = ["std", "read_core", "elf", "macho", "pe", "unaligned"] } | ||
24 | memmap = "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 | ||
8 | mod rpc; | ||
9 | mod process; | ||
10 | pub mod msg; | 8 | pub mod msg; |
9 | mod process; | ||
10 | mod rpc; | ||
11 | mod version; | ||
11 | 12 | ||
13 | use base_db::{Env, ProcMacro}; | ||
12 | use std::{ | 14 | use 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 | ||
19 | use base_db::{Env, ProcMacro}; | ||
20 | use tt::{SmolStr, Subtree}; | 21 | use tt::{SmolStr, Subtree}; |
21 | 22 | ||
22 | use crate::process::{ProcMacroProcessSrv, ProcMacroProcessThread}; | 23 | use 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 | |||
3 | use std::{ | ||
4 | fs::File, | ||
5 | io::{self, Read}, | ||
6 | path::Path, | ||
7 | }; | ||
8 | |||
9 | use memmap::Mmap; | ||
10 | use object::read::{File as BinaryFile, Object, ObjectSection}; | ||
11 | use snap::read::FrameDecoder as SnapDecoder; | ||
12 | |||
13 | #[derive(Debug)] | ||
14 | pub(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 | |||
21 | pub(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. | ||
69 | fn 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 | ||
98 | fn 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" | |||
17 | tt = { path = "../tt", version = "0.0.0" } | 17 | tt = { path = "../tt", version = "0.0.0" } |
18 | mbe = { path = "../mbe", version = "0.0.0" } | 18 | mbe = { path = "../mbe", version = "0.0.0" } |
19 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } | 19 | proc_macro_api = { path = "../proc_macro_api", version = "0.0.0" } |
20 | test_utils = { path = "../test_utils", version = "0.0.0" } | ||
21 | 20 | ||
22 | [dev-dependencies] | 21 | [dev-dependencies] |
22 | test_utils = { path = "../test_utils" } | ||
23 | cargo_metadata = "0.13" | 23 | cargo_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 | ||
7 | use ide::{ | 7 | use 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 | }; |
13 | use ide_db::SymbolKind; | 14 | use ide_db::SymbolKind; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
@@ -173,20 +174,14 @@ pub(crate) fn snippet_text_edit_vec( | |||
173 | 174 | ||
174 | pub(crate) fn completion_item( | 175 | pub(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 | ||
252 | pub(crate) fn signature_help( | 262 | pub(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 | |||
14 | cov-mark = "1.1" | 14 | cov-mark = "1.1" |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "0.12.2" | 16 | rowan = "0.12.2" |
17 | rustc_lexer = { version = "709.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "710.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.5.1" | 19 | arrayvec = "0.5.1" |
20 | once_cell = "1.3.1" | 20 | once_cell = "1.3.1" |