Hello community, here is the log from the commit of package racer for openSUSE:Factory checked in at 2017-05-04 15:05:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/racer (Old) and /work/SRC/openSUSE:Factory/.racer.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "racer" Thu May 4 15:05:23 2017 rev:3 rq:492762 version:2.0.6 Changes: -------- --- /work/SRC/openSUSE:Factory/racer/racer.changes 2017-02-16 16:51:24.731775965 +0100 +++ /work/SRC/openSUSE:Factory/.racer.new/racer.changes 2017-05-04 15:05:25.753762810 +0200 @@ -1,0 +2,6 @@ +Tue May 2 21:18:50 UTC 2017 - luke.nukem.jones@gmail.com + +- resolve Self (e.g. in-impl function calls like Self::myfunction()) +- Fix stack overflow issue on unresolvable imports + +------------------------------------------------------------------- Old: ---- racer-2.0.5.tar.gz New: ---- racer-2.0.6.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ racer.spec ++++++ --- /var/tmp/diff_new_pack.n51G2O/_old 2017-05-04 15:05:28.809331405 +0200 +++ /var/tmp/diff_new_pack.n51G2O/_new 2017-05-04 15:05:28.809331405 +0200 @@ -20,7 +20,7 @@ Name: racer -Version: 2.0.5 +Version: 2.0.6 Release: 0 Summary: Code completion for Rust License: MIT @@ -35,8 +35,8 @@ BuildRequires: cargo BuildRequires: git -BuildRequires: rust >= 1.14.0 -BuildRequires: rust-std >= 1.14.0 +BuildRequires: rust +BuildRequires: rust-std BuildRoot: %{_tmppath}/%{name}-%{version}-build ++++++ racer-2.0.5.tar.gz -> racer-2.0.6.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/CHANGELOG.md new/racer-2.0.6/CHANGELOG.md --- old/racer-2.0.5/CHANGELOG.md 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/CHANGELOG.md 2017-02-17 17:32:13.000000000 +0100 @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## 2.0.6 + +- resolve Self (e.g. in-impl function calls like Self::myfunction()) +- Fix stack overflow issue on unresolvable imports :tada: #698 + ## 2.0.5 - Chained completions on separate lines now work #686 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/Cargo.lock new/racer-2.0.6/Cargo.lock --- old/racer-2.0.5/Cargo.lock 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/Cargo.lock 2017-02-17 17:32:13.000000000 +0100 @@ -1,6 +1,6 @@ [root] name = "racer" -version = "2.0.5" +version = "2.0.6" dependencies = [ "clap 2.19.1 (registry+https://github.com/rust-lang/crates.io-index)", "clippy 0.0.103 (registry+https://github.com/rust-lang/crates.io-index)", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/Cargo.toml new/racer-2.0.6/Cargo.toml --- old/racer-2.0.5/Cargo.toml 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/Cargo.toml 2017-02-17 17:32:13.000000000 +0100 @@ -1,7 +1,7 @@ [package] name = "racer" -version = "2.0.5" +version = "2.0.6" license = "MIT" description = "Code completion for Rust" authors = ["Phil Dawes <phil@phildawes.net>"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/src/racer/core.rs new/racer-2.0.6/src/racer/core.rs --- old/racer-2.0.5/src/racer/core.rs 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/src/racer/core.rs 2017-02-17 17:32:13.000000000 +0100 @@ -11,6 +11,7 @@ use std::iter::{Fuse, Iterator}; use std::rc::Rc; use codeiter::StmtIndicesIter; +use matchers::PendingImports; use scopes; use nameres; @@ -947,8 +948,8 @@ let path = Path::from_vec(is_global, v); for m in nameres::resolve_path(&path, filepath, pos, - SearchType::StartsWith, Namespace::Both, - session) { + SearchType::StartsWith, Namespace::Both, + session, &PendingImports::empty()) { out.push(m); } }, @@ -1082,7 +1083,7 @@ nameres::resolve_path(&path, filepath, pos, SearchType::ExactMatch, Namespace::Both, - session).nth(0) + session, &PendingImports::empty()).nth(0) }, CompletionType::Field => { let context = ast::get_type_of(contextstr.to_owned(), filepath, pos, session); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/src/racer/matchers.rs new/racer-2.0.6/src/racer/matchers.rs --- old/racer-2.0.5/src/racer/matchers.rs 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/src/racer/matchers.rs 2017-02-17 17:32:13.000000000 +0100 @@ -1,15 +1,25 @@ use {scopes, typeinf, ast}; use core::{Match, PathSegment, Src, Session, Coordinate, SessionExt}; -use util::{symbol_matches, txt_matches, find_ident_end, is_ident_char, char_at}; +use util::{StackLinkedListNode, symbol_matches, txt_matches, find_ident_end, is_ident_char, char_at}; use nameres::{get_module_file, get_crate_file, resolve_path}; use core::SearchType::{self, StartsWith, ExactMatch}; use core::MatchType::{self, Let, Module, Function, Struct, Type, Trait, Enum, EnumVariant, Const, Static, IfLet, WhileLet, For, Macro}; use core::Namespace; -use std::cell::Cell; use std::path::Path; use std::{iter, option, str, vec}; +/// The location of an import (`use` item) currently being resolved. +#[derive(PartialEq, Eq)] +pub struct PendingImport<'fp> { + filepath: &'fp Path, + blobstart: usize, + blobend: usize, +} + +/// A stack of imports (`use` items) currently being resolved. +pub type PendingImports<'stack, 'fp> = StackLinkedListNode<'stack, PendingImport<'fp>>; + pub type MIter = option::IntoIter<Match>; pub type MChain<T> = iter::Chain<T, MIter>; @@ -17,14 +27,15 @@ pub fn match_types(src: Src, blobstart: usize, blobend: usize, searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool, session: &Session) -> iter::Chain<MChain<MChain<MChain<MChain<MChain<MIter>>>>>, vec::IntoIter<Match>> { + local: bool, session: &Session, + pending_imports: &PendingImports) -> iter::Chain<MChain<MChain<MChain<MChain<MChain<MIter>>>>>, vec::IntoIter<Match>> { let it = match_extern_crate(&src, blobstart, blobend, searchstr, filepath, search_type, session).into_iter(); let it = it.chain(match_mod(src, blobstart, blobend, searchstr, filepath, search_type, local, session).into_iter()); let it = it.chain(match_struct(&src, blobstart, blobend, searchstr, filepath, search_type, local).into_iter()); let it = it.chain(match_type(&src, blobstart, blobend, searchstr, filepath, search_type, local).into_iter()); let it = it.chain(match_trait(&src, blobstart, blobend, searchstr, filepath, search_type, local).into_iter()); let it = it.chain(match_enum(&src, blobstart, blobend, searchstr, filepath, search_type, local).into_iter()); - it.chain(match_use(&src, blobstart, blobend, searchstr, filepath, search_type, local, session).into_iter()) + it.chain(match_use(&src, blobstart, blobend, searchstr, filepath, search_type, local, session, pending_imports).into_iter()) } pub fn match_values(src: Src, blobstart: usize, blobend: usize, @@ -515,18 +526,30 @@ } } -// HACK: recursion protection. With 'use glob' statements it's easy to -// get into a recursive loop and exchaust the stack. Currently we -// avoid this by not following a glob if we're already searching -// through one. -thread_local!(static ALREADY_GLOBBING: Cell<Option<bool>> = Cell::new(None)); - pub fn match_use(msrc: &str, blobstart: usize, blobend: usize, searchstr: &str, filepath: &Path, search_type: SearchType, - local: bool, session: &Session) -> Vec<Match> { - let mut out = Vec::new(); + local: bool, session: &Session, + pending_imports: &PendingImports) -> Vec<Match> { + let import = PendingImport { + filepath: &filepath, + blobstart: blobstart, + blobend: blobend, + }; + let blob = &msrc[blobstart..blobend]; + // If we're trying to resolve the same import recursively, + // do not return any matches this time. + if pending_imports.contains(&import) { + debug!("import {} involved in a cycle; ignoring", blob); + return Vec::new(); + } + + // Push this import on the stack of pending imports. + let pending_imports = &pending_imports.push(import); + + let mut out = Vec::new(); + if find_keyword(blob, "use", "", StartsWith, local).is_none() { return out; } if blob.contains('*') { @@ -536,36 +559,17 @@ if use_item.is_glob { let basepath = use_item.paths.into_iter().nth(0).unwrap(); - let mut follow_glob = true; - { - // don't follow glob if we are already following one otherwise - // otherwise we get a recursive mess - follow_glob &= ALREADY_GLOBBING.with(|c| { c.get().is_none() }); - - // don't follow the glob if the path base is the searchstr - follow_glob &= !(basepath.segments[0].name == searchstr || - (basepath.segments[0].name == "self" && basepath.segments[1].name == searchstr)); - } - - if follow_glob { - ALREADY_GLOBBING.with(|c| { c.set(Some(true)) }); - - let seg = PathSegment{ name: searchstr.to_owned(), types: Vec::new() }; - let mut path = basepath.clone(); - path.segments.push(seg); - debug!("found a glob: now searching for {:?}", path); - let iter_path = resolve_path(&path, filepath, blobstart, search_type, Namespace::Both, session); - if let StartsWith = search_type { - ALREADY_GLOBBING.with(|c| { c.set(None) }); - return iter_path.collect(); - } - for m in iter_path { - out.push(m); - break; - } - ALREADY_GLOBBING.with(|c| { c.set(None) }); - } else { - debug!("not following glob"); + let seg = PathSegment{ name: searchstr.to_owned(), types: Vec::new() }; + let mut path = basepath.clone(); + path.segments.push(seg); + debug!("found a glob: now searching for {:?}", path); + let iter_path = resolve_path(&path, filepath, blobstart, search_type, Namespace::Both, session, pending_imports); + if let StartsWith = search_type { + return iter_path.collect(); + } + for m in iter_path { + out.push(m); + break; } } } else if txt_matches(search_type, searchstr, blob) { @@ -582,7 +586,7 @@ // Do nothing because this will be picked up by the module // search in a bit. } else { - for m in resolve_path(&path, filepath, blobstart, ExactMatch, Namespace::Both, session) { + for m in resolve_path(&path, filepath, blobstart, ExactMatch, Namespace::Both, session, pending_imports) { out.push(m); if let ExactMatch = search_type { return out; @@ -614,7 +618,7 @@ if symbol_matches(search_type, searchstr, &path.segments.last().unwrap().name) { // last path segment matches the path. find it! for m in resolve_path(&path, filepath, blobstart, - ExactMatch, Namespace::Both, session) { + ExactMatch, Namespace::Both, session, pending_imports) { out.push(m); if let ExactMatch = search_type { return out; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/src/racer/nameres.rs new/racer-2.0.6/src/racer/nameres.rs --- old/racer-2.0.5/src/racer/nameres.rs 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/src/racer/nameres.rs 2017-02-17 17:32:13.000000000 +0100 @@ -2,7 +2,7 @@ use {core, ast, matchers, scopes, typeinf}; use core::SearchType::{self, ExactMatch, StartsWith}; -use core::{Match, Src, Session, Coordinate, SessionExt}; +use core::{Match, Src, Session, Coordinate, SessionExt, Ty}; use core::MatchType::{Module, Function, Struct, Enum, FnArg, Trait, StructField, Impl, TraitImpl, MatchArm, Builtin}; use core::Namespace; use util::{symbol_matches, txt_matches, find_ident_end}; @@ -10,6 +10,7 @@ use cargo; use std::path::{Path, PathBuf}; use std::{self, vec}; +use matchers::PendingImports; #[cfg(unix)] pub const PATH_SEP: char = ':'; @@ -62,7 +63,7 @@ let mut out = Vec::new(); - for m in search_for_impls(point, implsearchstr, fpath, local, true, session) { + for m in search_for_impls(point, implsearchstr, fpath, local, true, session, &PendingImports::empty()) { debug!("found impl!! |{:?}| looking for methods", m); if m.matchstr == "Deref" { @@ -228,7 +229,7 @@ pub fn search_for_impls(pos: usize, searchstr: &str, filepath: &Path, local: bool, include_traits: bool, - session: &Session) -> vec::IntoIter<Match> { + session: &Session, pending_imports: &PendingImports) -> vec::IntoIter<Match> { debug!("search_for_impls {}, {}, {:?}", pos, searchstr, filepath.display()); let s = session.load_file(filepath); let scope_start = scopes::scope_start(s.as_src(), pos); @@ -274,8 +275,8 @@ if include_traits && is_trait_impl { let trait_path = implres.trait_path.unwrap(); let mut m = resolve_path(&trait_path, - filepath, scope_start + start, ExactMatch, Namespace::Type, - session).nth(0); + filepath, scope_start + start, ExactMatch, Namespace::Type, + session, pending_imports).nth(0); debug!("found trait |{:?}| {:?}", trait_path, m); if let Some(ref mut m) = m { @@ -364,8 +365,8 @@ // scope headers include fn decls, if let, while let etc.. fn search_scope_headers(point: usize, scopestart: usize, msrc: Src, searchstr: &str, - filepath: &Path, search_type: SearchType, session: &Session) - -> vec::IntoIter<Match> { + filepath: &Path, search_type: SearchType, session: &Session, + pending_imports: &PendingImports) -> vec::IntoIter<Match> { debug!("search_scope_headers for |{}| pt: {}", searchstr, scopestart); if let Some(stmtstart) = scopes::find_stmt_start(msrc, scopestart) { let preblock = &msrc[stmtstart..scopestart]; @@ -479,7 +480,8 @@ stmtstart + n - 1, SearchType::ExactMatch, Namespace::Both, - session) + session, + pending_imports) .filter(|m| m.mtype == Trait) .nth(0); if let Some(m) = m { @@ -647,7 +649,7 @@ pub fn search_crate_root(pathseg: &core::PathSegment, modfpath: &Path, searchtype: SearchType, namespace: Namespace, - session: &Session) -> vec::IntoIter<Match> { + session: &Session, pending_imports: &PendingImports) -> vec::IntoIter<Match> { debug!("search_crate_root |{:?}| {:?}", pathseg, modfpath.display()); let crateroots = find_possible_crate_root_modules(modfpath.parent().unwrap(), session); @@ -657,7 +659,7 @@ continue; } debug!("going to search for {:?} in crateroot {:?}", pathseg, crateroot.display()); - for m in resolve_name(pathseg, &crateroot, 0, searchtype, namespace, session) { + for m in resolve_name(pathseg, &crateroot, 0, searchtype, namespace, session, pending_imports) { out.push(m); if let ExactMatch = searchtype { break; @@ -691,8 +693,8 @@ pub fn search_next_scope(mut startpoint: usize, pathseg: &core::PathSegment, filepath:&Path, search_type: SearchType, local: bool, - namespace: Namespace, - session: &Session) -> vec::IntoIter<Match> { + namespace: Namespace, session: &Session, + pending_imports: &PendingImports) -> vec::IntoIter<Match> { let filesrc = session.load_file(filepath); if startpoint != 0 { // is a scope inside the file. Point should point to the definition @@ -704,7 +706,7 @@ startpoint += n + 1; }); } - search_scope(startpoint, startpoint, filesrc.as_src(), pathseg, filepath, search_type, local, namespace, session) + search_scope(startpoint, startpoint, filesrc.as_src(), pathseg, filepath, search_type, local, namespace, session, pending_imports) } pub fn get_crate_file(name: &str, from_path: &Path, session: &Session) -> Option<PathBuf> { @@ -758,7 +760,8 @@ pathseg: &core::PathSegment, filepath:&Path, search_type: SearchType, local: bool, namespace: Namespace, - session: &Session) -> vec::IntoIter<Match> { + session: &Session, + pending_imports: &PendingImports) -> vec::IntoIter<Match> { let searchstr = &pathseg.name; let mut out = Vec::new(); @@ -767,7 +770,8 @@ let scopesrc = src.from(start); let mut skip_next_block = false; - let mut delayed_use_globs = Vec::new(); + let mut delayed_single_imports = Vec::new(); + let mut delayed_glob_imports = Vec::new(); let mut codeit = scopesrc.iter_stmts(); let mut v = Vec::new(); @@ -833,12 +837,31 @@ continue; } - let is_a_use_glob = (blob.starts_with("use") || blob.starts_with("pub use")) - && blob.find("::*").is_some(); + let is_an_import = blob.starts_with("use") || blob.starts_with("pub use"); + + if is_an_import { + // A `use` item can import a value + // with the same name as a "type" (type/module/etc.) in the same scope. + // However, that type might appear after the `use`, + // so we need to process the type first and the `use` later (if necessary). + // If we didn't delay imports, + // we'd try to resolve such a `use` item by recursing onto itself. + + // Optimisation: if the search string is not in the blob and it is not + // a glob import, this cannot match so fail fast! + let is_glob_import = blob.contains("::*"); + if !is_glob_import { + if !blob.contains(searchstr.trim_right_matches('!')) { + continue; + } + } + + if is_glob_import { + delayed_glob_imports.push((blobstart, blobend)); + } else { + delayed_single_imports.push((blobstart, blobend)); + } - if is_a_use_glob { - // globs are *really* expensive to process. delay them until later - delayed_use_globs.push((blobstart, blobend)); continue; } @@ -861,16 +884,16 @@ }); } - // Optimisation: if the search string is not in the blob and it is not - // a 'use glob', this cannot match so fail fast! - if blob.find(searchstr.trim_right_matches('!')).is_none() { + // Optimisation: if the search string is not in the blob, + // this cannot match so fail fast! + if !blob.contains(searchstr.trim_right_matches('!')) { continue; } // There's a good chance of a match. Run the matchers out.extend(run_matchers_on_blob(src, start+blobstart, start+blobend, searchstr, - filepath, search_type, local, namespace, session)); + filepath, search_type, local, namespace, session, pending_imports)); if let ExactMatch = search_type { if !out.is_empty() { return out.into_iter(); @@ -878,12 +901,13 @@ } } - // finally process any use-globs that we skipped before - for (blobstart, blobend) in delayed_use_globs { + // Finally, process the imports that we skipped before. + // Process single imports first, because they shadow glob imports. + for (blobstart, blobend) in delayed_single_imports.into_iter().chain(delayed_glob_imports) { // There's a good chance of a match. Run the matchers for m in run_matchers_on_blob(src, start+blobstart, start+blobend, searchstr, filepath, search_type, - local, namespace, session).into_iter() { + local, namespace, session, pending_imports).into_iter() { out.push(m); if let ExactMatch = search_type { return out.into_iter(); @@ -896,14 +920,15 @@ } fn run_matchers_on_blob(src: Src, start: usize, end: usize, searchstr: &str, - filepath: &Path, search_type: SearchType, local: bool, - namespace: Namespace, session: &Session) -> Vec<Match> { + filepath: &Path, search_type: SearchType, local: bool, + namespace: Namespace, session: &Session, + pending_imports: &PendingImports) -> Vec<Match> { let mut out = Vec::new(); match namespace { Namespace::Type => for m in matchers::match_types(src, start, end, searchstr, - filepath, search_type, local, session) { + filepath, search_type, local, session, pending_imports) { out.push(m); if let ExactMatch = search_type { return out; @@ -921,7 +946,7 @@ Namespace::Both => { for m in matchers::match_types(src, start, end, searchstr, - filepath, search_type, local, session) { + filepath, search_type, local, session, pending_imports) { out.push(m); if let ExactMatch = search_type { return out; @@ -942,21 +967,21 @@ fn search_local_scopes(pathseg: &core::PathSegment, filepath: &Path, msrc: Src, point: usize, search_type: SearchType, - namespace: Namespace, - session: &Session) -> vec::IntoIter<Match> { + namespace: Namespace, session: &Session, + pending_imports: &PendingImports) -> vec::IntoIter<Match> { debug!("search_local_scopes {:?} {:?} {} {:?} {:?}", pathseg, filepath.display(), point, search_type, namespace); if point == 0 { // search the whole file - search_scope(0, 0, msrc, pathseg, filepath, search_type, true, namespace, session) + search_scope(0, 0, msrc, pathseg, filepath, search_type, true, namespace, session, pending_imports) } else { let mut out = Vec::new(); let mut start = point; // search each parent scope in turn while start > 0 { start = scopes::scope_start(msrc, start); - for m in search_scope(start, point, msrc, pathseg, filepath, search_type, true, namespace, session) { + for m in search_scope(start, point, msrc, pathseg, filepath, search_type, true, namespace, session, pending_imports) { out.push(m); if let ExactMatch = search_type { return out.into_iter(); @@ -969,7 +994,7 @@ let searchstr = &pathseg.name; // scope headers = fn decls, if let, match, etc.. - for m in search_scope_headers(point, start, msrc, searchstr, filepath, search_type, session) { + for m in search_scope_headers(point, start, msrc, searchstr, filepath, search_type, session, pending_imports) { out.push(m); if let ExactMatch = search_type { return out.into_iter(); @@ -981,7 +1006,8 @@ } pub fn search_prelude_file(pathseg: &core::PathSegment, search_type: SearchType, - namespace: Namespace, session: &Session) -> vec::IntoIter<Match> { + namespace: Namespace, session: &Session, + pending_imports: &PendingImports) -> vec::IntoIter<Match> { debug!("search_prelude file {:?} {:?} {:?}", pathseg, search_type, namespace); let mut out : Vec<Match> = Vec::new(); @@ -998,7 +1024,7 @@ if filepath.exists() || session.contains_file(&filepath) { let msrc = session.load_file_and_mask_comments(&filepath); let is_local = true; - for m in search_scope(0, 0, msrc.as_src(), pathseg, &filepath, search_type, is_local, namespace, session) { + for m in search_scope(0, 0, msrc.as_src(), pathseg, &filepath, search_type, is_local, namespace, session, pending_imports) { out.push(m); } } @@ -1019,7 +1045,7 @@ if let Some(module) = resolve_path(&core::Path::from_vec(true, vec!["std","str"]), filepath, pos, search_type, namespace, - session).nth(0) { + session, &PendingImports::empty()).nth(0) { out.push(Match { matchstr: "str".into(), filepath: module.filepath, @@ -1036,7 +1062,7 @@ } } else { - for m in resolve_path(path, filepath, pos, search_type, namespace, session) { + for m in resolve_path(path, filepath, pos, search_type, namespace, session, &PendingImports::empty()) { out.push(m); if let ExactMatch = search_type { break; @@ -1055,7 +1081,7 @@ pub fn resolve_name(pathseg: &core::PathSegment, filepath: &Path, pos: usize, search_type: SearchType, namespace: Namespace, - session: &Session) -> vec::IntoIter<Match> { + session: &Session, pending_imports: &PendingImports) -> vec::IntoIter<Match> { let mut out = Vec::new(); let searchstr = &pathseg.name; @@ -1064,6 +1090,12 @@ let msrc = session.load_file(filepath); let is_exact_match = match search_type { ExactMatch => true, StartsWith => false }; + if is_exact_match && &searchstr[..] == "Self" { + if let Some(Ty::Match(m)) = typeinf::get_type_of_self(pos, filepath, true, msrc.as_src(), session) { + out.push(m.clone()); + } + } + if (is_exact_match && &searchstr[..] == "std") || (!is_exact_match && "std".starts_with(searchstr)) { get_crate_file("std", filepath, session).map(|cratepath| { @@ -1089,7 +1121,7 @@ } } - for m in search_local_scopes(pathseg, filepath, msrc.as_src(), pos, search_type, namespace, session) { + for m in search_local_scopes(pathseg, filepath, msrc.as_src(), pos, search_type, namespace, session, pending_imports) { out.push(m); if let ExactMatch = search_type { if !out.is_empty() { @@ -1098,7 +1130,7 @@ } } - for m in search_crate_root(pathseg, filepath, search_type, namespace, session) { + for m in search_crate_root(pathseg, filepath, search_type, namespace, session, pending_imports) { out.push(m); if let ExactMatch = search_type { if !out.is_empty() { @@ -1107,7 +1139,7 @@ } } - for m in search_prelude_file(pathseg, search_type, namespace, session) { + for m in search_prelude_file(pathseg, search_type, namespace, session, pending_imports) { out.push(m); if let ExactMatch = search_type { if !out.is_empty() { @@ -1125,7 +1157,8 @@ } // Get the scope corresponding to super:: -pub fn get_super_scope(filepath: &Path, pos: usize, session: &Session) -> Optioncore::Scope { +pub fn get_super_scope(filepath: &Path, pos: usize, session: &Session, + pending_imports: &PendingImports) -> Optioncore::Scope { let msrc = session.load_file_and_mask_comments(filepath); let mut path = scopes::get_local_module_path(msrc.as_src(), pos); debug!("get_super_scope: path: {:?} filepath: {:?} {} {:?}", path, filepath, pos, session); @@ -1153,7 +1186,7 @@ let path = core::Path::from_svec(false, path); debug!("get_super_scope looking for local scope {:?}", path); resolve_path(&path, filepath, 0, SearchType::ExactMatch, - Namespace::Type, session).nth(0) + Namespace::Type, session, pending_imports).nth(0) .and_then(|m| msrc[m.point..].find('{') .map(|p| core::Scope{ filepath: filepath.to_path_buf(), point:m.point + p + 1 })) @@ -1162,28 +1195,28 @@ pub fn resolve_path(path: &core::Path, filepath: &Path, pos: usize, search_type: SearchType, namespace: Namespace, - session: &Session) -> vec::IntoIter<Match> { + session: &Session, pending_imports: &PendingImports) -> vec::IntoIter<Match> { debug!("resolve_path {:?} {:?} {} {:?}", path, filepath.display(), pos, search_type); let len = path.segments.len(); if len == 1 { let pathseg = &path.segments[0]; - resolve_name(pathseg, filepath, pos, search_type, namespace, session) + resolve_name(pathseg, filepath, pos, search_type, namespace, session, pending_imports) } else if len != 0 { if path.segments[0].name == "self" { // just remove self let mut newpath: core::Path = path.clone(); newpath.segments.remove(0); - return resolve_path(&newpath, filepath, pos, search_type, namespace, session); + return resolve_path(&newpath, filepath, pos, search_type, namespace, session, pending_imports); } if path.segments[0].name == "super" { - if let Some(scope) = get_super_scope(filepath, pos, session) { + if let Some(scope) = get_super_scope(filepath, pos, session, pending_imports) { debug!("PHIL super scope is {:?}", scope); let mut newpath: core::Path = path.clone(); newpath.segments.remove(0); return resolve_path(&newpath, &scope.filepath, - scope.point, search_type, namespace, session); + scope.point, search_type, namespace, session, pending_imports); } else { // can't find super scope. Return no matches debug!("can't resolve path {:?}, returning no matches", path); @@ -1194,7 +1227,7 @@ let mut out = Vec::new(); let mut parent_path: core::Path = path.clone(); parent_path.segments.remove(len-1); - let context = resolve_path(&parent_path, filepath, pos, ExactMatch, Namespace::Type, session).nth(0); + let context = resolve_path(&parent_path, filepath, pos, ExactMatch, Namespace::Type, session, pending_imports).nth(0); context.map(|m| { match m.mtype { Module => { @@ -1207,7 +1240,7 @@ } let pathseg = core::PathSegment{name: searchstr.to_owned(), types: Vec::new()}; debug!("searching a module '{}' for {} (whole path: {:?})", m.matchstr, pathseg.name, path); - for m in search_next_scope(m.point, &pathseg, &m.filepath, search_type, false, namespace, session) { + for m in search_next_scope(m.point, &pathseg, &m.filepath, search_type, false, namespace, session, pending_imports) { out.push(m); } } @@ -1230,14 +1263,14 @@ } Struct => { debug!("found a struct. Now need to look for impl"); - for m_impl in search_for_impls(m.point, &m.matchstr, &m.filepath, m.local, true, session) { + for m_impl in search_for_impls(m.point, &m.matchstr, &m.filepath, m.local, true, session, pending_imports) { debug!("found impl!! {:?}", m_impl); let pathseg = &path.segments[len-1]; let src = session.load_file(&m_impl.filepath); // find the opening brace and skip to it. src[m_impl.point..].find('{').map(|n| { let point = m_impl.point + n + 1; - for m_impl in search_scope(point, point, src.as_src(), pathseg, &m_impl.filepath, search_type, m_impl.local, namespace, session) { + for m_impl in search_scope(point, point, src.as_src(), pathseg, &m_impl.filepath, search_type, m_impl.local, namespace, session, pending_imports) { out.push(m_impl); } }); @@ -1248,7 +1281,7 @@ // find the opening brace and skip to it. src[m_gen.point..].find('{').map(|n| { let point = m_gen.point + n + 1; - for m_gen in search_scope(point, point, src.as_src(), pathseg, &m_gen.filepath, search_type, m_gen.local, namespace, session) { + for m_gen in search_scope(point, point, src.as_src(), pathseg, &m_gen.filepath, search_type, m_gen.local, namespace, session, pending_imports) { out.push(m_gen); } }); @@ -1277,7 +1310,7 @@ let pathseg = core::PathSegment{name: searchstr.to_owned(), types: Vec::new()}; - for m in search_next_scope(pos, &pathseg, filepath, search_type, false, namespace, session) { + for m in search_next_scope(pos, &pathseg, filepath, search_type, false, namespace, session, &PendingImports::empty()) { out.push(m); } @@ -1300,6 +1333,7 @@ let parent_path = &path[..(path.len()-1)]; let context = do_external_search(parent_path, filepath, pos, ExactMatch, Namespace::Type, session).nth(0); context.map(|m| { + let pending_imports = &PendingImports::empty(); match m.mtype { Module => { debug!("found an external module {}", m.matchstr); @@ -1310,14 +1344,14 @@ }; let pathseg = core::PathSegment{name: searchstr.to_owned(), types: Vec::new()}; - for m in search_next_scope(m.point, &pathseg, &m.filepath, search_type, false, namespace, session) { + for m in search_next_scope(m.point, &pathseg, &m.filepath, search_type, false, namespace, session, pending_imports) { out.push(m); } } Struct => { debug!("found a pub struct. Now need to look for impl"); - for m in search_for_impls(m.point, &m.matchstr, &m.filepath, m.local, false, session) { + for m in search_for_impls(m.point, &m.matchstr, &m.filepath, m.local, false, session, pending_imports) { debug!("found impl2!! {}", m.matchstr); // deal with started with "{", so that "foo::{bar" will be same as "foo::bar" let searchstr = match path[path.len()-1].chars().next() { @@ -1327,7 +1361,7 @@ let pathseg = core::PathSegment{name: searchstr.to_owned(), types: Vec::new()}; debug!("about to search impl scope..."); - for m in search_next_scope(m.point, &pathseg, &m.filepath, search_type, m.local, namespace, session) { + for m in search_next_scope(m.point, &pathseg, &m.filepath, search_type, m.local, namespace, session, pending_imports) { out.push(m); } }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/src/racer/typeinf.rs new/racer-2.0.6/src/racer/typeinf.rs --- old/racer-2.0.5/src/racer/typeinf.rs 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/src/racer/typeinf.rs 2017-02-17 17:32:13.000000000 +0100 @@ -9,6 +9,7 @@ use matchers; use core::SearchType::ExactMatch; use util::txt_matches; +use std::path::Path; fn find_start_of_function_body(src: &str) -> usize { // TODO: this should ignore anything inside parens so as to skip the arg list @@ -78,7 +79,11 @@ fn get_type_of_self_arg(m: &Match, msrc: Src, session: &Session) -> Optioncore::Ty { debug!("get_type_of_self_arg {:?}", m); - scopes::find_impl_start(msrc, m.point, 0).and_then(|start| { + get_type_of_self(m.point, &m.filepath, m.local, msrc, session) +} + +pub fn get_type_of_self(point: usize, filepath: &Path, local: bool, msrc: Src, session: &Session) -> Optioncore::Ty { + scopes::find_impl_start(msrc, point, 0).and_then(|start| { let decl = generate_skeleton_for_parsing(&msrc.from(start)); debug!("get_type_of_self_arg impl skeleton |{}|", decl); @@ -86,23 +91,23 @@ let implres = ast::parse_impl(decl); debug!("get_type_of_self_arg implres |{:?}|", implres); resolve_path_with_str(&implres.name_path.expect("failed parsing impl name"), - &m.filepath, start, + filepath, start, ExactMatch, Namespace::Type, session).nth(0).map(core::Ty::Match) } else { // // must be a trait ast::parse_trait(decl).name.and_then(|name| { Some(core::Ty::Match(Match { - matchstr: name, - filepath: m.filepath.clone(), - point: start, - coords: None, - local: m.local, - mtype: core::MatchType::Trait, - contextstr: matchers::first_line(&msrc[start..]), - generic_args: Vec::new(), - generic_types: Vec::new(), - docs: String::new(), + matchstr: name, + filepath: filepath.into(), + point: start, + coords: None, + local: local, + mtype: core::MatchType::Trait, + contextstr: matchers::first_line(&msrc[start..]), + generic_args: Vec::new(), + generic_types: Vec::new(), + docs: String::new(), })) }) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/src/racer/util.rs new/racer-2.0.6/src/racer/util.rs --- old/racer-2.0.5/src/racer/util.rs 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/src/racer/util.rs 2017-02-17 17:32:13.000000000 +0100 @@ -346,3 +346,50 @@ } } } + +/// An immutable stack implemented as a linked list backed by a thread's stack. +pub struct StackLinkedListNode<'stack, T>(Option<StackLinkedListNodeData<'stack, T>>) + where T: 'stack; + +struct StackLinkedListNodeData<'stack, T> + where T: 'stack +{ + item: T, + previous: &'stack StackLinkedListNode<'stack, T>, +} + +impl<'stack, T> StackLinkedListNode<'stack, T> + where T: 'stack +{ + /// Returns an empty node. + pub fn empty() -> Self { + StackLinkedListNode(None) + } + + /// Pushes a new node on the stack. Returns the new node. + pub fn push(&'stack self, item: T) -> Self { + StackLinkedListNode(Some(StackLinkedListNodeData { + item: item, + previous: self, + })) + } +} + +impl<'stack, T> StackLinkedListNode<'stack, T> + where T: 'stack + PartialEq +{ + /// Check if the stack contains the specified item. + /// Returns `true` if the item is found, or `false` if it's not found. + pub fn contains(&self, item: &T) -> bool { + let mut current = self; + while let &StackLinkedListNode(Some(StackLinkedListNodeData { item: ref current_item, previous })) = current { + if current_item == item { + return true; + } + + current = previous; + } + + false + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/racer-2.0.5/tests/system.rs new/racer-2.0.6/tests/system.rs --- old/racer-2.0.5/tests/system.rs 2017-01-16 20:47:26.000000000 +0100 +++ new/racer-2.0.6/tests/system.rs 2017-02-17 17:32:13.000000000 +0100 @@ -954,6 +954,33 @@ } #[test] +fn single_import_shadows_glob_import() { + let _lock = sync!(); + + let src = " + use shadowed::*; + use shadower::Foo; + + mod shadowed { + pub struct Foo; + } + + mod shadower { + pub struct Foo; + } + + fn main() { + Foo~; + } + "; + let got = get_definition(src, None); + assert_eq!(got.matchstr, "Foo"); + println!("{}", got.filepath.display()); + println!("{}", got.point); + assert_eq!(got.coords, Some(Coordinate { line: 10, column: 19 })); +} + +#[test] fn follows_use_self() { let _lock = sync!(); @@ -986,6 +1013,84 @@ } #[test] +fn completes_out_of_order_mod_use_with_same_fn_name_as_mod() { + let _lock = sync!(); + + let src = " + use foo::foo; + + mod foo { + pub fn foo() {} + } + + fn main() { + f~ + }"; + + let mut has_module = false; + let mut has_function = false; + let completions = get_all_completions(src, None); + for m in completions { + match (&*m.matchstr, m.mtype) { + ("foo", MatchType::Module) => has_module = true, + ("foo", MatchType::Function) => has_function = true, + _ => (), + } + } + assert!(has_module && has_function); +} + +#[test] +fn ignores_self_referential_unresolved_import() { + let _lock = sync!(); + + let src = "use foo::foo;f~"; + + let completions = get_all_completions(src, None); + assert!(!completions.iter().any(|m| m.matchstr == "foo")); +} + +#[test] +fn ignores_self_referential_unresolved_import_long() { + let _lock = sync!(); + + let src = "use foo::bar::foo;f~"; + + let completions = get_all_completions(src, None); + assert!(!completions.iter().any(|m| m.matchstr == "foo")); +} + +#[test] +fn ignores_self_referential_unresolved_imports() { + let _lock = sync!(); + + let src = " + use foo::bar; + use bar::baz; + use baz::foo; + f~"; + + let completions = get_all_completions(src, None); + assert!(!completions.iter().any(|m| m.matchstr == "foo")); +} + +#[test] +fn ignores_self_referential_unresolved_imports_across_modules() { + let _lock = sync!(); + + let src = " + use foo::bar; + + mod foo { + pub use super::bar; + } + b~"; + + let completions = get_all_completions(src, None); + assert!(!completions.iter().any(|m| m.matchstr == "bar")); +} + +#[test] fn finds_external_mod_docs() { let _lock = sync!(); @@ -1042,7 +1147,7 @@ let src1 = " /// Orange /// juice - + pub fn apple() { let x = 1; }"; @@ -1966,6 +2071,24 @@ } #[test] +fn finds_enum_variant_through_recursive_glob_imports() { + let _lock = sync!(); + + let src = " + use foo::*; + use Bar::*; + + mod foo { + pub enum Bar { MyVariant, MyVariant2 } + } + MyVa~riant + "; + + let got = get_definition(src, None); + assert_eq!("MyVariant", got.matchstr); +} + +#[test] #[ignore] fn uses_generic_arg_to_resolve_trait_method() { let _lock = sync!(); @@ -2481,3 +2604,38 @@ assert_eq!("same_name", got.matchstr); assert_eq!(MatchType::Function, got.mtype); } + +#[test] +fn finds_self() { + let _lock = sync!(); + + let src = " + struct Foo; + impl Foo { + fn foo() { + Se~lf + } + } + "; + + let got = get_definition(src, None); + assert_eq!("Foo", got.matchstr); +} + +#[test] +fn finds_self_referenced_functions() { + let _lock = sync!(); + + let src = " + struct Foo; + impl Foo { + fn foo() { + Self::myfun~ction + } + fn myfunction() {} + } + "; + + let got = get_definition(src, None); + assert_eq!("myfunction", got.matchstr); +} ++++++ vendor.tar.xz ++++++