blob: 959c781ce93084b6891295f5e43a88a92efba099 [file] [log] [blame]
use ast_types::Path as RacerPath;
use ast_types::{self, GenericsArgs, ImplHeader, Pat, PathAlias, PathAliasKind, TraitBounds, Ty};
use core::{self, BytePos, ByteRange, Match, MatchType, Scope, Session, SessionExt};
use nameres;
use typeinf;
use std::path::Path;
use std::rc::Rc;
use syntax::ast::{self, ExprKind, FunctionRetTy, ItemKind, PatKind, UseTree, UseTreeKind};
use syntax::errors::{emitter::Emitter, DiagnosticBuilder, Handler};
use syntax::parse::parser::Parser;
use syntax::parse::{self, ParseSess};
use syntax::source_map::{self, FileName, SourceMap, Span};
use syntax::{self, visit};
struct DummyEmitter;
impl Emitter for DummyEmitter {
fn emit(&mut self, _db: &DiagnosticBuilder<'_>) {}
fn should_show_explain(&self) -> bool {
false
}
}
/// construct parser from string
// From syntax/util/parser_testing.rs
pub fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser {
parse::new_parser_from_source_str(ps, FileName::Custom("racer-file".to_owned()), source_str)
}
/// Get parser from string s and then apply closure f to it
// TODO: use Result insated of Option
pub fn with_error_checking_parse<F, T>(s: String, f: F) -> Option<T>
where
F: FnOnce(&mut Parser) -> Option<T>,
{
syntax::with_globals(|| {
let codemap = Rc::new(SourceMap::new(source_map::FilePathMapping::empty()));
// We use DummyEmitter here not to print error messages to stderr
let handler = Handler::with_emitter(false, None, Box::new(DummyEmitter {}));
let parse_sess = ParseSess::with_span_handler(handler, codemap);
let mut p = string_to_parser(&parse_sess, s);
f(&mut p)
})
}
/// parse string source_str as statement and then apply f to it
/// return false if we can't parse s as statement
// TODO: make F FnOnce(&ast::Stmt) -> Result<Something, Error>
pub fn with_stmt<F>(source_str: String, f: F) -> bool
where
F: FnOnce(&ast::Stmt),
{
with_error_checking_parse(source_str, |p| {
let stmt = match p.parse_stmt() {
Ok(Some(stmt)) => stmt,
_ => return None,
};
f(&stmt);
Some(())
})
.is_some()
}
pub(crate) fn destruct_span(span: Span) -> (u32, u32) {
let source_map::BytePos(lo) = span.lo();
let source_map::BytePos(hi) = span.hi();
(lo, hi)
}
pub(crate) fn get_span_start(span: Span) -> u32 {
let source_map::BytePos(lo) = span.lo();
lo
}
/// collect paths from syntax::ast::UseTree
#[derive(Debug)]
pub struct UseVisitor {
pub path_list: Vec<PathAlias>,
pub contains_glob: bool,
}
impl<'ast> visit::Visitor<'ast> for UseVisitor {
fn visit_item(&mut self, i: &ast::Item) {
// collect items from use tree recursively
// returns (Paths, contains_glab)
fn collect_nested_items(
use_tree: &UseTree,
parent_path: Option<&ast_types::Path>,
) -> (Vec<PathAlias>, bool) {
let mut res = vec![];
let mut path = if let Some(parent) = parent_path {
let relative_path = RacerPath::from_ast_nogen(&use_tree.prefix);
let mut path = parent.clone();
path.extend(relative_path);
path
} else {
RacerPath::from_ast_nogen(&use_tree.prefix)
};
let mut contains_glob = false;
match use_tree.kind {
UseTreeKind::Simple(rename, _, _) => {
let ident = use_tree.ident().name.to_string();
let rename_pos: Option<BytePos> =
rename.map(|id| destruct_span(id.span).0.into());
let kind = if let Some(last_seg) = path.segments.last() {
//` self` is treated normaly in libsyntax,
// but we distinguish it here to make completion easy
if last_seg.name == "self" {
PathAliasKind::Self_(ident, rename_pos)
} else {
PathAliasKind::Ident(ident, rename_pos)
}
} else {
PathAliasKind::Ident(ident, rename_pos)
};
if let PathAliasKind::Self_(..) = kind {
path.segments.pop();
}
res.push(PathAlias {
kind,
path,
range: ByteRange::from(use_tree.span),
});
}
UseTreeKind::Nested(ref nested) => {
nested.iter().for_each(|(ref tree, _)| {
let (items, has_glob) = collect_nested_items(tree, Some(&path));
res.extend(items);
contains_glob |= has_glob;
});
}
UseTreeKind::Glob => {
res.push(PathAlias {
kind: PathAliasKind::Glob,
path,
range: ByteRange::from(use_tree.span),
});
contains_glob = true;
}
}
(res, contains_glob)
}
if let ItemKind::Use(ref use_tree) = i.node {
let (path_list, contains_glob) = collect_nested_items(use_tree, None);
self.path_list = path_list;
self.contains_glob = contains_glob;
}
}
}
pub struct PatBindVisitor {
ident_points: Vec<ByteRange>,
}
impl<'ast> visit::Visitor<'ast> for PatBindVisitor {
fn visit_local(&mut self, local: &ast::Local) {
// don't visit the RHS (init) side of the let stmt
self.visit_pat(&local.pat);
}
fn visit_expr(&mut self, ex: &ast::Expr) {
// don't visit the RHS or block of an 'if let' or 'for' stmt
match ex.node {
ExprKind::IfLet(ref pat, ..) | ExprKind::WhileLet(ref pat, ..) => {
pat.iter().for_each(|pat| self.visit_pat(pat))
}
ExprKind::ForLoop(ref pat, ..) => self.visit_pat(pat),
_ => visit::walk_expr(self, ex),
}
}
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);
}
}
}
}
pub struct PatVisitor {
ident_points: Vec<ByteRange>,
}
impl<'ast> visit::Visitor<'ast> for PatVisitor {
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);
}
}
}
}
pub struct FnArgVisitor {
idents: Vec<(Pat, Option<Ty>, ByteRange)>,
generics: GenericsArgs,
scope: Scope,
offset: i32,
}
impl<'ast> visit::Visitor<'ast> for FnArgVisitor {
fn visit_fn(
&mut self,
_fk: visit::FnKind,
fd: &ast::FnDecl,
_: source_map::Span,
_: ast::NodeId,
) {
debug!("[FnArgVisitor::visit_fn] inputs: {:?}", fd.inputs);
self.idents = fd
.inputs
.iter()
.map(|arg| {
debug!("[FnArgTypeVisitor::visit_fn] type {:?} was found", arg.ty);
let pat = Pat::from_ast(&arg.pat.node, &self.scope);
let ty = Ty::from_ast(&arg.ty, &self.scope);
let source_map::BytePos(lo) = arg.pat.span.lo();
let source_map::BytePos(hi) = arg.ty.span.hi();
(pat, ty, ByteRange::new(lo, hi))
})
.collect();
}
fn visit_generics(&mut self, g: &'ast ast::Generics) {
let generics = GenericsArgs::from_generics(g, &self.scope.filepath, 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);
point >= lo && point < hi
}
// The point must point to an ident within the pattern.
fn destructure_pattern_to_ty(
pat: &ast::Pat,
point: BytePos,
ty: &Ty,
scope: &Scope,
session: &Session,
) -> Option<Ty> {
debug!(
"destructure_pattern_to_ty point {:?} ty {:?} pat: {:?}",
point, ty, pat.node
);
match pat.node {
PatKind::Ident(_, ref spannedident, _) => {
if point_is_in_span(point, &spannedident.span) {
debug!("destructure_pattern_to_ty matched an ident!");
Some(ty.clone())
} else {
panic!(
"Expecting the point to be in the patident span. pt: {:?}",
point
);
}
}
PatKind::Tuple(ref tuple_elements, _) => match *ty {
Ty::Tuple(ref typeelems) => {
let mut res = None;
for (i, p) in tuple_elements.iter().enumerate() {
if !point_is_in_span(point, &p.span) {
continue;
}
if let Some(ref ty) = typeelems[i] {
res = destructure_pattern_to_ty(p, point, ty, scope, session);
break;
}
}
res
}
_ => panic!("Expecting TyTuple"),
},
PatKind::TupleStruct(ref path, ref children, _) => {
let m = resolve_ast_path(path, &scope.filepath, scope.point, session);
let contextty = path_to_match(ty.clone(), session);
if let Some(m) = m {
let mut res = None;
for (i, p) in children.iter().enumerate() {
if point_is_in_span(point, &p.span) {
res = typeinf::get_tuplestruct_field_type(i, &m, session)
.and_then(|ty|
// if context ty is a match, use its generics
if let Some(Ty::Match(ref contextm)) = contextty {
path_to_match_including_generics(ty, contextm.to_generics(), session)
} else {
path_to_match(ty, session)
})
.and_then(|ty| destructure_pattern_to_ty(p, point, &ty, scope, session));
break;
}
}
res
} else {
None
}
}
PatKind::Struct(ref path, ref children, _) => {
let m = resolve_ast_path(path, &scope.filepath, scope.point, session);
let contextty = path_to_match(ty.clone(), session);
if let Some(m) = m {
let mut res = None;
for child in children {
if point_is_in_span(point, &child.span) {
res = typeinf::get_struct_field_type(
&child.node.ident.name.as_str(),
&m,
session,
)
.and_then(|ty| {
if let Some(Ty::Match(ref contextm)) = contextty {
path_to_match_including_generics(
ty,
contextm.to_generics(),
session,
)
} else {
path_to_match(ty, session)
}
})
.and_then(|ty| {
destructure_pattern_to_ty(&child.node.pat, point, &ty, scope, session)
});
break;
}
}
res
} else {
None
}
}
_ => {
debug!("Could not destructure pattern {:?}", pat);
None
}
}
}
struct LetTypeVisitor<'c: 's, 's> {
scope: Scope,
session: &'s Session<'c>,
pos: BytePos, // pos is relative to the srctxt, scope is global
result: Option<Ty>,
}
impl<'c, 's, 'ast> visit::Visitor<'ast> for LetTypeVisitor<'c, 's> {
fn visit_local(&mut self, local: &ast::Local) {
let ty = match &local.ty {
Some(annon) => Ty::from_ast(&*annon, &self.scope),
None => local.init.as_ref().and_then(|initexpr| {
debug!("[LetTypeVisitor] initexpr is {:?}", initexpr.node);
let mut v = ExprTypeVisitor::new(self.scope.clone(), self.session);
v.visit_expr(initexpr);
v.result
}),
};
debug!("[LetTypeVisitor] ty is {:?}. pos is {:?}", ty, self.pos);
self.result = ty
.and_then(|ty| {
destructure_pattern_to_ty(&local.pat, self.pos, &ty, &self.scope, self.session)
})
.and_then(|ty| path_to_match(ty, self.session));
}
}
struct MatchTypeVisitor<'c: 's, 's> {
scope: Scope,
session: &'s Session<'c>,
pos: BytePos, // pos is relative to the srctxt, scope is global
result: Option<Ty>,
}
impl<'c, 's, 'ast> visit::Visitor<'ast> for MatchTypeVisitor<'c, 's> {
fn visit_expr(&mut self, ex: &ast::Expr) {
if let ExprKind::Match(ref subexpression, ref arms) = ex.node {
debug!("PHIL sub expr is {:?}", subexpression);
let mut v = ExprTypeVisitor::new(self.scope.clone(), self.session);
v.visit_expr(subexpression);
debug!("PHIL sub type is {:?}", v.result);
for arm in arms {
for pattern in &arm.pats {
if point_is_in_span(self.pos, &pattern.span) {
debug!("PHIL point is in pattern |{:?}|", pattern);
self.result = v
.result
.as_ref()
.and_then(|ty| {
destructure_pattern_to_ty(
pattern,
self.pos,
ty,
&self.scope,
self.session,
)
})
.and_then(|ty| path_to_match(ty, self.session));
}
}
}
}
}
}
fn resolve_ast_path(
path: &ast::Path,
filepath: &Path,
pos: BytePos,
session: &Session,
) -> Option<Match> {
let scope = Scope::new(filepath.to_owned(), pos);
let path = RacerPath::from_ast(path, &scope);
debug!("resolve_ast_path {:?} {:?}", path, scope);
nameres::resolve_path_with_primitive(
&path,
filepath,
pos,
core::SearchType::ExactMatch,
core::Namespace::Path,
session,
)
.into_iter()
.nth(0)
}
fn path_to_match(ty: Ty, session: &Session) -> Option<Ty> {
match ty {
Ty::PathSearch(paths) => {
find_type_match(&paths.path, &paths.filepath, paths.point, session).map(Ty::Match)
}
Ty::RefPtr(ty, _) => path_to_match(*ty, session),
_ => Some(ty),
}
}
pub(crate) fn find_type_match(
path: &RacerPath,
fpath: &Path,
pos: BytePos,
session: &Session,
) -> Option<Match> {
debug!("find_type_match {:?}, {:?}", path, fpath);
let mut res = nameres::resolve_path_with_primitive(
path,
fpath,
pos,
core::SearchType::ExactMatch,
core::Namespace::Type,
session,
)
.into_iter()
.nth(0)
.and_then(|m| match m.mtype {
MatchType::Type => typeinf::get_type_of_typedef(&m, session),
_ => Some(m),
})?;
// TODO: 'Type' support
// if res is Enum/Struct and has a generic type paramter, let's resolve it.
for (param, typ) in res.generics_mut().zip(path.generic_types()) {
param.resolve(typ.to_owned());
}
Some(res)
}
struct ExprTypeVisitor<'c: 's, 's> {
scope: Scope,
session: &'s Session<'c>,
// what we have before calling typeinf::get_type_of_match
path_match: Option<Match>,
result: Option<Ty>,
}
impl<'c: 's, 's> ExprTypeVisitor<'c, 's> {
fn new(scope: Scope, session: &'s Session<'c>) -> Self {
ExprTypeVisitor {
scope,
session,
path_match: None,
result: None,
}
}
fn same_scope(&self) -> Self {
Self {
scope: self.scope.clone(),
session: self.session,
path_match: None,
result: None,
}
}
}
impl<'c, 's, 'ast> visit::Visitor<'ast> for ExprTypeVisitor<'c, 's> {
fn visit_expr(&mut self, expr: &ast::Expr) {
debug!(
"ExprTypeVisitor::visit_expr {:?}(kind: {:?})",
expr, expr.node
);
//walk_expr(self, ex, e)
match expr.node {
ExprKind::Unary(_, ref expr) | ExprKind::AddrOf(_, ref expr) => {
self.visit_expr(expr);
}
ExprKind::Path(_, ref path) => {
let source_map::BytePos(lo) = path.span.lo();
self.result = resolve_ast_path(
path,
&self.scope.filepath,
self.scope.point + lo.into(),
self.session,
)
.and_then(|m| {
let msrc = self.session.load_source_file(&m.filepath);
self.path_match = Some(m.clone());
typeinf::get_type_of_match(m, msrc.as_src(), self.session)
});
}
ExprKind::Call(ref callee_expression, ref caller_expr) => {
self.visit_expr(callee_expression);
self.result = self.result.take().and_then(|m| {
if let Ty::Match(mut m) = m {
match m.mtype {
MatchType::Function => {
typeinf::get_return_type_of_function(&m, &m, self.session)
.and_then(|ty| path_to_match(ty, self.session))
}
MatchType::Method(ref gen) => {
typeinf::get_return_type_of_function(&m, &m, self.session).and_then(
|ty| {
path_to_match_including_generics(
ty,
gen.as_ref().map(AsRef::as_ref),
self.session,
)
},
)
}
// if we find tuple struct / enum variant, try to resolve its generics name
MatchType::Struct(ref mut gen) | MatchType::Enum(ref mut gen) => {
if gen.is_empty() {
return Some(Ty::Match(m));
}
let tuple_fields = match self.path_match {
Some(ref m) => typeinf::get_tuplestruct_fields(m, self.session),
None => return Some(Ty::Match(m)),
};
// search what is in callee e.g. Some(String::new()<-) for generics
for ((_, _, ty), expr) in tuple_fields.into_iter().zip(caller_expr)
{
let ty = try_continue!(ty).dereference();
if let Ty::PathSearch(paths) = ty {
let (id, _) =
try_continue!(gen.search_param_by_path(&paths.path));
let mut visitor = self.same_scope();
visitor.visit_expr(expr);
if let Some(ty) = visitor.result {
gen.0[id].resolve(ty.dereference());
}
}
}
Some(Ty::Match(m))
}
MatchType::TypeParameter(ref traitbounds)
if traitbounds.has_closure() =>
{
let mut output = None;
if let Some(path_search) = traitbounds.get_closure() {
for seg in path_search.path.segments.iter() {
if seg.output.is_some() {
output = seg.output.clone();
break;
}
}
}
output
}
_ => {
debug!(
"ExprTypeVisitor: Cannot handle ExprCall of {:?} type",
m.mtype
);
None
}
}
} else {
None
}
});
}
ExprKind::Struct(ref path, _, _) => {
let pathvec = RacerPath::from_ast(path, &self.scope);
self.result = find_type_match(
&pathvec,
&self.scope.filepath,
self.scope.point,
self.session,
)
.map(Ty::Match);
}
ExprKind::MethodCall(ref method_def, ref arguments) => {
let methodname = method_def.ident.name.as_str();
debug!("method call ast name {}", methodname);
// arguments[0] is receiver(e.g. self)
let objexpr = &arguments[0];
self.visit_expr(objexpr);
let result = self.result.take();
let get_method_output_ty = |contextm: Match| {
let matching_methods = nameres::search_for_fields_and_methods(
contextm.clone(),
&methodname,
core::SearchType::ExactMatch,
true,
self.session,
);
matching_methods
.into_iter()
.filter_map(|method| {
let ty = typeinf::get_return_type_of_function(
&method,
&contextm,
self.session,
)?;
path_to_match_including_generics(
ty,
contextm.to_generics(),
self.session,
)
})
.nth(0)
};
self.result = result.and_then(|ty| {
ty.resolve_as_field_match(self.session)
.and_then(get_method_output_ty)
});
}
ExprKind::Field(ref subexpression, spannedident) => {
let fieldname = spannedident.name.to_string();
debug!("exprfield {}", fieldname);
self.visit_expr(subexpression);
let result = self.result.take();
let match_to_field_ty = |structm: Match| {
typeinf::get_struct_field_type(&fieldname, &structm, self.session).and_then(
|fieldtypepath| {
find_type_match_including_generics(
fieldtypepath,
&structm.filepath,
structm.point,
&structm,
self.session,
)
},
)
};
self.result = result.and_then(|ty| {
ty.resolve_as_field_match(self.session)
.and_then(match_to_field_ty)
});
}
ExprKind::Tup(ref exprs) => {
let mut v = Vec::new();
for expr in exprs {
self.visit_expr(expr);
v.push(self.result.take());
}
self.result = Some(Ty::Tuple(v));
}
ExprKind::Lit(ref lit) => self.result = Ty::from_lit(lit),
ExprKind::Try(ref expr) => {
self.visit_expr(&expr);
debug!("ExprKind::Try result: {:?} expr: {:?}", self.result, expr);
self.result = if let Some(&Ty::Match(ref m)) = self.result.as_ref() {
// HACK for speed up (kngwyu)
// 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().map(|x| x.to_owned())
} else {
debug!("Unable to desugar Try expression; type was {:?}", m);
None
}
} else {
None
};
}
ExprKind::Match(_, ref arms) => {
debug!("match expr");
for arm in arms {
self.visit_expr(&arm.body);
// All match arms need to return the same result, so if we found a result
// we can end the search.
if self.result.is_some() {
break;
}
}
}
ExprKind::If(_, ref block, ref else_block)
| ExprKind::IfLet(_, _, ref block, ref else_block) => {
debug!("if/iflet expr");
if let Some(stmt) = block.stmts.last() {
visit::walk_stmt(self, stmt);
}
if self.result.is_some() {
return;
}
// if the block does not resolve to a type, try the else block
if let Some(expr) = else_block {
self.visit_expr(expr);
}
}
ExprKind::Block(ref block, ref _label) => {
debug!("block expr");
if let Some(stmt) = block.stmts.last() {
visit::walk_stmt(self, stmt);
}
}
ExprKind::Index(ref body, ref _index) => {
self.visit_expr(body);
// TODO(kngwyu) now we don't have support for literal so don't parse index
// but in the future, we should handle index's type
self.result = self
.result
.take()
.and_then(|ty| typeinf::get_type_of_indexed_value(ty, self.session));
}
ExprKind::Array(ref exprs) => {
for expr in exprs {
self.visit_expr(expr);
if self.result.is_some() {
self.result = self
.result
.take()
.map(|ty| Ty::Array(Box::new(ty), format!("{}", exprs.len())));
break;
}
}
if self.result.is_none() {
self.result = Some(Ty::Array(Box::new(Ty::Unsupported), String::new()));
}
}
ExprKind::Mac(ref m) => {
if let Some(name) = m.node.path.segments.last().map(|seg| seg.ident) {
// use some ad-hoc rules
if name.as_str() == "vec" {
let path = RacerPath::from_iter(
true,
["std", "vec", "Vec"].into_iter().map(|s| s.to_string()),
);
self.result = find_type_match(
&path,
&self.scope.filepath,
self.scope.point,
self.session,
)
.map(Ty::Match);
}
}
}
ExprKind::Binary(bin, ref left, ref right) => {
self.visit_expr(left);
let type_match = match self.result.take() {
Some(Ty::Match(m)) => m,
Some(Ty::PathSearch(ps)) => match ps.resolve_as_match(self.session) {
Some(m) => m,
_ => {
return;
}
},
_ => {
return;
}
};
self.visit_expr(right);
let right_expr_type = match self.result.take() {
Some(Ty::Match(m)) => Some(m.matchstr),
Some(Ty::PathSearch(ps)) => {
ps.resolve_as_match(self.session).map(|m| m.matchstr)
}
_ => None,
};
self.result = nameres::resolve_binary_expr_type(
&type_match,
bin.node,
right_expr_type.as_ref().map(|s| s.as_str()),
self.session,
);
}
_ => {
debug!("- Could not match expr node type: {:?}", expr.node);
}
};
}
fn visit_mac(&mut self, mac: &ast::Mac) {
// Just do nothing if we see a macro, but also prevent the panic! in the default impl.
debug!("ignoring visit_mac: {:?}", mac);
}
}
// gets generics info from the context match
fn path_to_match_including_generics(
mut ty: Ty,
generics: Option<&GenericsArgs>,
session: &Session,
) -> Option<Ty> {
if let Some(gen) = generics {
ty = ty.replace_by_generics(gen);
}
match ty {
Ty::PathSearch(paths) => {
let fieldtypepath = &paths.path;
find_type_match(&fieldtypepath, &paths.filepath, paths.point, session).map(Ty::Match)
}
_ => Some(ty),
}
}
fn find_type_match_including_generics(
fieldtype: Ty,
filepath: &Path,
pos: BytePos,
structm: &Match,
session: &Session,
) -> Option<Ty> {
assert_eq!(&structm.filepath, filepath);
let fieldtypepath = match fieldtype {
Ty::PathSearch(paths) => paths.path,
Ty::RefPtr(ty, _) => match ty.dereference() {
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)),
_ => {
return None;
}
};
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
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)
}
struct StructVisitor {
pub scope: Scope,
pub fields: Vec<(String, ByteRange, Option<Ty>)>,
}
impl<'ast> visit::Visitor<'ast> for StructVisitor {
fn visit_variant_data(
&mut self,
struct_definition: &ast::VariantData,
_: ast::Ident,
_: &ast::Generics,
_: ast::NodeId,
_: Span,
) {
for field in struct_definition.fields() {
let ty = Ty::from_ast(&field.ty, &self.scope);
let name = match field.ident {
Some(ref ident) => ident.to_string(),
// name unnamed field by its ordinal, since self.0 works
None => format!("{}", self.fields.len()),
};
self.fields.push((name, field.span.into(), ty));
}
}
}
#[derive(Debug)]
pub struct TypeVisitor<'s> {
pub name: Option<String>,
pub type_: Option<Ty>,
scope: &'s Scope,
}
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());
self.type_ = Ty::from_ast(&ty, self.scope);
debug!("typevisitor type is {:?}", self.type_);
}
}
}
pub struct TraitVisitor {
pub name: Option<String>,
}
impl<'ast> visit::Visitor<'ast> for TraitVisitor {
fn visit_item(&mut self, item: &ast::Item) {
if let ItemKind::Trait(..) = item.node {
self.name = Some(item.ident.name.to_string());
}
}
}
#[derive(Debug)]
pub struct ImplVisitor<'p> {
pub result: Option<ImplHeader>,
filepath: &'p Path,
offset: BytePos,
block_start: BytePos, // the point { appears
local: bool,
}
impl<'p> ImplVisitor<'p> {
fn new(filepath: &'p Path, offset: BytePos, local: bool, block_start: BytePos) -> Self {
ImplVisitor {
result: None,
filepath,
offset,
block_start,
local,
}
}
}
impl<'ast, 'p> visit::Visitor<'ast> for ImplVisitor<'p> {
fn visit_item(&mut self, item: &ast::Item) {
if let ItemKind::Impl(_, _, _, ref generics, ref otrait, ref self_typ, _) = item.node {
let impl_start = self.offset + get_span_start(item.span).into();
self.result = ImplHeader::new(
generics,
self.filepath,
otrait,
self_typ,
self.offset,
self.local,
impl_start,
self.block_start,
);
}
}
}
pub struct ExternCrateVisitor {
pub name: Option<String>,
pub realname: Option<String>,
}
impl<'ast> visit::Visitor<'ast> for ExternCrateVisitor {
fn visit_item(&mut self, item: &ast::Item) {
if let ItemKind::ExternCrate(ref optional_s) = item.node {
self.name = Some(item.ident.name.to_string());
if let Some(ref istr) = *optional_s {
self.realname = Some(istr.to_string());
}
}
}
}
#[derive(Debug)]
struct GenericsVisitor<P> {
result: GenericsArgs,
filepath: P,
}
impl<'ast, P: AsRef<Path>> visit::Visitor<'ast> for GenericsVisitor<P> {
fn visit_generics(&mut self, g: &ast::Generics) {
let path = &self.filepath;
if !self.result.0.is_empty() {
warn!("[visit_generics] called for multiple generics!");
}
self.result.extend(GenericsArgs::from_generics(g, path, 0));
}
}
pub struct EnumVisitor {
pub name: String,
pub values: Vec<(String, BytePos)>,
}
impl<'ast> visit::Visitor<'ast> for EnumVisitor {
fn visit_item(&mut self, i: &ast::Item) {
if let ItemKind::Enum(ref enum_definition, _) = i.node {
self.name = i.ident.name.to_string();
let (point1, point2) = destruct_span(i.span);
debug!("name point is {} {}", point1, point2);
for variant in &enum_definition.variants {
let source_map::BytePos(point) = variant.span.lo();
self.values
.push((variant.node.ident.to_string(), point.into()));
}
}
}
}
#[derive(Clone, Debug)]
pub struct StaticVisitor {
pub ty: Option<Ty>,
pub is_mutable: bool,
scope: Scope,
}
impl StaticVisitor {
fn new(scope: Scope) -> Self {
StaticVisitor {
ty: None,
is_mutable: false,
scope,
}
}
}
impl<'ast> visit::Visitor<'ast> for StaticVisitor {
fn visit_item(&mut self, i: &ast::Item) {
match i.node {
ItemKind::Const(ref ty, ref _expr) => self.ty = Ty::from_ast(ty, &self.scope),
ItemKind::Static(ref ty, m, ref _expr) => {
self.is_mutable = m == ast::Mutability::Mutable;
self.ty = Ty::from_ast(ty, &self.scope);
}
_ => {}
}
}
}
pub fn parse_use(s: String) -> UseVisitor {
let mut v = UseVisitor {
path_list: Vec::new(),
contains_glob: false,
};
// visit::walk_crate can be panic so we don't use it here
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}
pub fn parse_pat_bind_stmt(s: String) -> Vec<ByteRange> {
let mut v = PatBindVisitor {
ident_points: Vec::new(),
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.ident_points
}
pub fn parse_struct_fields(s: String, scope: Scope) -> Vec<(String, ByteRange, Option<Ty>)> {
let mut v = StructVisitor {
scope,
fields: Vec::new(),
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.fields
}
pub fn parse_impl(
s: String,
path: &Path,
offset: BytePos,
local: bool,
scope_start: BytePos,
) -> Option<ImplHeader> {
let mut v = ImplVisitor::new(path, offset, local, scope_start);
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
pub fn parse_trait(s: String) -> TraitVisitor {
let mut v = TraitVisitor { name: None };
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}
/// parse traits and collect inherited traits as TraitBounds
pub fn parse_inherited_traits<P: AsRef<Path>>(
s: String,
filepath: P,
offset: i32,
) -> Option<TraitBounds> {
let mut v = InheritedTraitsVisitor {
result: None,
file_path: filepath,
offset: offset,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
pub fn parse_generics(s: String, filepath: &Path) -> GenericsArgs {
let mut v = GenericsVisitor {
result: GenericsArgs::default(),
filepath: filepath,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
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
}
pub fn parse_fn_args_and_generics(
s: String,
scope: Scope,
offset: i32,
) -> (Vec<(Pat, Option<Ty>, ByteRange)>, GenericsArgs) {
let mut v = FnArgVisitor {
idents: Vec::new(),
generics: GenericsArgs::default(),
scope,
offset,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
(v.idents, v.generics)
}
pub fn parse_closure_args(s: String, scope: Scope) -> Vec<(Pat, Option<Ty>, ByteRange)> {
let mut v = FnArgVisitor {
idents: Vec::new(),
generics: GenericsArgs::default(),
scope,
offset: 0,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.idents
}
pub fn parse_pat_idents(s: String) -> Vec<ByteRange> {
let mut v = PatVisitor {
ident_points: Vec::new(),
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
debug!("ident points are {:?}", v.ident_points);
v.ident_points
}
pub fn parse_fn_output(s: String, scope: Scope) -> Option<Ty> {
let mut v = FnOutputVisitor {
result: None,
scope,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
pub fn parse_extern_crate(s: String) -> ExternCrateVisitor {
let mut v = ExternCrateVisitor {
name: None,
realname: None,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}
pub fn parse_enum(s: String) -> EnumVisitor {
let mut v = EnumVisitor {
name: String::new(),
values: Vec::new(),
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}
pub fn parse_static(s: String, scope: Scope) -> StaticVisitor {
let mut v = StaticVisitor::new(scope);
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}
pub fn get_type_of(s: String, fpath: &Path, pos: BytePos, session: &Session) -> Option<Ty> {
let startscope = Scope {
filepath: fpath.to_path_buf(),
point: pos,
};
let mut v = ExprTypeVisitor::new(startscope, session);
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
// pos points to an ident in the lhs of the stmtstr
pub fn get_let_type(s: String, pos: BytePos, scope: Scope, session: &Session) -> Option<Ty> {
let mut v = LetTypeVisitor {
scope,
session,
pos,
result: None,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
pub fn get_match_arm_type(s: String, pos: BytePos, scope: Scope, session: &Session) -> Option<Ty> {
let mut v = MatchTypeVisitor {
scope,
session,
pos,
result: None,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v.result
}
pub struct FnOutputVisitor {
scope: Scope,
pub result: Option<Ty>,
}
impl<'ast> visit::Visitor<'ast> for FnOutputVisitor {
fn visit_fn(
&mut self,
_: visit::FnKind,
fd: &ast::FnDecl,
_: source_map::Span,
_: ast::NodeId,
) {
self.result = match fd.output {
FunctionRetTy::Ty(ref ty) => Ty::from_ast(ty, &self.scope),
FunctionRetTy::Default(_) => None,
};
}
}
/// Visitor to collect Inherited Traits
pub struct InheritedTraitsVisitor<P> {
/// search result(list of Inherited Traits)
result: Option<TraitBounds>,
/// the file trait appears
file_path: P,
/// thecode point 'trait' statement starts
offset: i32,
}
impl<'ast, P> visit::Visitor<'ast> for InheritedTraitsVisitor<P>
where
P: AsRef<Path>,
{
fn visit_item(&mut self, item: &ast::Item) {
if let ItemKind::Trait(_, _, _, ref bounds, _) = item.node {
self.result = Some(TraitBounds::from_generic_bounds(
bounds,
&self.file_path,
self.offset,
));
}
}
}
/// Visitor for for ~ in .. statement
pub(crate) struct ForStmtVisitor<'r, 's: 'r> {
pub(crate) for_pat: Option<Pat>,
pub(crate) in_expr: Option<Ty>,
scope: Scope,
session: &'r Session<'s>,
}
impl<'ast, 'r, 's> visit::Visitor<'ast> for ForStmtVisitor<'r, 's> {
fn visit_expr(&mut self, ex: &'ast ast::Expr) {
if let ExprKind::ForLoop(ref pat, ref expr, _, _) = ex.node {
let for_pat = Pat::from_ast(&pat.node, &self.scope);
let mut expr_visitor = ExprTypeVisitor::new(self.scope.clone(), self.session);
expr_visitor.visit_expr(expr);
self.in_expr = expr_visitor.result;
self.for_pat = Some(for_pat);
}
}
}
pub(crate) fn parse_for_stmt<'r, 's: 'r>(
s: String,
scope: Scope,
session: &'r Session<'s>,
) -> ForStmtVisitor<'r, 's> {
let mut v = ForStmtVisitor {
for_pat: None,
in_expr: None,
scope,
session,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}
/// Visitor for if let / while let statement
pub(crate) struct IfLetVisitor<'r, 's: 'r> {
pub(crate) let_pat: Option<Pat>,
pub(crate) rh_expr: Option<Ty>,
scope: Scope,
session: &'r Session<'s>,
}
impl<'ast, 'r, 's> visit::Visitor<'ast> for IfLetVisitor<'r, 's> {
fn visit_expr(&mut self, ex: &'ast ast::Expr) {
match &ex.node {
ExprKind::IfLet(pats, expr, _, _) | ExprKind::WhileLet(pats, expr, _, _) => {
if let Some(pat) = pats.get(0) {
self.let_pat = Some(Pat::from_ast(&pat.node, &self.scope));
let mut expr_visitor = ExprTypeVisitor::new(self.scope.clone(), self.session);
expr_visitor.visit_expr(expr);
self.rh_expr = expr_visitor.result;
}
}
_ => {}
}
}
}
pub(crate) fn parse_if_let<'r, 's: 'r>(
s: String,
scope: Scope,
session: &'r Session<'s>,
) -> IfLetVisitor<'r, 's> {
let mut v = IfLetVisitor {
let_pat: None,
rh_expr: None,
scope,
session,
};
with_stmt(s, |stmt| visit::walk_stmt(&mut v, stmt));
v
}