Skip to content

Commit

Permalink
Merge pull request #948 from kngwyu/tbound2
Browse files Browse the repository at this point in the history
Completion based on impl<T: Bound>
  • Loading branch information
kngwyu authored Sep 5, 2018
2 parents e9f93fa + e07f2f5 commit 75dbb38
Show file tree
Hide file tree
Showing 7 changed files with 383 additions and 175 deletions.
95 changes: 65 additions & 30 deletions src/racer/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,30 @@ impl<'ast> visit::Visitor<'ast> for PatVisitor {
}
}

pub struct PatAndGenVisitor<'f> {
ident_points: Vec<ByteRange>,
generics: GenericsArgs,
fpath: &'f Path,
offset: i32,
}

impl<'ast, 'f> visit::Visitor<'ast> for PatAndGenVisitor<'f> {
fn visit_pat(&mut self, p: &ast::Pat) {
match p.node {
PatKind::Ident(_, ref spannedident, _) => {
self.ident_points.push(spannedident.span.into());
}
_ => {
visit::walk_pat(self, p);
}
}
}
fn visit_generics(&mut self, g: &'ast ast::Generics) {
let generics = GenericsArgs::from_generics(g, self.fpath, self.offset);
self.generics.extend(generics);
}
}

fn point_is_in_span(point: BytePos, span: &Span) -> bool {
let point: u32 = point.0 as u32;
let (lo, hi) = destruct_span(*span);
Expand Down Expand Up @@ -618,15 +642,15 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for ExprTypeVisitor<'c, 's> {
let fieldname = spannedident.name.to_string();
debug!("exprfield {}", fieldname);
self.visit_expr(subexpression);
self.result = self.result.as_ref().and_then(|structm| match *structm {
Ty::Match(ref structm) => {
typeinf::get_struct_field_type(&fieldname, structm, self.session).and_then(
self.result = self.result.take().and_then(|structm| match structm {
Ty::Match(structm) => {
typeinf::get_struct_field_type(&fieldname, &structm, self.session).and_then(
|fieldtypepath| {
find_type_match_including_generics(
&fieldtypepath,
fieldtypepath,
&structm.filepath,
structm.point,
structm,
&structm,
self.session,
)
},
Expand Down Expand Up @@ -755,47 +779,43 @@ fn path_to_match_including_generics(ty: Ty, contextm: &Match, session: &Session)
}

fn find_type_match_including_generics(
fieldtype: &Ty,
fieldtype: Ty,
filepath: &Path,
pos: BytePos,
structm: &Match,
session: &Session,
) -> Option<Ty> {
assert_eq!(&structm.filepath, filepath);
let fieldtypepath = match *fieldtype {
Ty::PathSearch(ref paths) => &paths.path,
Ty::RefPtr(ref ty) => match *ty.as_ref() {
Ty::PathSearch(ref paths) => &paths.path,
_ => {
debug!(
"EXPECTING A PATH!! Cannot handle other types yet. {:?}",
fieldtype
);
return None;
}
let fieldtypepath = match fieldtype {
Ty::PathSearch(paths) => paths.path,
Ty::RefPtr(ty) => match destruct_ty_refptr(*ty) {
Ty::PathSearch(paths) => paths.path,
Ty::Match(m) => return Some(Ty::Match(m)),
_ => return None,
},
// already resolved
Ty::Match(m) => return Some(Ty::Match(m)),
_ => {
debug!(
"EXPECTING A PATH!! Cannot handle other types yet. {:?}",
fieldtype
);
return None;
}
};

// TODO(kngwyu): why len() == 1
let generics = match &structm.mtype {
MatchType::Struct(gen) => gen,
_ => return None,
};
if fieldtypepath.segments.len() == 1 {
// could be a generic arg! - try and resolve it
let typename = &fieldtypepath.segments[0].name;
for type_param in structm.generics() {
let resolved = try_continue!(type_param.resolved());
if type_param.name() == typename {
return Some(resolved.to_owned());
if let Some((_, param)) = generics.search_param_by_path(&fieldtypepath) {
if let Some(res) = param.resolved() {
return Some(res.to_owned());
}
let mut m = param.to_owned().into_match();
m.local = structm.local;
return Some(Ty::Match(m));
}
}

find_type_match(fieldtypepath, filepath, pos, session).map(Ty::Match)
find_type_match(&fieldtypepath, filepath, pos, session).map(Ty::Match)
}

struct StructVisitor {
Expand Down Expand Up @@ -1060,6 +1080,21 @@ pub fn parse_type<'s>(s: String, scope: &'s Scope) -> TypeVisitor<'s> {
v
}

pub fn parse_fn_args_and_generics(
s: String,
fpath: &Path,
offset: i32,
) -> (Vec<ByteRange>, GenericsArgs) {
let mut v = PatAndGenVisitor {
ident_points: vec![],
generics: GenericsArgs::default(),
fpath,
offset,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
(v.ident_points, v.generics)
}

pub fn parse_fn_args(s: String) -> Vec<ByteRange> {
parse_pat_idents(s)
}
Expand Down Expand Up @@ -1241,7 +1276,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for FnArgTypeVisitor<'c, 's> {
if segments.len() == 1 {
let name = &segments[0].name;
if let Some(bounds) = self.generics.find_type_param(name) {
let res = bounds.to_owned().into_match()?;
let res = bounds.to_owned().into_match();
return Some(Ty::Match(res));
}
}
Expand Down
34 changes: 30 additions & 4 deletions src/racer/ast_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,9 +548,9 @@ impl TypeParameter {
pub fn name(&self) -> &str {
&(*self.name)
}
pub(crate) fn into_match(self) -> Option<Match> {
pub(crate) fn into_match(self) -> Match {
// TODO: contextstr, local
Some(Match {
Match {
matchstr: self.name,
filepath: self.filepath,
point: self.point,
Expand All @@ -559,14 +559,27 @@ impl TypeParameter {
mtype: MatchType::TypeParameter(Box::new(self.bounds)),
contextstr: String::new(),
docs: String::new(),
})
}
}
pub(crate) fn resolve(&mut self, ty: Ty) {
self.resolved = Some(ty);
}
pub(crate) fn resolved(&self) -> Option<&Ty> {
self.resolved.as_ref()
}
pub(crate) fn add_bound(&mut self, bound: TraitBounds) {
let add_bounds: Vec<_> = bound
.0
.into_iter()
.filter(|p| {
if let Some(name) = p.path.name() {
self.bounds.find_by_name(name).is_none()
} else {
true
}
}).collect();
self.bounds.0.extend(add_bounds);
}
}

/// List of Args in generics, e.g. <T: Clone, U, P>
Expand Down Expand Up @@ -654,14 +667,27 @@ impl GenericsArgs {
}
None
}
pub fn search_param_by_name(&self, name: &str) -> Option<(usize, &TypeParameter)> {
for (i, typ) in self.0.iter().enumerate() {
if typ.name() == name {
return Some((i, typ));
}
}
None
}
pub(crate) fn add_bound(&mut self, pos: usize, bound: TraitBounds) {
if let Some(param) = self.0.get_mut(pos) {
param.add_bound(bound);
}
}
}

/// `Impl` information
#[derive(Clone, Debug, PartialEq)]
pub struct ImplHeader {
self_path: Path,
trait_path: Option<Path>,
generics: GenericsArgs,
pub(crate) generics: GenericsArgs,
filepath: PathBuf,
// TODO: should be removed
local: bool,
Expand Down
2 changes: 1 addition & 1 deletion src/racer/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1123,7 +1123,7 @@ fn complete_from_file_(filepath: &path::Path, cursor: Location, session: &Sessio
}
CompletionType::Field => {
let context = ast::get_type_of(contextstr.to_owned(), filepath, pos, session);
debug!("complete_from_file context is {:?}", context);
println!("complete_from_file context is {:?}", context);
if let Some(ty) = context {
out.extend(nameres::get_field_matches_from_ty(
ty,
Expand Down
16 changes: 15 additions & 1 deletion src/racer/matchers.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use ast_types::{PathAliasKind, PathSegment};
use ast_types::{ImplHeader, PathAliasKind, PathSegment};
use core::MatchType::{
self, Const, Enum, EnumVariant, For, Function, IfLet, Let, Macro, Module, Static, Struct,
Trait, Type, WhileLet,
Expand Down Expand Up @@ -753,3 +753,17 @@ fn find_mod_doc(msrc: &str, blobstart: BytePos) -> String {
}
doc
}

// DON'T USE MatchCxt's range
pub fn match_impl(decl: String, context: &MatchCxt, offset: BytePos) -> Option<Vec<Match>> {
let ImplHeader { generics, .. } =
ast::parse_impl(decl, context.filepath, offset, true, offset)?;
let mut out = Vec::new();
for type_param in generics.0 {
if !symbol_matches(context.search_type, context.search_str, &type_param.name) {
continue;
}
out.push(type_param.into_match());
}
Some(out)
}
Loading

0 comments on commit 75dbb38

Please sign in to comment.