From d21cdf3c998bb24e48e81a7e6909df2146ce097c Mon Sep 17 00:00:00 2001
From: Florian Diebold <flodiebold@gmail.com>
Date: Sat, 7 Sep 2019 15:13:05 +0200
Subject: Lower `Fn(X, Y) -> Z` paths

---
 crates/ra_hir/src/path.rs     | 44 +++++++++++++++++++++++++++++++++++++++----
 crates/ra_hir/src/ty/tests.rs | 16 ++++++++--------
 2 files changed, 48 insertions(+), 12 deletions(-)

(limited to 'crates/ra_hir')

diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 24316fc91..d6c78593b 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -1,11 +1,11 @@
 use std::sync::Arc;
 
 use ra_syntax::{
-    ast::{self, NameOwner},
+    ast::{self, NameOwner, TypeAscriptionOwner},
     AstNode,
 };
 
-use crate::{type_ref::TypeRef, AsName, Name};
+use crate::{name, type_ref::TypeRef, AsName, Name};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Path {
@@ -76,8 +76,16 @@ impl Path {
 
             match segment.kind()? {
                 ast::PathSegmentKind::Name(name) => {
-                    let args =
-                        segment.type_arg_list().and_then(GenericArgs::from_ast).map(Arc::new);
+                    let args = segment
+                        .type_arg_list()
+                        .and_then(GenericArgs::from_ast)
+                        .or_else(|| {
+                            GenericArgs::from_fn_like_path_ast(
+                                segment.param_list(),
+                                segment.ret_type(),
+                            )
+                        })
+                        .map(Arc::new);
                     let segment = PathSegment { name: name.as_name(), args_and_bindings: args };
                     segments.push(segment);
                 }
@@ -187,6 +195,34 @@ impl GenericArgs {
         }
     }
 
+    /// Collect `GenericArgs` from the parts of a fn-like path, i.e. `Fn(X, Y)
+    /// -> Z` (which desugars to `Fn<(X, Y), Output=Z>`).
+    pub(crate) fn from_fn_like_path_ast(
+        params: Option<ast::ParamList>,
+        ret_type: Option<ast::RetType>,
+    ) -> Option<GenericArgs> {
+        let mut args = Vec::new();
+        let mut bindings = Vec::new();
+        if let Some(params) = params {
+            let mut param_types = Vec::new();
+            for param in params.params() {
+                let type_ref = TypeRef::from_ast_opt(param.ascribed_type());
+                param_types.push(type_ref);
+            }
+            let arg = GenericArg::Type(TypeRef::Tuple(param_types));
+            args.push(arg);
+        }
+        if let Some(ret_type) = ret_type {
+            let type_ref = TypeRef::from_ast_opt(ret_type.type_ref());
+            bindings.push((name::OUTPUT, type_ref))
+        }
+        if args.is_empty() && bindings.is_empty() {
+            None
+        } else {
+            Some(GenericArgs { args, has_self_type: false, bindings })
+        }
+    }
+
     pub(crate) fn empty() -> GenericArgs {
         GenericArgs { args: Vec::new(), has_self_type: false, bindings: Vec::new() }
     }
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 17c4e3556..c414e6a95 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -3736,7 +3736,7 @@ fn fn_trait() {
 trait FnOnce<Args> {
     type Output;
 
-    fn call_once(self, args: Args) -> Self::Output;
+    fn call_once(self, args: Args) -> <Self as FnOnce<Args>>::Output;
 }
 
 fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
@@ -3746,13 +3746,13 @@ fn test<F: FnOnce(u32, u64) -> u128>(f: F) {
         @r###"
     [57; 61) 'self': Self
     [63; 67) 'args': Args
-    [132; 133) 'f': F
-    [138; 166) '{     ...2)); }': ()
-    [144; 145) 'f': F
-    [144; 163) 'f.call...1, 2))': {unknown}
-    [156; 162) '(1, 2)': (i32, i32)
-    [157; 158) '1': i32
-    [160; 161) '2': i32
+    [150; 151) 'f': F
+    [156; 184) '{     ...2)); }': ()
+    [162; 163) 'f': F
+    [162; 181) 'f.call...1, 2))': {unknown}
+    [174; 180) '(1, 2)': (u32, u64)
+    [175; 176) '1': u32
+    [178; 179) '2': u64
     "###
     );
 }
-- 
cgit v1.2.3