#![allow(dead_code)]
use crate::SourceRead;
use super::super::obj;
use super::super::objects::{ObjKey, PackageKey, ScopeKey, TCObjects};
use super::super::scope::Scope;
use super::super::typ;
use super::check::{Checker, FilesContext, RcIfaceInfo};
use go_parser::ast::{self, Expr, Node};
use go_parser::{AstObjects, FieldKey, IdentKey, Map, Pos};
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashSet;
use std::fmt;
use std::fmt::Write;
use std::rc::Rc;
#[derive(Clone, Debug)]
pub struct MethodInfo {
data: Rc<RefCell<MethodInfoData>>,
}
#[derive(Debug)]
struct MethodInfoData {
scope: Option<ScopeKey>,
src: Option<FieldKey>,
func: Option<ObjKey>,
}
impl MethodInfo {
pub fn with_fun(fun: ObjKey) -> MethodInfo {
MethodInfo {
data: Rc::new(RefCell::new(MethodInfoData {
scope: None,
src: None,
func: Some(fun),
})),
}
}
pub fn with_scope_src(skey: ScopeKey, fkey: FieldKey) -> MethodInfo {
MethodInfo {
data: Rc::new(RefCell::new(MethodInfoData {
scope: Some(skey),
src: Some(fkey),
func: None,
})),
}
}
pub fn scope(&self) -> Option<ScopeKey> {
self.data.borrow().scope
}
pub fn src(&self) -> Option<FieldKey> {
self.data.borrow().src
}
pub fn func(&self) -> Option<ObjKey> {
self.data.borrow().func
}
pub fn set_func(&self, func: ObjKey) {
self.data.borrow_mut().func = Some(func);
}
pub fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
tc_objs: &TCObjects,
ast_objs: &AstObjects,
) -> fmt::Result {
let s = if let Some(okey) = self.func() {
tc_objs.lobjs[okey].name()
} else {
&ast_objs.idents[ast_objs.fields[self.src().unwrap()].names[0]].name
};
f.write_str(s)
}
pub fn pos(&self, tc_objs: &TCObjects, ast_objs: &AstObjects) -> Pos {
if let Some(okey) = self.func() {
tc_objs.lobjs[okey].pos()
} else {
self.src().unwrap().pos(ast_objs)
}
}
pub fn id<'a>(
&self,
pkey: PackageKey,
tc_objs: &'a TCObjects,
ast_objs: &'a AstObjects,
) -> Cow<'a, str> {
if let Some(okey) = self.func() {
tc_objs.lobjs[okey].id(tc_objs)
} else {
let pkg = Some(&tc_objs.pkgs[pkey]);
let name = &ast_objs.idents[ast_objs.fields[self.src().unwrap()].names[0]].name;
obj::get_id(pkg, name)
}
}
}
#[derive(Debug)]
pub struct IfaceInfo {
pub explicits: usize,
pub methods: Vec<MethodInfo>,
}
impl IfaceInfo {
pub fn new(explicits: usize, methods: Vec<MethodInfo>) -> IfaceInfo {
IfaceInfo {
explicits: explicits,
methods: methods,
}
}
pub fn new_empty() -> IfaceInfo {
IfaceInfo::new(0, vec![])
}
pub fn is_empty(&self) -> bool {
self.methods.is_empty()
}
pub fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
tc_objs: &TCObjects,
ast_objs: &AstObjects,
) -> fmt::Result {
f.write_str("interface{")?;
for (i, m) in self.methods.iter().enumerate() {
if i > 0 {
f.write_char(' ')?;
}
m.fmt(f, tc_objs, ast_objs)?;
}
f.write_char('}')
}
}
impl<'a, S: SourceRead> Checker<'a, S> {
pub fn info_from_type_lit(
&self,
skey: ScopeKey,
iface: &Rc<ast::InterfaceType>,
tname: Option<ObjKey>,
path: &Vec<ObjKey>,
fctx: &mut FilesContext<S>,
) -> Option<RcIfaceInfo> {
if self.trace() {
let expr = Expr::Interface(iface.clone());
let ed = self.new_dis(&expr);
let pstr = self.obj_path_str(path);
let opstr = self.obj_path_str(&fctx.obj_path);
let msg = format!(
"-- collect methods for {} (path = {}, objPath = {})",
ed, pstr, opstr
);
self.trace_begin(iface.interface, &msg);
}
let end = |ret: Option<RcIfaceInfo>| {
if self.trace() {
let expr = Expr::Interface(iface.clone());
let ed = self.new_dis(&expr);
self.trace_end(iface.interface, &format!("=> {}", ed));
}
ret
};
if let Some(okey) = tname {
debug_assert!(path[path.len() - 1] == okey);
if let Some(info) = fctx.ifaces.get(&okey) {
let cloned_info = info.clone();
if info.is_none() {
let yes = self.has_cycle(okey, path, true);
assert!(yes);
}
return end(cloned_info);
} else {
fctx.ifaces.insert(okey, None);
}
}
let iinfo = if iface.methods.list.len() == 0 {
Rc::new(IfaceInfo::new_empty())
} else {
let mut mset = Map::new();
let mut methods = vec![];
let mut embeddeds = vec![];
let mut positions = vec![];
for fkey in iface.methods.list.iter() {
let field = &self.ast_objs.fields[*fkey];
if field.names.len() > 0 {
let name = self.ast_ident(field.names[0]);
if name.name == "_" {
self.error_str(name.pos, "invalid method name _");
continue; }
let m = MethodInfo::with_scope_src(skey, *fkey);
if self.declare_in_method_set(&mut mset, m.clone(), fkey.pos(self.ast_objs)) {
methods.push(m);
}
} else {
let e = match &field.typ {
Expr::Ident(i) => self.info_from_type_name(skey, *i, path, fctx),
Expr::Selector(sel) => self.info_from_qualified_type_mame(skey, sel),
_ => {
None
}
};
if let Some(emb) = e {
embeddeds.push(emb);
positions.push(fkey.pos(self.ast_objs));
}
}
}
let explicites = methods.len();
for (i, e) in embeddeds.into_iter().enumerate() {
let pos = positions[i];
for m in e.methods.iter() {
if self.declare_in_method_set(&mut mset, m.clone(), pos) {
methods.push(m.clone());
}
}
}
Rc::new(IfaceInfo::new(explicites, methods))
};
if let Some(okey) = tname {
fctx.ifaces.insert(okey, Some(iinfo.clone()));
}
end(Some(iinfo))
}
fn info_from_type_name(
&self,
skey: ScopeKey,
name: IdentKey,
path: &Vec<ObjKey>,
fctx: &mut FilesContext<S>,
) -> Option<RcIfaceInfo> {
let start = path.len();
let mut cur_path = path.clone();
let mut ident = self.ast_ident(name);
loop {
let lookup = Scope::lookup_parent(&skey, &ident.name, self.octx.pos, self.tc_objs);
if lookup.is_none() {
break;
}
let tname = lookup.as_ref().unwrap().1;
let tname_val = self.lobj(tname);
if &obj::EntityType::TypeName != tname_val.entity_type() {
break;
}
if self.has_cycle(tname, &cur_path[start..], false) {
break;
}
if self.has_cycle(tname, &cur_path, true) {
break;
}
cur_path.push(tname);
if let Some(decl_key) = self.obj_map.get(&tname) {
let decl = &self.tc_objs.decls[*decl_key].as_type();
let ty = Checker::<S>::unparen(&decl.typ);
match ty {
Expr::Ident(i) => {
ident = self.ast_ident(*i);
}
Expr::Selector(sel) => {
return self.info_from_qualified_type_mame(decl.file_scope, sel);
}
Expr::Interface(iface) => {
return self.info_from_type_lit(
decl.file_scope,
iface,
Some(tname),
&cur_path,
fctx,
);
}
_ => break,
}
} else {
if let Some(ty) = tname_val.typ() {
let ty = typ::underlying_type(ty, self.tc_objs);
if let typ::Type::Interface(i) = self.otype(ty) {
return Some(self.info_from_type(i));
}
}
break;
}
}
None
}
fn declare_in_method_set(
&self,
set: &mut Map<String, MethodInfo>,
mi: MethodInfo,
pos: Pos,
) -> bool {
let id = mi.id(self.pkg, self.tc_objs, self.ast_objs);
if let Some(alt) = set.insert(id.to_string(), mi) {
let mi_ref = set.get(id.as_ref()).unwrap();
let md = self.new_dis(mi_ref);
self.error(pos, format!("{} redeclared", md));
let mpos = mi_ref.pos(self.tc_objs, self.ast_objs);
if mpos > 0 {
let md = self.new_dis(&alt);
self.error(mpos, format!("\tother declaration of {}", md));
}
false
} else {
true
}
}
fn info_from_qualified_type_mame(
&self,
skey: ScopeKey,
sel: &ast::SelectorExpr,
) -> Option<RcIfaceInfo> {
if let Some(name) = sel.expr.try_as_ident() {
let ident = self.ast_ident(*name);
if let Some((_, obj1)) =
Scope::lookup_parent(&skey, &ident.name, self.octx.pos, self.tc_objs)
{
let obj_val = self.lobj(obj1);
if let obj::EntityType::PkgName(imported, _) = obj_val.entity_type() {
debug_assert!(obj_val.pkg() == Some(self.pkg));
let imported_val = &self.tc_objs.pkgs[*imported];
let scope = &self.tc_objs.scopes[*imported_val.scope()];
if let Some(obj2) = scope.lookup(&self.ast_ident(sel.sel).name) {
let obj_val2 = self.lobj(*obj2);
if !obj_val2.exported() {
return None;
}
if let obj::EntityType::TypeName = obj_val2.entity_type() {
let t = typ::underlying_type(obj_val2.typ().unwrap(), self.tc_objs);
if let Some(iface) = self.otype(t).try_as_interface() {
return Some(self.info_from_type(iface));
}
}
}
}
}
}
None
}
fn info_from_type(&self, iface: &typ::InterfaceDetail) -> RcIfaceInfo {
let all_methods_ref = iface.all_methods();
let all_methods = all_methods_ref.as_ref().unwrap();
let all_methods_len = all_methods.len();
let mut mis = iface
.methods()
.iter()
.map(|x| MethodInfo::with_fun(*x))
.collect();
if all_methods_len == iface.methods().len() {
return Rc::new(IfaceInfo::new(all_methods_len, mis));
}
let set: HashSet<ObjKey> = iface.methods().clone().into_iter().collect();
let mut embedded: Vec<MethodInfo> = all_methods
.iter()
.filter_map(|x| {
if set.contains(x) {
None
} else {
Some(MethodInfo::with_fun(*x))
}
})
.collect();
mis.append(&mut embedded);
Rc::new(IfaceInfo::new(iface.methods().len(), mis))
}
}