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

Issue:
Local variable shadows `::name`
In the following code:

```rust
fn name() { unimplemented!() }

#[test]
fn test_name() {
    let name = 1;
    let value = ::name();
}
```

Go to definition on `::name` jumps to the local variable `name`, but it should jump to the function outside.


Discussion / hints:

Fix patch:
diff --git a/src/racer/nameres.rs b/src/racer/nameres.rs
index f447f894..bf005c10 100644
--- a/src/racer/nameres.rs
+++ b/src/racer/nameres.rs
@@ -861,15 +861,23 @@ pub fn search_crate_root(
     namespace: Namespace,
     session: &Session,
     import_info: &ImportInfo,
+    // Skip current file or not
+    // If we aren't searching paths with global prefix, should do so
+    skip_modfpath: bool,
 ) -> Vec<Match> {
     debug!("search_crate_root |{:?}| {:?}", pathseg, modfpath.display());
 
-    let crateroots = find_possible_crate_root_modules(modfpath.parent().unwrap(), session);
+    let mut crateroots = find_possible_crate_root_modules(modfpath.parent().unwrap(), session);
+    // for cases when file is not part of a project
+    if crateroots.is_empty() {
+        crateroots.push(modfpath.to_path_buf());
+    }
+
     let mut out = Vec::new();
-    for crateroot in crateroots {
-        if *modfpath == *crateroot {
-            continue;
-        }
+    for crateroot in crateroots
+        .into_iter()
+        .filter(|c| !skip_modfpath || modfpath != c)
+    {
         debug!(
             "going to search for {:?} in crateroot {:?}",
             pathseg,
@@ -889,7 +897,6 @@ pub fn search_crate_root(
                 break;
             }
         }
-        break;
     }
     out
 }
@@ -907,12 +914,7 @@ pub fn find_possible_crate_root_modules(currentdir: &Path, session: &Session) ->
     // recurse up the directory structure
     if let Some(parentdir) = currentdir.parent() {
         if parentdir != currentdir {
-            // PD: this was using the vec.push_all() api, but that is now unstable
-            res.extend(
-                find_possible_crate_root_modules(parentdir, session)
-                    .iter()
-                    .cloned(),
-            );
+            res.append(&mut find_possible_crate_root_modules(parentdir, session));
             return res; // for now stop at the first match
         }
     }
@@ -969,7 +971,7 @@ pub fn search_scope(
     let mut out = Vec::new();
 
     debug!(
-        "searching scope {:?} start: {:?} point: {:?} '{}' {:?} {:?} local: {}, session: {:?}",
+        "searching scope {:?} start: {:?} point: {:?} '{}' {:?} {:?} local: {}",
         namespace,
         start,
         complete_point,
@@ -977,7 +979,6 @@ pub fn search_scope(
         filepath.display(),
         search_type,
         is_local,
-        session
     );
 
     let scopesrc = src.shift_start(start);
@@ -1498,6 +1499,7 @@ pub fn resolve_name(
         namespace,
         session,
         import_info,
+        true,
     ) {
         out.push(m);
         if is_exact_match {
@@ -1768,6 +1770,16 @@ pub fn resolve_path(
                     return Vec::new();
                 }
             }
+            PathPrefix::Global => {
+                return resolve_global_path(
+                    path,
+                    filepath,
+                    search_type,
+                    namespace,
+                    session,
+                    import_info,
+                ).unwrap_or_else(Vec::new);
+            }
             _ => {}
         }
     }
@@ -1819,6 +1831,46 @@ pub fn resolve_path(
     }
 }
 
+/// resolve paths like ::path::to::file
+fn resolve_global_path(
+    path: &RacerPath,
+    filepath: &Path,
+    search_type: SearchType,
+    namespace: Namespace,
+    session: &Session,
+    import_info: &ImportInfo,
+) -> Option<Vec<Match>> {
+    let mut segs = path.segments.iter().enumerate();
+    let first_stype = if path.segments.len() == 1 {
+        search_type
+    } else {
+        SearchType::ExactMatch
+    };
+    let mut context = search_crate_root(
+        segs.next()?.1,
+        filepath,
+        first_stype,
+        namespace,
+        session,
+        import_info,
+        false,
+    );
+    for (i, seg) in segs {
+        let cxt = context.into_iter().next()?;
+        let is_last =  i + 1 == path.segments.len();
+        let stype = if is_last { search_type } else { SearchType::ExactMatch };
+        context = resolve_following_path(
+            cxt,
+            seg,
+            namespace,
+            stype,
+            import_info,
+            session
+        );
+    }
+    Some(context)
+}
+
 fn resolve_following_path(
     followed_match: Match,
     following_seg: &PathSegment,


Test patch:
diff --git a/test_project/src/lib.rs b/test_project/src/lib.rs
index 67455439..97763790 100644
--- a/test_project/src/lib.rs
+++ b/test_project/src/lib.rs
@@ -18,3 +18,5 @@ impl TestStruct {
         }
     }
 }
