diff options
author | Florian Diebold <[email protected]> | 2019-10-31 18:28:33 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2019-11-01 18:57:08 +0000 |
commit | 1173c3dab5f77a1afd367d547790dd82c558fe0d (patch) | |
tree | 50d39c98ad4ac7deae11800d20c0a5dbeb9ed8bc /crates | |
parent | c7cedea270c492e9a2c8b81c1312fda44fd8217e (diff) |
Refactor to unify with method resolution
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 9 | ||||
-rw-r--r-- | crates/ra_hir/src/generics.rs | 3 | ||||
-rw-r--r-- | crates/ra_hir/src/source_binder.rs | 7 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/infer/path.rs | 135 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/method_resolution.rs | 90 | ||||
-rw-r--r-- | crates/ra_hir/src/ty/tests.rs | 4 | ||||
-rw-r--r-- | crates/ra_ide_api/src/completion/complete_dot.rs | 10 |
7 files changed, 117 insertions, 141 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index ae6ef7606..c97ea18a2 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -1053,4 +1053,13 @@ impl AssocItem { | |||
1053 | AssocItem::TypeAlias(t) => t.module(db), | 1053 | AssocItem::TypeAlias(t) => t.module(db), |
1054 | } | 1054 | } |
1055 | } | 1055 | } |
1056 | |||
1057 | pub fn container(self, db: &impl DefDatabase) -> Container { | ||
1058 | match self { | ||
1059 | AssocItem::Function(f) => f.container(db), | ||
1060 | AssocItem::Const(c) => c.container(db), | ||
1061 | AssocItem::TypeAlias(t) => t.container(db), | ||
1062 | } | ||
1063 | .expect("AssocItem without container") | ||
1064 | } | ||
1056 | } | 1065 | } |
diff --git a/crates/ra_hir/src/generics.rs b/crates/ra_hir/src/generics.rs index 52e1fbf29..9c261eda9 100644 --- a/crates/ra_hir/src/generics.rs +++ b/crates/ra_hir/src/generics.rs | |||
@@ -77,9 +77,10 @@ impl GenericParams { | |||
77 | let parent = match def { | 77 | let parent = match def { |
78 | GenericDef::Function(it) => it.container(db).map(GenericDef::from), | 78 | GenericDef::Function(it) => it.container(db).map(GenericDef::from), |
79 | GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), | 79 | GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from), |
80 | GenericDef::Const(it) => it.container(db).map(GenericDef::from), | ||
80 | GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()), | 81 | GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()), |
81 | GenericDef::Adt(_) | GenericDef::Trait(_) => None, | 82 | GenericDef::Adt(_) | GenericDef::Trait(_) => None, |
82 | GenericDef::ImplBlock(_) | GenericDef::Const(_) => None, | 83 | GenericDef::ImplBlock(_) => None, |
83 | }; | 84 | }; |
84 | let mut generics = GenericParams { | 85 | let mut generics = GenericParams { |
85 | def, | 86 | def, |
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 0008cb232..0398806fd 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs | |||
@@ -28,8 +28,8 @@ use crate::{ | |||
28 | ids::LocationCtx, | 28 | ids::LocationCtx, |
29 | resolve::{ScopeDef, TypeNs, ValueNs}, | 29 | resolve::{ScopeDef, TypeNs, ValueNs}, |
30 | ty::method_resolution::implements_trait, | 30 | ty::method_resolution::implements_trait, |
31 | Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module, | 31 | AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, |
32 | Name, Path, Resolver, Static, Struct, Ty, | 32 | MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty, |
33 | }; | 33 | }; |
34 | 34 | ||
35 | fn try_get_resolver_for_node( | 35 | fn try_get_resolver_for_node( |
@@ -327,7 +327,7 @@ impl SourceAnalyzer { | |||
327 | db: &impl HirDatabase, | 327 | db: &impl HirDatabase, |
328 | ty: Ty, | 328 | ty: Ty, |
329 | name: Option<&Name>, | 329 | name: Option<&Name>, |
330 | callback: impl FnMut(&Ty, Function) -> Option<T>, | 330 | callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
331 | ) -> Option<T> { | 331 | ) -> Option<T> { |
332 | // There should be no inference vars in types passed here | 332 | // There should be no inference vars in types passed here |
333 | // FIXME check that? | 333 | // FIXME check that? |
@@ -337,6 +337,7 @@ impl SourceAnalyzer { | |||
337 | db, | 337 | db, |
338 | &self.resolver, | 338 | &self.resolver, |
339 | name, | 339 | name, |
340 | crate::ty::method_resolution::LookupMode::MethodCall, | ||
340 | callback, | 341 | callback, |
341 | ) | 342 | ) |
342 | } | 343 | } |
diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs index c58564b22..1946bf608 100644 --- a/crates/ra_hir/src/ty/infer/path.rs +++ b/crates/ra_hir/src/ty/infer/path.rs | |||
@@ -6,11 +6,9 @@ use super::{ExprOrPatId, InferenceContext, TraitRef}; | |||
6 | use crate::{ | 6 | use crate::{ |
7 | db::HirDatabase, | 7 | db::HirDatabase, |
8 | resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, | 8 | resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, |
9 | ty::{lower, traits::TraitEnvironment, Canonical}, | 9 | ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk}, |
10 | ty::{Substs, Ty, TypableDef, TypeWalk}, | 10 | AssocItem, Container, HasGenericParams, Name, Namespace, Path, |
11 | AssocItem, HasGenericParams, Name, Namespace, Path, Trait, | ||
12 | }; | 11 | }; |
13 | use std::sync::Arc; | ||
14 | 12 | ||
15 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | 13 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { |
16 | pub(super) fn infer_path( | 14 | pub(super) fn infer_path( |
@@ -184,91 +182,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
184 | return None; | 182 | return None; |
185 | } | 183 | } |
186 | 184 | ||
187 | self.find_inherent_assoc_candidate(ty.clone(), name, id) | ||
188 | .or_else(|| self.find_trait_assoc_candidate(ty.clone(), name, id)) | ||
189 | } | ||
190 | |||
191 | fn find_inherent_assoc_candidate( | ||
192 | &mut self, | ||
193 | ty: Ty, | ||
194 | name: &Name, | ||
195 | id: ExprOrPatId, | ||
196 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
197 | let krate = self.resolver.krate()?; | ||
198 | |||
199 | // Find impl | ||
200 | let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { | ||
201 | AssocItem::Function(func) => { | ||
202 | if *name == func.name(self.db) { | ||
203 | Some(AssocItem::Function(func)) | ||
204 | } else { | ||
205 | None | ||
206 | } | ||
207 | } | ||
208 | |||
209 | AssocItem::Const(konst) => { | ||
210 | if konst.name(self.db).map_or(false, |n| n == *name) { | ||
211 | Some(AssocItem::Const(konst)) | ||
212 | } else { | ||
213 | None | ||
214 | } | ||
215 | } | ||
216 | AssocItem::TypeAlias(_) => None, | ||
217 | })?; | ||
218 | let def = match item { | ||
219 | AssocItem::Function(f) => ValueNs::Function(f), | ||
220 | AssocItem::Const(c) => ValueNs::Const(c), | ||
221 | AssocItem::TypeAlias(_) => unreachable!(), | ||
222 | }; | ||
223 | let substs = self.find_self_types(&def, ty); | ||
224 | |||
225 | self.write_assoc_resolution(id, item); | ||
226 | Some((def, substs)) | ||
227 | } | ||
228 | |||
229 | fn find_trait_assoc_candidate( | ||
230 | &mut self, | ||
231 | ty: Ty, | ||
232 | name: &Name, | ||
233 | id: ExprOrPatId, | ||
234 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
235 | let krate = self.resolver.krate()?; | ||
236 | |||
237 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); | 185 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); |
238 | 186 | ||
239 | let env = lower::trait_env(self.db, &self.resolver); | 187 | method_resolution::iterate_method_candidates( |
240 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope | 188 | &canonical_ty.value, |
241 | let traits_from_env = env | 189 | self.db, |
242 | .trait_predicates_for_self_ty(&ty) | 190 | &self.resolver.clone(), |
243 | .map(|tr| tr.trait_) | 191 | Some(name), |
244 | .flat_map(|t| t.all_super_traits(self.db)); | 192 | method_resolution::LookupMode::Path, |
245 | let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db)); | 193 | move |_ty, item| { |
246 | 194 | let def = match item { | |
247 | 'traits: for t in traits { | 195 | AssocItem::Function(f) => ValueNs::Function(f), |
248 | let data = t.trait_data(self.db); | 196 | AssocItem::Const(c) => ValueNs::Const(c), |
249 | let mut known_implemented = false; | 197 | AssocItem::TypeAlias(_) => unreachable!(), |
250 | for item in data.items() { | 198 | }; |
251 | if let AssocItem::Function(f) = *item { | 199 | match item.container(self.db) { |
252 | if f.name(self.db) == *name { | 200 | Container::ImplBlock(_) => { |
253 | if !known_implemented { | 201 | let substs = self.find_self_types(&def, ty.clone()); |
254 | let goal = generic_implements_goal( | 202 | |
255 | self.db, | 203 | self.write_assoc_resolution(id, item); |
256 | env.clone(), | 204 | Some((def, substs)) |
257 | t, | 205 | } |
258 | canonical_ty.value.clone(), | 206 | Container::Trait(t) => { |
259 | ); | ||
260 | if self.db.trait_solve(krate, goal).is_none() { | ||
261 | continue 'traits; | ||
262 | } | ||
263 | } | ||
264 | known_implemented = true; | ||
265 | |||
266 | // we're picking this method | 207 | // we're picking this method |
267 | let trait_substs = Substs::build_for_def(self.db, t) | 208 | let trait_substs = Substs::build_for_def(self.db, t) |
268 | .push(ty.clone()) | 209 | .push(ty.clone()) |
269 | .fill(std::iter::repeat_with(|| self.new_type_var())) | 210 | .fill(std::iter::repeat_with(|| self.new_type_var())) |
270 | .build(); | 211 | .build(); |
271 | let substs = Substs::build_for_def(self.db, f) | 212 | let substs = Substs::build_for_def(self.db, item) |
272 | .use_parent_substs(&trait_substs) | 213 | .use_parent_substs(&trait_substs) |
273 | .fill_with_params() | 214 | .fill_with_params() |
274 | .build(); | 215 | .build(); |
@@ -277,14 +218,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
277 | substs: trait_substs, | 218 | substs: trait_substs, |
278 | })); | 219 | })); |
279 | 220 | ||
280 | self.write_assoc_resolution(id, *item); | 221 | self.write_assoc_resolution(id, item); |
281 | return Some((ValueNs::Function(f), Some(substs))); | 222 | Some((def, Some(substs))) |
282 | } | 223 | } |
283 | } | 224 | } |
284 | } | 225 | }, |
285 | } | 226 | ) |
286 | |||
287 | None | ||
288 | } | 227 | } |
289 | 228 | ||
290 | fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { | 229 | fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { |
@@ -317,23 +256,3 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
317 | } | 256 | } |
318 | } | 257 | } |
319 | } | 258 | } |
320 | |||
321 | // TODO remove duplication | ||
322 | /// This creates Substs for a trait with the given Self type and type variables | ||
323 | /// for all other parameters, to query Chalk with it. | ||
324 | fn generic_implements_goal( | ||
325 | db: &impl HirDatabase, | ||
326 | env: Arc<TraitEnvironment>, | ||
327 | trait_: Trait, | ||
328 | self_ty: Canonical<Ty>, | ||
329 | ) -> Canonical<super::InEnvironment<super::Obligation>> { | ||
330 | let num_vars = self_ty.num_vars; | ||
331 | let substs = super::Substs::build_for_def(db, trait_) | ||
332 | .push(self_ty.value) | ||
333 | .fill_with_bound_vars(num_vars as u32) | ||
334 | .build(); | ||
335 | let num_vars = substs.len() - 1 + self_ty.num_vars; | ||
336 | let trait_ref = TraitRef { trait_, substs }; | ||
337 | let obligation = super::Obligation::Trait(trait_ref); | ||
338 | Canonical { num_vars, value: super::InEnvironment::new(env, obligation) } | ||
339 | } | ||
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index eb69344f6..ee0c7b00f 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs | |||
@@ -166,7 +166,19 @@ pub(crate) fn lookup_method( | |||
166 | name: &Name, | 166 | name: &Name, |
167 | resolver: &Resolver, | 167 | resolver: &Resolver, |
168 | ) -> Option<(Ty, Function)> { | 168 | ) -> Option<(Ty, Function)> { |
169 | iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f))) | 169 | iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| { |
170 | if let AssocItem::Function(f) = f { | ||
171 | Some((ty.clone(), f)) | ||
172 | } else { | ||
173 | None | ||
174 | } | ||
175 | }) | ||
176 | } | ||
177 | |||
178 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
179 | pub(crate) enum LookupMode { | ||
180 | MethodCall, | ||
181 | Path, | ||
170 | } | 182 | } |
171 | 183 | ||
172 | // This would be nicer if it just returned an iterator, but that runs into | 184 | // This would be nicer if it just returned an iterator, but that runs into |
@@ -176,7 +188,8 @@ pub(crate) fn iterate_method_candidates<T>( | |||
176 | db: &impl HirDatabase, | 188 | db: &impl HirDatabase, |
177 | resolver: &Resolver, | 189 | resolver: &Resolver, |
178 | name: Option<&Name>, | 190 | name: Option<&Name>, |
179 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 191 | mode: LookupMode, |
192 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
180 | ) -> Option<T> { | 193 | ) -> Option<T> { |
181 | // For method calls, rust first does any number of autoderef, and then one | 194 | // For method calls, rust first does any number of autoderef, and then one |
182 | // autoref (i.e. when the method takes &self or &mut self). We just ignore | 195 | // autoref (i.e. when the method takes &self or &mut self). We just ignore |
@@ -188,13 +201,15 @@ pub(crate) fn iterate_method_candidates<T>( | |||
188 | // rustc does an autoderef and then autoref again). | 201 | // rustc does an autoderef and then autoref again). |
189 | 202 | ||
190 | let krate = resolver.krate()?; | 203 | let krate = resolver.krate()?; |
204 | // TODO no autoderef in LookupMode::Path | ||
191 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { | 205 | for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { |
192 | if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) | 206 | if let Some(result) = |
207 | iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) | ||
193 | { | 208 | { |
194 | return Some(result); | 209 | return Some(result); |
195 | } | 210 | } |
196 | if let Some(result) = | 211 | if let Some(result) = |
197 | iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) | 212 | iterate_trait_method_candidates(&derefed_ty, db, resolver, name, mode, &mut callback) |
198 | { | 213 | { |
199 | return Some(result); | 214 | return Some(result); |
200 | } | 215 | } |
@@ -207,7 +222,8 @@ fn iterate_trait_method_candidates<T>( | |||
207 | db: &impl HirDatabase, | 222 | db: &impl HirDatabase, |
208 | resolver: &Resolver, | 223 | resolver: &Resolver, |
209 | name: Option<&Name>, | 224 | name: Option<&Name>, |
210 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 225 | mode: LookupMode, |
226 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, | ||
211 | ) -> Option<T> { | 227 | ) -> Option<T> { |
212 | let krate = resolver.krate()?; | 228 | let krate = resolver.krate()?; |
213 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) | 229 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) |
@@ -231,21 +247,35 @@ fn iterate_trait_method_candidates<T>( | |||
231 | // trait, but if we find out it doesn't, we'll skip the rest of the | 247 | // trait, but if we find out it doesn't, we'll skip the rest of the |
232 | // iteration | 248 | // iteration |
233 | let mut known_implemented = inherently_implemented; | 249 | let mut known_implemented = inherently_implemented; |
234 | for item in data.items() { | 250 | for &item in data.items() { |
235 | if let AssocItem::Function(m) = *item { | 251 | // TODO unify with the impl case |
236 | let data = m.data(db); | 252 | match item { |
237 | if name.map_or(true, |name| data.name() == name) && data.has_self_param() { | 253 | AssocItem::Function(m) => { |
238 | if !known_implemented { | 254 | let data = m.data(db); |
239 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | 255 | if !name.map_or(true, |name| data.name() == name) |
240 | if db.trait_solve(krate, goal).is_none() { | 256 | || (!data.has_self_param() && mode != LookupMode::Path) |
241 | continue 'traits; | 257 | { |
242 | } | 258 | continue; |
243 | } | 259 | } |
244 | known_implemented = true; | 260 | } |
245 | if let Some(result) = callback(&ty.value, m) { | 261 | AssocItem::Const(c) => { |
246 | return Some(result); | 262 | if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) |
263 | || (mode != LookupMode::Path) | ||
264 | { | ||
265 | continue; | ||
247 | } | 266 | } |
248 | } | 267 | } |
268 | _ => {} | ||
269 | }; | ||
270 | if !known_implemented { | ||
271 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | ||
272 | if db.trait_solve(krate, goal).is_none() { | ||
273 | continue 'traits; | ||
274 | } | ||
275 | } | ||
276 | known_implemented = true; | ||
277 | if let Some(result) = callback(&ty.value, item) { | ||
278 | return Some(result); | ||
249 | } | 279 | } |
250 | } | 280 | } |
251 | } | 281 | } |
@@ -256,21 +286,35 @@ fn iterate_inherent_methods<T>( | |||
256 | ty: &Canonical<Ty>, | 286 | ty: &Canonical<Ty>, |
257 | db: &impl HirDatabase, | 287 | db: &impl HirDatabase, |
258 | name: Option<&Name>, | 288 | name: Option<&Name>, |
289 | mode: LookupMode, | ||
259 | krate: Crate, | 290 | krate: Crate, |
260 | mut callback: impl FnMut(&Ty, Function) -> Option<T>, | 291 | mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>, |
261 | ) -> Option<T> { | 292 | ) -> Option<T> { |
262 | for krate in def_crates(db, krate, &ty.value)? { | 293 | for krate in def_crates(db, krate, &ty.value)? { |
263 | let impls = db.impls_in_crate(krate); | 294 | let impls = db.impls_in_crate(krate); |
264 | 295 | ||
265 | for impl_block in impls.lookup_impl_blocks(&ty.value) { | 296 | for impl_block in impls.lookup_impl_blocks(&ty.value) { |
266 | for item in impl_block.items(db) { | 297 | for item in impl_block.items(db) { |
267 | if let AssocItem::Function(f) = item { | 298 | match item { |
268 | let data = f.data(db); | 299 | AssocItem::Function(f) => { |
269 | if name.map_or(true, |name| data.name() == name) && data.has_self_param() { | 300 | let data = f.data(db); |
270 | if let Some(result) = callback(&ty.value, f) { | 301 | if !name.map_or(true, |name| data.name() == name) |
271 | return Some(result); | 302 | || (!data.has_self_param() && mode != LookupMode::Path) |
303 | { | ||
304 | continue; | ||
272 | } | 305 | } |
273 | } | 306 | } |
307 | AssocItem::Const(c) => { | ||
308 | if !name.map_or(true, |name| Some(name) == c.name(db).as_ref()) | ||
309 | || (mode != LookupMode::Path) | ||
310 | { | ||
311 | continue; | ||
312 | } | ||
313 | } | ||
314 | _ => {} | ||
315 | } | ||
316 | if let Some(result) = callback(&ty.value, item) { | ||
317 | return Some(result); | ||
274 | } | 318 | } |
275 | } | 319 | } |
276 | } | 320 | } |
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index e071e4d4e..bfef48b16 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs | |||
@@ -1841,8 +1841,8 @@ fn test() { | |||
1841 | [243; 254) 'Struct::FOO': u32 | 1841 | [243; 254) 'Struct::FOO': u32 |
1842 | [264; 265) 'y': u32 | 1842 | [264; 265) 'y': u32 |
1843 | [268; 277) 'Enum::BAR': u32 | 1843 | [268; 277) 'Enum::BAR': u32 |
1844 | [287; 288) 'z': {unknown} | 1844 | [287; 288) 'z': u32 |
1845 | [291; 304) 'TraitTest::ID': {unknown} | 1845 | [291; 304) 'TraitTest::ID': u32 |
1846 | "### | 1846 | "### |
1847 | ); | 1847 | ); |
1848 | } | 1848 | } |
diff --git a/crates/ra_ide_api/src/completion/complete_dot.rs b/crates/ra_ide_api/src/completion/complete_dot.rs index b4df6ee2a..7135f481d 100644 --- a/crates/ra_ide_api/src/completion/complete_dot.rs +++ b/crates/ra_ide_api/src/completion/complete_dot.rs | |||
@@ -58,10 +58,12 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) | |||
58 | 58 | ||
59 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { | 59 | fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) { |
60 | let mut seen_methods = FxHashSet::default(); | 60 | let mut seen_methods = FxHashSet::default(); |
61 | ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| { | 61 | ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, item| { |
62 | let data = func.data(ctx.db); | 62 | if let hir::AssocItem::Function(func) = item { |
63 | if data.has_self_param() && seen_methods.insert(data.name().clone()) { | 63 | let data = func.data(ctx.db); |
64 | acc.add_function(ctx, func); | 64 | if data.has_self_param() && seen_methods.insert(data.name().clone()) { |
65 | acc.add_function(ctx, func); | ||
66 | } | ||
65 | } | 67 | } |
66 | None::<()> | 68 | None::<()> |
67 | }); | 69 | }); |