Expr::MethodCall { receiver, args, method_name, generic_args } => {
let receiver_ty = self.infer_expr(*receiver, &Expectation::none());
let resolved = receiver_ty.clone().lookup_method(self.db, method_name);
- let (method_ty, def_generics) = match resolved {
- Some(func) => {
+ let (derefed_receiver_ty, method_ty, def_generics) = match resolved {
+ Some((ty, func)) => {
self.write_method_resolution(tgt_expr, func);
- (self.db.type_for_def(func.into()), Some(func.generic_params(self.db)))
+ (ty, self.db.type_for_def(func.into()), Some(func.generic_params(self.db)))
}
- None => (Ty::Unknown, None),
+ None => (Ty::Unknown, receiver_ty, None),
};
// handle provided type arguments
let method_ty = if let Some(generic_args) = generic_args {
}
_ => (Ty::Unknown, Vec::new(), Ty::Unknown),
};
- // TODO we would have to apply the autoderef/autoref steps here
- // to get the correct receiver type to unify...
- self.unify(&expected_receiver_ty, &receiver_ty);
+ // Apply autoref so the below unification works correctly
+ let actual_receiver_ty = match expected_receiver_ty {
+ Ty::Ref(_, mutability) => Ty::Ref(Arc::new(derefed_receiver_ty), mutability),
+ _ => derefed_receiver_ty,
+ };
+ self.unify(&expected_receiver_ty, &actual_receiver_ty);
+
let param_iter = param_tys.into_iter().chain(repeat(Ty::Unknown));
for (arg, param) in args.iter().zip(param_iter) {
self.infer_expr(*arg, &Expectation::has_type(param));
// TODO: cache this as a query?
// - if so, what signature? (TyFingerprint, Name)?
// - or maybe cache all names and def_ids of methods per fingerprint?
- pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<Function> {
- self.iterate_methods(db, |f| {
+ /// Look up the method with the given name, returning the actual autoderefed
+ /// receiver type (but without autoref applied yet).
+ pub fn lookup_method(self, db: &impl HirDatabase, name: &Name) -> Option<(Ty, Function)> {
+ self.iterate_methods(db, |ty, f| {
let sig = f.signature(db);
if sig.name() == name && sig.has_self_param() {
- Some(f)
+ Some((ty.clone(), f))
} else {
None
}
pub fn iterate_methods<T>(
self,
db: &impl HirDatabase,
- mut callback: impl FnMut(Function) -> Option<T>,
+ mut callback: impl FnMut(&Ty, Function) -> Option<T>,
) -> Option<T> {
// For method calls, rust first does any number of autoderef, and then one
// autoref (i.e. when the method takes &self or &mut self). We just ignore
for item in impl_block.items(db) {
match item {
ImplItem::Method(f) => {
- if let Some(result) = callback(f) {
+ if let Some(result) = callback(&derefed_ty, f) {
return Some(result);
}
}
--- /dev/null
+---
+created: "2019-02-17T13:35:06.385679926Z"
+creator: insta@0.6.2
+source: crates/ra_hir/src/ty/tests.rs
+expression: "&result"
+---
+[78; 82) 'self': &Option<T>
+[98; 100) '{}': ()
+[111; 112) 'o': Option<u32>
+[127; 165) '{ ...f(); }': ()
+[133; 146) '(&o).as_ref()': Option<&u32>
+[134; 136) '&o': &Option<u32>
+[135; 136) 'o': Option<u32>
+[152; 153) 'o': Option<u32>
+[152; 162) 'o.as_ref()': Option<&u32>
+
);
}
+#[test]
+fn infer_impl_generics_with_autoderef() {
+ check_inference(
+ "infer_impl_generics_with_autoderef",
+ r#"
+enum Option<T> {
+ Some(T),
+ None,
+}
+impl<T> Option<T> {
+ fn as_ref(&self) -> Option<&T> {}
+}
+fn test(o: Option<u32>) {
+ (&o).as_ref();
+ o.as_ref();
+}
+"#,
+ );
+}
+
#[test]
fn infer_generic_chain() {
check_inference(
}
fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
- receiver.iterate_methods(ctx.db, |func| {
+ receiver.iterate_methods(ctx.db, |_ty, func| {
let sig = func.signature(ctx.db);
if sig.has_self_param() {
CompletionItem::new(