func check_type_expr(e ast.Expr) bool {
	switch t := e.(type) {
	case *ast.StarExpr:
		return check_type_expr(t.X)
	case *ast.ArrayType:
		return check_type_expr(t.Elt)
	case *ast.SelectorExpr:
		return check_type_expr(t.X)
	case *ast.FuncType:
		a := check_func_field_list(t.Params)
		b := check_func_field_list(t.Results)
		return a && b
	case *ast.MapType:
		a := check_type_expr(t.Key)
		b := check_type_expr(t.Value)
		return a && b
	case *ast.Ellipsis:
		return check_type_expr(t.Elt)
	case *ast.ChanType:
		return check_type_expr(t.Value)
	case *ast.BadExpr:
		return false
	default:
		return true
	}
	return true
}
checks for a type expression correctness, it the type expression has
ast.BadExpr somewhere, returns false, otherwise true
package gocode
import (
	"bytes"
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"time"
)
type candidate struct {
	Name  string
	Type  string
	Class decl_class
}
type out_buffers struct {
	tmpbuf     *bytes.Buffer
	candidates []candidate
	ctx        *auto_complete_context
	tmpns      map[string]bool
	ignorecase bool
}
func new_out_buffers(ctx *auto_complete_context) *out_buffers {
	b := new(out_buffers)
	b.tmpbuf = bytes.NewBuffer(make([]byte, 0, 1024))
	b.candidates = make([]candidate, 0, 64)
	b.ctx = ctx
	return b
}
func (b *out_buffers) Len() int {
	return len(b.candidates)
}
func (b *out_buffers) Less(i, j int) bool {
	x := b.candidates[i]
	y := b.candidates[j]
	if x.Class == y.Class {
		return x.Name < y.Name
	}
	return x.Class < y.Class
}
func (b *out_buffers) Swap(i, j int) {
	b.candidates[i], b.candidates[j] = b.candidates[j], b.candidates[i]
}
func (b *out_buffers) append_decl(p, name string, decl *decl, class decl_class) {
	c1 := !g_config.ProposeBuiltins && decl.scope == g_universe_scope && decl.name != "Error"
	c2 := class != decl_invalid && decl.class != class
	c3 := class == decl_invalid && !has_prefix(name, p, b.ignorecase)
	c4 := !decl.matches()
	c5 := !check_type_expr(decl.typ)
	if c1 || c2 || c3 || c4 || c5 {
		return
	}
	decl.pretty_print_type(b.tmpbuf)
	b.candidates = append(b.candidates, candidate{
		Name:  name,
		Type:  b.tmpbuf.String(),
		Class: decl.class,
	})
	b.tmpbuf.Reset()
}
func (b *out_buffers) append_embedded(p string, decl *decl, class decl_class) {
	if decl.embedded == nil {
		return
	}
	first_level := false
	if b.tmpns == nil {
		b.tmpns = make(map[string]bool)
		first_level = true
		for _, c := range decl.children {
			b.tmpns[c.name] = true
		}
	}
	for _, emb := range decl.embedded {
		typedecl := type_to_decl(emb, decl.scope)
		if typedecl == nil {
			continue
		}
		if typedecl.flags&decl_visited != 0 {
			continue
		}
		typedecl.flags |= decl_visited
		defer typedecl.clear_visited()
		for _, c := range typedecl.children {
			if _, has := b.tmpns[c.name]; has {
				continue
			}
			b.append_decl(p, c.name, c, class)
			b.tmpns[c.name] = true
		}
		b.append_embedded(p, typedecl, class)
	}
	if first_level {
		b.tmpns = nil
	}
}
type auto_complete_context struct {
	current *auto_complete_file  
	others  []*decl_file_cache   
	pkg     *scope
	pcache    package_cache  
	declcache *decl_cache    
}
func new_auto_complete_context(pcache package_cache, declcache *decl_cache) *auto_complete_context {
	c := new(auto_complete_context)
	c.current = new_auto_complete_file("", declcache.context)
	c.pcache = pcache
	c.declcache = declcache
	return c
}
func (c *auto_complete_context) update_caches() {
	ps := make(map[string]*package_file_cache)
	c.pcache.append_packages(ps, c.current.packages)
	c.others = get_other_package_files(c.current.name, c.current.package_name, c.declcache)
	for _, other := range c.others {
		c.pcache.append_packages(ps, other.packages)
	}
	update_packages(ps)
	fixup_packages(c.current.filescope, c.current.packages, c.pcache)
	for _, f := range c.others {
		fixup_packages(f.filescope, f.packages, c.pcache)
	}
	c.merge_decls()
}
func (c *auto_complete_context) merge_decls() {
	c.pkg = new_scope(g_universe_scope)
	merge_decls(c.current.filescope, c.pkg, c.current.decls)
	merge_decls_from_packages(c.pkg, c.current.packages, c.pcache)
	for _, f := range c.others {
		merge_decls(f.filescope, c.pkg, f.decls)
		merge_decls_from_packages(c.pkg, f.packages, c.pcache)
	}
}
func (c *auto_complete_context) make_decl_set(scope *scope) map[string]*decl {
	set := make(map[string]*decl, len(c.pkg.entities)*2)
	make_decl_set_recursive(set, scope)
	return set
}
func (c *auto_complete_context) get_candidates_from_set(set map[string]*decl, partial string, class decl_class, b *out_buffers) {
	for key, value := range set {
		if value == nil {
			continue
		}
		value.infer_type()
		b.append_decl(partial, key, value, class)
	}
}
func (c *auto_complete_context) get_candidates_from_decl(cc cursor_context, class decl_class, b *out_buffers) {
	for _, decl := range cc.decl.children {
		if cc.decl.class == decl_package && !ast.IsExported(decl.name) {
			continue
		}
		if cc.struct_field {
			if _, ok := decl.typ.(*ast.FuncType); ok {
				continue
			}
		}
		b.append_decl(cc.partial, decl.name, decl, class)
	}
	adecl := advance_to_struct_or_interface(cc.decl)
	if adecl != nil && adecl != cc.decl {
		for _, decl := range adecl.children {
			if decl.class == decl_var {
				b.append_decl(cc.partial, decl.name, decl, class)
			}
		}
	}
	b.append_embedded(cc.partial, cc.decl, class)
}
func (c *auto_complete_context) apropos(file []byte, filename string, cursor int) ([]candidate, int) {
	c.current.cursor = cursor
	c.current.name = filename
	filesemi := make([]byte, len(file)+1)
	copy(filesemi, file[:cursor])
	filesemi[cursor] = ';'
	copy(filesemi[cursor+1:], file[cursor:])
	c.current.process_data(filesemi)
	c.update_caches()
	b := new_out_buffers(c)
	partial := 0
	cc, ok := c.deduce_cursor_context(file, cursor)
	if !ok {
		return nil, 0
	}
	class := decl_invalid
	switch cc.partial {
	case "const":
		class = decl_const
	case "var":
		class = decl_var
	case "type":
		class = decl_type
	case "func":
		class = decl_func
	case "package":
		class = decl_package
	}
	if cc.decl == nil {
		set := c.make_decl_set(c.current.scope)
		c.get_candidates_from_set(set, cc.partial, class, b)
		if cc.partial != "" && len(b.candidates) == 0 {
			b.ignorecase = true
			c.get_candidates_from_set(set, cc.partial, class, b)
		}
	} else {
		c.get_candidates_from_decl(cc, class, b)
		if cc.partial != "" && len(b.candidates) == 0 {
			b.ignorecase = true
			c.get_candidates_from_decl(cc, class, b)
		}
	}
	partial = len(cc.partial)
	if len(b.candidates) == 0 {
		return nil, 0
	}
	sort.Sort(b)
	return b.candidates, partial
}
func update_packages(ps map[string]*package_file_cache) {
	done := make(chan bool)
	for _, p := range ps {
		go func(p *package_file_cache) {
			defer func() {
				if err := recover(); err != nil {
					print_backtrace(err)
					done <- false
				}
			}()
			p.update_cache()
			done <- true
		}(p)
	}
	for _ = range ps {
		if !<-done {
			panic("One of the package cache updaters panicked")
		}
	}
}
func merge_decls(filescope *scope, pkg *scope, decls map[string]*decl) {
	for _, d := range decls {
		pkg.merge_decl(d)
	}
	filescope.parent = pkg
}
func merge_decls_from_packages(pkgscope *scope, pkgs []package_import, pcache package_cache) {
	for _, p := range pkgs {
		path, alias := p.path, p.alias
		if alias != "." {
			continue
		}
		p := pcache[path].main
		if p == nil {
			continue
		}
		for _, d := range p.children {
			if ast.IsExported(d.name) {
				pkgscope.merge_decl(d)
			}
		}
	}
}
func fixup_packages(filescope *scope, pkgs []package_import, pcache package_cache) {
	for _, p := range pkgs {
		path, alias := p.path, p.alias
		if alias == "" {
			alias = pcache[path].defalias
		}
		if alias == "." {
			continue
		}
		filescope.replace_decl(alias, pcache[path].main)
	}
}
func get_other_package_files(filename, packageName string, declcache *decl_cache) []*decl_file_cache {
	others := find_other_package_files(filename, packageName)
	ret := make([]*decl_file_cache, len(others))
	done := make(chan *decl_file_cache)
	for _, nm := range others {
		go func(name string) {
			defer func() {
				if err := recover(); err != nil {
					print_backtrace(err)
					done <- nil
				}
			}()
			done <- declcache.get_and_update(name)
		}(nm)
	}
	for i := range others {
		ret[i] = <-done
		if ret[i] == nil {
			panic("One of the decl cache updaters panicked")
		}
	}
	return ret
}
func find_other_package_files(filename, package_name string) []string {
	if filename == "" {
		return nil
	}
	dir, file := filepath.Split(filename)
	files_in_dir, err := readdir(dir)
	if err != nil {
		panic(err)
	}
	count := 0
	for _, stat := range files_in_dir {
		ok, _ := filepath.Match("*.go", stat.Name())
		if !ok || stat.Name() == file {
			continue
		}
		count++
	}
	out := make([]string, 0, count)
	for _, stat := range files_in_dir {
		const non_regular = os.ModeDir | os.ModeSymlink |
			os.ModeDevice | os.ModeNamedPipe | os.ModeSocket
		ok, _ := filepath.Match("*.go", stat.Name())
		if !ok || stat.Name() == file || stat.Mode()&non_regular != 0 {
			continue
		}
		abspath := filepath.Join(dir, stat.Name())
		if file_package_name(abspath) == package_name {
			n := len(out)
			out = out[:n+1]
			out[n] = abspath
		}
	}
	return out
}
func file_package_name(filename string) string {
	file, _ := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly)
	return file.Name.Name
}
func make_decl_set_recursive(set map[string]*decl, scope *scope) {
	for name, ent := range scope.entities {
		if _, ok := set[name]; !ok {
			set[name] = ent
		}
	}
	if scope.parent != nil {
		make_decl_set_recursive(set, scope.parent)
	}
}
func check_func_field_list(f *ast.FieldList) bool {
	if f == nil {
		return true
	}
	for _, field := range f.List {
		if !check_type_expr(field.Type) {
			return false
		}
	}
	return true
}