diff options
-rw-r--r-- | crates/hir/src/lib.rs | 58 | ||||
-rw-r--r-- | crates/hir_ty/src/method_resolution.rs | 10 | ||||
-rw-r--r-- | crates/ide/src/annotations.rs | 87 | ||||
-rw-r--r-- | crates/ide/src/goto_implementation.rs | 130 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 60 | ||||
-rw-r--r-- | crates/ide_completion/src/context.rs | 297 | ||||
-rw-r--r-- | crates/ide_completion/src/render.rs | 25 | ||||
-rw-r--r-- | crates/ide_db/src/helpers.rs | 28 |
9 files changed, 531 insertions, 166 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 25e5bfb01..c5161dadd 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -51,7 +51,8 @@ use hir_expand::{diagnostics::DiagnosticSink, name::name, MacroDefKind}; | |||
51 | use hir_ty::{ | 51 | use hir_ty::{ |
52 | autoderef, | 52 | autoderef, |
53 | display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, | 53 | display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, |
54 | method_resolution, to_assoc_type_id, | 54 | method_resolution::{self, TyFingerprint}, |
55 | to_assoc_type_id, | ||
55 | traits::{FnTrait, Solution, SolutionVariables}, | 56 | traits::{FnTrait, Solution, SolutionVariables}, |
56 | AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, | 57 | AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, |
57 | InEnvironment, Interner, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, Ty, | 58 | InEnvironment, Interner, Obligation, ProjectionPredicate, ProjectionTy, Scalar, Substs, Ty, |
@@ -695,8 +696,8 @@ impl Adt { | |||
695 | } | 696 | } |
696 | } | 697 | } |
697 | 698 | ||
698 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | 699 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { |
699 | Some(self.module(db).krate()) | 700 | self.module(db).krate() |
700 | } | 701 | } |
701 | 702 | ||
702 | pub fn name(self, db: &dyn HirDatabase) -> Name { | 703 | pub fn name(self, db: &dyn HirDatabase) -> Name { |
@@ -1018,8 +1019,8 @@ impl TypeAlias { | |||
1018 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } | 1019 | Module { id: self.id.lookup(db.upcast()).module(db.upcast()) } |
1019 | } | 1020 | } |
1020 | 1021 | ||
1021 | pub fn krate(self, db: &dyn HirDatabase) -> Option<Crate> { | 1022 | pub fn krate(self, db: &dyn HirDatabase) -> Crate { |
1022 | Some(self.module(db).krate()) | 1023 | self.module(db).krate() |
1023 | } | 1024 | } |
1024 | 1025 | ||
1025 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { | 1026 | pub fn type_ref(self, db: &dyn HirDatabase) -> Option<TypeRef> { |
@@ -1482,9 +1483,44 @@ impl Impl { | |||
1482 | 1483 | ||
1483 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() | 1484 | inherent.all_impls().chain(trait_.all_impls()).map(Self::from).collect() |
1484 | } | 1485 | } |
1485 | pub fn for_trait(db: &dyn HirDatabase, krate: Crate, trait_: Trait) -> Vec<Impl> { | 1486 | |
1486 | let impls = db.trait_impls_in_crate(krate.id); | 1487 | pub fn all_for_type(db: &dyn HirDatabase, Type { krate, ty }: Type) -> Vec<Impl> { |
1487 | impls.for_trait(trait_.id).map(Self::from).collect() | 1488 | let def_crates = match ty.value.def_crates(db, krate) { |
1489 | Some(def_crates) => def_crates, | ||
1490 | None => return Vec::new(), | ||
1491 | }; | ||
1492 | |||
1493 | let filter = |impl_def: &Impl| { | ||
1494 | let target_ty = impl_def.target_ty(db); | ||
1495 | let rref = target_ty.remove_ref(); | ||
1496 | ty.value.equals_ctor(rref.as_ref().map_or(&target_ty.ty.value, |it| &it.ty.value)) | ||
1497 | }; | ||
1498 | |||
1499 | let mut all = Vec::new(); | ||
1500 | def_crates.into_iter().for_each(|id| { | ||
1501 | all.extend(db.inherent_impls_in_crate(id).all_impls().map(Self::from).filter(filter)) | ||
1502 | }); | ||
1503 | let fp = TyFingerprint::for_impl(&ty.value); | ||
1504 | for id in db.crate_graph().iter() { | ||
1505 | match fp { | ||
1506 | Some(fp) => all.extend( | ||
1507 | db.trait_impls_in_crate(id).for_self_ty(fp).map(Self::from).filter(filter), | ||
1508 | ), | ||
1509 | None => all | ||
1510 | .extend(db.trait_impls_in_crate(id).all_impls().map(Self::from).filter(filter)), | ||
1511 | } | ||
1512 | } | ||
1513 | all | ||
1514 | } | ||
1515 | |||
1516 | pub fn all_for_trait(db: &dyn HirDatabase, trait_: Trait) -> Vec<Impl> { | ||
1517 | let krate = trait_.module(db).krate(); | ||
1518 | let mut all = Vec::new(); | ||
1519 | for Crate { id } in krate.reverse_dependencies(db).into_iter().chain(Some(krate)) { | ||
1520 | let impls = db.trait_impls_in_crate(id); | ||
1521 | all.extend(impls.for_trait(trait_.id).map(Self::from)) | ||
1522 | } | ||
1523 | all | ||
1488 | } | 1524 | } |
1489 | 1525 | ||
1490 | // FIXME: the return type is wrong. This should be a hir version of | 1526 | // FIXME: the return type is wrong. This should be a hir version of |
@@ -1932,12 +1968,6 @@ impl Type { | |||
1932 | self.ty.value.associated_type_parent_trait(db).map(Into::into) | 1968 | self.ty.value.associated_type_parent_trait(db).map(Into::into) |
1933 | } | 1969 | } |
1934 | 1970 | ||
1935 | // FIXME: provide required accessors such that it becomes implementable from outside. | ||
1936 | pub fn is_equal_for_find_impls(&self, other: &Type) -> bool { | ||
1937 | let rref = other.remove_ref(); | ||
1938 | self.ty.value.equals_ctor(rref.as_ref().map_or(&other.ty.value, |it| &it.ty.value)) | ||
1939 | } | ||
1940 | |||
1941 | fn derived(&self, ty: Ty) -> Type { | 1971 | fn derived(&self, ty: Ty) -> Type { |
1942 | Type { | 1972 | Type { |
1943 | krate: self.krate, | 1973 | krate: self.krate, |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 741440006..be72c4a1c 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -44,7 +44,7 @@ impl TyFingerprint { | |||
44 | /// Creates a TyFingerprint for looking up an impl. Only certain types can | 44 | /// Creates a TyFingerprint for looking up an impl. Only certain types can |
45 | /// have impls: if we have some `struct S`, we can have an `impl S`, but not | 45 | /// have impls: if we have some `struct S`, we can have an `impl S`, but not |
46 | /// `impl &S`. Hence, this will return `None` for reference types and such. | 46 | /// `impl &S`. Hence, this will return `None` for reference types and such. |
47 | pub(crate) fn for_impl(ty: &Ty) -> Option<TyFingerprint> { | 47 | pub fn for_impl(ty: &Ty) -> Option<TyFingerprint> { |
48 | let fp = match *ty.interned(&Interner) { | 48 | let fp = match *ty.interned(&Interner) { |
49 | TyKind::Str => TyFingerprint::Str, | 49 | TyKind::Str => TyFingerprint::Str, |
50 | TyKind::Never => TyFingerprint::Never, | 50 | TyKind::Never => TyFingerprint::Never, |
@@ -141,6 +141,14 @@ impl TraitImpls { | |||
141 | } | 141 | } |
142 | } | 142 | } |
143 | 143 | ||
144 | /// Queries all trait impls for the given type. | ||
145 | pub fn for_self_ty(&self, fp: TyFingerprint) -> impl Iterator<Item = ImplId> + '_ { | ||
146 | self.map | ||
147 | .values() | ||
148 | .flat_map(move |impls| impls.get(&None).into_iter().chain(impls.get(&Some(fp)))) | ||
149 | .flat_map(|it| it.iter().copied()) | ||
150 | } | ||
151 | |||
144 | /// Queries all impls of the given trait. | 152 | /// Queries all impls of the given trait. |
145 | pub fn for_trait(&self, trait_: TraitId) -> impl Iterator<Item = ImplId> + '_ { | 153 | pub fn for_trait(&self, trait_: TraitId) -> impl Iterator<Item = ImplId> + '_ { |
146 | self.map | 154 | self.map |
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 8e0a8fd8d..72492f826 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs | |||
@@ -1,17 +1,18 @@ | |||
1 | use hir::Semantics; | 1 | use either::Either; |
2 | use hir::{HasSource, Semantics}; | ||
2 | use ide_db::{ | 3 | use ide_db::{ |
3 | base_db::{FileId, FilePosition, FileRange, SourceDatabase}, | 4 | base_db::{FileId, FilePosition, FileRange}, |
4 | RootDatabase, SymbolKind, | 5 | helpers::visit_file_defs, |
6 | RootDatabase, | ||
5 | }; | 7 | }; |
6 | use syntax::TextRange; | 8 | use syntax::{ast::NameOwner, AstNode, TextRange, TextSize}; |
7 | 9 | ||
8 | use crate::{ | 10 | use crate::{ |
9 | file_structure::file_structure, | ||
10 | fn_references::find_all_methods, | 11 | fn_references::find_all_methods, |
11 | goto_implementation::goto_implementation, | 12 | goto_implementation::goto_implementation, |
12 | references::find_all_refs, | 13 | references::find_all_refs, |
13 | runnables::{runnables, Runnable}, | 14 | runnables::{runnables, Runnable}, |
14 | NavigationTarget, RunnableKind, StructureNodeKind, | 15 | NavigationTarget, RunnableKind, |
15 | }; | 16 | }; |
16 | 17 | ||
17 | // Feature: Annotations | 18 | // Feature: Annotations |
@@ -75,41 +76,56 @@ pub(crate) fn annotations( | |||
75 | } | 76 | } |
76 | } | 77 | } |
77 | 78 | ||
78 | file_structure(&db.parse(file_id).tree()) | 79 | visit_file_defs(&Semantics::new(db), file_id, &mut |def| match def { |
79 | .into_iter() | 80 | Either::Left(def) => { |
80 | .filter(|node| { | 81 | let node = match def { |
81 | matches!( | 82 | hir::ModuleDef::Const(konst) => { |
82 | node.kind, | 83 | konst.source(db).and_then(|node| range_and_position_of(&node.value)) |
83 | StructureNodeKind::SymbolKind(SymbolKind::Trait) | 84 | } |
84 | | StructureNodeKind::SymbolKind(SymbolKind::Struct) | 85 | hir::ModuleDef::Trait(trait_) => { |
85 | | StructureNodeKind::SymbolKind(SymbolKind::Enum) | 86 | trait_.source(db).and_then(|node| range_and_position_of(&node.value)) |
86 | | StructureNodeKind::SymbolKind(SymbolKind::Union) | 87 | } |
87 | | StructureNodeKind::SymbolKind(SymbolKind::Const) | 88 | hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { |
88 | ) | 89 | strukt.source(db).and_then(|node| range_and_position_of(&node.value)) |
89 | }) | 90 | } |
90 | .for_each(|node| { | 91 | hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => { |
91 | if config.annotate_impls | 92 | enum_.source(db).and_then(|node| range_and_position_of(&node.value)) |
92 | && node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const) | 93 | } |
93 | { | 94 | hir::ModuleDef::Adt(hir::Adt::Union(union)) => { |
95 | union.source(db).and_then(|node| range_and_position_of(&node.value)) | ||
96 | } | ||
97 | _ => None, | ||
98 | }; | ||
99 | let (offset, range) = match node { | ||
100 | Some(node) => node, | ||
101 | None => return, | ||
102 | }; | ||
103 | |||
104 | if config.annotate_impls && !matches!(def, hir::ModuleDef::Const(_)) { | ||
94 | annotations.push(Annotation { | 105 | annotations.push(Annotation { |
95 | range: node.node_range, | 106 | range, |
96 | kind: AnnotationKind::HasImpls { | 107 | kind: AnnotationKind::HasImpls { |
97 | position: FilePosition { file_id, offset: node.navigation_range.start() }, | 108 | position: FilePosition { file_id, offset }, |
98 | data: None, | 109 | data: None, |
99 | }, | 110 | }, |
100 | }); | 111 | }); |
101 | } | 112 | } |
102 | |||
103 | if config.annotate_references { | 113 | if config.annotate_references { |
104 | annotations.push(Annotation { | 114 | annotations.push(Annotation { |
105 | range: node.node_range, | 115 | range, |
106 | kind: AnnotationKind::HasReferences { | 116 | kind: AnnotationKind::HasReferences { |
107 | position: FilePosition { file_id, offset: node.navigation_range.start() }, | 117 | position: FilePosition { file_id, offset }, |
108 | data: None, | 118 | data: None, |
109 | }, | 119 | }, |
110 | }); | 120 | }); |
111 | } | 121 | } |
112 | }); | 122 | |
123 | fn range_and_position_of(node: &dyn NameOwner) -> Option<(TextSize, TextRange)> { | ||
124 | Some((node.name()?.syntax().text_range().start(), node.syntax().text_range())) | ||
125 | } | ||
126 | } | ||
127 | Either::Right(_) => (), | ||
128 | }); | ||
113 | 129 | ||
114 | if config.annotate_method_references { | 130 | if config.annotate_method_references { |
115 | annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { | 131 | annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { |
@@ -936,4 +952,19 @@ mod tests { | |||
936 | "#]], | 952 | "#]], |
937 | ); | 953 | ); |
938 | } | 954 | } |
955 | |||
956 | #[test] | ||
957 | fn test_no_annotations_outside_module_tree() { | ||
958 | check( | ||
959 | r#" | ||
960 | //- /foo.rs | ||
961 | struct Foo; | ||
962 | //- /lib.rs | ||
963 | // this file comes last since `check` checks the first file only | ||
964 | "#, | ||
965 | expect![[r#" | ||
966 | [] | ||
967 | "#]], | ||
968 | ); | ||
969 | } | ||
939 | } | 970 | } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 3990305fc..f4d7c14a6 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | use hir::{Crate, Impl, Semantics}; | 1 | use hir::{Impl, Semantics}; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::{ |
3 | use syntax::{algo::find_node_at_offset, ast, AstNode}; | 3 | defs::{Definition, NameClass, NameRefClass}, |
4 | RootDatabase, | ||
5 | }; | ||
6 | use syntax::{ast, AstNode}; | ||
4 | 7 | ||
5 | use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; | 8 | use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; |
6 | 9 | ||
@@ -21,55 +24,42 @@ pub(crate) fn goto_implementation( | |||
21 | let source_file = sema.parse(position.file_id); | 24 | let source_file = sema.parse(position.file_id); |
22 | let syntax = source_file.syntax().clone(); | 25 | let syntax = source_file.syntax().clone(); |
23 | 26 | ||
24 | let krate = sema.to_module_def(position.file_id)?.krate(); | 27 | let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?; |
25 | 28 | let def = match &node { | |
26 | if let Some(nominal_def) = find_node_at_offset::<ast::Adt>(&syntax, position.offset) { | 29 | ast::NameLike::Name(name) => { |
27 | return Some(RangeInfo::new( | 30 | NameClass::classify(&sema, name).map(|class| class.referenced_or_defined(sema.db)) |
28 | nominal_def.syntax().text_range(), | 31 | } |
29 | impls_for_def(&sema, &nominal_def, krate)?, | 32 | ast::NameLike::NameRef(name_ref) => { |
30 | )); | 33 | NameRefClass::classify(&sema, name_ref).map(|class| class.referenced(sema.db)) |
31 | } else if let Some(trait_def) = find_node_at_offset::<ast::Trait>(&syntax, position.offset) { | 34 | } |
32 | return Some(RangeInfo::new( | 35 | ast::NameLike::Lifetime(_) => None, |
33 | trait_def.syntax().text_range(), | 36 | }?; |
34 | impls_for_trait(&sema, &trait_def, krate)?, | 37 | let def = match def { |
35 | )); | 38 | Definition::ModuleDef(def) => def, |
36 | } | 39 | _ => return None, |
37 | |||
38 | None | ||
39 | } | ||
40 | |||
41 | fn impls_for_def( | ||
42 | sema: &Semantics<RootDatabase>, | ||
43 | node: &ast::Adt, | ||
44 | krate: Crate, | ||
45 | ) -> Option<Vec<NavigationTarget>> { | ||
46 | let ty = match node { | ||
47 | ast::Adt::Struct(def) => sema.to_def(def)?.ty(sema.db), | ||
48 | ast::Adt::Enum(def) => sema.to_def(def)?.ty(sema.db), | ||
49 | ast::Adt::Union(def) => sema.to_def(def)?.ty(sema.db), | ||
50 | }; | 40 | }; |
51 | 41 | let navs = match def { | |
52 | let impls = Impl::all_in_crate(sema.db, krate); | 42 | hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_), |
53 | 43 | hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)), | |
54 | Some( | 44 | hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)), |
55 | impls | 45 | hir::ModuleDef::BuiltinType(builtin) => { |
56 | .into_iter() | 46 | let module = sema.to_module_def(position.file_id)?; |
57 | .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) | 47 | impls_for_ty(&sema, builtin.ty(sema.db, module)) |
58 | .filter_map(|imp| imp.try_to_nav(sema.db)) | 48 | } |
59 | .collect(), | 49 | _ => return None, |
60 | ) | 50 | }; |
51 | Some(RangeInfo { range: node.syntax().text_range(), info: navs }) | ||
61 | } | 52 | } |
62 | 53 | ||
63 | fn impls_for_trait( | 54 | fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> { |
64 | sema: &Semantics<RootDatabase>, | 55 | Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect() |
65 | node: &ast::Trait, | 56 | } |
66 | krate: Crate, | ||
67 | ) -> Option<Vec<NavigationTarget>> { | ||
68 | let tr = sema.to_def(node)?; | ||
69 | |||
70 | let impls = Impl::for_trait(sema.db, krate, tr); | ||
71 | 57 | ||
72 | Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()) | 58 | fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> { |
59 | Impl::all_for_trait(sema.db, trait_) | ||
60 | .into_iter() | ||
61 | .filter_map(|imp| imp.try_to_nav(sema.db)) | ||
62 | .collect() | ||
73 | } | 63 | } |
74 | 64 | ||
75 | #[cfg(test)] | 65 | #[cfg(test)] |
@@ -226,4 +216,48 @@ macro Copy {} | |||
226 | "#, | 216 | "#, |
227 | ); | 217 | ); |
228 | } | 218 | } |
219 | |||
220 | #[test] | ||
221 | fn goto_implementation_type_alias() { | ||
222 | check( | ||
223 | r#" | ||
224 | struct Foo; | ||
225 | |||
226 | type Bar$0 = Foo; | ||
227 | |||
228 | impl Foo {} | ||
229 | //^^^ | ||
230 | impl Bar {} | ||
231 | //^^^ | ||
232 | "#, | ||
233 | ); | ||
234 | } | ||
235 | |||
236 | #[test] | ||
237 | fn goto_implementation_adt_generic() { | ||
238 | check( | ||
239 | r#" | ||
240 | struct Foo$0<T>; | ||
241 | |||
242 | impl<T> Foo<T> {} | ||
243 | //^^^^^^ | ||
244 | impl Foo<str> {} | ||
245 | //^^^^^^^^ | ||
246 | "#, | ||
247 | ); | ||
248 | } | ||
249 | |||
250 | #[test] | ||
251 | fn goto_implementation_builtin() { | ||
252 | check( | ||
253 | r#" | ||
254 | //- /lib.rs crate:main deps:core | ||
255 | fn foo(_: bool$0) {{}} | ||
256 | //- /libcore.rs crate:core | ||
257 | #[lang = "bool"] | ||
258 | impl bool {} | ||
259 | //^^^^ | ||
260 | "#, | ||
261 | ); | ||
262 | } | ||
229 | } | 263 | } |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 4ceb20742..16c04eeee 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -219,7 +219,7 @@ fn hint_iterator( | |||
219 | let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) | 219 | let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) |
220 | .last() | 220 | .last() |
221 | .and_then(|strukt| strukt.as_adt())?; | 221 | .and_then(|strukt| strukt.as_adt())?; |
222 | let krate = strukt.krate(db)?; | 222 | let krate = strukt.krate(db); |
223 | if krate != famous_defs.core()? { | 223 | if krate != famous_defs.core()? { |
224 | return None; | 224 | return None; |
225 | } | 225 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 27d35de5b..0c7a8fbf8 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -2,11 +2,13 @@ use std::fmt; | |||
2 | 2 | ||
3 | use ast::NameOwner; | 3 | use ast::NameOwner; |
4 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
5 | use either::Either; | ||
5 | use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; | 6 | use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; |
6 | use ide_assists::utils::test_related_attribute; | 7 | use ide_assists::utils::test_related_attribute; |
7 | use ide_db::{ | 8 | use ide_db::{ |
8 | base_db::{FilePosition, FileRange}, | 9 | base_db::{FilePosition, FileRange}, |
9 | defs::Definition, | 10 | defs::Definition, |
11 | helpers::visit_file_defs, | ||
10 | search::SearchScope, | 12 | search::SearchScope, |
11 | RootDatabase, SymbolKind, | 13 | RootDatabase, SymbolKind, |
12 | }; | 14 | }; |
@@ -102,13 +104,27 @@ impl Runnable { | |||
102 | // |=== | 104 | // |=== |
103 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 105 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
104 | let sema = Semantics::new(db); | 106 | let sema = Semantics::new(db); |
105 | let module = match sema.to_module_def(file_id) { | ||
106 | None => return Vec::new(), | ||
107 | Some(it) => it, | ||
108 | }; | ||
109 | 107 | ||
110 | let mut res = Vec::new(); | 108 | let mut res = Vec::new(); |
111 | runnables_mod(&sema, &mut res, module); | 109 | visit_file_defs(&sema, file_id, &mut |def| match def { |
110 | Either::Left(def) => { | ||
111 | let runnable = match def { | ||
112 | hir::ModuleDef::Module(it) => runnable_mod(&sema, it), | ||
113 | hir::ModuleDef::Function(it) => runnable_fn(&sema, it), | ||
114 | _ => None, | ||
115 | }; | ||
116 | res.extend(runnable.or_else(|| module_def_doctest(&sema, def))) | ||
117 | } | ||
118 | Either::Right(impl_) => { | ||
119 | res.extend(impl_.items(db).into_iter().filter_map(|assoc| match assoc { | ||
120 | hir::AssocItem::Function(it) => { | ||
121 | runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) | ||
122 | } | ||
123 | hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), | ||
124 | hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), | ||
125 | })) | ||
126 | } | ||
127 | }); | ||
112 | res | 128 | res |
113 | } | 129 | } |
114 | 130 | ||
@@ -211,39 +227,6 @@ fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Optio | |||
211 | }) | 227 | }) |
212 | } | 228 | } |
213 | 229 | ||
214 | fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) { | ||
215 | acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| { | ||
216 | let runnable = match def { | ||
217 | hir::ModuleDef::Module(it) => runnable_mod(&sema, it), | ||
218 | hir::ModuleDef::Function(it) => runnable_fn(&sema, it), | ||
219 | _ => None, | ||
220 | }; | ||
221 | runnable.or_else(|| module_def_doctest(&sema, def)) | ||
222 | })); | ||
223 | |||
224 | acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map( | ||
225 | |def| match def { | ||
226 | hir::AssocItem::Function(it) => { | ||
227 | runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) | ||
228 | } | ||
229 | hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), | ||
230 | hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), | ||
231 | }, | ||
232 | )); | ||
233 | |||
234 | for def in module.declarations(sema.db) { | ||
235 | if let hir::ModuleDef::Module(submodule) = def { | ||
236 | match submodule.definition_source(sema.db).value { | ||
237 | hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule), | ||
238 | hir::ModuleSource::SourceFile(_) => { | ||
239 | cov_mark::hit!(dont_recurse_in_outline_submodules) | ||
240 | } | ||
241 | hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable | ||
242 | } | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { | 230 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { |
248 | let func = def.source(sema.db)?; | 231 | let func = def.source(sema.db)?; |
249 | let name_string = def.name(sema.db).to_string(); | 232 | let name_string = def.name(sema.db).to_string(); |
@@ -1178,7 +1161,6 @@ mod tests { | |||
1178 | 1161 | ||
1179 | #[test] | 1162 | #[test] |
1180 | fn dont_recurse_in_outline_submodules() { | 1163 | fn dont_recurse_in_outline_submodules() { |
1181 | cov_mark::check!(dont_recurse_in_outline_submodules); | ||
1182 | check( | 1164 | check( |
1183 | r#" | 1165 | r#" |
1184 | //- /lib.rs | 1166 | //- /lib.rs |
diff --git a/crates/ide_completion/src/context.rs b/crates/ide_completion/src/context.rs index 17d9a3adf..89e9bda78 100644 --- a/crates/ide_completion/src/context.rs +++ b/crates/ide_completion/src/context.rs | |||
@@ -35,6 +35,7 @@ pub(crate) struct CompletionContext<'a> { | |||
35 | /// The token before the cursor, in the macro-expanded file. | 35 | /// The token before the cursor, in the macro-expanded file. |
36 | pub(super) token: SyntaxToken, | 36 | pub(super) token: SyntaxToken, |
37 | pub(super) krate: Option<hir::Crate>, | 37 | pub(super) krate: Option<hir::Crate>, |
38 | pub(super) expected_name: Option<String>, | ||
38 | pub(super) expected_type: Option<Type>, | 39 | pub(super) expected_type: Option<Type>, |
39 | pub(super) name_ref_syntax: Option<ast::NameRef>, | 40 | pub(super) name_ref_syntax: Option<ast::NameRef>, |
40 | pub(super) function_syntax: Option<ast::Fn>, | 41 | pub(super) function_syntax: Option<ast::Fn>, |
@@ -135,6 +136,7 @@ impl<'a> CompletionContext<'a> { | |||
135 | original_token, | 136 | original_token, |
136 | token, | 137 | token, |
137 | krate, | 138 | krate, |
139 | expected_name: None, | ||
138 | expected_type: None, | 140 | expected_type: None, |
139 | name_ref_syntax: None, | 141 | name_ref_syntax: None, |
140 | function_syntax: None, | 142 | function_syntax: None, |
@@ -290,23 +292,96 @@ impl<'a> CompletionContext<'a> { | |||
290 | file_with_fake_ident: SyntaxNode, | 292 | file_with_fake_ident: SyntaxNode, |
291 | offset: TextSize, | 293 | offset: TextSize, |
292 | ) { | 294 | ) { |
293 | // FIXME: this is wrong in at least two cases: | 295 | let expected = { |
294 | // * when there's no token `foo($0)` | 296 | let mut node = self.token.parent(); |
295 | // * when there is a token, but it happens to have type of it's own | 297 | loop { |
296 | self.expected_type = self | 298 | let ret = match_ast! { |
297 | .token | ||
298 | .ancestors() | ||
299 | .find_map(|node| { | ||
300 | let ty = match_ast! { | ||
301 | match node { | 299 | match node { |
302 | ast::Pat(it) => self.sema.type_of_pat(&it), | 300 | ast::LetStmt(it) => { |
303 | ast::Expr(it) => self.sema.type_of_expr(&it), | 301 | cov_mark::hit!(expected_type_let_with_leading_char); |
304 | _ => return None, | 302 | cov_mark::hit!(expected_type_let_without_leading_char); |
303 | let ty = it.pat() | ||
304 | .and_then(|pat| self.sema.type_of_pat(&pat)); | ||
305 | let name = if let Some(ast::Pat::IdentPat(ident)) = it.pat() { | ||
306 | Some(ident.syntax().text().to_string()) | ||
307 | } else { | ||
308 | None | ||
309 | }; | ||
310 | |||
311 | (ty, name) | ||
312 | }, | ||
313 | ast::ArgList(it) => { | ||
314 | cov_mark::hit!(expected_type_fn_param_with_leading_char); | ||
315 | cov_mark::hit!(expected_type_fn_param_without_leading_char); | ||
316 | ActiveParameter::at_token( | ||
317 | &self.sema, | ||
318 | self.token.clone(), | ||
319 | ).map(|ap| (Some(ap.ty), Some(ap.name))) | ||
320 | .unwrap_or((None, None)) | ||
321 | }, | ||
322 | ast::RecordExprFieldList(it) => { | ||
323 | cov_mark::hit!(expected_type_struct_field_without_leading_char); | ||
324 | self.token.prev_sibling_or_token() | ||
325 | .and_then(|se| se.into_node()) | ||
326 | .and_then(|node| ast::RecordExprField::cast(node)) | ||
327 | .and_then(|rf| self.sema.resolve_record_field(&rf)) | ||
328 | .map(|f|( | ||
329 | Some(f.0.signature_ty(self.db)), | ||
330 | Some(f.0.name(self.db).to_string()), | ||
331 | )) | ||
332 | .unwrap_or((None, None)) | ||
333 | }, | ||
334 | ast::RecordExprField(it) => { | ||
335 | cov_mark::hit!(expected_type_struct_field_with_leading_char); | ||
336 | self.sema | ||
337 | .resolve_record_field(&it) | ||
338 | .map(|f|( | ||
339 | Some(f.0.signature_ty(self.db)), | ||
340 | Some(f.0.name(self.db).to_string()), | ||
341 | )) | ||
342 | .unwrap_or((None, None)) | ||
343 | }, | ||
344 | ast::MatchExpr(it) => { | ||
345 | cov_mark::hit!(expected_type_match_arm_without_leading_char); | ||
346 | let ty = it.expr() | ||
347 | .and_then(|e| self.sema.type_of_expr(&e)); | ||
348 | |||
349 | (ty, None) | ||
350 | }, | ||
351 | ast::IdentPat(it) => { | ||
352 | cov_mark::hit!(expected_type_if_let_with_leading_char); | ||
353 | cov_mark::hit!(expected_type_if_let_without_leading_char); | ||
354 | cov_mark::hit!(expected_type_match_arm_with_leading_char); | ||
355 | let ty = self.sema.type_of_pat(&ast::Pat::from(it)); | ||
356 | |||
357 | (ty, None) | ||
358 | }, | ||
359 | ast::Fn(it) => { | ||
360 | cov_mark::hit!(expected_type_fn_ret_with_leading_char); | ||
361 | cov_mark::hit!(expected_type_fn_ret_without_leading_char); | ||
362 | let ty = self.token.ancestors() | ||
363 | .find_map(|ancestor| ast::Expr::cast(ancestor)) | ||
364 | .and_then(|expr| self.sema.type_of_expr(&expr)); | ||
365 | |||
366 | (ty, None) | ||
367 | }, | ||
368 | _ => { | ||
369 | match node.parent() { | ||
370 | Some(n) => { | ||
371 | node = n; | ||
372 | continue; | ||
373 | }, | ||
374 | None => (None, None), | ||
375 | } | ||
376 | }, | ||
305 | } | 377 | } |
306 | }; | 378 | }; |
307 | Some(ty) | 379 | |
308 | }) | 380 | break ret; |
309 | .flatten(); | 381 | } |
382 | }; | ||
383 | self.expected_type = expected.0; | ||
384 | self.expected_name = expected.1; | ||
310 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); | 385 | self.attribute_under_caret = find_node_at_offset(&file_with_fake_ident, offset); |
311 | 386 | ||
312 | // First, let's try to complete a reference to some declaration. | 387 | // First, let's try to complete a reference to some declaration. |
@@ -535,3 +610,197 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<ast::Path> { | |||
535 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; | 610 | let use_tree = use_tree_list.syntax().parent().and_then(ast::UseTree::cast)?; |
536 | use_tree.path() | 611 | use_tree.path() |
537 | } | 612 | } |
613 | |||
614 | #[cfg(test)] | ||
615 | mod tests { | ||
616 | use expect_test::{expect, Expect}; | ||
617 | use hir::HirDisplay; | ||
618 | |||
619 | use crate::test_utils::{position, TEST_CONFIG}; | ||
620 | |||
621 | use super::CompletionContext; | ||
622 | |||
623 | fn check_expected_type_and_name(ra_fixture: &str, expect: Expect) { | ||
624 | let (db, pos) = position(ra_fixture); | ||
625 | let completion_context = CompletionContext::new(&db, pos, &TEST_CONFIG).unwrap(); | ||
626 | |||
627 | let ty = completion_context | ||
628 | .expected_type | ||
629 | .map(|t| t.display_test(&db).to_string()) | ||
630 | .unwrap_or("?".to_owned()); | ||
631 | |||
632 | let name = completion_context.expected_name.unwrap_or("?".to_owned()); | ||
633 | |||
634 | expect.assert_eq(&format!("ty: {}, name: {}", ty, name)); | ||
635 | } | ||
636 | |||
637 | #[test] | ||
638 | fn expected_type_let_without_leading_char() { | ||
639 | cov_mark::check!(expected_type_let_without_leading_char); | ||
640 | check_expected_type_and_name( | ||
641 | r#" | ||
642 | fn foo() { | ||
643 | let x: u32 = $0; | ||
644 | } | ||
645 | "#, | ||
646 | expect![[r#"ty: u32, name: x"#]], | ||
647 | ); | ||
648 | } | ||
649 | |||
650 | #[test] | ||
651 | fn expected_type_let_with_leading_char() { | ||
652 | cov_mark::check!(expected_type_let_with_leading_char); | ||
653 | check_expected_type_and_name( | ||
654 | r#" | ||
655 | fn foo() { | ||
656 | let x: u32 = c$0; | ||
657 | } | ||
658 | "#, | ||
659 | expect![[r#"ty: u32, name: x"#]], | ||
660 | ); | ||
661 | } | ||
662 | |||
663 | #[test] | ||
664 | fn expected_type_fn_param_without_leading_char() { | ||
665 | cov_mark::check!(expected_type_fn_param_without_leading_char); | ||
666 | check_expected_type_and_name( | ||
667 | r#" | ||
668 | fn foo() { | ||
669 | bar($0); | ||
670 | } | ||
671 | |||
672 | fn bar(x: u32) {} | ||
673 | "#, | ||
674 | expect![[r#"ty: u32, name: x"#]], | ||
675 | ); | ||
676 | } | ||
677 | |||
678 | #[test] | ||
679 | fn expected_type_fn_param_with_leading_char() { | ||
680 | cov_mark::check!(expected_type_fn_param_with_leading_char); | ||
681 | check_expected_type_and_name( | ||
682 | r#" | ||
683 | fn foo() { | ||
684 | bar(c$0); | ||
685 | } | ||
686 | |||
687 | fn bar(x: u32) {} | ||
688 | "#, | ||
689 | expect![[r#"ty: u32, name: x"#]], | ||
690 | ); | ||
691 | } | ||
692 | |||
693 | #[test] | ||
694 | fn expected_type_struct_field_without_leading_char() { | ||
695 | cov_mark::check!(expected_type_struct_field_without_leading_char); | ||
696 | check_expected_type_and_name( | ||
697 | r#" | ||
698 | struct Foo { a: u32 } | ||
699 | fn foo() { | ||
700 | Foo { a: $0 }; | ||
701 | } | ||
702 | "#, | ||
703 | expect![[r#"ty: u32, name: a"#]], | ||
704 | ) | ||
705 | } | ||
706 | |||
707 | #[test] | ||
708 | fn expected_type_struct_field_with_leading_char() { | ||
709 | cov_mark::check!(expected_type_struct_field_with_leading_char); | ||
710 | check_expected_type_and_name( | ||
711 | r#" | ||
712 | struct Foo { a: u32 } | ||
713 | fn foo() { | ||
714 | Foo { a: c$0 }; | ||
715 | } | ||
716 | "#, | ||
717 | expect![[r#"ty: u32, name: a"#]], | ||
718 | ); | ||
719 | } | ||
720 | |||
721 | #[test] | ||
722 | fn expected_type_match_arm_without_leading_char() { | ||
723 | cov_mark::check!(expected_type_match_arm_without_leading_char); | ||
724 | check_expected_type_and_name( | ||
725 | r#" | ||
726 | enum E { X } | ||
727 | fn foo() { | ||
728 | match E::X { $0 } | ||
729 | } | ||
730 | "#, | ||
731 | expect![[r#"ty: E, name: ?"#]], | ||
732 | ); | ||
733 | } | ||
734 | |||
735 | #[test] | ||
736 | fn expected_type_match_arm_with_leading_char() { | ||
737 | cov_mark::check!(expected_type_match_arm_with_leading_char); | ||
738 | check_expected_type_and_name( | ||
739 | r#" | ||
740 | enum E { X } | ||
741 | fn foo() { | ||
742 | match E::X { c$0 } | ||
743 | } | ||
744 | "#, | ||
745 | expect![[r#"ty: E, name: ?"#]], | ||
746 | ); | ||
747 | } | ||
748 | |||
749 | #[test] | ||
750 | fn expected_type_if_let_without_leading_char() { | ||
751 | cov_mark::check!(expected_type_if_let_without_leading_char); | ||
752 | check_expected_type_and_name( | ||
753 | r#" | ||
754 | enum Foo { Bar, Baz, Quux } | ||
755 | |||
756 | fn foo() { | ||
757 | let f = Foo::Quux; | ||
758 | if let $0 = f { } | ||
759 | } | ||
760 | "#, | ||
761 | expect![[r#"ty: (), name: ?"#]], | ||
762 | ) // FIXME should be `ty: u32, name: ?` | ||
763 | } | ||
764 | |||
765 | #[test] | ||
766 | fn expected_type_if_let_with_leading_char() { | ||
767 | cov_mark::check!(expected_type_if_let_with_leading_char); | ||
768 | check_expected_type_and_name( | ||
769 | r#" | ||
770 | enum Foo { Bar, Baz, Quux } | ||
771 | |||
772 | fn foo() { | ||
773 | let f = Foo::Quux; | ||
774 | if let c$0 = f { } | ||
775 | } | ||
776 | "#, | ||
777 | expect![[r#"ty: Foo, name: ?"#]], | ||
778 | ) | ||
779 | } | ||
780 | |||
781 | #[test] | ||
782 | fn expected_type_fn_ret_without_leading_char() { | ||
783 | cov_mark::check!(expected_type_fn_ret_without_leading_char); | ||
784 | check_expected_type_and_name( | ||
785 | r#" | ||
786 | fn foo() -> u32 { | ||
787 | $0 | ||
788 | } | ||
789 | "#, | ||
790 | expect![[r#"ty: (), name: ?"#]], | ||
791 | ) // FIXME this should be `ty: u32, name: ?` | ||
792 | } | ||
793 | |||
794 | #[test] | ||
795 | fn expected_type_fn_ret_with_leading_char() { | ||
796 | cov_mark::check!(expected_type_fn_ret_with_leading_char); | ||
797 | check_expected_type_and_name( | ||
798 | r#" | ||
799 | fn foo() -> u32 { | ||
800 | c$0 | ||
801 | } | ||
802 | "#, | ||
803 | expect![[r#"ty: u32, name: ?"#]], | ||
804 | ) | ||
805 | } | ||
806 | } | ||
diff --git a/crates/ide_completion/src/render.rs b/crates/ide_completion/src/render.rs index 905f0b197..670563e50 100644 --- a/crates/ide_completion/src/render.rs +++ b/crates/ide_completion/src/render.rs | |||
@@ -118,19 +118,6 @@ impl<'a> RenderContext<'a> { | |||
118 | fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { | 118 | fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { |
119 | node.docs(self.db()) | 119 | node.docs(self.db()) |
120 | } | 120 | } |
121 | |||
122 | fn expected_name_and_type(&self) -> Option<(String, Type)> { | ||
123 | if let Some(record_field) = &self.completion.record_field_syntax { | ||
124 | cov_mark::hit!(record_field_type_match); | ||
125 | let (struct_field, _local) = self.completion.sema.resolve_record_field(record_field)?; | ||
126 | Some((struct_field.name(self.db()).to_string(), struct_field.signature_ty(self.db()))) | ||
127 | } else if let Some(active_parameter) = &self.completion.active_parameter { | ||
128 | cov_mark::hit!(active_param_type_match); | ||
129 | Some((active_parameter.name.clone(), active_parameter.ty.clone())) | ||
130 | } else { | ||
131 | None | ||
132 | } | ||
133 | } | ||
134 | } | 121 | } |
135 | 122 | ||
136 | /// Generic renderer for completion items. | 123 | /// Generic renderer for completion items. |
@@ -256,8 +243,8 @@ impl<'a> Render<'a> { | |||
256 | relevance.is_local = true; | 243 | relevance.is_local = true; |
257 | item.set_relevance(relevance); | 244 | item.set_relevance(relevance); |
258 | 245 | ||
259 | if let Some((_expected_name, expected_type)) = self.ctx.expected_name_and_type() { | 246 | if let Some(expected_type) = self.ctx.completion.expected_type.as_ref() { |
260 | if ty != expected_type { | 247 | if &ty != expected_type { |
261 | if let Some(ty_without_ref) = expected_type.remove_ref() { | 248 | if let Some(ty_without_ref) = expected_type.remove_ref() { |
262 | if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { | 249 | if relevance_type_match(self.ctx.db().upcast(), &ty, &ty_without_ref) { |
263 | cov_mark::hit!(suggest_ref); | 250 | cov_mark::hit!(suggest_ref); |
@@ -329,10 +316,8 @@ impl<'a> Render<'a> { | |||
329 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { | 316 | fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { |
330 | let mut res = CompletionRelevance::default(); | 317 | let mut res = CompletionRelevance::default(); |
331 | 318 | ||
332 | if let Some((expected_name, expected_type)) = ctx.expected_name_and_type() { | 319 | res.exact_type_match = Some(ty) == ctx.completion.expected_type.as_ref(); |
333 | res.exact_type_match = ty == &expected_type; | 320 | res.exact_name_match = Some(name) == ctx.completion.expected_name.as_deref(); |
334 | res.exact_name_match = name == &expected_name; | ||
335 | } | ||
336 | 321 | ||
337 | res | 322 | res |
338 | } | 323 | } |
@@ -852,7 +837,6 @@ fn foo(xs: Vec<i128>) | |||
852 | 837 | ||
853 | #[test] | 838 | #[test] |
854 | fn active_param_relevance() { | 839 | fn active_param_relevance() { |
855 | cov_mark::check!(active_param_type_match); | ||
856 | check_relevance( | 840 | check_relevance( |
857 | r#" | 841 | r#" |
858 | struct S { foo: i64, bar: u32, baz: u32 } | 842 | struct S { foo: i64, bar: u32, baz: u32 } |
@@ -869,7 +853,6 @@ fn foo(s: S) { test(s.$0) } | |||
869 | 853 | ||
870 | #[test] | 854 | #[test] |
871 | fn record_field_relevances() { | 855 | fn record_field_relevances() { |
872 | cov_mark::check!(record_field_type_match); | ||
873 | check_relevance( | 856 | check_relevance( |
874 | r#" | 857 | r#" |
875 | struct A { foo: i64, bar: u32, baz: u32 } | 858 | struct A { foo: i64, bar: u32, baz: u32 } |
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 3c95d3cff..9992a92bd 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -2,6 +2,10 @@ | |||
2 | pub mod insert_use; | 2 | pub mod insert_use; |
3 | pub mod import_assets; | 3 | pub mod import_assets; |
4 | 4 | ||
5 | use std::collections::VecDeque; | ||
6 | |||
7 | use base_db::FileId; | ||
8 | use either::Either; | ||
5 | use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; | 9 | use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; |
6 | use syntax::ast::{self, make}; | 10 | use syntax::ast::{self, make}; |
7 | 11 | ||
@@ -39,6 +43,30 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path { | |||
39 | make::path_from_segments(segments, is_abs) | 43 | make::path_from_segments(segments, is_abs) |
40 | } | 44 | } |
41 | 45 | ||
46 | /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. | ||
47 | pub fn visit_file_defs( | ||
48 | sema: &Semantics<RootDatabase>, | ||
49 | file_id: FileId, | ||
50 | cb: &mut dyn FnMut(Either<hir::ModuleDef, hir::Impl>), | ||
51 | ) { | ||
52 | let db = sema.db; | ||
53 | let module = match sema.to_module_def(file_id) { | ||
54 | Some(it) => it, | ||
55 | None => return, | ||
56 | }; | ||
57 | let mut defs: VecDeque<_> = module.declarations(db).into(); | ||
58 | while let Some(def) = defs.pop_front() { | ||
59 | if let ModuleDef::Module(submodule) = def { | ||
60 | if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value { | ||
61 | defs.extend(submodule.declarations(db)); | ||
62 | submodule.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); | ||
63 | } | ||
64 | } | ||
65 | cb(Either::Left(def)); | ||
66 | } | ||
67 | module.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); | ||
68 | } | ||
69 | |||
42 | /// Helps with finding well-know things inside the standard library. This is | 70 | /// Helps with finding well-know things inside the standard library. This is |
43 | /// somewhat similar to the known paths infra inside hir, but it different; We | 71 | /// somewhat similar to the known paths infra inside hir, but it different; We |
44 | /// want to make sure that IDE specific paths don't become interesting inside | 72 | /// want to make sure that IDE specific paths don't become interesting inside |