aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/hir/src/lib.rs58
-rw-r--r--crates/hir_ty/src/method_resolution.rs10
-rw-r--r--crates/ide/src/annotations.rs87
-rw-r--r--crates/ide/src/goto_implementation.rs130
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/runnables.rs60
-rw-r--r--crates/ide_completion/src/context.rs297
-rw-r--r--crates/ide_completion/src/render.rs25
-rw-r--r--crates/ide_db/src/helpers.rs28
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};
51use hir_ty::{ 51use 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 @@
1use hir::Semantics; 1use either::Either;
2use hir::{HasSource, Semantics};
2use ide_db::{ 3use 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};
6use syntax::TextRange; 8use syntax::{ast::NameOwner, AstNode, TextRange, TextSize};
7 9
8use crate::{ 10use 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
961struct 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 @@
1use hir::{Crate, Impl, Semantics}; 1use hir::{Impl, Semantics};
2use ide_db::RootDatabase; 2use ide_db::{
3use syntax::{algo::find_node_at_offset, ast, AstNode}; 3 defs::{Definition, NameClass, NameRefClass},
4 RootDatabase,
5};
6use syntax::{ast, AstNode};
4 7
5use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; 8use 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
41fn 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
63fn impls_for_trait( 54fn 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()) 58fn 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#"
224struct Foo;
225
226type Bar$0 = Foo;
227
228impl Foo {}
229 //^^^
230impl Bar {}
231 //^^^
232"#,
233 );
234 }
235
236 #[test]
237 fn goto_implementation_adt_generic() {
238 check(
239 r#"
240struct Foo$0<T>;
241
242impl<T> Foo<T> {}
243 //^^^^^^
244impl 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
255fn foo(_: bool$0) {{}}
256//- /libcore.rs crate:core
257#[lang = "bool"]
258impl 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
3use ast::NameOwner; 3use ast::NameOwner;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use either::Either;
5use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; 6use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
6use ide_assists::utils::test_related_attribute; 7use ide_assists::utils::test_related_attribute;
7use ide_db::{ 8use 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// |===
103pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 105pub(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
214fn 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
247pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 230pub(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)]
615mod 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#"
642fn 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#"
655fn 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#"
668fn foo() {
669 bar($0);
670}
671
672fn 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#"
683fn foo() {
684 bar(c$0);
685}
686
687fn 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#"
698struct Foo { a: u32 }
699fn 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#"
712struct Foo { a: u32 }
713fn 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#"
726enum E { X }
727fn 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#"
740enum E { X }
741fn 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#"
754enum Foo { Bar, Baz, Quux }
755
756fn 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#"
770enum Foo { Bar, Baz, Quux }
771
772fn 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#"
786fn 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#"
799fn 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> {
329fn compute_relevance(ctx: &RenderContext, ty: &Type, name: &str) -> CompletionRelevance { 316fn 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#"
858struct S { foo: i64, bar: u32, baz: u32 } 842struct 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#"
875struct A { foo: i64, bar: u32, baz: u32 } 858struct 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 @@
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4 4
5use std::collections::VecDeque;
6
7use base_db::FileId;
8use either::Either;
5use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; 9use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
6use syntax::ast::{self, make}; 10use 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.
47pub 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