|  | // Copyright 2011 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package main | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "go/ast" | 
|  | "go/parser" | 
|  | "go/token" | 
|  | "io/ioutil" | 
|  | "os" | 
|  | "os/exec" | 
|  | "path/filepath" | 
|  | "reflect" | 
|  | "runtime" | 
|  | "strings" | 
|  | ) | 
|  |  | 
|  | // Partial type checker. | 
|  | // | 
|  | // The fact that it is partial is very important: the input is | 
|  | // an AST and a description of some type information to | 
|  | // assume about one or more packages, but not all the | 
|  | // packages that the program imports. The checker is | 
|  | // expected to do as much as it can with what it has been | 
|  | // given. There is not enough information supplied to do | 
|  | // a full type check, but the type checker is expected to | 
|  | // apply information that can be derived from variable | 
|  | // declarations, function and method returns, and type switches | 
|  | // as far as it can, so that the caller can still tell the types | 
|  | // of expression relevant to a particular fix. | 
|  | // | 
|  | // TODO(rsc,gri): Replace with go/typechecker. | 
|  | // Doing that could be an interesting test case for go/typechecker: | 
|  | // the constraints about working with partial information will | 
|  | // likely exercise it in interesting ways. The ideal interface would | 
|  | // be to pass typecheck a map from importpath to package API text | 
|  | // (Go source code), but for now we use data structures (TypeConfig, Type). | 
|  | // | 
|  | // The strings mostly use gofmt form. | 
|  | // | 
|  | // A Field or FieldList has as its type a comma-separated list | 
|  | // of the types of the fields. For example, the field list | 
|  | //	x, y, z int | 
|  | // has type "int, int, int". | 
|  |  | 
|  | // The prefix "type " is the type of a type. | 
|  | // For example, given | 
|  | //	var x int | 
|  | //	type T int | 
|  | // x's type is "int" but T's type is "type int". | 
|  | // mkType inserts the "type " prefix. | 
|  | // getType removes it. | 
|  | // isType tests for it. | 
|  |  | 
|  | func mkType(t string) string { | 
|  | return "type " + t | 
|  | } | 
|  |  | 
|  | func getType(t string) string { | 
|  | if !isType(t) { | 
|  | return "" | 
|  | } | 
|  | return t[len("type "):] | 
|  | } | 
|  |  | 
|  | func isType(t string) bool { | 
|  | return strings.HasPrefix(t, "type ") | 
|  | } | 
|  |  | 
|  | // TypeConfig describes the universe of relevant types. | 
|  | // For ease of creation, the types are all referred to by string | 
|  | // name (e.g., "reflect.Value").  TypeByName is the only place | 
|  | // where the strings are resolved. | 
|  |  | 
|  | type TypeConfig struct { | 
|  | Type map[string]*Type | 
|  | Var  map[string]string | 
|  | Func map[string]string | 
|  |  | 
|  | // External maps from a name to its type. | 
|  | // It provides additional typings not present in the Go source itself. | 
|  | // For now, the only additional typings are those generated by cgo. | 
|  | External map[string]string | 
|  | } | 
|  |  | 
|  | // typeof returns the type of the given name, which may be of | 
|  | // the form "x" or "p.X". | 
|  | func (cfg *TypeConfig) typeof(name string) string { | 
|  | if cfg.Var != nil { | 
|  | if t := cfg.Var[name]; t != "" { | 
|  | return t | 
|  | } | 
|  | } | 
|  | if cfg.Func != nil { | 
|  | if t := cfg.Func[name]; t != "" { | 
|  | return "func()" + t | 
|  | } | 
|  | } | 
|  | return "" | 
|  | } | 
|  |  | 
|  | // Type describes the Fields and Methods of a type. | 
|  | // If the field or method cannot be found there, it is next | 
|  | // looked for in the Embed list. | 
|  | type Type struct { | 
|  | Field  map[string]string // map field name to type | 
|  | Method map[string]string // map method name to comma-separated return types (should start with "func ") | 
|  | Embed  []string          // list of types this type embeds (for extra methods) | 
|  | Def    string            // definition of named type | 
|  | } | 
|  |  | 
|  | // dot returns the type of "typ.name", making its decision | 
|  | // using the type information in cfg. | 
|  | func (typ *Type) dot(cfg *TypeConfig, name string) string { | 
|  | if typ.Field != nil { | 
|  | if t := typ.Field[name]; t != "" { | 
|  | return t | 
|  | } | 
|  | } | 
|  | if typ.Method != nil { | 
|  | if t := typ.Method[name]; t != "" { | 
|  | return t | 
|  | } | 
|  | } | 
|  |  | 
|  | for _, e := range typ.Embed { | 
|  | etyp := cfg.Type[e] | 
|  | if etyp != nil { | 
|  | if t := etyp.dot(cfg, name); t != "" { | 
|  | return t | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return "" | 
|  | } | 
|  |  | 
|  | // typecheck type checks the AST f assuming the information in cfg. | 
|  | // It returns two maps with type information: | 
|  | // typeof maps AST nodes to type information in gofmt string form. | 
|  | // assign maps type strings to lists of expressions that were assigned | 
|  | // to values of another type that were assigned to that type. | 
|  | func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[interface{}]string, assign map[string][]interface{}) { | 
|  | typeof = make(map[interface{}]string) | 
|  | assign = make(map[string][]interface{}) | 
|  | cfg1 := &TypeConfig{} | 
|  | *cfg1 = *cfg // make copy so we can add locally | 
|  | copied := false | 
|  |  | 
|  | // If we import "C", add types of cgo objects. | 
|  | cfg.External = map[string]string{} | 
|  | cfg1.External = cfg.External | 
|  | if imports(f, "C") { | 
|  | // Run cgo on gofmtFile(f) | 
|  | // Parse, extract decls from _cgo_gotypes.go | 
|  | // Map _Ctype_* types to C.* types. | 
|  | err := func() error { | 
|  | txt, err := gofmtFile(f) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | dir, err := ioutil.TempDir(os.TempDir(), "fix_cgo_typecheck") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | defer os.RemoveAll(dir) | 
|  | err = ioutil.WriteFile(filepath.Join(dir, "in.go"), txt, 0600) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | cmd := exec.Command(filepath.Join(runtime.GOROOT(), "bin", "go"), "tool", "cgo", "-objdir", dir, "-srcdir", dir, "in.go") | 
|  | err = cmd.Run() | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | out, err := ioutil.ReadFile(filepath.Join(dir, "_cgo_gotypes.go")) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | cgo, err := parser.ParseFile(token.NewFileSet(), "cgo.go", out, 0) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | for _, decl := range cgo.Decls { | 
|  | fn, ok := decl.(*ast.FuncDecl) | 
|  | if !ok { | 
|  | continue | 
|  | } | 
|  | if strings.HasPrefix(fn.Name.Name, "_Cfunc_") { | 
|  | var params, results []string | 
|  | for _, p := range fn.Type.Params.List { | 
|  | t := gofmt(p.Type) | 
|  | t = strings.ReplaceAll(t, "_Ctype_", "C.") | 
|  | params = append(params, t) | 
|  | } | 
|  | for _, r := range fn.Type.Results.List { | 
|  | t := gofmt(r.Type) | 
|  | t = strings.ReplaceAll(t, "_Ctype_", "C.") | 
|  | results = append(results, t) | 
|  | } | 
|  | cfg.External["C."+fn.Name.Name[7:]] = joinFunc(params, results) | 
|  | } | 
|  | } | 
|  | return nil | 
|  | }() | 
|  | if err != nil { | 
|  | fmt.Printf("warning: no cgo types: %s\n", err) | 
|  | } | 
|  | } | 
|  |  | 
|  | // gather function declarations | 
|  | for _, decl := range f.Decls { | 
|  | fn, ok := decl.(*ast.FuncDecl) | 
|  | if !ok { | 
|  | continue | 
|  | } | 
|  | typecheck1(cfg, fn.Type, typeof, assign) | 
|  | t := typeof[fn.Type] | 
|  | if fn.Recv != nil { | 
|  | // The receiver must be a type. | 
|  | rcvr := typeof[fn.Recv] | 
|  | if !isType(rcvr) { | 
|  | if len(fn.Recv.List) != 1 { | 
|  | continue | 
|  | } | 
|  | rcvr = mkType(gofmt(fn.Recv.List[0].Type)) | 
|  | typeof[fn.Recv.List[0].Type] = rcvr | 
|  | } | 
|  | rcvr = getType(rcvr) | 
|  | if rcvr != "" && rcvr[0] == '*' { | 
|  | rcvr = rcvr[1:] | 
|  | } | 
|  | typeof[rcvr+"."+fn.Name.Name] = t | 
|  | } else { | 
|  | if isType(t) { | 
|  | t = getType(t) | 
|  | } else { | 
|  | t = gofmt(fn.Type) | 
|  | } | 
|  | typeof[fn.Name] = t | 
|  |  | 
|  | // Record typeof[fn.Name.Obj] for future references to fn.Name. | 
|  | typeof[fn.Name.Obj] = t | 
|  | } | 
|  | } | 
|  |  | 
|  | // gather struct declarations | 
|  | for _, decl := range f.Decls { | 
|  | d, ok := decl.(*ast.GenDecl) | 
|  | if ok { | 
|  | for _, s := range d.Specs { | 
|  | switch s := s.(type) { | 
|  | case *ast.TypeSpec: | 
|  | if cfg1.Type[s.Name.Name] != nil { | 
|  | break | 
|  | } | 
|  | if !copied { | 
|  | copied = true | 
|  | // Copy map lazily: it's time. | 
|  | cfg1.Type = make(map[string]*Type) | 
|  | for k, v := range cfg.Type { | 
|  | cfg1.Type[k] = v | 
|  | } | 
|  | } | 
|  | t := &Type{Field: map[string]string{}} | 
|  | cfg1.Type[s.Name.Name] = t | 
|  | switch st := s.Type.(type) { | 
|  | case *ast.StructType: | 
|  | for _, f := range st.Fields.List { | 
|  | for _, n := range f.Names { | 
|  | t.Field[n.Name] = gofmt(f.Type) | 
|  | } | 
|  | } | 
|  | case *ast.ArrayType, *ast.StarExpr, *ast.MapType: | 
|  | t.Def = gofmt(st) | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | typecheck1(cfg1, f, typeof, assign) | 
|  | return typeof, assign | 
|  | } | 
|  |  | 
|  | func makeExprList(a []*ast.Ident) []ast.Expr { | 
|  | var b []ast.Expr | 
|  | for _, x := range a { | 
|  | b = append(b, x) | 
|  | } | 
|  | return b | 
|  | } | 
|  |  | 
|  | // Typecheck1 is the recursive form of typecheck. | 
|  | // It is like typecheck but adds to the information in typeof | 
|  | // instead of allocating a new map. | 
|  | func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, assign map[string][]interface{}) { | 
|  | // set sets the type of n to typ. | 
|  | // If isDecl is true, n is being declared. | 
|  | set := func(n ast.Expr, typ string, isDecl bool) { | 
|  | if typeof[n] != "" || typ == "" { | 
|  | if typeof[n] != typ { | 
|  | assign[typ] = append(assign[typ], n) | 
|  | } | 
|  | return | 
|  | } | 
|  | typeof[n] = typ | 
|  |  | 
|  | // If we obtained typ from the declaration of x | 
|  | // propagate the type to all the uses. | 
|  | // The !isDecl case is a cheat here, but it makes | 
|  | // up in some cases for not paying attention to | 
|  | // struct fields. The real type checker will be | 
|  | // more accurate so we won't need the cheat. | 
|  | if id, ok := n.(*ast.Ident); ok && id.Obj != nil && (isDecl || typeof[id.Obj] == "") { | 
|  | typeof[id.Obj] = typ | 
|  | } | 
|  | } | 
|  |  | 
|  | // Type-check an assignment lhs = rhs. | 
|  | // If isDecl is true, this is := so we can update | 
|  | // the types of the objects that lhs refers to. | 
|  | typecheckAssign := func(lhs, rhs []ast.Expr, isDecl bool) { | 
|  | if len(lhs) > 1 && len(rhs) == 1 { | 
|  | if _, ok := rhs[0].(*ast.CallExpr); ok { | 
|  | t := split(typeof[rhs[0]]) | 
|  | // Lists should have same length but may not; pair what can be paired. | 
|  | for i := 0; i < len(lhs) && i < len(t); i++ { | 
|  | set(lhs[i], t[i], isDecl) | 
|  | } | 
|  | return | 
|  | } | 
|  | } | 
|  | if len(lhs) == 1 && len(rhs) == 2 { | 
|  | // x = y, ok | 
|  | rhs = rhs[:1] | 
|  | } else if len(lhs) == 2 && len(rhs) == 1 { | 
|  | // x, ok = y | 
|  | lhs = lhs[:1] | 
|  | } | 
|  |  | 
|  | // Match as much as we can. | 
|  | for i := 0; i < len(lhs) && i < len(rhs); i++ { | 
|  | x, y := lhs[i], rhs[i] | 
|  | if typeof[y] != "" { | 
|  | set(x, typeof[y], isDecl) | 
|  | } else { | 
|  | set(y, typeof[x], false) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | expand := func(s string) string { | 
|  | typ := cfg.Type[s] | 
|  | if typ != nil && typ.Def != "" { | 
|  | return typ.Def | 
|  | } | 
|  | return s | 
|  | } | 
|  |  | 
|  | // The main type check is a recursive algorithm implemented | 
|  | // by walkBeforeAfter(n, before, after). | 
|  | // Most of it is bottom-up, but in a few places we need | 
|  | // to know the type of the function we are checking. | 
|  | // The before function records that information on | 
|  | // the curfn stack. | 
|  | var curfn []*ast.FuncType | 
|  |  | 
|  | before := func(n interface{}) { | 
|  | // push function type on stack | 
|  | switch n := n.(type) { | 
|  | case *ast.FuncDecl: | 
|  | curfn = append(curfn, n.Type) | 
|  | case *ast.FuncLit: | 
|  | curfn = append(curfn, n.Type) | 
|  | } | 
|  | } | 
|  |  | 
|  | // After is the real type checker. | 
|  | after := func(n interface{}) { | 
|  | if n == nil { | 
|  | return | 
|  | } | 
|  | if false && reflect.TypeOf(n).Kind() == reflect.Ptr { // debugging trace | 
|  | defer func() { | 
|  | if t := typeof[n]; t != "" { | 
|  | pos := fset.Position(n.(ast.Node).Pos()) | 
|  | fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos, gofmt(n), t) | 
|  | } | 
|  | }() | 
|  | } | 
|  |  | 
|  | switch n := n.(type) { | 
|  | case *ast.FuncDecl, *ast.FuncLit: | 
|  | // pop function type off stack | 
|  | curfn = curfn[:len(curfn)-1] | 
|  |  | 
|  | case *ast.FuncType: | 
|  | typeof[n] = mkType(joinFunc(split(typeof[n.Params]), split(typeof[n.Results]))) | 
|  |  | 
|  | case *ast.FieldList: | 
|  | // Field list is concatenation of sub-lists. | 
|  | t := "" | 
|  | for _, field := range n.List { | 
|  | if t != "" { | 
|  | t += ", " | 
|  | } | 
|  | t += typeof[field] | 
|  | } | 
|  | typeof[n] = t | 
|  |  | 
|  | case *ast.Field: | 
|  | // Field is one instance of the type per name. | 
|  | all := "" | 
|  | t := typeof[n.Type] | 
|  | if !isType(t) { | 
|  | // Create a type, because it is typically *T or *p.T | 
|  | // and we might care about that type. | 
|  | t = mkType(gofmt(n.Type)) | 
|  | typeof[n.Type] = t | 
|  | } | 
|  | t = getType(t) | 
|  | if len(n.Names) == 0 { | 
|  | all = t | 
|  | } else { | 
|  | for _, id := range n.Names { | 
|  | if all != "" { | 
|  | all += ", " | 
|  | } | 
|  | all += t | 
|  | typeof[id.Obj] = t | 
|  | typeof[id] = t | 
|  | } | 
|  | } | 
|  | typeof[n] = all | 
|  |  | 
|  | case *ast.ValueSpec: | 
|  | // var declaration. Use type if present. | 
|  | if n.Type != nil { | 
|  | t := typeof[n.Type] | 
|  | if !isType(t) { | 
|  | t = mkType(gofmt(n.Type)) | 
|  | typeof[n.Type] = t | 
|  | } | 
|  | t = getType(t) | 
|  | for _, id := range n.Names { | 
|  | set(id, t, true) | 
|  | } | 
|  | } | 
|  | // Now treat same as assignment. | 
|  | typecheckAssign(makeExprList(n.Names), n.Values, true) | 
|  |  | 
|  | case *ast.AssignStmt: | 
|  | typecheckAssign(n.Lhs, n.Rhs, n.Tok == token.DEFINE) | 
|  |  | 
|  | case *ast.Ident: | 
|  | // Identifier can take its type from underlying object. | 
|  | if t := typeof[n.Obj]; t != "" { | 
|  | typeof[n] = t | 
|  | } | 
|  |  | 
|  | case *ast.SelectorExpr: | 
|  | // Field or method. | 
|  | name := n.Sel.Name | 
|  | if t := typeof[n.X]; t != "" { | 
|  | t = strings.TrimPrefix(t, "*") // implicit * | 
|  | if typ := cfg.Type[t]; typ != nil { | 
|  | if t := typ.dot(cfg, name); t != "" { | 
|  | typeof[n] = t | 
|  | return | 
|  | } | 
|  | } | 
|  | tt := typeof[t+"."+name] | 
|  | if isType(tt) { | 
|  | typeof[n] = getType(tt) | 
|  | return | 
|  | } | 
|  | } | 
|  | // Package selector. | 
|  | if x, ok := n.X.(*ast.Ident); ok && x.Obj == nil { | 
|  | str := x.Name + "." + name | 
|  | if cfg.Type[str] != nil { | 
|  | typeof[n] = mkType(str) | 
|  | return | 
|  | } | 
|  | if t := cfg.typeof(x.Name + "." + name); t != "" { | 
|  | typeof[n] = t | 
|  | return | 
|  | } | 
|  | } | 
|  |  | 
|  | case *ast.CallExpr: | 
|  | // make(T) has type T. | 
|  | if isTopName(n.Fun, "make") && len(n.Args) >= 1 { | 
|  | typeof[n] = gofmt(n.Args[0]) | 
|  | return | 
|  | } | 
|  | // new(T) has type *T | 
|  | if isTopName(n.Fun, "new") && len(n.Args) == 1 { | 
|  | typeof[n] = "*" + gofmt(n.Args[0]) | 
|  | return | 
|  | } | 
|  | // Otherwise, use type of function to determine arguments. | 
|  | t := typeof[n.Fun] | 
|  | if t == "" { | 
|  | t = cfg.External[gofmt(n.Fun)] | 
|  | } | 
|  | in, out := splitFunc(t) | 
|  | if in == nil && out == nil { | 
|  | return | 
|  | } | 
|  | typeof[n] = join(out) | 
|  | for i, arg := range n.Args { | 
|  | if i >= len(in) { | 
|  | break | 
|  | } | 
|  | if typeof[arg] == "" { | 
|  | typeof[arg] = in[i] | 
|  | } | 
|  | } | 
|  |  | 
|  | case *ast.TypeAssertExpr: | 
|  | // x.(type) has type of x. | 
|  | if n.Type == nil { | 
|  | typeof[n] = typeof[n.X] | 
|  | return | 
|  | } | 
|  | // x.(T) has type T. | 
|  | if t := typeof[n.Type]; isType(t) { | 
|  | typeof[n] = getType(t) | 
|  | } else { | 
|  | typeof[n] = gofmt(n.Type) | 
|  | } | 
|  |  | 
|  | case *ast.SliceExpr: | 
|  | // x[i:j] has type of x. | 
|  | typeof[n] = typeof[n.X] | 
|  |  | 
|  | case *ast.IndexExpr: | 
|  | // x[i] has key type of x's type. | 
|  | t := expand(typeof[n.X]) | 
|  | if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") { | 
|  | // Lazy: assume there are no nested [] in the array | 
|  | // length or map key type. | 
|  | if i := strings.Index(t, "]"); i >= 0 { | 
|  | typeof[n] = t[i+1:] | 
|  | } | 
|  | } | 
|  |  | 
|  | case *ast.StarExpr: | 
|  | // *x for x of type *T has type T when x is an expr. | 
|  | // We don't use the result when *x is a type, but | 
|  | // compute it anyway. | 
|  | t := expand(typeof[n.X]) | 
|  | if isType(t) { | 
|  | typeof[n] = "type *" + getType(t) | 
|  | } else if strings.HasPrefix(t, "*") { | 
|  | typeof[n] = t[len("*"):] | 
|  | } | 
|  |  | 
|  | case *ast.UnaryExpr: | 
|  | // &x for x of type T has type *T. | 
|  | t := typeof[n.X] | 
|  | if t != "" && n.Op == token.AND { | 
|  | typeof[n] = "*" + t | 
|  | } | 
|  |  | 
|  | case *ast.CompositeLit: | 
|  | // T{...} has type T. | 
|  | typeof[n] = gofmt(n.Type) | 
|  |  | 
|  | // Propagate types down to values used in the composite literal. | 
|  | t := expand(typeof[n]) | 
|  | if strings.HasPrefix(t, "[") { // array or slice | 
|  | // Lazy: assume there are no nested [] in the array length. | 
|  | if i := strings.Index(t, "]"); i >= 0 { | 
|  | et := t[i+1:] | 
|  | for _, e := range n.Elts { | 
|  | if kv, ok := e.(*ast.KeyValueExpr); ok { | 
|  | e = kv.Value | 
|  | } | 
|  | if typeof[e] == "" { | 
|  | typeof[e] = et | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if strings.HasPrefix(t, "map[") { // map | 
|  | // Lazy: assume there are no nested [] in the map key type. | 
|  | if i := strings.Index(t, "]"); i >= 0 { | 
|  | kt, vt := t[4:i], t[i+1:] | 
|  | for _, e := range n.Elts { | 
|  | if kv, ok := e.(*ast.KeyValueExpr); ok { | 
|  | if typeof[kv.Key] == "" { | 
|  | typeof[kv.Key] = kt | 
|  | } | 
|  | if typeof[kv.Value] == "" { | 
|  | typeof[kv.Value] = vt | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if typ := cfg.Type[t]; typ != nil && len(typ.Field) > 0 { // struct | 
|  | for _, e := range n.Elts { | 
|  | if kv, ok := e.(*ast.KeyValueExpr); ok { | 
|  | if ft := typ.Field[fmt.Sprintf("%s", kv.Key)]; ft != "" { | 
|  | if typeof[kv.Value] == "" { | 
|  | typeof[kv.Value] = ft | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | case *ast.ParenExpr: | 
|  | // (x) has type of x. | 
|  | typeof[n] = typeof[n.X] | 
|  |  | 
|  | case *ast.RangeStmt: | 
|  | t := expand(typeof[n.X]) | 
|  | if t == "" { | 
|  | return | 
|  | } | 
|  | var key, value string | 
|  | if t == "string" { | 
|  | key, value = "int", "rune" | 
|  | } else if strings.HasPrefix(t, "[") { | 
|  | key = "int" | 
|  | if i := strings.Index(t, "]"); i >= 0 { | 
|  | value = t[i+1:] | 
|  | } | 
|  | } else if strings.HasPrefix(t, "map[") { | 
|  | if i := strings.Index(t, "]"); i >= 0 { | 
|  | key, value = t[4:i], t[i+1:] | 
|  | } | 
|  | } | 
|  | changed := false | 
|  | if n.Key != nil && key != "" { | 
|  | changed = true | 
|  | set(n.Key, key, n.Tok == token.DEFINE) | 
|  | } | 
|  | if n.Value != nil && value != "" { | 
|  | changed = true | 
|  | set(n.Value, value, n.Tok == token.DEFINE) | 
|  | } | 
|  | // Ugly failure of vision: already type-checked body. | 
|  | // Do it again now that we have that type info. | 
|  | if changed { | 
|  | typecheck1(cfg, n.Body, typeof, assign) | 
|  | } | 
|  |  | 
|  | case *ast.TypeSwitchStmt: | 
|  | // Type of variable changes for each case in type switch, | 
|  | // but go/parser generates just one variable. | 
|  | // Repeat type check for each case with more precise | 
|  | // type information. | 
|  | as, ok := n.Assign.(*ast.AssignStmt) | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  | varx, ok := as.Lhs[0].(*ast.Ident) | 
|  | if !ok { | 
|  | return | 
|  | } | 
|  | t := typeof[varx] | 
|  | for _, cas := range n.Body.List { | 
|  | cas := cas.(*ast.CaseClause) | 
|  | if len(cas.List) == 1 { | 
|  | // Variable has specific type only when there is | 
|  | // exactly one type in the case list. | 
|  | if tt := typeof[cas.List[0]]; isType(tt) { | 
|  | tt = getType(tt) | 
|  | typeof[varx] = tt | 
|  | typeof[varx.Obj] = tt | 
|  | typecheck1(cfg, cas.Body, typeof, assign) | 
|  | } | 
|  | } | 
|  | } | 
|  | // Restore t. | 
|  | typeof[varx] = t | 
|  | typeof[varx.Obj] = t | 
|  |  | 
|  | case *ast.ReturnStmt: | 
|  | if len(curfn) == 0 { | 
|  | // Probably can't happen. | 
|  | return | 
|  | } | 
|  | f := curfn[len(curfn)-1] | 
|  | res := n.Results | 
|  | if f.Results != nil { | 
|  | t := split(typeof[f.Results]) | 
|  | for i := 0; i < len(res) && i < len(t); i++ { | 
|  | set(res[i], t[i], false) | 
|  | } | 
|  | } | 
|  |  | 
|  | case *ast.BinaryExpr: | 
|  | // Propagate types across binary ops that require two args of the same type. | 
|  | switch n.Op { | 
|  | case token.EQL, token.NEQ: // TODO: more cases. This is enough for the cftype fix. | 
|  | if typeof[n.X] != "" && typeof[n.Y] == "" { | 
|  | typeof[n.Y] = typeof[n.X] | 
|  | } | 
|  | if typeof[n.X] == "" && typeof[n.Y] != "" { | 
|  | typeof[n.X] = typeof[n.Y] | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | walkBeforeAfter(f, before, after) | 
|  | } | 
|  |  | 
|  | // Convert between function type strings and lists of types. | 
|  | // Using strings makes this a little harder, but it makes | 
|  | // a lot of the rest of the code easier. This will all go away | 
|  | // when we can use go/typechecker directly. | 
|  |  | 
|  | // splitFunc splits "func(x,y,z) (a,b,c)" into ["x", "y", "z"] and ["a", "b", "c"]. | 
|  | func splitFunc(s string) (in, out []string) { | 
|  | if !strings.HasPrefix(s, "func(") { | 
|  | return nil, nil | 
|  | } | 
|  |  | 
|  | i := len("func(") // index of beginning of 'in' arguments | 
|  | nparen := 0 | 
|  | for j := i; j < len(s); j++ { | 
|  | switch s[j] { | 
|  | case '(': | 
|  | nparen++ | 
|  | case ')': | 
|  | nparen-- | 
|  | if nparen < 0 { | 
|  | // found end of parameter list | 
|  | out := strings.TrimSpace(s[j+1:]) | 
|  | if len(out) >= 2 && out[0] == '(' && out[len(out)-1] == ')' { | 
|  | out = out[1 : len(out)-1] | 
|  | } | 
|  | return split(s[i:j]), split(out) | 
|  | } | 
|  | } | 
|  | } | 
|  | return nil, nil | 
|  | } | 
|  |  | 
|  | // joinFunc is the inverse of splitFunc. | 
|  | func joinFunc(in, out []string) string { | 
|  | outs := "" | 
|  | if len(out) == 1 { | 
|  | outs = " " + out[0] | 
|  | } else if len(out) > 1 { | 
|  | outs = " (" + join(out) + ")" | 
|  | } | 
|  | return "func(" + join(in) + ")" + outs | 
|  | } | 
|  |  | 
|  | // split splits "int, float" into ["int", "float"] and splits "" into []. | 
|  | func split(s string) []string { | 
|  | out := []string{} | 
|  | i := 0 // current type being scanned is s[i:j]. | 
|  | nparen := 0 | 
|  | for j := 0; j < len(s); j++ { | 
|  | switch s[j] { | 
|  | case ' ': | 
|  | if i == j { | 
|  | i++ | 
|  | } | 
|  | case '(': | 
|  | nparen++ | 
|  | case ')': | 
|  | nparen-- | 
|  | if nparen < 0 { | 
|  | // probably can't happen | 
|  | return nil | 
|  | } | 
|  | case ',': | 
|  | if nparen == 0 { | 
|  | if i < j { | 
|  | out = append(out, s[i:j]) | 
|  | } | 
|  | i = j + 1 | 
|  | } | 
|  | } | 
|  | } | 
|  | if nparen != 0 { | 
|  | // probably can't happen | 
|  | return nil | 
|  | } | 
|  | if i < len(s) { | 
|  | out = append(out, s[i:]) | 
|  | } | 
|  | return out | 
|  | } | 
|  |  | 
|  | // join is the inverse of split. | 
|  | func join(x []string) string { | 
|  | return strings.Join(x, ", ") | 
|  | } |