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

Issue:
Refactor `PathSegment`
https://github.com/racer-rust/racer/blob/1a38903e2640d0cac735f918a858c3596f0222dc/src/racer/ast_types.rs#L302-L306

Currently, `PathSegement` can only have `Path` as Generic types.
So racer can't provide completion in the cases like
```rust
let v: Vec<(String, usize)> = vec![];
for x in v {
    x.0.~
}
```
.
It's very problematic.
It should have `GenericsArgs` instead of `Vec<Path>`.


Discussion / hints:

Fix patch:
diff --git a/src/racer/ast.rs b/src/racer/ast.rs
index 7fb7d61e..c8deed64 100644
--- a/src/racer/ast.rs
+++ b/src/racer/ast.rs
@@ -1,7 +1,5 @@
 use ast_types::Path as RacerPath;
-use ast_types::{
-    self, GenericsArgs, ImplHeader, Pat, PathAlias, PathAliasKind, PathSearch, TraitBounds, Ty,
-};
+use ast_types::{self, GenericsArgs, ImplHeader, Pat, PathAlias, PathAliasKind, TraitBounds, Ty};
 use core::{self, BytePos, ByteRange, Match, MatchType, Scope, Session, SessionExt};
 use nameres::{self, resolve_path_with_str};
 use scopes;
