func describeType(qpos *queryPos, path []ast.Node) (*describeTypeResult, error) {
	var description string
	var t types.Type
	switch n := path[0].(type) {
	case *ast.Ident:
		t = qpos.info.TypeOf(n)
		switch t := t.(type) {
		case *types.Basic:
			description = "reference to built-in "
		case *types.Named:
			isDef := t.Obj().Pos() == n.Pos()  
			if isDef {
				description = "definition of "
			} else {
				description = "reference to "
			}
		}
	case ast.Expr:
		t = qpos.info.TypeOf(n)
	default:
		return nil, fmt.Errorf("unexpected AST for type: %T", n)
	}
	description = description + "type " + qpos.typeString(t)
	switch t.(type) {
	case *types.Named, *types.Struct:
		szs := types.StdSizes{WordSize: 8, MaxAlign: 8}  
		description = fmt.Sprintf("%s (size %d, align %d)", description,
			szs.Sizeof(t), szs.Alignof(t))
	}
	return &describeTypeResult{
		qpos:        qpos,
		node:        path[0],
		description: description,
		typ:         t,
		methods:     accessibleMethods(t, qpos.info.Pkg),
	}, nil
}
---- TYPE ------------------------------------------------------------
package oracle
import (
	"bytes"
	"fmt"
	"go/ast"
	exact "go/constant"
	"go/token"
	"go/types"
	"log"
	"os"
	"strings"
	"github.com/visualfc/gotools/oracle/oracle/serial"
	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/loader"
	"golang.org/x/tools/go/types/typeutil"
)
func describe(q *Query) error {
	lconf := loader.Config{Build: q.Build}
	allowErrors(&lconf)
	if _, err := importQueryPackage(q.Pos, &lconf); err != nil {
		return err
	}
	lprog, err := lconf.Load()
	if err != nil {
		return err
	}
	q.Fset = lprog.Fset
	qpos, err := parseQueryPos(lprog, q.Pos, true)  
	if err != nil {
		return err
	}
	if false {  
		fprintf(os.Stderr, lprog.Fset, qpos.path[0], "you selected: %s %s",
			astutil.NodeDescription(qpos.path[0]), pathToString(qpos.path))
	}
	path, action := findInterestingNode(qpos.info, qpos.path)
	switch action {
	case actionExpr:
		q.result, err = describeValue(qpos, path)
	case actionType:
		q.result, err = describeType(qpos, path)
	case actionPackage:
		q.result, err = describePackage(qpos, path)
	case actionStmt:
		q.result, err = describeStmt(qpos, path)
	case actionUnknown:
		q.result = &describeUnknownResult{path[0]}
	default:
		panic(action)  
	}
	return err
}
type describeUnknownResult struct {
	node ast.Node
}
func (r *describeUnknownResult) display(printf printfFunc) {
	printf(r.node, "%s", astutil.NodeDescription(r.node))
}
func (r *describeUnknownResult) toSerial(res *serial.Result, fset *token.FileSet) {
	res.Describe = &serial.Describe{
		Desc: astutil.NodeDescription(r.node),
		Pos:  fset.Position(r.node.Pos()).String(),
	}
}
type action int
const (
	actionUnknown action = iota  
	actionExpr                   
	actionType                   
	actionStmt                   
	actionPackage                
)
func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
	for len(path) > 0 {
		switch n := path[0].(type) {
		case *ast.GenDecl:
			if len(n.Specs) == 1 {
				path = append([]ast.Node{n.Specs[0]}, path...)
				continue
			}
			return path, actionUnknown  
		case *ast.FuncDecl:
			path = append([]ast.Node{n.Name}, path...)
			continue
		case *ast.ImportSpec:
			return path, actionPackage
		case *ast.ValueSpec:
			if len(n.Names) == 1 {
				path = append([]ast.Node{n.Names[0]}, path...)
				continue
			}
			return path, actionUnknown  
		case *ast.TypeSpec:
			path = append([]ast.Node{n.Name}, path...)
			continue
		case ast.Stmt:
			return path, actionStmt
		case *ast.ArrayType,
			*ast.StructType,
			*ast.FuncType,
			*ast.InterfaceType,
			*ast.MapType,
			*ast.ChanType:
			return path, actionType
		case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
			return path, actionUnknown  
		case *ast.Ellipsis:
		case *ast.Field:
			if len(n.Names) == 1 {
				path = append([]ast.Node{n.Names[0]}, path...)
				continue
			}
		case *ast.FieldList:
		case *ast.BasicLit:
			if _, ok := path[1].(*ast.ImportSpec); ok {
				return path[1:], actionPackage
			}
			return path, actionExpr
		case *ast.SelectorExpr:
			if pkginfo.Uses[n.Sel] == nil {
				return path, actionUnknown
			}
			path = append([]ast.Node{n.Sel}, path...)
			continue
		case *ast.Ident:
			switch pkginfo.ObjectOf(n).(type) {
			case *types.PkgName:
				return path, actionPackage
			case *types.Const:
				return path, actionExpr
			case *types.Label:
				return path, actionStmt
			case *types.TypeName:
				return path, actionType
			case *types.Var:
				if _, ok := path[1].(*ast.Field); ok {
					_ = path[2].(*ast.FieldList)  
					if _, ok := path[3].(*ast.StructType); ok {
						return path[3:], actionType
					}
				}
				return path, actionExpr
			case *types.Func:
				return path, actionExpr
			case *types.Builtin:
				path = path[1:]  
				continue
			case *types.Nil:
				return path, actionExpr
			}
			switch path[1].(type) {
			case *ast.SelectorExpr:
				return path[1:], actionExpr
			case *ast.Field:
			case *ast.File:
				return path, actionPackage
			case *ast.ImportSpec:
				return path[1:], actionPackage
			default:
				log.Printf("unknown reference %s in %T\n", n, path[1])
				return path, actionUnknown
			}
		case *ast.StarExpr:
			if pkginfo.Types[n].IsType() {
				return path, actionType
			}
			return path, actionExpr
		case ast.Expr:
			return path, actionExpr
		}
		path = path[1:]
	}
	return nil, actionUnknown  
}
func describeValue(qpos *queryPos, path []ast.Node) (*describeValueResult, error) {
	var expr ast.Expr
	var obj types.Object
	switch n := path[0].(type) {
	case *ast.ValueSpec:
		return nil, fmt.Errorf("multiple value specification")
	case *ast.Ident:
		obj = qpos.info.ObjectOf(n)
		expr = n
	case ast.Expr:
		expr = n
	default:
		return nil, fmt.Errorf("unexpected AST for expr: %T", n)
	}
	typ := qpos.info.TypeOf(expr)
	constVal := qpos.info.Types[expr].Value
	return &describeValueResult{
		qpos:     qpos,
		expr:     expr,
		typ:      typ,
		constVal: constVal,
		obj:      obj,
	}, nil
}
type describeValueResult struct {
	qpos     *queryPos
	expr     ast.Expr      
	typ      types.Type    
	constVal exact.Value   
	obj      types.Object  
}
func (r *describeValueResult) display(printf printfFunc) {
	var prefix, suffix string
	if r.constVal != nil {
		suffix = fmt.Sprintf(" of constant value %s", r.constVal)
	}
	switch obj := r.obj.(type) {
	case *types.Func:
		if recv := obj.Type().(*types.Signature).Recv(); recv != nil {
			if _, ok := recv.Type().Underlying().(*types.Interface); ok {
				prefix = "interface method "
			} else {
				prefix = "method "
			}
		}
	}
	if r.obj != nil {
		if r.obj.Pos() == r.expr.Pos() {
			printf(r.expr, "definition of %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
		} else {
			printf(r.expr, "reference to %s%s%s", prefix, r.qpos.objectString(r.obj), suffix)
			if def := r.obj.Pos(); def != token.NoPos {
				printf(def, "defined here")
			}
		}
	} else {
		desc := astutil.NodeDescription(r.expr)
		if suffix != "" {
			printf(r.expr, "%s%s", desc, suffix)
		} else {
			printf(r.expr, "%s of type %s", desc, r.qpos.typeString(r.typ))
		}
	}
}
func (r *describeValueResult) toSerial(res *serial.Result, fset *token.FileSet) {
	var value, objpos string
	if r.constVal != nil {
		value = r.constVal.String()
	}
	if r.obj != nil {
		objpos = fset.Position(r.obj.Pos()).String()
	}
	res.Describe = &serial.Describe{
		Desc:   astutil.NodeDescription(r.expr),
		Pos:    fset.Position(r.expr.Pos()).String(),
		Detail: "value",
		Value: &serial.DescribeValue{
			Type:   r.qpos.typeString(r.typ),
			Value:  value,
			ObjPos: objpos,
		},
	}
}