Repository: racer-rust/racer (language: rust)

Issue:
racer-find-definition async syntax support?
I tried using racer on what I believe is a soon-to-be stable [async/await Rust code](https://github.com/rustasync/runtime/blob/master/examples/hello.rs):

![Screenshot from 2019-07-09 16-58-51](https://user-images.githubusercontent.com/6098822/60899508-e1339d00-a26a-11e9-8b9b-562f13b8ceca.png)

when the cursor is over `say_hi().await` I cannot jump to the function definition above:
```
$ racer find-definition 9 5 say_hi src/main.rs 
END
```

If I turn that snippet into sync code, it resolves the definition:
```
$ racer find-definition 9 5 say_hi src/main.rs 
MATCH say_hi,3,3,say_hi,Function,fn say_hi()
END
```

Opinions? Is my assumption correct?


Discussion / hints:
Yeah, it's not supported yet because of my negligence :cold_sweat: 
Good catch, thanks.



No worries! Just needed confirmation that it's not a bug but a ... feature :-)

FWIW: thanks for all your work on racer :1st_place_medal: 
Hey! How are we looking on this? I jump a lot between async functions so this would really help :+1: 

Fix patch:
diff --git a/src/racer/ast.rs b/src/racer/ast.rs
index 3040de36..7a5c0aff 100644
--- a/src/racer/ast.rs
+++ b/src/racer/ast.rs
@@ -1137,13 +1137,11 @@ pub fn parse_pat_idents(s: String) -> Vec<ByteRange> {
     v.ident_points
 }
 
-pub fn parse_fn_output(s: String, scope: Scope) -> Option<Ty> {
-    let mut v = FnOutputVisitor {
-        result: None,
-        scope,
-    };
+pub fn parse_fn_output(s: String, scope: Scope) -> (Option<Ty>, bool) {
+    let mut v = FnOutputVisitor::new(scope);
     with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
-    v.result
+    let FnOutputVisitor { ty, is_async, .. } = v;
+    (ty, is_async)
 }
 
 pub fn parse_extern_crate(s: String) -> ExternCrateVisitor {
@@ -1212,20 +1210,35 @@ pub fn get_match_arm_type(
 
 pub struct FnOutputVisitor {
     scope: Scope,
-    pub result: Option<Ty>,
+    pub ty: Option<Ty>,
+    pub is_async: bool,
+}
+
+impl FnOutputVisitor {
+    pub(crate) fn new(scope: Scope) -> Self {
+        FnOutputVisitor {
+            scope,
+            ty: None,
+            is_async: false,
+        }
+    }
 }
 
 impl<'ast> visit::Visitor<'ast> for FnOutputVisitor {
     fn visit_fn(
         &mut self,
-        _: visit::FnKind<'_>,
+        kind: visit::FnKind<'_>,
         fd: &ast::FnDecl,
         _: source_map::Span,
         _: ast::NodeId,
     ) {
-        self.result = match fd.output {
+        self.is_async = kind
+            .header()
+            .map(|header| header.asyncness.node.is_async())
+            .unwrap_or(false);
+        self.ty = match fd.output {
             FunctionRetTy::Ty(ref ty) => Ty::from_ast(ty, &self.scope),
-            FunctionRetTy::Default(_) => None,
+            FunctionRetTy::Default(_) => Some(Ty::Default),
         };
     }
 }
diff --git a/src/racer/ast_types.rs b/src/racer/ast_types.rs
index 59e2a6bb..4ccdc4a5 100644
--- a/src/racer/ast_types.rs
+++ b/src/racer/ast_types.rs
@@ -58,6 +58,9 @@ pub enum Ty {
     Ptr(Box<Ty>, Mutability),
     TraitObject(TraitBounds),
     Self_(Scope),
+    Future(Box<Ty>, Scope),
+    Never,
+    Default,
     Unsupported,
 }
 
@@ -249,6 +252,9 @@ impl fmt::Display for Ty {
                 write!(f, ">")
             }
             Ty::Self_(_) => write!(f, "Self"),
+            Ty::Future(ref ty, _) => write!(f, "impl Future<Output={}>", ty),
+            Ty::Never => write!(f, "!"),
+            Ty::Default => write!(f, "()"),
             Ty::Unsupported => write!(f, "_"),
         }
     }
diff --git a/src/racer/lib.rs b/src/racer/lib.rs
old mode 100644
new mode 100755
diff --git a/src/racer/matchers.rs b/src/racer/matchers.rs
index 07ab1b1c..12f5c961 100644
--- a/src/racer/matchers.rs
+++ b/src/racer/matchers.rs
@@ -723,7 +723,7 @@ fn match_fn_common(
     context: &MatchCxt<'_, '_>,
     session: &Session<'_>,
 ) -> Option<Match> {
-    let (start, s) = context.get_key_ident(blob, "fn", &["const", "unsafe"])?;
+    let (start, s) = context.get_key_ident(blob, "fn", &["const", "unsafe", "async"])?;
     let start = context.range.start + start;
     let doc_src = session.load_raw_src_ranged(&msrc, context.filepath);
     Some(Match {
diff --git a/src/racer/nameres.rs b/src/racer/nameres.rs
index 98858236..52e72c2e 100644
--- a/src/racer/nameres.rs
+++ b/src/racer/nameres.rs
@@ -18,7 +18,7 @@ use crate::matchers::{find_doc, ImportInfo, MatchCxt};
 use crate::primitive;
 use crate::util::{
     self, calculate_str_hash, find_ident_end, get_rust_src_path, strip_words, symbol_matches,
-    trim_visibility, txt_matches,
+    trim_visibility, txt_matches, txt_matches_with_pos,
 };
 use crate::{ast, core, matchers, scopes, typeinf};
 
@@ -660,7 +660,7 @@ fn search_scope_headers(
 // TODO: handle extern ".." fn
 fn preblock_is_fn(preblock: &str) -> bool {
     let s = trim_visibility(preblock);
-    let p = strip_words(s, &["const", "unsafe"]);
+    let p = strip_words(s, &["const", "unsafe", "async"]);
     if p.0 < s.len() {
         s[p.0..].starts_with("fn")
     } else {
@@ -672,6 +672,7 @@ fn preblock_is_fn(preblock: &str) -> bool {
 fn is_fn() {
     assert!(preblock_is_fn("pub fn bar()"));
     assert!(preblock_is_fn("fn foo()"));
+    assert!(preblock_is_fn("async fn foo()"));
     assert!(preblock_is_fn("const fn baz()"));
     assert!(preblock_is_fn("pub(crate) fn bar()"));
     assert!(preblock_is_fn("pub(in foo::bar) fn bar()"));
@@ -2370,10 +2371,28 @@ pub(crate) fn get_field_matches_from_ty(
             .into_iter()
             .flat_map(|ps| get_field_matches_from_ty(Ty::PathSearch(ps), searchstr, stype, session))
             .collect(),
+        Ty::Future(_, scope) => get_future(scope, session)
+            .into_iter()
+            .flat_map(|f| search_for_trait_methods(f, searchstr, stype, session))
+            .chain(
+                txt_matches_with_pos(stype, searchstr, "await")
+                    .and_then(|_| PrimKind::Await.to_doc_match(session))
+                    .into_iter(),
+            )
+            .collect(),
         _ => vec![],
     }
 }
 
+fn get_future(scope: Scope, session: &Session<'_>) -> Option<Match> {
+    let path = RacerPath::from_iter(
+        false,
+        ["std", "future", "Future"].iter().map(|s| s.to_string()),
+    );
+
+    ast::find_type_match(&path, &scope.filepath, scope.point, session)
+}
+
 fn get_assoc_type_from_header(
     target_path: &RacerPath, // type target = ~
     type_match: &Match,      // the type which implements trait
@@ -2424,11 +2443,12 @@ fn get_std_macros(
     for macro_file in &[
         "libstd/macros.rs",
         "libcore/macros.rs",
+        "libcore/macros/mod.rs",
         "liballoc/macros.rs",
     ] {
         let macro_path = std_path.join(macro_file);
         if !macro_path.exists() {
-            return;
+            continue;
         }
         get_std_macros_(
             &macro_path,
diff --git a/src/racer/primitive.rs b/src/racer/primitive.rs
index 7326a952..f307c16f 100644
--- a/src/racer/primitive.rs
+++ b/src/racer/primitive.rs
@@ -1,4 +1,3 @@
-use crate::ast_types::PathSegment;
 use crate::core::{BytePos, Match, MatchType, Namespace, SearchType, Session};
 use crate::matchers::ImportInfo;
 use crate::nameres::{self, RUST_SRC_PATH};
@@ -6,6 +5,7 @@ use std::path::PathBuf;
 use syntax::ast::{IntTy, LitIntType, UintTy};
 
 const PRIM_DOC: &str = "libstd/primitive_docs.rs";
+const KEY_DOC: &str = "libstd/keyword_docs.rs";
 
 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
 pub enum PrimKind {
@@ -34,6 +34,7 @@ pub enum PrimKind {
     Usize,
     Ref,
     Fn,
+    Await,
 }
 
 const PRIM_MATCHES: [PrimKind; 17] = [
@@ -105,6 +106,13 @@ impl PrimKind {
             PrimKind::Usize => Some(&["libcore/num/mod.rs"]),
             PrimKind::Ref => None,
             PrimKind::Fn => None,
+            PrimKind::Await => None,
+        }
+    }
+    fn is_keyword(self) -> bool {
+        match self {
+            PrimKind::Await => true,
+            _ => false,
         }
     }
     fn match_name(self) -> &'static str {
@@ -134,6 +142,7 @@ impl PrimKind {
             PrimKind::Usize => "usize",
             PrimKind::Ref => "ref",
             PrimKind::Fn => "fn",
+            PrimKind::Await => "await",
         }
     }
     pub(crate) fn get_impl_files(&self) -> Option<Vec<PathBuf>> {
@@ -155,12 +164,21 @@ impl PrimKind {
         })
     }
     pub fn to_doc_match(self, session: &Session<'_>) -> Option<Match> {
-        let seg: PathSegment = format!("prim_{}", self.match_name()).into();
         let src_path = RUST_SRC_PATH.as_ref()?;
-        let prim_path = src_path.join(PRIM_DOC);
+        let (path, seg) = if self.is_keyword() {
+            (
+                src_path.join(KEY_DOC),
+                format!("{}_keyword", self.match_name()),
+            )
+        } else {
+            (
+                src_path.join(PRIM_DOC),
+                format!("prim_{}", self.match_name()),
+            )
+        };
         let mut m = nameres::resolve_name(
-            &seg,
-            &prim_path,
+            &seg.into(),
+            &path,
             BytePos::ZERO,
             SearchType::ExactMatch,
             Namespace::Mod,
diff --git a/src/racer/typeinf.rs b/src/racer/typeinf.rs
index 4b8b693e..bbcdc5c4 100644
--- a/src/racer/typeinf.rs
+++ b/src/racer/typeinf.rs
@@ -520,32 +520,38 @@ pub fn get_return_type_of_function(
 ) -> Option<Ty> {
     let src = session.load_source_file(&fnmatch.filepath);
     let point = scopes::expect_stmt_start(src.as_src(), fnmatch.point);
-    let out = src[point.0..].find('{').and_then(|n| {
-        // wrap in "impl b { }" so that methods get parsed correctly too
-        let decl = "impl b{".to_string() + &src[point.0..point.0 + n + 1] + "}}";
-        debug!("get_return_type_of_function: passing in |{}|", decl);
-        let mut scope = Scope::from_match(fnmatch);
-        // TODO(kngwyu): if point <= 5 scope is incorrect
-        scope.point = point.checked_sub("impl b{".len()).unwrap_or(BytePos::ZERO);
-        ast::parse_fn_output(decl, scope)
-    });
-    // Convert output arg of type Self to the correct type
-    if let Some(Ty::PathSearch(ref paths)) = out {
-        let path = &paths.path;
-        if let Some(ref path_seg) = path.segments.get(0) {
-            if "Self" == path_seg.name {
-                return get_type_of_self_arg(fnmatch, src.as_src(), session);
-            }
-            if path.segments.len() == 1 && path_seg.generics.is_empty() {
-                for type_param in fnmatch.generics() {
-                    if type_param.name() == &path_seg.name {
-                        return Some(Ty::Match(contextm.clone()));
+    let block_start = src[point.0..].find('{')?;
+    let decl = "impl b{".to_string() + &src[point.0..point.0 + block_start + 1] + "}}";
+    debug!("get_return_type_of_function: passing in |{}|", decl);
+    let mut scope = Scope::from_match(fnmatch);
+    // TODO(kngwyu): if point <= 5 scope is incorrect
+    scope.point = point.checked_sub("impl b{".len()).unwrap_or(BytePos::ZERO);
+    let (ty, is_async) = ast::parse_fn_output(decl, scope);
+    let resolve_ty = |ty| {
+        if let Some(Ty::PathSearch(ref paths)) = ty {
+            let path = &paths.path;
+            if let Some(ref path_seg) = path.segments.get(0) {
+                if "Self" == path_seg.name {
+                    return get_type_of_self_arg(fnmatch, src.as_src(), session);
+                }
+                if path.segments.len() == 1 && path_seg.generics.is_empty() {
+                    for type_param in fnmatch.generics() {
+                        if type_param.name() == &path_seg.name {
+                            return Some(Ty::Match(contextm.clone()));
+                        }
                     }
                 }
             }
         }
-    }
-    out
+        ty
+    };
+    resolve_ty(ty).map(|ty| {
+        if is_async {
+            Ty::Future(Box::new(ty), Scope::from_match(fnmatch))
+        } else {
+            ty
+        }
+    })
 }
 
 pub(crate) fn get_type_of_indexed_value(body: Ty, session: &Session<'_>) -> Option<Ty> {


Test patch:
diff --git a/tests/async.rs b/tests/async.rs
new file mode 100644
index 00000000..ecdccc00
--- /dev/null
+++ b/tests/async.rs
@@ -0,0 +1,45 @@
+use racer_testutils::*;
+
+#[test]
+fn completes_async_fn() {
+    let src = r#"
+    async fn say_hey() {
+        println!("Hey!")
+    }
+    fn main() {
+        say_h~
+    }
+"#;
+    let got = get_only_completion(src, None);
+    assert_eq!(got.matchstr, "say_hey");
+}
+
+#[test]
+fn completes_poll() {
+    let src = r#"
+    async fn say_hey() {
+        println!("Hey!")
+    }
+    async fn waiting_for() {
+        let handle = say_hey();
+        handle.po~
+    }
+"#;
+    let got = get_only_completion(src, None);
+    assert_eq!(got.matchstr, "poll");
+}
+
+#[test]
+fn completes_await() {
+    let src = r#"
+    async fn say_hey() {
+        println!("Hey!")
+    }
+    async fn waiting_for() {
+        let handle = say_hey();
+        handle.awa~
+    }
+"#;
+    let got = get_only_completion(src, None);
+    assert_eq!(got.matchstr, "await");
+}