@@ -89,12 +87,12 @@ impl<'ast> visit::Visitor<'ast> for UseVisitor {
         ) -> (Vec<PathAlias>, bool) {
             let mut res = vec![];
             let mut path = if let Some(parent) = parent_path {
-                let relative_path = RacerPath::from_ast(&use_tree.prefix);
+                let relative_path = RacerPath::from_ast_nogen(&use_tree.prefix);
                 let mut path = parent.clone();
                 path.extend(relative_path);
                 path
             } else {
-                RacerPath::from_ast(&use_tree.prefix)
+                RacerPath::from_ast_nogen(&use_tree.prefix)
             };
             let mut contains_glob = false;
             match use_tree.kind {
@@ -420,7 +418,8 @@ fn resolve_ast_path(
     pos: BytePos,
     session: &Session,
 ) -> Option<Match> {
-    let path = RacerPath::from_ast(path);
+    let scope = Scope::new(filepath.to_owned(), pos);
+    let path = RacerPath::from_ast(path, &scope);
     debug!("resolve_ast_path {:?}", path);
     nameres::resolve_path_with_str(
         &path,
@@ -464,11 +463,7 @@ pub(crate) fn find_type_match(
     // TODO: 'Type' support
     // if res is Enum/Struct and has a generic type paramter, let's resolve it.
     for (mut param, typ) in res.generics_mut().zip(path.generic_types()) {
-        param.resolve(PathSearch {
-            path: typ.clone(),
-            filepath: fpath.to_owned(),
-            point: pos,
-        })
+        param.resolve(typ.to_owned());
     }
     Some(Ty::Match(res))
 }
@@ -478,13 +473,13 @@ pub(crate) fn get_type_of_typedef(m: &Match, session: &Session) -> Option<Match>
     let msrc = session.load_source_file(&m.filepath);
     let blobstart = m.point - BytePos(5); // 5 == "type ".len()
     let blob = msrc.get_src_from_start(blobstart);
-
     blob.iter_stmts()
         .nth(0)
         .and_then(|range| {
             let blob = msrc[range.shift(blobstart).to_range()].to_owned();
             debug!("get_type_of_typedef blob string {}", blob);
-            let res = parse_type(blob);
+            let scope = Scope::new(m.filepath.clone(), range.start);
+            let res = parse_type(blob, &scope);
             debug!("get_type_of_typedef parsed type {:?}", res.type_);
             res.type_
         }).and_then(|type_| {
@@ -574,7 +569,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for ExprTypeVisitor<'c, 's> {
                 });
             }
             ExprKind::Struct(ref path, _, _) => {
-                let pathvec = RacerPath::from_ast(path);
+                let pathvec = RacerPath::from_ast(path, &self.scope);
                 self.result = find_type_match(
                     &pathvec,
                     &self.scope.filepath,
@@ -684,14 +679,7 @@ impl<'c, 's, 'ast> visit::Visitor<'ast> for ExprTypeVisitor<'c, 's> {
                     // Yeah there're many corner cases but it'll work well in most cases
                     if m.matchstr == "Result" || m.matchstr == "Option" {
                         debug!("Option or Result: {:?}", m);
-                        m.resolved_generics().next().and_then(|ok_param| {
-                            find_type_match(
-                                &ok_param.path,
-                                &ok_param.filepath,
-                                ok_param.point,
-                                self.session,
-                            )
-                        })
+                        m.resolved_generics().next().map(|x| x.to_owned())
                     } else {
                         debug!("Unable to desugar Try expression; type was {:?}", m);
                         None
@@ -753,49 +741,13 @@ fn path_to_match_including_generics(ty: Ty, contextm: &Match, session: &Session)
             if fieldtypepath.segments.len() == 1 {
                 let typename = &fieldtypepath.segments[0].name;
                 // could have generic args! - try and resolve them
-                let mut typepath = fieldtypepath.to_owned();
-                let mut gentypefound = false;
                 for type_param in contextm.generics() {
                     let resolved = try_continue!(type_param.resolved());
                     if type_param.name() == typename {
-                        return find_type_match(
-                            &resolved.path,
-                            &resolved.filepath,
-                            resolved.point,
-                            session,
-                        );
-                    }
-                    for typ in &mut typepath.segments[0].types {
-                        if type_param.name() == &typ.segments[0].name {
-                            *typ = resolved.path.clone();
-                            gentypefound = true;
-                        }
-                    }
-                }
-                if gentypefound {
-                    let mut out = find_type_match(&typepath, &paths.filepath, paths.point, session);
-                    // Fix the paths on the generic types in out
-                    // TODO(kngwyu): I DON'T KNOW WHAT THIS CODE DO COMPLETELY
-                    if let Some(Ty::Match(ref mut m)) = out {
-                        for type_search in contextm.resolved_generics() {
-                            for gen_param in m.generics_mut() {
-                                debug!(
-                                    "gen_param: {:?}, type_search: {:?}",
-                                    gen_param, type_search
-                                );
-                                let same_name = gen_param.resolved().map_or(false, |typ| {
-                                    typ.path.segments[0].name == type_search.path.segments[0].name
-                                });
-                                if same_name {
-                                    gen_param.resolve(type_search.to_owned());
-                                }
-                            }
-                        }
+                        return Some(resolved.to_owned());
                     }
-                    return out;
                 }
             }
-
             find_type_match(&fieldtypepath, &paths.filepath, paths.point, session)
         }
         _ => Some(ty),
@@ -831,19 +783,20 @@ fn find_type_match_including_generics(
         }
     };
 
+    // TODO(kngwyu): why len() == 1
     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() {
-            if let Some(type_search) = type_param.resolved() {
-                if type_param.name() == typename {
-                    return find_type_match(
-                        &type_search.path,
-                        &type_search.filepath,
-                        type_search.point,
-                        session,
-                    );
-                }
+            let resolved = try_continue!(type_param.resolved());
+            if type_param.name() == typename {
+                return Some(resolved.to_owned());
+                // return find_type_match(
+                //     &type_search.path,
+                //     &type_search.filepath,
+                //     type_search.point,
+                //     session,
+                // );
             }
         }
     }
@@ -881,12 +834,13 @@ impl<'ast> visit::Visitor<'ast> for StructVisitor {
 }
 
 #[derive(Debug)]
-pub struct TypeVisitor {
+pub struct TypeVisitor<'s> {
     pub name: Option<String>,
     pub type_: Option<RacerPath>,
+    scope: &'s Scope,
 }
 
-impl<'ast> visit::Visitor<'ast> for TypeVisitor {
+impl<'ast, 's> visit::Visitor<'ast> for TypeVisitor<'s> {
     fn visit_item(&mut self, item: &ast::Item) {
         if let ItemKind::Ty(ref ty, _) = item.node {
             self.name = Some(item.ident.name.to_string());
@@ -894,14 +848,14 @@ impl<'ast> visit::Visitor<'ast> for TypeVisitor {
             let typepath = match ty.node {
                 TyKind::Rptr(_, ref ty) => match ty.ty.node {
                     TyKind::Path(_, ref path) => {
-                        let type_ = RacerPath::from_ast(path);
+                        let type_ = RacerPath::from_ast(path, self.scope);
                         debug!("type type is {:?}", type_);
                         Some(type_)
                     }
                     _ => None,
                 },
                 TyKind::Path(_, ref path) => {
-                    let type_ = RacerPath::from_ast(path);
+                    let type_ = RacerPath::from_ast(path, self.scope);
                     debug!("type type is {:?}", type_);
                     Some(type_)
                 }
@@ -1102,10 +1056,11 @@ pub fn parse_generics(s: String, filepath: &Path) -> GenericsArgs {
     v.result
 }
 
-pub fn parse_type(s: String) -> TypeVisitor {
+pub fn parse_type<'s>(s: String, scope: &'s Scope) -> TypeVisitor<'s> {
     let mut v = TypeVisitor {
         name: None,
         type_: None,
+        scope,
     };
     with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
     v
@@ -1355,7 +1310,7 @@ impl<'ast, 'r, 's> visit::Visitor<'ast> for ForStmtVisitor<'r, 's> {
             };
             expr_visitor.visit_expr(expr);
             self.in_expr = expr_visitor.result;
-            self.for_pat = Some(Pat::from_ast(&pat.node));
+            self.for_pat = Some(Pat::from_ast(&pat.node, &self.scope));
         }
     }
 }
diff --git a/src/racer/ast_types.rs b/src/racer/ast_types.rs
index 1e3bf9f5..34da36bb 100644
--- a/src/racer/ast_types.rs
+++ b/src/racer/ast_types.rs
@@ -38,7 +38,7 @@ impl AsRef<Path> for PathAlias {
 }
 
 // Represents a type. Equivilent to rustc's ast::Ty but can be passed across threads
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
 pub enum Ty {
     Match(Match),
     PathSearch(PathSearch), // A path + the scope to be able to resolve it
@@ -57,7 +57,6 @@ impl Ty {
         }
         ty
     }
-
     pub(crate) fn destruct_ref(self) -> (Ty, usize) {
         fn destruct_ref_inner(ty: Ty, cur: usize) -> (Ty, usize) {
             if let Ty::RefPtr(ty) = ty {
@@ -84,7 +83,7 @@ impl Ty {
                 Ty::from_ast(&ty.ty, scope).map(|ref_ty| Ty::RefPtr(Box::new(ref_ty)))
             }
             TyKind::Path(_, ref path) => Some(Ty::PathSearch(PathSearch {
-                path: Path::from_ast(path),
+                path: Path::from_ast(path, scope),
                 filepath: scope.filepath.clone(),
                 point: scope.point,
             })),
@@ -158,35 +157,41 @@ pub enum Pat {
 }
 
 impl Pat {
-    pub fn from_ast(pat: &PatKind) -> Self {
+    pub fn from_ast(pat: &PatKind, scope: &Scope) -> Self {
         match pat {
             PatKind::Wild => Pat::Wild,
             PatKind::Ident(bi, ident, _) => Pat::Ident(*bi, ident.to_string()),
             PatKind::Struct(path, fields, _) => {
-                let path = Path::from_ast(path);
+                let path = Path::from_ast(path, scope);
                 let fields = fields
                     .iter()
-                    .map(|fld| FieldPat::from_ast(&fld.node))
+                    .map(|fld| FieldPat::from_ast(&fld.node, scope))
                     .collect();
                 Pat::Struct(path, fields)
             }
             PatKind::TupleStruct(path, pats, _) => {
-                let path = Path::from_ast(path);
-                let pats = pats.iter().map(|pat| Pat::from_ast(&pat.node)).collect();
+                let path = Path::from_ast(path, scope);
+                let pats = pats
+                    .iter()
+                    .map(|pat| Pat::from_ast(&pat.node, scope))
+                    .collect();
                 Pat::TupleStruct(path, pats)
             }
-            PatKind::Path(_, path) => Pat::Path(Path::from_ast(&path)),
+            PatKind::Path(_, path) => Pat::Path(Path::from_ast(&path, scope)),
             PatKind::Tuple(pats, _) => {
-                let pats = pats.iter().map(|pat| Pat::from_ast(&pat.node)).collect();
+                let pats = pats
+                    .iter()
+                    .map(|pat| Pat::from_ast(&pat.node, scope))
+                    .collect();
                 Pat::Tuple(pats)
             }
             PatKind::Box(_) => Pat::Box,
-            PatKind::Ref(pat, mut_) => Pat::Ref(Box::new(Pat::from_ast(&pat.node)), *mut_),
+            PatKind::Ref(pat, mut_) => Pat::Ref(Box::new(Pat::from_ast(&pat.node, scope)), *mut_),
             PatKind::Lit(_) => Pat::Lit,
             PatKind::Range(..) => Pat::Range,
             PatKind::Slice(..) => Pat::Slice,
             // ignore paren
-            PatKind::Paren(pat) => Pat::from_ast(&pat.node),
+            PatKind::Paren(pat) => Pat::from_ast(&pat.node, scope),
             PatKind::Mac(_) => Pat::Mac,
         }
     }
@@ -199,10 +204,10 @@ pub struct FieldPat {
 }
 
 impl FieldPat {
-    pub fn from_ast(fpat: &ast::FieldPat) -> Self {
+    pub fn from_ast(fpat: &ast::FieldPat, scope: &Scope) -> Self {
         FieldPat {
             field_name: fpat.ident.to_string(),
-            pat: Box::new(Pat::from_ast(&fpat.pat.node)),
+            pat: Box::new(Pat::from_ast(&fpat.pat.node, scope)),
         }
     }
 }
@@ -242,7 +247,19 @@ impl Path {
         self.segments.len() == 1
     }
 
-    pub fn from_ast(path: &ast::Path) -> Path {
+    pub fn from_ast_nogen(path: &ast::Path) -> Path {
+        let mut segments = Vec::new();
+        for seg in path.segments.iter() {
+            let name = seg.ident.name.to_string();
+            segments.push(PathSegment::new(name, vec![]));
+        }
+        Path {
+            prefix: None,
+            segments,
+        }
+    }
+
+    pub fn from_ast(path: &ast::Path, scope: &Scope) -> Path {
         let mut segments = Vec::new();
         for seg in path.segments.iter() {
             let name = seg.ident.name.to_string();
@@ -252,8 +269,8 @@ impl Path {
                 if let ast::GenericArgs::AngleBracketed(ref angle_args) = **params {
                     angle_args.args.iter().for_each(|arg| {
                         if let ast::GenericArg::Type(ty) = arg {
-                            if let TyKind::Path(_, ref path) = ty.node {
-                                types.push(Path::from_ast(path));
+                            if let Some(ty) = Ty::from_ast(ty, scope) {
+                                types.push(ty);
                             }
                         }
                     })
@@ -267,8 +284,8 @@ impl Path {
         }
     }
 
-    pub fn generic_types(&self) -> ::std::slice::Iter<Path> {
-        self.segments[self.segments.len() - 1].types.iter()
+    pub fn generic_types(&self) -> impl Iterator<Item = &Ty> {
+        self.segments[self.segments.len() - 1].generics.iter()
     }
 
     pub fn single(seg: PathSegment) -> Path {
@@ -345,15 +362,13 @@ impl fmt::Debug for Path {
                 write!(f, "::{}", seg.name)?;
             }
 
-            if !seg.types.is_empty() {
+            if !seg.generics.is_empty() {
                 write!(f, "<")?;
-                let mut t_first = true;
-                for typath in &seg.types {
-                    if t_first {
-                        write!(f, "{:?}", typath)?;
-                        t_first = false;
+                for (i, ty) in seg.generics.iter().enumerate() {
+                    if i == 0 {
+                        write!(f, "{:?}", ty)?;
                     } else {
-                        write!(f, ",{:?}", typath)?
+                        write!(f, ",{:?}", ty)?
                     }
                 }
                 write!(f, ">")?;
@@ -374,15 +389,13 @@ impl fmt::Display for Path {
                 write!(f, "::{}", seg.name)?;
             }
 
-            if !seg.types.is_empty() {
+            if !seg.generics.is_empty() {
                 write!(f, "<")?;
-                let mut t_first = true;
-                for typath in &seg.types {
-                    if t_first {
-                        write!(f, "{}", typath)?;
-                        t_first = false;
+                for (i, ty) in seg.generics.iter().enumerate() {
+                    if i == 0 {
+                        write!(f, "{}", ty)?;
                     } else {
-                        write!(f, ", {}", typath)?
+                        write!(f, ", {}", ty)?
                     }
                 }
                 write!(f, ">")?;
@@ -395,12 +408,12 @@ impl fmt::Display for Path {
 #[derive(Debug, Clone, PartialEq)]
 pub struct PathSegment {
     pub name: String,
-    pub types: Vec<Path>,
+    pub generics: Vec<Ty>,
 }
 
 impl PathSegment {
-    pub fn new(name: String, types: Vec<Path>) -> Self {
-        PathSegment { name, types }
+    pub fn new(name: String, generics: Vec<Ty>) -> Self {
+        PathSegment { name, generics }
     }
 }
 
@@ -408,7 +421,7 @@ impl From<String> for PathSegment {
     fn from(name: String) -> Self {
         PathSegment {
             name,
-            types: Vec::new(),
+            generics: Vec::new(),
         }
     }
 }
@@ -422,7 +435,15 @@ pub struct PathSearch {
 }
 
 impl PathSearch {
-    pub(crate) fn resolve(&self, session: &Session) -> Option<Match> {
+    pub fn new(path: Path, scope: Scope) -> Self {
+        let Scope { filepath, point } = scope;
+        PathSearch {
+            path,
+            filepath,
+            point,
+        }
+    }
+    pub(crate) fn resolve_as_ty(&self, session: &Session) -> Option<Match> {
         if let Some(Ty::Match(m)) = find_type_match(&self.path, &self.filepath, self.point, session)
         {
             Some(m)
@@ -494,12 +515,12 @@ impl TraitBounds {
                 if let GenericBound::Trait(ref ptrait_ref, _) = *bound {
                     let ast_path = &ptrait_ref.trait_ref.path;
                     let source_map::BytePos(point) = ast_path.span.lo();
-                    let path = Path::from_ast(&ast_path);
-                    let path_search = PathSearch {
-                        path: path,
-                        filepath: filepath.as_ref().to_path_buf(),
-                        point: BytePos::from((point as i32 + offset) as u32),
-                    };
+                    let scope = Scope::new(
+                        filepath.as_ref().to_path_buf(),
+                        BytePos::from((point as i32 + offset) as u32),
+                    );
+                    let path = Path::from_ast(&ast_path, &scope);
+                    let path_search = PathSearch::new(path, scope);
                     Some(path_search)
                 } else {
                     None
@@ -510,8 +531,8 @@ impl TraitBounds {
     fn extend(&mut self, other: Self) {
         self.0.extend(other.0)
     }
-    fn to_paths(&self) -> Vec<Path> {
-        self.0.iter().map(|paths| paths.path.clone()).collect()
+    fn paths(&self) -> impl Iterator<Item = &Path> {
+        self.0.iter().map(|paths| &paths.path)
     }
 }
 
@@ -530,7 +551,7 @@ pub struct TypeParameter {
     /// bounds
     pub bounds: TraitBounds,
     /// Resolved Type
-    pub resolved: Option<PathSearch>,
+    pub resolved: Option<Ty>,
 }
 
 impl TypeParameter {
@@ -550,19 +571,12 @@ impl TypeParameter {
             docs: String::new(),
         })
     }
-    pub(crate) fn resolve(&mut self, paths: PathSearch) {
-        self.resolved = Some(paths);
+    pub(crate) fn resolve(&mut self, ty: Ty) {
+        self.resolved = Some(ty);
     }
-    pub(crate) fn resolved(&self) -> Option<&PathSearch> {
+    pub(crate) fn resolved(&self) -> Option<&Ty> {
         self.resolved.as_ref()
     }
-    pub fn to_racer_path(&self) -> Path {
-        let segment = PathSegment {
-            name: self.name.clone(),
-            types: self.bounds.to_paths(),
-        };
-        Path::single(segment)
-    }
 }
 
 /// List of Args in generics, e.g. <T: Clone, U, P>
@@ -677,8 +691,11 @@ impl ImplHeader {
         block_start: BytePos,
     ) -> Option<Self> {
         let generics = GenericsArgs::from_generics(generics, path, offset.0 as i32);
-        let self_path = destruct_ref_ptr(&self_type.node).map(Path::from_ast)?;
-        let trait_path = otrait.as_ref().map(|tref| Path::from_ast(&tref.path));
+        let scope = Scope::new(path.to_owned(), impl_start);
+        let self_path = destruct_ref_ptr(&self_type.node).map(|p| Path::from_ast(p, &scope))?;
+        let trait_path = otrait
+            .as_ref()
+            .map(|tref| Path::from_ast(&tref.path, &scope));
         Some(ImplHeader {
             self_path,
             trait_path,
diff --git a/src/racer/core.rs b/src/racer/core.rs
index e10b6bd3..4a0dc72f 100644
--- a/src/racer/core.rs
+++ b/src/racer/core.rs
@@ -1,4 +1,4 @@
-use ast_types::{GenericsArgs, ImplHeader, Path, PathSearch, TraitBounds, Ty, TypeParameter};
+use ast_types::{GenericsArgs, ImplHeader, Path, TraitBounds, Ty, TypeParameter};
 use codecleaner;
 use codeiter::StmtIndicesIter;
 use matchers::ImportInfo;
@@ -306,7 +306,7 @@ impl Match {
         };
         opt.into_iter().flat_map(|gen_arg| gen_arg.args())
     }
-    pub(crate) fn resolved_generics(&self) -> impl Iterator<Item = &PathSearch> {
+    pub(crate) fn resolved_generics(&self) -> impl Iterator<Item = &Ty> {
         let opt = match self.mtype {
             MatchType::Struct(ref gen_arg) | MatchType::Enum(ref gen_arg) => Some(&**gen_arg),
             MatchType::Method(ref header) => Some(header.generics()),
@@ -316,6 +316,7 @@ impl Match {
             .flat_map(|gen_arg| gen_arg.args())
             .filter_map(|ty_param| ty_param.resolved.as_ref())
     }
+    // currently we can't resolve method's type parameter
     pub(crate) fn generics_mut(&mut self) -> impl Iterator<Item = &mut TypeParameter> {
         let opt = match &mut self.mtype {
             MatchType::Struct(gen_arg) | MatchType::Enum(gen_arg) => Some(&mut **gen_arg),
@@ -396,6 +397,13 @@ pub struct Scope {
 }
 
 impl Scope {
+    pub fn new(path: path::PathBuf, pos: BytePos) -> Self {
+        Scope {
+            filepath: path,
+            point: pos,
+        }
+    }
+
     pub fn from_match(m: &Match) -> Scope {
         Scope {
             filepath: m.filepath.clone(),
@@ -1115,9 +1123,14 @@ 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 {
-                complete_field_for_ty(ty, searchstr, SearchType::StartsWith, session, &mut out);
+                out.extend(nameres::get_field_matches_from_ty(
+                    ty,
+                    searchstr,
+                    SearchType::StartsWith,
+                    session,
+                ));
             }
         }
     }
@@ -1125,26 +1138,6 @@ fn complete_from_file_(filepath: &path::Path, cursor: Location, session: &Sessio
     out
 }
 
-fn complete_field_for_ty(
-    ty: Ty,
-    searchstr: &str,
-    stype: SearchType,
-    session: &Session,
-    out: &mut Vec<Match>,
-) {
-    // TODO would be nice if this and other methods could operate on a ref instead of requiring
-    // ownership
-    match ty {
-        Ty::Match(m) => {
-            for m in nameres::search_for_field_or_method(m, searchstr, stype, session) {
-                out.push(m)
-            }
-        }
-        Ty::RefPtr(m) => complete_field_for_ty(*m, searchstr, stype, session, out),
-        _ => {}
-    }
-}
-
 /// Find the definition for item at given a file, source, and cursor index
 ///
 /// # Examples
@@ -1266,26 +1259,12 @@ pub fn find_definition_(
         CompletionType::Field => {
             let context = ast::get_type_of(contextstr.to_owned(), filepath, pos, session);
             debug!("context is {:?}", context);
-
-            let match_type: MatchType = if src[range.end.0..].starts_with('(') {
-                MatchType::Function
-            } else {
-                MatchType::StructField
-            };
+            let only_method = src[range.end.0..].starts_with('(');
             context.and_then(|ty| {
-                // for now, just handle matches
-                if let Ty::Match(m) = ty {
-                    nameres::search_for_field_or_method(
-                        m,
-                        searchstr,
-                        SearchType::ExactMatch,
-                        session,
-                    ).into_iter()
-                    .filter(|m| m.mtype == match_type)
-                    .nth(0)
-                } else {
-                    None
-                }
+                nameres::get_field_matches_from_ty(ty, searchstr, SearchType::ExactMatch, session)
+                    .into_iter()
+                    .filter(|m| !only_method || m.mtype.is_function())
+                    .next()
             })
         }
     }
diff --git a/src/racer/matchers.rs b/src/racer/matchers.rs
index 38225eb6..40c9f527 100644
--- a/src/racer/matchers.rs
+++ b/src/racer/matchers.rs
@@ -652,10 +652,9 @@ pub fn match_use(
                     None
                 };
                 let mut search_path = path_alias.path;
-                search_path.segments.push(PathSegment {
-                    name: context.search_str.to_owned(),
-                    types: vec![],
-                });
+                search_path
+                    .segments
+                    .push(PathSegment::new(context.search_str.to_owned(), vec![]));
                 let mut path_iter = resolve_path(
                     &search_path,
                     context.filepath,
diff --git a/src/racer/nameres.rs b/src/racer/nameres.rs
index e73f6420..6a845f89 100644
--- a/src/racer/nameres.rs
+++ b/src/racer/nameres.rs
@@ -8,7 +8,7 @@ use std::{self, vec};
 use ast_types::{ImplHeader, Path as RacerPath, PathPrefix, PathSegment, Ty};
 use core::Namespace;
 use core::SearchType::{self, ExactMatch, StartsWith};
-use core::{BytePos, ByteRange, Coordinate, Match, MatchType, Session, SessionExt, Src};
+use core::{BytePos, ByteRange, Coordinate, Match, MatchType, Scope, Session, SessionExt, Src};
 use fileres::{get_crate_file, get_module_file, get_std_file};
 use matchers::{find_doc, ImportInfo, MatchCxt};
 use util::{
@@ -256,10 +256,11 @@ fn search_scope_for_impled_assoc_types(
     let src = session.load_source_file(header.file_path());
     let scope_src = src.as_src().shift_start(header.scope_start());
     let mut out = vec![];
+    let scope = Scope::new(header.file_path().to_owned(), header.scope_start());
     for blob_range in scope_src.iter_stmts() {
         let blob = &scope_src[blob_range.to_range()];
         if blob.starts_with("type") {
-            let ast::TypeVisitor { name, type_ } = ast::parse_type(blob.to_owned());
+            let ast::TypeVisitor { name, type_, .. } = ast::parse_type(blob.to_owned(), &scope);
             let name = try_continue!(name);
             let type_ = try_continue!(type_);
             match search_type {
@@ -721,12 +722,11 @@ fn search_fn_args(
     local: bool,
 ) -> vec::IntoIter<Match> {
     let mut out = Vec::new();
-    let mut fndecl = String::new();
     // wrap in 'impl blah {}' so that methods get parsed correctly too
-    fndecl.push_str("impl blah {");
+    let mut fndecl = "impl blah {".to_owned();
     let impl_header_len = fndecl.len();
-    fndecl.push_str(&msrc[fnstart.0..open_brace_pos.increment().0]);
-    fndecl.push_str("}}");
+    fndecl += &msrc[fnstart.0..open_brace_pos.increment().0];
+    fndecl += "}}";
     debug!(
         "search_fn_args: found start of fn!! {:?} |{}| {}",
         fnstart, fndecl, searchstr
@@ -1805,10 +1805,7 @@ pub fn resolve_path(
                 if searchstr.starts_with('{') {
                     searchstr = &searchstr[1..];
                 }
-                let pathseg = PathSegment {
-                    name: searchstr.to_owned(),
-                    types: Vec::new(),
-                };
+                let pathseg = PathSegment::new(searchstr.to_owned(), vec![]);
                 debug!(
                     "searching a module '{}' for {} (whole path: {:?})",
                     m.matchstr, pathseg.name, path
@@ -1952,10 +1949,7 @@ pub fn do_external_search(
     if path.len() == 1 {
         let searchstr = path[0];
         // hack for now
-        let pathseg = PathSegment {
-            name: searchstr.to_owned(),
-            types: Vec::new(),
-        };
+        let pathseg = PathSegment::new(path[0].to_owned(), vec![]);
         out.extend(search_next_scope(
             pos,
             &pathseg,
@@ -2000,10 +1994,7 @@ pub fn do_external_search(
                         Some('{') => &path[path.len() - 1][1..],
                         _ => path[path.len() - 1],
                     };
-                    let pathseg = PathSegment {
-                        name: searchstr.to_owned(),
-                        types: Vec::new(),
-                    };
+                    let pathseg = PathSegment::new(searchstr.to_owned(), vec![]);
                     for m in search_next_scope(
                         m.point,
                         &pathseg,
@@ -2028,10 +2019,7 @@ pub fn do_external_search(
                             Some('{') => &path[path.len() - 1][1..],
                             _ => path[path.len() - 1],
                         };
-                        let pathseg = PathSegment {
-                            name: searchstr.to_owned(),
-                            types: Vec::new(),
-                        };
+                        let pathseg = PathSegment::new(searchstr.to_owned(), vec![]);
                         debug!("about to search impl scope...");
                         for m in search_next_scope(
                             impl_header.impl_start(),
@@ -2201,12 +2189,30 @@ fn search_for_deref_matches(
     fieldsearchstr: &str,
     session: &Session,
 ) -> Vec<Match> {
-    get_assoc_type_from_header(target_path, type_match, impl_header, session).map_or_else(
-        Vec::new,
-        |ty_match| {
-            search_for_field_or_method(ty_match, fieldsearchstr, SearchType::StartsWith, session)
-        },
-    )
+    get_assoc_type_from_header(target_path, type_match, impl_header, session)
+        .map_or_else(Vec::new, |ty| {
+            get_field_matches_from_ty(ty, fieldsearchstr, SearchType::StartsWith, session)
+        })
+}
+
+pub(crate) fn get_field_matches_from_ty(
+    ty: Ty,
+    searchstr: &str,
+    stype: SearchType,
+    session: &Session,
+) -> Vec<Match> {
+    match ty {
+        Ty::Match(m) => search_for_field_or_method(m, searchstr, stype, session),
+        Ty::PathSearch(paths) => paths.resolve_as_ty(session).map_or_else(Vec::new, |m| {
+            search_for_field_or_method(m, searchstr, stype, session)
+        }),
+        Ty::Tuple(v) => get_tuple_field_matches(v.len(), searchstr, stype).collect(),
+        Ty::RefPtr(ty) => {
+            // TODO(kngwyu): support impl &Type {..}
+            get_field_matches_from_ty(*ty, searchstr, stype, session)
+        }
+        _ => vec![],
+    }
 }
 
 fn get_assoc_type_from_header(
@@ -2214,34 +2220,27 @@ fn get_assoc_type_from_header(
     type_match: &Match,      // the type which implements Deref
     impl_header: &ImplHeader,
     session: &Session,
-) -> Option<Match> {
+) -> Option<Ty> {
     debug!(
         "[search_for_deref_matches] target: {:?} impl: {:?}",
         target_path, impl_header
     );
     if let Some((pos, _)) = impl_header.generics().search_param_by_path(target_path) {
-        if let Some(path_search) = type_match.resolved_generics().nth(pos) {
-            // TODO(kngwyu): does it work for nested generics?
-            return resolve_path_with_str(
-                &path_search.path,
-                &path_search.filepath,
-                BytePos::ZERO,
-                SearchType::ExactMatch,
-                Namespace::Type,
-                session,
-            ).nth(0);
-        }
+        type_match
+            .resolved_generics()
+            .nth(pos)
+            .map(|x| x.to_owned())
     } else {
-        return resolve_path_with_str(
+        resolve_path_with_str(
             &target_path,
             impl_header.file_path(),
             BytePos::ZERO,
             SearchType::ExactMatch,
             Namespace::Type,
             session,
-        ).nth(0);
+        ).nth(0)
+        .map(Ty::Match)
     }
-    None
 }
 
 fn get_std_macros(
@@ -2362,7 +2361,7 @@ fn get_std_macros_(
     }
 }
 
-pub(crate) fn get_intoiter_target(selfm: &Match, session: &Session) -> Option<Match> {
+pub(crate) fn get_iter_item(selfm: &Match, session: &Session) -> Option<Ty> {
     let iter_header = search_trait_impls(
         selfm.point,
         &selfm.matchstr,
@@ -2383,3 +2382,26 @@ pub(crate) fn get_intoiter_target(selfm: &Match, session: &Session) -> Option<Ma
         get_assoc_type_from_header(item_path, selfm, &iter_header, session)
     })
 }
+
+pub(crate) fn get_tuple_field_matches<'a>(
+    fields: usize,
+    search_str: &'a str,
+    search_type: SearchType,
+) -> impl 'a + Iterator<Item = Match> {
+    util::gen_tuple_fields(fields).filter_map(move |field| {
+        if txt_matches(search_type, search_str, field) {
+            Some(Match {
+                matchstr: field.to_owned(),
+                filepath: PathBuf::from("foo"),
+                point: BytePos::ZERO,
+                coords: None,
+                local: false,
+                mtype: MatchType::StructField,
+                contextstr: String::new(),
+                docs: String::new(),
+            })
+        } else {
+            None
+        }
+    })
+}
diff --git a/src/racer/typeinf.rs b/src/racer/typeinf.rs
index 5cc0c9fb..6b57c531 100644
--- a/src/racer/typeinf.rs
+++ b/src/racer/typeinf.rs
@@ -247,7 +247,7 @@ fn get_type_of_let_block_expr(m: &Match, msrc: Src, session: &Session, prefix: &
     }
 }
 
-fn get_type_of_for_arg(m: &Match, msrc: Src, session: &Session) -> Option<Ty> {
+fn get_type_of_for_arg(m: &Match, session: &Session) -> Option<Ty> {
     if m.mtype != core::MatchType::For {
         warn!("[get_type_of_for_expr] invalid match type: {:?}", m.mtype);
         return None;
@@ -259,9 +259,9 @@ fn get_type_of_for_arg(m: &Match, msrc: Src, session: &Session) -> Option<Ty> {
     );
     fn get_item(ty: Ty, session: &Session) -> Option<Ty> {
         match ty {
-            Ty::Match(ma) => nameres::get_intoiter_target(&ma, session).map(Ty::Match),
+            Ty::Match(ma) => nameres::get_iter_item(&ma, session),
             Ty::PathSearch(paths) => {
-                nameres::get_intoiter_target(&paths.resolve(session)?, session).map(Ty::Match)
+                nameres::get_iter_item(&paths.resolve_as_ty(session)?, session)
             }
             _ => None,
         }
@@ -368,7 +368,7 @@ pub fn get_type_of_match(m: Match, msrc: Src, session: &Session) -> Option<Ty> {
         core::MatchType::Let => get_type_of_let_expr(&m, msrc, session),
         core::MatchType::IfLet => get_type_of_let_block_expr(&m, msrc, session, "if let"),
         core::MatchType::WhileLet => get_type_of_let_block_expr(&m, msrc, session, "while let"),
-        core::MatchType::For => get_type_of_for_arg(&m, msrc, session),
+        core::MatchType::For => get_type_of_for_arg(&m, session),
         core::MatchType::FnArg => get_type_of_fnarg(&m, msrc, session),
         core::MatchType::MatchArm => get_type_from_match_arm(&m, msrc, session),
         core::MatchType::Struct(_)
@@ -468,7 +468,7 @@ pub fn get_return_type_of_function(
             if "Self" == path_seg.name {
                 return get_type_of_self_arg(fnmatch, src.as_src(), session);
             }
-            if path.segments.len() == 1 && path_seg.types.is_empty() {
+            if path.segments.len() == 1 && path_seg.generics.is_empty() {
                 for type_param in fnmatch.generics() {
                     if type_param.name() == &path_seg.name {
                         return Some(Ty::Match(contextm.clone()));
diff --git a/src/racer/util.rs b/src/racer/util.rs
index 5f83fad6..21415969 100644
--- a/src/racer/util.rs
+++ b/src/racer/util.rs
@@ -733,3 +733,10 @@ macro_rules! try_continue {
         }
     };
 }
+
+pub(crate) fn gen_tuple_fields(u: usize) -> impl Iterator<Item = &'static str> {
+    const NUM: [&'static str; 16] = [
+        "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15",
+    ];
+    NUM.into_iter().take(::std::cmp::min(u, 16)).map(|x| *x)
+}


Test patch:
diff --git a/tests/system.rs b/tests/system.rs
index d0a9ee74..eccdfa94 100644
--- a/tests/system.rs
+++ b/tests/system.rs
@@ -2504,7 +2504,6 @@ fn finds_method_with_same_name_as_field() {
     let a: Foo;
     a.same_na~me();
     ";
-
     let got = get_definition(src, None);
     assert_eq!("same_name", got.matchstr);
     assert_eq!(MatchType::Function, got.mtype);