+
+pub(crate) fn name() {}
diff --git a/tests/system.rs b/tests/system.rs
index a698d9f2..1f690195 100644
--- a/tests/system.rs
+++ b/tests/system.rs
@@ -3807,3 +3807,46 @@ fn import_stmt_doesnt_jump_to_closure_arg() {
 
     assert!(find_definition(src, None).is_none());
 }
+
+#[test]
+fn test_resolve_global_path() {
+    let src = r#"
+pub fn name() {
+}
+
+fn main() {
+    let name = 1;
+    let _ = ::nam~e();
+}
+"#;
+    let got = find_definition_with_name(src, None, "lib.rs").unwrap();
+    assert_eq!(got.matchstr, "name");
+    assert_eq!(got.mtype, MatchType::Function);
+}
+
+#[test]
+fn test_resolve_global_path_in_modules() {
+    let src = r#"
+    fn foo() {
+        let name = 1;
+        let a = ::nam~e();
+    }
+"#;
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_one_completion(src, Some(srcdir));
+        assert_eq!(got.matchstr, "name");
+        assert_eq!(got.mtype, MatchType::Function);
+    });
+
+    let src = r#"
+    fn foo() {
+        let a = ::TestStruct::n~
+    }
+"#;
+    with_test_project(|dir| {
+        let srcdir = dir.nested_dir("src");
+        let got = get_one_completion(src, Some(srcdir));
+        assert_eq!(got.matchstr, "new");
+    })
+}
diff --git a/testutils/src/lib.rs b/testutils/src/lib.rs
index 842d39db..f88c2129 100644
--- a/testutils/src/lib.rs
+++ b/testutils/src/lib.rs
@@ -184,9 +184,13 @@ pub fn get_pos_and_source(src: &str) -> (BytePos, String) {
 ///
 /// The point to find completions at must be marked with '~'.
 pub fn get_all_completions(src: &str, dir: Option<TmpDir>) -> Vec<Match> {
+    get_all_completions_with_name(src, dir, "src.rs")
+}
+
+pub fn get_all_completions_with_name(src: &str, dir: Option<TmpDir>, fname: &str) -> Vec<Match> {
     let dir = dir.unwrap_or_else(|| TmpDir::new());
     let (completion_point, clean_src) = get_pos_and_source(src);
-    let path = dir.write_file("src.rs", &clean_src);
+    let path = dir.write_file(fname, &clean_src);
     let cache = racer::FileCache::default();
     let session = racer::Session::new(&cache);
     complete_from_file(&path, completion_point, &session).collect()
@@ -220,9 +224,14 @@ pub fn get_definition(src: &str, dir: Option<TmpDir>) -> Match {
 
 /// Optionally returns a defintion for the given source
 pub fn find_definition(src: &str, dir: Option<TmpDir>) -> Option<Match> {
+    find_definition_with_name(src, dir, "src.rs")
+}
+
+/// Optionally returns a defintion for the given source
+pub fn find_definition_with_name(src: &str, dir: Option<TmpDir>, fname: &str) -> Option<Match> {
     let dir = dir.unwrap_or_else(|| TmpDir::new());
     let (completion_point, clean_src) = get_pos_and_source(src);
-    let path = dir.write_file("src.rs", &clean_src);
+    let path = dir.write_file(fname, &clean_src);
     let cache = racer::FileCache::default();
     let session = racer::Session::new(&cache);
     racer_find_definition(&path, completion_point, &session)