aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/method_resolution.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty/method_resolution.rs')
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs141
1 files changed, 100 insertions, 41 deletions
diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs
index eb69344f6..8c3d32d09 100644
--- a/crates/ra_hir/src/ty/method_resolution.rs
+++ b/crates/ra_hir/src/ty/method_resolution.rs
@@ -166,37 +166,78 @@ 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| match f
170 {
171 AssocItem::Function(f) => Some((ty.clone(), f)),
172 _ => None,
173 })
174}
175
176/// Whether we're looking up a dotted method call (like `v.len()`) or a path
177/// (like `Vec::new`).
178#[derive(Copy, Clone, Debug, PartialEq, Eq)]
179pub enum LookupMode {
180 /// Looking up a method call like `v.len()`: We only consider candidates
181 /// that have a `self` parameter, and do autoderef.
182 MethodCall,
183 /// Looking up a path like `Vec::new` or `Vec::default`: We consider all
184 /// candidates including associated constants, but don't do autoderef.
185 Path,
170} 186}
171 187
172// This would be nicer if it just returned an iterator, but that runs into 188// This would be nicer if it just returned an iterator, but that runs into
173// lifetime problems, because we need to borrow temp `CrateImplBlocks`. 189// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
190// FIXME add a context type here?
174pub(crate) fn iterate_method_candidates<T>( 191pub(crate) fn iterate_method_candidates<T>(
175 ty: &Canonical<Ty>, 192 ty: &Canonical<Ty>,
176 db: &impl HirDatabase, 193 db: &impl HirDatabase,
177 resolver: &Resolver, 194 resolver: &Resolver,
178 name: Option<&Name>, 195 name: Option<&Name>,
179 mut callback: impl FnMut(&Ty, Function) -> Option<T>, 196 mode: LookupMode,
197 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
180) -> Option<T> { 198) -> Option<T> {
181 // For method calls, rust first does any number of autoderef, and then one 199 let krate = resolver.krate()?;
182 // autoref (i.e. when the method takes &self or &mut self). We just ignore 200 match mode {
183 // the autoref currently -- when we find a method matching the given name, 201 LookupMode::MethodCall => {
184 // we assume it fits. 202 // For method calls, rust first does any number of autoderef, and then one
203 // autoref (i.e. when the method takes &self or &mut self). We just ignore
204 // the autoref currently -- when we find a method matching the given name,
205 // we assume it fits.
185 206
186 // Also note that when we've got a receiver like &S, even if the method we 207 // Also note that when we've got a receiver like &S, even if the method we
187 // find in the end takes &self, we still do the autoderef step (just as 208 // find in the end takes &self, we still do the autoderef step (just as
188 // rustc does an autoderef and then autoref again). 209 // rustc does an autoderef and then autoref again).
189 210
190 let krate = resolver.krate()?; 211 for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
191 for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) { 212 if let Some(result) =
192 if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback) 213 iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
193 { 214 {
194 return Some(result); 215 return Some(result);
216 }
217 if let Some(result) = iterate_trait_method_candidates(
218 &derefed_ty,
219 db,
220 resolver,
221 name,
222 mode,
223 &mut callback,
224 ) {
225 return Some(result);
226 }
227 }
195 } 228 }
196 if let Some(result) = 229 LookupMode::Path => {
197 iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback) 230 // No autoderef for path lookups
198 { 231 if let Some(result) =
199 return Some(result); 232 iterate_inherent_methods(&ty, db, name, mode, krate, &mut callback)
233 {
234 return Some(result);
235 }
236 if let Some(result) =
237 iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback)
238 {
239 return Some(result);
240 }
200 } 241 }
201 } 242 }
202 None 243 None
@@ -207,7 +248,8 @@ fn iterate_trait_method_candidates<T>(
207 db: &impl HirDatabase, 248 db: &impl HirDatabase,
208 resolver: &Resolver, 249 resolver: &Resolver,
209 name: Option<&Name>, 250 name: Option<&Name>,
210 mut callback: impl FnMut(&Ty, Function) -> Option<T>, 251 mode: LookupMode,
252 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
211) -> Option<T> { 253) -> Option<T> {
212 let krate = resolver.krate()?; 254 let krate = resolver.krate()?;
213 // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) 255 // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
@@ -231,22 +273,20 @@ fn iterate_trait_method_candidates<T>(
231 // trait, but if we find out it doesn't, we'll skip the rest of the 273 // trait, but if we find out it doesn't, we'll skip the rest of the
232 // iteration 274 // iteration
233 let mut known_implemented = inherently_implemented; 275 let mut known_implemented = inherently_implemented;
234 for item in data.items() { 276 for &item in data.items() {
235 if let AssocItem::Function(m) = *item { 277 if !is_valid_candidate(db, name, mode, item) {
236 let data = m.data(db); 278 continue;
237 if name.map_or(true, |name| data.name() == name) && data.has_self_param() { 279 }
238 if !known_implemented { 280 if !known_implemented {
239 let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); 281 let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
240 if db.trait_solve(krate, goal).is_none() { 282 if db.trait_solve(krate, goal).is_none() {
241 continue 'traits; 283 continue 'traits;
242 }
243 }
244 known_implemented = true;
245 if let Some(result) = callback(&ty.value, m) {
246 return Some(result);
247 }
248 } 284 }
249 } 285 }
286 known_implemented = true;
287 if let Some(result) = callback(&ty.value, item) {
288 return Some(result);
289 }
250 } 290 }
251 } 291 }
252 None 292 None
@@ -256,21 +296,20 @@ fn iterate_inherent_methods<T>(
256 ty: &Canonical<Ty>, 296 ty: &Canonical<Ty>,
257 db: &impl HirDatabase, 297 db: &impl HirDatabase,
258 name: Option<&Name>, 298 name: Option<&Name>,
299 mode: LookupMode,
259 krate: Crate, 300 krate: Crate,
260 mut callback: impl FnMut(&Ty, Function) -> Option<T>, 301 mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
261) -> Option<T> { 302) -> Option<T> {
262 for krate in def_crates(db, krate, &ty.value)? { 303 for krate in def_crates(db, krate, &ty.value)? {
263 let impls = db.impls_in_crate(krate); 304 let impls = db.impls_in_crate(krate);
264 305
265 for impl_block in impls.lookup_impl_blocks(&ty.value) { 306 for impl_block in impls.lookup_impl_blocks(&ty.value) {
266 for item in impl_block.items(db) { 307 for item in impl_block.items(db) {
267 if let AssocItem::Function(f) = item { 308 if !is_valid_candidate(db, name, mode, item) {
268 let data = f.data(db); 309 continue;
269 if name.map_or(true, |name| data.name() == name) && data.has_self_param() { 310 }
270 if let Some(result) = callback(&ty.value, f) { 311 if let Some(result) = callback(&ty.value, item) {
271 return Some(result); 312 return Some(result);
272 }
273 }
274 } 313 }
275 } 314 }
276 } 315 }
@@ -278,6 +317,26 @@ fn iterate_inherent_methods<T>(
278 None 317 None
279} 318}
280 319
320fn is_valid_candidate(
321 db: &impl HirDatabase,
322 name: Option<&Name>,
323 mode: LookupMode,
324 item: AssocItem,
325) -> bool {
326 match item {
327 AssocItem::Function(m) => {
328 let data = m.data(db);
329 name.map_or(true, |name| data.name() == name)
330 && (data.has_self_param() || mode == LookupMode::Path)
331 }
332 AssocItem::Const(c) => {
333 name.map_or(true, |name| Some(name) == c.name(db).as_ref())
334 && (mode == LookupMode::Path)
335 }
336 _ => false,
337 }
338}
339
281pub(crate) fn implements_trait( 340pub(crate) fn implements_trait(
282 ty: &Canonical<Ty>, 341 ty: &Canonical<Ty>,
283 db: &impl HirDatabase, 342 db: &impl HirDatabase,