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

Issue:
Completion bug around `use ... as ...` syntax
 See https://github.com/racer-rust/emacs-racer/issues/105


Discussion / hints:
This is because when matches in use statements are returned, their matchstr is [replaced](https://github.com/racer-rust/racer/blob/98b761cce6ddc57dd51c147d8f473cb4c4d8fd55/src/racer/matchers.rs#L595) with the path alias and subsequent searches are done using the matchstr instead of the original name. To fix this we need a pass context boolean which denotes whether or not to replace the matchstr.

Fix patch:
diff --git a/src/racer/ast.rs b/src/racer/ast.rs
index 8130be5d..1e4f60f1 100644
--- a/src/racer/ast.rs
+++ b/src/racer/ast.rs
@@ -96,23 +96,29 @@ impl<'ast> visit::Visitor<'ast> for UseVisitor {
             };
             let mut contains_glob = false;
             match use_tree.kind {
-                UseTreeKind::Simple(_, _, _) => {
+                UseTreeKind::Simple(rename, _, _) => {
                     let ident = use_tree.ident().name.to_string();
+                    let rename_pos: Option<BytePos> =
+                        rename.map(|id| destruct_span(id.span).0.into());
                     let kind = if let Some(last_seg) = path.segments.last() {
                         //` self` is treated normaly in libsyntax,
                         //  but we distinguish it here to make completion easy
                         if last_seg.name == "self" {
                             PathAliasKind::Self_(ident)
                         } else {
-                            PathAliasKind::Ident(ident)
+                            PathAliasKind::Ident(ident, rename_pos)
                         }
                     } else {
-                        PathAliasKind::Ident(ident)
+                        PathAliasKind::Ident(ident, rename_pos)
                     };
                     if let PathAliasKind::Self_(_) = kind {
                         path.segments.pop();
                     }
-                    res.push(PathAlias { kind, path });
+                    res.push(PathAlias {
+                        kind,
+                        path,
+                        range: ByteRange::from(use_tree.span),
+                    });
                 }
                 UseTreeKind::Nested(ref nested) => {
                     nested.iter().for_each(|(ref tree, _)| {
@@ -125,6 +131,7 @@ impl<'ast> visit::Visitor<'ast> for UseVisitor {
                     res.push(PathAlias {
                         kind: PathAliasKind::Glob,
                         path,
+                        range: ByteRange::from(use_tree.span),
                     });
                     contains_glob = true;
                 }
@@ -492,7 +499,7 @@ pub(crate) fn find_type_match(
     .into_iter()
     .nth(0)
     .and_then(|m| match m.mtype {
-        MatchType::Type => get_type_of_typedef(&m, session),
+        MatchType::Type(ref resolved) => get_type_of_typedef(&m, session),
         _ => Some(m),
     })?;
     // TODO: 'Type' support
diff --git a/src/racer/ast_types.rs b/src/racer/ast_types.rs
index d50094b4..de476d55 100644
--- a/src/racer/ast_types.rs
+++ b/src/racer/ast_types.rs
@@ -1,6 +1,6 @@
 //! type conversion between racer types and libsyntax types
 use super::ast::find_type_match;
-use core::{self, BytePos, Match, MatchType, Scope, SearchType, Session, SessionExt};
+use core::{self, BytePos, ByteRange, Match, MatchType, Scope, SearchType, Session, SessionExt};
 use matchers::ImportInfo;
 use nameres;
 use primitive;
@@ -27,11 +27,13 @@ pub struct PathAlias {
     pub kind: PathAliasKind,
     /// The path.
     pub path: Path,
+    /// range of item
+    pub range: ByteRange,
 }
 
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub enum PathAliasKind {
-    Ident(String),
+    Ident(String, Option<BytePos>),
     Self_(String),
     Glob,
 }
diff --git a/src/racer/core.rs b/src/racer/core.rs
index cc810766..47b7bb76 100644
--- a/src/racer/core.rs
+++ b/src/racer/core.rs
@@ -42,7 +42,7 @@ pub enum MatchType {
     Enum(Box<GenericsArgs>),
     /// EnumVariant needs to have Enum type to complete methods
     EnumVariant(Option<Box<Match>>),
-    Type,
+    Type(Option<Box<Match>>),
     FnArg(Box<(Pat, Option<Ty>)>),
     Trait,
     Const,
@@ -86,6 +86,7 @@ impl fmt::Display for MatchType {
             MatchType::EnumVariant(_) => write!(f, "EnumVariant"),
             MatchType::TypeParameter(_) => write!(f, "TypeParameter"),
             MatchType::FnArg(_) => write!(f, "FnArg"),
+            MatchType::Type(_) => write!(f, "Type"),
             _ => fmt::Debug::fmt(self, f),
         }
     }
diff --git a/src/racer/matchers.rs b/src/racer/matchers.rs
index 57cfde73..46caa8b3 100644
--- a/src/racer/matchers.rs
+++ b/src/racer/matchers.rs
@@ -1,4 +1,4 @@
-use ast_types::{ImplHeader, PathAliasKind, PathSegment};
+use ast_types::{ImplHeader, PathAlias, PathAliasKind, PathSegment};
 use core::MatchType::{
     self, Const, Enum, EnumVariant, For, Function, IfLet, Let, Macro, Module, Static, Struct,
     Trait, Type, WhileLet,
@@ -441,7 +441,7 @@ pub fn match_type(msrc: Src, context: &MatchCxt, session: &Session) -> Option<Ma
         point: start,
         coords: None,
         local: context.is_local,
-        mtype: Type,
+        mtype: Type(None),
         contextstr: first_line(blob),
         docs: find_doc(&doc_src, start),
     })
@@ -573,9 +573,8 @@ pub fn match_use(
                 session,
                 &import_info,
             );
-            for mut m in path_iter {
-                $f(&mut m);
-                out.push(m);
+            for m in path_iter {
+                out.push($f(m));
                 if context.search_type == ExactMatch {
                     return out;
                 }
@@ -583,37 +582,54 @@ pub fn match_use(
         };
     }
     // let's find searchstr using path_aliases
-    for mut path_alias in use_item.path_list {
-        path_alias.path.set_prefix();
-        match path_alias.kind {
-            PathAliasKind::Ident(ref ident) => {
-                if !symbol_matches(context.search_type, context.search_str, ident) {
+    for path_alias in use_item.path_list {
+        let PathAlias {
+            path: mut alias_path,
+            kind: alias_kind,
+            range: alias_range,
+        } = path_alias;
+        alias_path.set_prefix();
+        match alias_kind {
+            PathAliasKind::Ident(ref ident, rename_start) => {
+                if !symbol_matches(context.search_type, context.search_str, &ident) {
                     continue;
                 }
-                with_match!(path_alias.as_ref(), Namespace::Path, |m: &mut Match| {
+                with_match!(&alias_path, Namespace::Path, |m: Match| {
                     debug!("[match_use] PathAliasKind::Ident {:?} was found", ident);
-                    if m.matchstr != *ident {
-                        m.matchstr = ident.clone();
-                    }
+                    let rename_start = match rename_start {
+                        Some(r) => r,
+                        None => return m,
+                    };
+                    // if use A as B found, we treat this type as type alias
+                    let range = alias_range.shift(context.range.start);
+                    let context_str = &msrc[range.to_range()];
+                    let alias_match = Match {
+                        matchstr: ident.clone(),
+                        filepath: context.filepath.to_owned(),
+                        point: context.range.start + rename_start,
+                        coords: None,
+                        local: context.is_local,
+                        mtype: MatchType::Type(Some(Box::new(m))),
+                        contextstr: context_str.to_owned(),
+                        docs: String::new(),
+                    };
+                    alias_match
                 });
             }
             PathAliasKind::Self_(ref ident) => {
-                if let Some(last_seg) = path_alias.path.segments.last() {
+                if let Some(last_seg) = alias_path.segments.last() {
                     let is_aliased = ident != "self";
                     let search_name = if is_aliased { ident } else { &last_seg.name };
                     if !symbol_matches(context.search_type, context.search_str, search_name) {
                         continue;
                     }
-                    with_match!(
-                        path_alias.as_ref(),
-                        Namespace::PathParen,
-                        |m: &mut Match| {
-                            debug!("[match_use] PathAliasKind::Self_ {:?} was found", ident);
-                            if is_aliased && m.matchstr != *ident {
-                                m.matchstr = ident.clone();
-                            }
+                    with_match!(&alias_path, Namespace::PathParen, |mut m: Match| {
+                        debug!("[match_use] PathAliasKind::Self_ {:?} was found", ident);
+                        if is_aliased && m.matchstr != *ident {
+                            m.matchstr = ident.clone();
                         }
-                    );
+                        m
+                    });
                 }
             }
             PathAliasKind::Glob => {
@@ -628,7 +644,7 @@ pub fn match_use(
                     import_info.glob_limit = Some(GLOB_LIMIT - 1);
                     None
                 };
-                let mut search_path = path_alias.path;
+                let mut search_path = alias_path;
                 search_path.segments.push(PathSegment::new(
                     context.search_str.to_owned(),
                     vec![],
diff --git a/src/racer/nameres.rs b/src/racer/nameres.rs
index 0db2cd9f..56d2e891 100644
--- a/src/racer/nameres.rs
+++ b/src/racer/nameres.rs
@@ -1765,7 +1765,8 @@ pub fn resolve_path(
         )
         .into_iter()
         .nth(0);
-        context.map(|m| match m.mtype {
+        println!("{:?}", context);
+        context.map(|mut m| match m.mtype {
             MatchType::Module => {
                 let mut searchstr: &str = &path.segments[len - 1].name;
                 if let Some(i) = searchstr.rfind(',') {
@@ -1805,8 +1806,11 @@ pub fn resolve_path(
                     out.extend(collect_trait_methods(&m, search_type, name, true, session));
                 }
             }
-            MatchType::Type => {
-                if let Some(match_) = ast::get_type_of_typedef(&m, session) {
+            MatchType::Type(ref mut resolved) => {
+                let resolved = resolved.take().map(|x| *x);
+                println!("{:?}", resolved);
+                if let Some(match_) = resolved.or_else(|| ast::get_type_of_typedef(&m, session)) {
+                    println!("path: {:?}", &path.segments[len - 1]);
                     out.extend(get_path_items(
                         &path.segments[len - 1],
                         namespace,


Test patch:
diff --git a/tests/alias.rs b/tests/alias.rs
new file mode 100644
index 00000000..418fb0cc
--- /dev/null
+++ b/tests/alias.rs
@@ -0,0 +1,93 @@
+extern crate racer;
+extern crate racer_testutils;
+use racer::Coordinate;
+use racer_testutils::*;
+
+#[test]
+fn complets_static_methods_for_alias() {
+    let src = r#"
+        mod mymod {
+            pub struct St {
+                mem1: String,
+                mem2: usize,
+            }
+            impl St {
+                pub fn new() -> Self {
+                    St { mem1: "a".to_owned(), mem2: 5 }
+                }
+            }
+        }
+        fn main() {
+            use mymod::St as S;
+            let s = S::ne~
+        }
+    "#;
+    assert_eq!(get_only_completion(src, None).matchstr, "new");
+}
+
+#[test]
+fn finds_definition_of_use_as() {
+    let src = r#"
+        mod mymod {
+            pub struct St {
+                mem1: String,
+                mem2: usize,
+            }
+        }
+        fn main() {
+            use mymod::St as S;
+            let s = S~::new();
+        }
+    "#;
+    let got = get_definition(src, None);
+    assert_eq!(got.matchstr, "S");
+    assert_eq!(got.coords.unwrap(), Coordinate::new(9, 29));
+}
+
+// moved from system.rs
+#[test]
+fn follows_use_as() {
+    let src2 = "
+    pub fn myfn() {}
+    pub fn foo() {}
+    ";
+    let src = "
+    use src2::myfn as myfoofn;
+    mod src2;
+    fn main() {
+        my~foofn();
+    }
+    ";
+
+    let dir = TmpDir::new();
+    let _src2 = dir.write_file("src2.rs", src2);
+    let got = get_definition(src, Some(dir));
+    assert_eq!(got.matchstr, "myfoofn");
+    assert_eq!(got.contextstr, "src2::myfn as myfoofn");
+}
+
+// moved from system.rs
+/// Verifies fix for https://github.com/racer-rust/racer/issues/753
+#[test]
+fn follows_use_as_in_braces() {
+    let src = "
+        mod m {
+        pub struct Wrapper {
+            pub x: i32,
+        }
+
+        pub struct Second {
+            pub y: i32,
+        }
+    }
+
+    fn main() {
+        use m::{Wrapper as Wpr, Second};
+        let _ = W~pr { x: 1 };
+    }
+    ";
+
+    let got = get_definition(src, None);
+    assert_eq!(got.matchstr, "Wpr");
+    assert_eq!(got.contextstr, "Wrapper as Wpr");
+}
diff --git a/tests/system.rs b/tests/system.rs
index ab56bb72..e3614909 100644
--- a/tests/system.rs
+++ b/tests/system.rs
@@ -589,52 +589,6 @@ fn follows_use_in_braces() {
     assert_eq!(got.matchstr, "myfn");
 }
 
-#[test]
-fn follows_use_as() {
-    let src2 = "
-    pub fn myfn() {}
-    pub fn foo() {}
-    ";
-    let src = "
-    use src2::myfn as myfoofn;
-    mod src2;
-    fn main() {
-        my~foofn();
-    }
-    ";
-
-    let dir = TmpDir::new();
-    let _src2 = dir.write_file("src2.rs", src2);
-    let got = get_definition(src, Some(dir));
-    assert_eq!(got.matchstr, "myfoofn");
-    assert_eq!(got.contextstr, "pub fn myfn()");
-}
-
-/// Verifies fix for https://github.com/racer-rust/racer/issues/753
-#[test]
-fn follows_use_as_in_braces() {
-    let src = "
-        mod m {
-        pub struct Wrapper {
-            pub x: i32,
-        }
-
-        pub struct Second {
-            pub y: i32,
-        }
-    }
-
-    fn main() {
-        use m::{Wrapper as Wpr, Second};
-        let _ = W~pr { x: 1 };
-    }
-    ";
-
-    let got = get_definition(src, None);
-    assert_eq!(got.matchstr, "Wpr");
-    assert_eq!(got.contextstr, "pub struct Wrapper");
-}
-
 #[test]
 fn follows_use_glob() {
     let src3 = "