diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index aa43bc800d3..12e10c75187 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -221,6 +221,14 @@ impl FnSig {
         &self.params_and_return[self.params_and_return.len() - 1]
     }
 
+    /// Applies the given substitutions to all types in this signature and
+    /// returns the result.
+    pub fn subst(&self, substs: &Substs) -> FnSig {
+        let result: Vec<_> =
+            self.params_and_return.iter().map(|ty| ty.clone().subst(substs)).collect();
+        FnSig { params_and_return: result.into() }
+    }
+
     pub fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
         // Without an Arc::make_mut_slice, we can't avoid the clone here:
         let mut v: Vec<_> = self.params_and_return.iter().cloned().collect();
@@ -320,6 +328,20 @@ impl Ty {
         }
     }
 
+    fn callable_sig(&self, db: &impl HirDatabase) -> Option<FnSig> {
+        match self {
+            Ty::Apply(a_ty) => match a_ty.ctor {
+                TypeCtor::FnPtr => Some(FnSig::from_fn_ptr_substs(&a_ty.parameters)),
+                TypeCtor::FnDef(def) => {
+                    let sig = db.callable_item_signature(def);
+                    Some(sig.subst(&a_ty.parameters))
+                }
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+
     /// If this is a type with type parameters (an ADT or function), replaces
     /// the `Substs` for these type parameters with the given ones. (So e.g. if
     /// `self` is `Option<_>` and the substs contain `u32`, we'll have
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index b28bb47c6f3..28459d75086 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -41,7 +41,7 @@ use crate::{
     ty::infer::diagnostics::InferenceDiagnostic,
     diagnostics::DiagnosticSink,
 };
-use super::{Ty, TypableDef, Substs, primitive, op, FnSig, ApplicationTy, TypeCtor, traits::{ Solution, Obligation, Guidance}, CallableDef, TraitRef};
+use super::{Ty, TypableDef, Substs, primitive, op, ApplicationTy, TypeCtor, traits::{ Solution, Obligation, Guidance}, CallableDef, TraitRef};
 
 /// The entry point of type inference.
 pub fn infer(db: &impl HirDatabase, def: DefWithBody) -> Arc<InferenceResult> {
@@ -839,32 +839,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         let method_ty = method_ty.apply_substs(substs);
         let method_ty = self.insert_type_vars(method_ty);
         self.register_obligations_for_call(&method_ty);
-        let (expected_receiver_ty, param_tys, ret_ty) = match &method_ty {
-            Ty::Apply(a_ty) => match a_ty.ctor {
-                TypeCtor::FnPtr => {
-                    let sig = FnSig::from_fn_ptr_substs(&a_ty.parameters);
-                    if !sig.params().is_empty() {
-                        (sig.params()[0].clone(), sig.params()[1..].to_vec(), sig.ret().clone())
-                    } else {
-                        (Ty::Unknown, Vec::new(), sig.ret().clone())
-                    }
+        let (expected_receiver_ty, param_tys, ret_ty) = match method_ty.callable_sig(self.db) {
+            Some(sig) => {
+                if !sig.params().is_empty() {
+                    (sig.params()[0].clone(), sig.params()[1..].to_vec(), sig.ret().clone())
+                } else {
+                    (Ty::Unknown, Vec::new(), sig.ret().clone())
                 }
-                TypeCtor::FnDef(def) => {
-                    let sig = self.db.callable_item_signature(def);
-                    let ret_ty = sig.ret().clone().subst(&a_ty.parameters);
-
-                    if !sig.params().is_empty() {
-                        let mut params_iter =
-                            sig.params().iter().map(|ty| ty.clone().subst(&a_ty.parameters));
-                        let receiver_ty = params_iter.next().unwrap();
-                        (receiver_ty, params_iter.collect(), ret_ty)
-                    } else {
-                        (Ty::Unknown, Vec::new(), ret_ty)
-                    }
-                }
-                _ => (Ty::Unknown, Vec::new(), Ty::Unknown),
-            },
-            _ => (Ty::Unknown, Vec::new(), Ty::Unknown),
+            }
+            None => (Ty::Unknown, Vec::new(), Ty::Unknown),
         };
         // Apply autoref so the below unification works correctly
         // FIXME: return correct autorefs from lookup_method
@@ -937,27 +920,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             }
             Expr::Call { callee, args } => {
                 let callee_ty = self.infer_expr(*callee, &Expectation::none());
-                let (param_tys, ret_ty) = match &callee_ty {
-                    Ty::Apply(a_ty) => match a_ty.ctor {
-                        TypeCtor::FnPtr => {
-                            let sig = FnSig::from_fn_ptr_substs(&a_ty.parameters);
-                            (sig.params().to_vec(), sig.ret().clone())
-                        }
-                        TypeCtor::FnDef(def) => {
-                            let sig = self.db.callable_item_signature(def);
-                            let ret_ty = sig.ret().clone().subst(&a_ty.parameters);
-                            let param_tys = sig
-                                .params()
-                                .iter()
-                                .map(|ty| ty.clone().subst(&a_ty.parameters))
-                                .collect();
-                            (param_tys, ret_ty)
-                        }
-                        _ => (Vec::new(), Ty::Unknown),
-                    },
-                    _ => {
-                        // not callable
-                        // FIXME report an error?
+                let (param_tys, ret_ty) = match callee_ty.callable_sig(self.db) {
+                    Some(sig) => (sig.params().to_vec(), sig.ret().clone()),
+                    None => {
+                        // Not callable
+                        // FIXME: report an error
                         (Vec::new(), Ty::Unknown)
                     }
                 };