Skip to content

Commit

Permalink
Merge pull request racer-rust#839 from birkenfeld/cache_generic_impls
Browse files Browse the repository at this point in the history
Cache generic impls
  • Loading branch information
nrc authored May 9, 2018
2 parents ced9d5d + 2ba7fb8 commit 0efe206
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 52 deletions.
12 changes: 12 additions & 0 deletions src/racer/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub fn with_error_checking_parse<F, T>(s: String, f: F) -> Option<T>

// parse a string, return a stmt
pub fn string_to_stmt(source_str: String) -> Option<ast::Stmt> {
trace!("string_to_stmt: {:?}", source_str);
with_error_checking_parse(source_str, |p| {
match p.parse_stmt() {
Ok(Some(stmt)) => Some(stmt),
Expand All @@ -49,6 +50,7 @@ pub fn string_to_stmt(source_str: String) -> Option<ast::Stmt> {

// parse a string, return a crate.
pub fn string_to_crate(source_str: String) -> Option<ast::Crate> {
trace!("string_to_crate: {:?}", source_str);
with_error_checking_parse(source_str.clone(), |p| {
use std::result::Result::{Ok, Err};
match p.parse_crate_mod() {
Expand Down Expand Up @@ -1069,6 +1071,16 @@ pub fn parse_generics(s: String) -> GenericsVisitor {
v
}

pub fn parse_generics_and_impl(s: String) -> (GenericsVisitor, ImplVisitor) {
let mut v = GenericsVisitor { generic_args: Vec::new() };
let mut w = ImplVisitor { name_path: None, trait_path: None };
if let Some(stmt) = string_to_stmt(s) {
visit::walk_stmt(&mut v, &stmt);
visit::walk_stmt(&mut w, &stmt);
}
(v, w)
}

pub fn parse_type(s: String) -> TypeVisitor {
let mut v = TypeVisitor { name: None, type_: None };
if let Some(stmt) = string_to_stmt(s) {
Expand Down
7 changes: 6 additions & 1 deletion src/racer/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,10 @@ pub struct Session<'c> {
/// The file cache is used within a session to prevent multiple reads. It is
/// borrowed here in order to support reuse across Racer operations.
cache: &'c FileCache,
/// Cache for generic impls
pub generic_impls: RefCell<HashMap<(path::PathBuf, usize),
Rc<Vec<(usize, String,
ast::GenericsVisitor, ast::ImplVisitor)>>>>,
}

impl<'c> fmt::Debug for Session<'c> {
Expand All @@ -764,7 +768,8 @@ impl<'c> Session<'c> {
/// [`FileCache`]: struct.FileCache.html
pub fn new(cache: &'c FileCache) -> Session<'c> {
Session {
cache: cache
cache: cache,
generic_impls: Default::default(),
}
}

Expand Down
119 changes: 68 additions & 51 deletions src/racer/nameres.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
// Name resolution

use std::{self, vec};
use std::rc::Rc;
use std::path::{Path, PathBuf};

use {core, ast, matchers, scopes, typeinf};
use core::SearchType::{self, ExactMatch, StartsWith};
use core::{Match, Src, Session, Coordinate, SessionExt, Ty, Point};
use core::MatchType::{Module, Function, Struct, Enum, FnArg, Trait, StructField,
Impl, TraitImpl, MatchArm, Builtin};
use core::Namespace;
use ast::{GenericsVisitor, ImplVisitor};


use util::{self, closure_valid_arg_scope, symbol_matches, txt_matches,
find_ident_end, get_rust_src_path};
use matchers::find_doc;
use cargo;
use std::path::{Path, PathBuf};
use std::{self, vec, iter};
use matchers::PendingImports;

lazy_static! {
Expand Down Expand Up @@ -320,63 +323,77 @@ pub fn search_for_impls(pos: Point, searchstr: &str, filepath: &Path, local: boo
out.into_iter()
}

fn cached_generic_impls(filepath: &Path, session: &Session, scope_start: usize)
-> Rc<Vec<(usize, String, GenericsVisitor, ImplVisitor)>> {
// the cache is keyed by path and the scope we search in
session.generic_impls.borrow_mut().entry((filepath.into(), scope_start))
.or_insert_with(|| {
let s = session.load_file(&filepath);
let src = s.from(scope_start);
let mut out = Vec::new();
for (start, end) in src.iter_stmts() {
let blob = &src[start..end];

if blob.starts_with("impl") {
blob.find('{').map(|n| {
let ref decl = blob[..n+1];
if decl.contains('!') {
// Guard against macros
debug!("impl was probably a macro: {} {}", filepath.display(), start);
return;
}
let mut decl = decl.to_owned();
decl.push_str("}");
let (generics, impls) = ast::parse_generics_and_impl(decl);
out.push((start, blob.into(), generics, impls));
});
}
}
Rc::new(out)
})
.clone()
}

pub fn search_for_generic_impls(pos: Point, searchstr: &str, contextm: &Match, filepath: &Path, session: &Session) -> vec::IntoIter<Match> {
debug!("search_for_generic_impls {}, {}, {:?}", pos, searchstr, filepath.display());
let s = session.load_file(filepath);
let scope_start = scopes::scope_start(s.as_src(), pos);
let src = s.from(scope_start);

let mut out = Vec::new();
for (start, end) in src.iter_stmts() {
let blob = &src[start..end];
for &(start, ref blob, ref generics, ref implres) in cached_generic_impls(filepath, session, scope_start).iter() {
if let (Some(name_path), Some(trait_path)) = (implres.name_path.as_ref(), implres.trait_path.as_ref()) {
if let (Some(name), Some(trait_name)) = (name_path.segments.last(), trait_path.segments.last()) {
for gen_arg in &generics.generic_args {
if symbol_matches(ExactMatch, &gen_arg.name, &name.name)
&& gen_arg.bounds.len() == 1
&& gen_arg.bounds[0] == searchstr {
debug!("generic impl decl {}", blob);

let trait_pos = blob.find(&trait_name.name).unwrap();
let self_path = core::Path::from_vec(false, vec![&contextm.matchstr]);
let self_pathsearch = core::PathSearch {
path: self_path,
filepath: contextm.filepath.clone(),
point: contextm.point
};

if blob.starts_with("impl") {
blob.find('{').map(|n| {
let ref decl = blob[..n+1];
if decl.contains('!') {
// Guard against macros
debug!("impl was probably a macro: {} {}", filepath.display(), start);
return;
}
let mut decl = decl.to_owned();
decl.push_str("}");
let generics = ast::parse_generics(decl.clone());
let implres = ast::parse_impl(decl.clone());
if let (Some(name_path), Some(trait_path)) = (implres.name_path, implres.trait_path) {
if let (Some(name), Some(trait_name)) = (name_path.segments.last(), trait_path.segments.last()) {
for gen_arg in generics.generic_args {
if symbol_matches(ExactMatch, &gen_arg.name, &name.name)
&& gen_arg.bounds.len() == 1
&& gen_arg.bounds[0] == searchstr {
debug!("generic impl decl {}", decl);

let trait_pos = blob.find(&trait_name.name).unwrap();
let self_path = core::Path::from_vec(false, vec![&contextm.matchstr]);
let self_pathsearch = core::PathSearch {
path: self_path,
filepath: contextm.filepath.clone(),
point: contextm.point
};

let m = Match {
matchstr: trait_name.name.clone(),
filepath: filepath.to_path_buf(),
point: scope_start + start + trait_pos,
coords: None,
local: true,
mtype: TraitImpl,
contextstr: "".into(),
generic_args: vec![gen_arg.name],
generic_types: vec![self_pathsearch],
docs: String::new(),
};
debug!("Found a trait! {:?}", m);
out.push(m);
}
let m = Match {
matchstr: trait_name.name.clone(),
filepath: filepath.to_path_buf(),
point: start + trait_pos,
coords: None,
local: true,
mtype: TraitImpl,
contextstr: "".into(),
generic_args: vec![gen_arg.name.clone()],
generic_types: vec![self_pathsearch],
docs: String::new(),
};
debug!("Found a trait! {:?}", m);
out.push(m);
}
}
}
});
}
}
}
out.into_iter()
Expand Down Expand Up @@ -1414,7 +1431,7 @@ pub fn resolve_method(point: Point, msrc: Src, searchstr: &str,
scopestart,
msrc.src.point_to_coords(scopestart));

if let Some(stmtstart) = scopes::find_stmt_start(msrc, (scopestart - 1)) {
if let Some(stmtstart) = scopes::find_stmt_start(msrc, scopestart - 1) {
let preblock = &msrc[stmtstart..scopestart];
debug!("search_scope_headers preblock is |{}|", preblock);

Expand Down
1 change: 1 addition & 0 deletions src/racer/snippets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ impl MethodInfo {
let trim: &[_] = &['\n', '\r', '{', ' '];
let decorated = format!("{} {{}}()", source.trim_right_matches(trim));

trace!("MethodInfo::from_source_str: {:?}", decorated);
with_error_checking_parse(decorated, |p| {
if let Ok(method) = p.parse_impl_item() {
if let ImplItemKind::Method(ref msig, _) = method.node {
Expand Down

0 comments on commit 0efe206

Please sign in to comment.