...

Source file src/cmd/fix/typecheck.go

Documentation: cmd/fix

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/parser"
    11  	"go/token"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"reflect"
    16  	"runtime"
    17  	"strings"
    18  )
    19  
    20  // Partial type checker.
    21  //
    22  // The fact that it is partial is very important: the input is
    23  // an AST and a description of some type information to
    24  // assume about one or more packages, but not all the
    25  // packages that the program imports. The checker is
    26  // expected to do as much as it can with what it has been
    27  // given. There is not enough information supplied to do
    28  // a full type check, but the type checker is expected to
    29  // apply information that can be derived from variable
    30  // declarations, function and method returns, and type switches
    31  // as far as it can, so that the caller can still tell the types
    32  // of expression relevant to a particular fix.
    33  //
    34  // TODO(rsc,gri): Replace with go/typechecker.
    35  // Doing that could be an interesting test case for go/typechecker:
    36  // the constraints about working with partial information will
    37  // likely exercise it in interesting ways. The ideal interface would
    38  // be to pass typecheck a map from importpath to package API text
    39  // (Go source code), but for now we use data structures (TypeConfig, Type).
    40  //
    41  // The strings mostly use gofmt form.
    42  //
    43  // A Field or FieldList has as its type a comma-separated list
    44  // of the types of the fields. For example, the field list
    45  //	x, y, z int
    46  // has type "int, int, int".
    47  
    48  // The prefix "type " is the type of a type.
    49  // For example, given
    50  //	var x int
    51  //	type T int
    52  // x's type is "int" but T's type is "type int".
    53  // mkType inserts the "type " prefix.
    54  // getType removes it.
    55  // isType tests for it.
    56  
    57  func mkType(t string) string {
    58  	return "type " + t
    59  }
    60  
    61  func getType(t string) string {
    62  	if !isType(t) {
    63  		return ""
    64  	}
    65  	return t[len("type "):]
    66  }
    67  
    68  func isType(t string) bool {
    69  	return strings.HasPrefix(t, "type ")
    70  }
    71  
    72  // TypeConfig describes the universe of relevant types.
    73  // For ease of creation, the types are all referred to by string
    74  // name (e.g., "reflect.Value").  TypeByName is the only place
    75  // where the strings are resolved.
    76  
    77  type TypeConfig struct {
    78  	Type map[string]*Type
    79  	Var  map[string]string
    80  	Func map[string]string
    81  
    82  	// External maps from a name to its type.
    83  	// It provides additional typings not present in the Go source itself.
    84  	// For now, the only additional typings are those generated by cgo.
    85  	External map[string]string
    86  }
    87  
    88  // typeof returns the type of the given name, which may be of
    89  // the form "x" or "p.X".
    90  func (cfg *TypeConfig) typeof(name string) string {
    91  	if cfg.Var != nil {
    92  		if t := cfg.Var[name]; t != "" {
    93  			return t
    94  		}
    95  	}
    96  	if cfg.Func != nil {
    97  		if t := cfg.Func[name]; t != "" {
    98  			return "func()" + t
    99  		}
   100  	}
   101  	return ""
   102  }
   103  
   104  // Type describes the Fields and Methods of a type.
   105  // If the field or method cannot be found there, it is next
   106  // looked for in the Embed list.
   107  type Type struct {
   108  	Field  map[string]string // map field name to type
   109  	Method map[string]string // map method name to comma-separated return types (should start with "func ")
   110  	Embed  []string          // list of types this type embeds (for extra methods)
   111  	Def    string            // definition of named type
   112  }
   113  
   114  // dot returns the type of "typ.name", making its decision
   115  // using the type information in cfg.
   116  func (typ *Type) dot(cfg *TypeConfig, name string) string {
   117  	if typ.Field != nil {
   118  		if t := typ.Field[name]; t != "" {
   119  			return t
   120  		}
   121  	}
   122  	if typ.Method != nil {
   123  		if t := typ.Method[name]; t != "" {
   124  			return t
   125  		}
   126  	}
   127  
   128  	for _, e := range typ.Embed {
   129  		etyp := cfg.Type[e]
   130  		if etyp != nil {
   131  			if t := etyp.dot(cfg, name); t != "" {
   132  				return t
   133  			}
   134  		}
   135  	}
   136  
   137  	return ""
   138  }
   139  
   140  // typecheck type checks the AST f assuming the information in cfg.
   141  // It returns two maps with type information:
   142  // typeof maps AST nodes to type information in gofmt string form.
   143  // assign maps type strings to lists of expressions that were assigned
   144  // to values of another type that were assigned to that type.
   145  func typecheck(cfg *TypeConfig, f *ast.File) (typeof map[any]string, assign map[string][]any) {
   146  	typeof = make(map[any]string)
   147  	assign = make(map[string][]any)
   148  	cfg1 := &TypeConfig{}
   149  	*cfg1 = *cfg // make copy so we can add locally
   150  	copied := false
   151  
   152  	// If we import "C", add types of cgo objects.
   153  	cfg.External = map[string]string{}
   154  	cfg1.External = cfg.External
   155  	if imports(f, "C") {
   156  		// Run cgo on gofmtFile(f)
   157  		// Parse, extract decls from _cgo_gotypes.go
   158  		// Map _Ctype_* types to C.* types.
   159  		err := func() error {
   160  			txt, err := gofmtFile(f)
   161  			if err != nil {
   162  				return err
   163  			}
   164  			dir, err := os.MkdirTemp(os.TempDir(), "fix_cgo_typecheck")
   165  			if err != nil {
   166  				return err
   167  			}
   168  			defer os.RemoveAll(dir)
   169  			err = os.WriteFile(filepath.Join(dir, "in.go"), txt, 0600)
   170  			if err != nil {
   171  				return err
   172  			}
   173  			goCmd := "go"
   174  			if goroot := runtime.GOROOT(); goroot != "" {
   175  				goCmd = filepath.Join(goroot, "bin", "go")
   176  			}
   177  			cmd := exec.Command(goCmd, "tool", "cgo", "-objdir", dir, "-srcdir", dir, "in.go")
   178  			if reportCgoError != nil {
   179  				// Since cgo command errors will be reported, also forward the error
   180  				// output from the command for debugging.
   181  				cmd.Stderr = os.Stderr
   182  			}
   183  			err = cmd.Run()
   184  			if err != nil {
   185  				return err
   186  			}
   187  			out, err := os.ReadFile(filepath.Join(dir, "_cgo_gotypes.go"))
   188  			if err != nil {
   189  				return err
   190  			}
   191  			cgo, err := parser.ParseFile(token.NewFileSet(), "cgo.go", out, 0)
   192  			if err != nil {
   193  				return err
   194  			}
   195  			for _, decl := range cgo.Decls {
   196  				fn, ok := decl.(*ast.FuncDecl)
   197  				if !ok {
   198  					continue
   199  				}
   200  				if strings.HasPrefix(fn.Name.Name, "_Cfunc_") {
   201  					var params, results []string
   202  					for _, p := range fn.Type.Params.List {
   203  						t := gofmt(p.Type)
   204  						t = strings.ReplaceAll(t, "_Ctype_", "C.")
   205  						params = append(params, t)
   206  					}
   207  					for _, r := range fn.Type.Results.List {
   208  						t := gofmt(r.Type)
   209  						t = strings.ReplaceAll(t, "_Ctype_", "C.")
   210  						results = append(results, t)
   211  					}
   212  					cfg.External["C."+fn.Name.Name[7:]] = joinFunc(params, results)
   213  				}
   214  			}
   215  			return nil
   216  		}()
   217  		if err != nil {
   218  			if reportCgoError == nil {
   219  				fmt.Fprintf(os.Stderr, "go fix: warning: no cgo types: %s\n", err)
   220  			} else {
   221  				reportCgoError(err)
   222  			}
   223  		}
   224  	}
   225  
   226  	// gather function declarations
   227  	for _, decl := range f.Decls {
   228  		fn, ok := decl.(*ast.FuncDecl)
   229  		if !ok {
   230  			continue
   231  		}
   232  		typecheck1(cfg, fn.Type, typeof, assign)
   233  		t := typeof[fn.Type]
   234  		if fn.Recv != nil {
   235  			// The receiver must be a type.
   236  			rcvr := typeof[fn.Recv]
   237  			if !isType(rcvr) {
   238  				if len(fn.Recv.List) != 1 {
   239  					continue
   240  				}
   241  				rcvr = mkType(gofmt(fn.Recv.List[0].Type))
   242  				typeof[fn.Recv.List[0].Type] = rcvr
   243  			}
   244  			rcvr = getType(rcvr)
   245  			if rcvr != "" && rcvr[0] == '*' {
   246  				rcvr = rcvr[1:]
   247  			}
   248  			typeof[rcvr+"."+fn.Name.Name] = t
   249  		} else {
   250  			if isType(t) {
   251  				t = getType(t)
   252  			} else {
   253  				t = gofmt(fn.Type)
   254  			}
   255  			typeof[fn.Name] = t
   256  
   257  			// Record typeof[fn.Name.Obj] for future references to fn.Name.
   258  			typeof[fn.Name.Obj] = t
   259  		}
   260  	}
   261  
   262  	// gather struct declarations
   263  	for _, decl := range f.Decls {
   264  		d, ok := decl.(*ast.GenDecl)
   265  		if ok {
   266  			for _, s := range d.Specs {
   267  				switch s := s.(type) {
   268  				case *ast.TypeSpec:
   269  					if cfg1.Type[s.Name.Name] != nil {
   270  						break
   271  					}
   272  					if !copied {
   273  						copied = true
   274  						// Copy map lazily: it's time.
   275  						cfg1.Type = make(map[string]*Type)
   276  						for k, v := range cfg.Type {
   277  							cfg1.Type[k] = v
   278  						}
   279  					}
   280  					t := &Type{Field: map[string]string{}}
   281  					cfg1.Type[s.Name.Name] = t
   282  					switch st := s.Type.(type) {
   283  					case *ast.StructType:
   284  						for _, f := range st.Fields.List {
   285  							for _, n := range f.Names {
   286  								t.Field[n.Name] = gofmt(f.Type)
   287  							}
   288  						}
   289  					case *ast.ArrayType, *ast.StarExpr, *ast.MapType:
   290  						t.Def = gofmt(st)
   291  					}
   292  				}
   293  			}
   294  		}
   295  	}
   296  
   297  	typecheck1(cfg1, f, typeof, assign)
   298  	return typeof, assign
   299  }
   300  
   301  // reportCgoError, if non-nil, reports a non-nil error from running the "cgo"
   302  // tool. (Set to a non-nil hook during testing if cgo is expected to work.)
   303  var reportCgoError func(err error)
   304  
   305  func makeExprList(a []*ast.Ident) []ast.Expr {
   306  	var b []ast.Expr
   307  	for _, x := range a {
   308  		b = append(b, x)
   309  	}
   310  	return b
   311  }
   312  
   313  // typecheck1 is the recursive form of typecheck.
   314  // It is like typecheck but adds to the information in typeof
   315  // instead of allocating a new map.
   316  func typecheck1(cfg *TypeConfig, f any, typeof map[any]string, assign map[string][]any) {
   317  	// set sets the type of n to typ.
   318  	// If isDecl is true, n is being declared.
   319  	set := func(n ast.Expr, typ string, isDecl bool) {
   320  		if typeof[n] != "" || typ == "" {
   321  			if typeof[n] != typ {
   322  				assign[typ] = append(assign[typ], n)
   323  			}
   324  			return
   325  		}
   326  		typeof[n] = typ
   327  
   328  		// If we obtained typ from the declaration of x
   329  		// propagate the type to all the uses.
   330  		// The !isDecl case is a cheat here, but it makes
   331  		// up in some cases for not paying attention to
   332  		// struct fields. The real type checker will be
   333  		// more accurate so we won't need the cheat.
   334  		if id, ok := n.(*ast.Ident); ok && id.Obj != nil && (isDecl || typeof[id.Obj] == "") {
   335  			typeof[id.Obj] = typ
   336  		}
   337  	}
   338  
   339  	// Type-check an assignment lhs = rhs.
   340  	// If isDecl is true, this is := so we can update
   341  	// the types of the objects that lhs refers to.
   342  	typecheckAssign := func(lhs, rhs []ast.Expr, isDecl bool) {
   343  		if len(lhs) > 1 && len(rhs) == 1 {
   344  			if _, ok := rhs[0].(*ast.CallExpr); ok {
   345  				t := split(typeof[rhs[0]])
   346  				// Lists should have same length but may not; pair what can be paired.
   347  				for i := 0; i < len(lhs) && i < len(t); i++ {
   348  					set(lhs[i], t[i], isDecl)
   349  				}
   350  				return
   351  			}
   352  		}
   353  		if len(lhs) == 1 && len(rhs) == 2 {
   354  			// x = y, ok
   355  			rhs = rhs[:1]
   356  		} else if len(lhs) == 2 && len(rhs) == 1 {
   357  			// x, ok = y
   358  			lhs = lhs[:1]
   359  		}
   360  
   361  		// Match as much as we can.
   362  		for i := 0; i < len(lhs) && i < len(rhs); i++ {
   363  			x, y := lhs[i], rhs[i]
   364  			if typeof[y] != "" {
   365  				set(x, typeof[y], isDecl)
   366  			} else {
   367  				set(y, typeof[x], false)
   368  			}
   369  		}
   370  	}
   371  
   372  	expand := func(s string) string {
   373  		typ := cfg.Type[s]
   374  		if typ != nil && typ.Def != "" {
   375  			return typ.Def
   376  		}
   377  		return s
   378  	}
   379  
   380  	// The main type check is a recursive algorithm implemented
   381  	// by walkBeforeAfter(n, before, after).
   382  	// Most of it is bottom-up, but in a few places we need
   383  	// to know the type of the function we are checking.
   384  	// The before function records that information on
   385  	// the curfn stack.
   386  	var curfn []*ast.FuncType
   387  
   388  	before := func(n any) {
   389  		// push function type on stack
   390  		switch n := n.(type) {
   391  		case *ast.FuncDecl:
   392  			curfn = append(curfn, n.Type)
   393  		case *ast.FuncLit:
   394  			curfn = append(curfn, n.Type)
   395  		}
   396  	}
   397  
   398  	// After is the real type checker.
   399  	after := func(n any) {
   400  		if n == nil {
   401  			return
   402  		}
   403  		if false && reflect.TypeOf(n).Kind() == reflect.Pointer { // debugging trace
   404  			defer func() {
   405  				if t := typeof[n]; t != "" {
   406  					pos := fset.Position(n.(ast.Node).Pos())
   407  					fmt.Fprintf(os.Stderr, "%s: typeof[%s] = %s\n", pos, gofmt(n), t)
   408  				}
   409  			}()
   410  		}
   411  
   412  		switch n := n.(type) {
   413  		case *ast.FuncDecl, *ast.FuncLit:
   414  			// pop function type off stack
   415  			curfn = curfn[:len(curfn)-1]
   416  
   417  		case *ast.FuncType:
   418  			typeof[n] = mkType(joinFunc(split(typeof[n.Params]), split(typeof[n.Results])))
   419  
   420  		case *ast.FieldList:
   421  			// Field list is concatenation of sub-lists.
   422  			t := ""
   423  			for _, field := range n.List {
   424  				if t != "" {
   425  					t += ", "
   426  				}
   427  				t += typeof[field]
   428  			}
   429  			typeof[n] = t
   430  
   431  		case *ast.Field:
   432  			// Field is one instance of the type per name.
   433  			all := ""
   434  			t := typeof[n.Type]
   435  			if !isType(t) {
   436  				// Create a type, because it is typically *T or *p.T
   437  				// and we might care about that type.
   438  				t = mkType(gofmt(n.Type))
   439  				typeof[n.Type] = t
   440  			}
   441  			t = getType(t)
   442  			if len(n.Names) == 0 {
   443  				all = t
   444  			} else {
   445  				for _, id := range n.Names {
   446  					if all != "" {
   447  						all += ", "
   448  					}
   449  					all += t
   450  					typeof[id.Obj] = t
   451  					typeof[id] = t
   452  				}
   453  			}
   454  			typeof[n] = all
   455  
   456  		case *ast.ValueSpec:
   457  			// var declaration. Use type if present.
   458  			if n.Type != nil {
   459  				t := typeof[n.Type]
   460  				if !isType(t) {
   461  					t = mkType(gofmt(n.Type))
   462  					typeof[n.Type] = t
   463  				}
   464  				t = getType(t)
   465  				for _, id := range n.Names {
   466  					set(id, t, true)
   467  				}
   468  			}
   469  			// Now treat same as assignment.
   470  			typecheckAssign(makeExprList(n.Names), n.Values, true)
   471  
   472  		case *ast.AssignStmt:
   473  			typecheckAssign(n.Lhs, n.Rhs, n.Tok == token.DEFINE)
   474  
   475  		case *ast.Ident:
   476  			// Identifier can take its type from underlying object.
   477  			if t := typeof[n.Obj]; t != "" {
   478  				typeof[n] = t
   479  			}
   480  
   481  		case *ast.SelectorExpr:
   482  			// Field or method.
   483  			name := n.Sel.Name
   484  			if t := typeof[n.X]; t != "" {
   485  				t = strings.TrimPrefix(t, "*") // implicit *
   486  				if typ := cfg.Type[t]; typ != nil {
   487  					if t := typ.dot(cfg, name); t != "" {
   488  						typeof[n] = t
   489  						return
   490  					}
   491  				}
   492  				tt := typeof[t+"."+name]
   493  				if isType(tt) {
   494  					typeof[n] = getType(tt)
   495  					return
   496  				}
   497  			}
   498  			// Package selector.
   499  			if x, ok := n.X.(*ast.Ident); ok && x.Obj == nil {
   500  				str := x.Name + "." + name
   501  				if cfg.Type[str] != nil {
   502  					typeof[n] = mkType(str)
   503  					return
   504  				}
   505  				if t := cfg.typeof(x.Name + "." + name); t != "" {
   506  					typeof[n] = t
   507  					return
   508  				}
   509  			}
   510  
   511  		case *ast.CallExpr:
   512  			// make(T) has type T.
   513  			if isTopName(n.Fun, "make") && len(n.Args) >= 1 {
   514  				typeof[n] = gofmt(n.Args[0])
   515  				return
   516  			}
   517  			// new(T) has type *T
   518  			if isTopName(n.Fun, "new") && len(n.Args) == 1 {
   519  				typeof[n] = "*" + gofmt(n.Args[0])
   520  				return
   521  			}
   522  			// Otherwise, use type of function to determine arguments.
   523  			t := typeof[n.Fun]
   524  			if t == "" {
   525  				t = cfg.External[gofmt(n.Fun)]
   526  			}
   527  			in, out := splitFunc(t)
   528  			if in == nil && out == nil {
   529  				return
   530  			}
   531  			typeof[n] = join(out)
   532  			for i, arg := range n.Args {
   533  				if i >= len(in) {
   534  					break
   535  				}
   536  				if typeof[arg] == "" {
   537  					typeof[arg] = in[i]
   538  				}
   539  			}
   540  
   541  		case *ast.TypeAssertExpr:
   542  			// x.(type) has type of x.
   543  			if n.Type == nil {
   544  				typeof[n] = typeof[n.X]
   545  				return
   546  			}
   547  			// x.(T) has type T.
   548  			if t := typeof[n.Type]; isType(t) {
   549  				typeof[n] = getType(t)
   550  			} else {
   551  				typeof[n] = gofmt(n.Type)
   552  			}
   553  
   554  		case *ast.SliceExpr:
   555  			// x[i:j] has type of x.
   556  			typeof[n] = typeof[n.X]
   557  
   558  		case *ast.IndexExpr:
   559  			// x[i] has key type of x's type.
   560  			t := expand(typeof[n.X])
   561  			if strings.HasPrefix(t, "[") || strings.HasPrefix(t, "map[") {
   562  				// Lazy: assume there are no nested [] in the array
   563  				// length or map key type.
   564  				if _, elem, ok := strings.Cut(t, "]"); ok {
   565  					typeof[n] = elem
   566  				}
   567  			}
   568  
   569  		case *ast.StarExpr:
   570  			// *x for x of type *T has type T when x is an expr.
   571  			// We don't use the result when *x is a type, but
   572  			// compute it anyway.
   573  			t := expand(typeof[n.X])
   574  			if isType(t) {
   575  				typeof[n] = "type *" + getType(t)
   576  			} else if strings.HasPrefix(t, "*") {
   577  				typeof[n] = t[len("*"):]
   578  			}
   579  
   580  		case *ast.UnaryExpr:
   581  			// &x for x of type T has type *T.
   582  			t := typeof[n.X]
   583  			if t != "" && n.Op == token.AND {
   584  				typeof[n] = "*" + t
   585  			}
   586  
   587  		case *ast.CompositeLit:
   588  			// T{...} has type T.
   589  			typeof[n] = gofmt(n.Type)
   590  
   591  			// Propagate types down to values used in the composite literal.
   592  			t := expand(typeof[n])
   593  			if strings.HasPrefix(t, "[") { // array or slice
   594  				// Lazy: assume there are no nested [] in the array length.
   595  				if _, et, ok := strings.Cut(t, "]"); ok {
   596  					for _, e := range n.Elts {
   597  						if kv, ok := e.(*ast.KeyValueExpr); ok {
   598  							e = kv.Value
   599  						}
   600  						if typeof[e] == "" {
   601  							typeof[e] = et
   602  						}
   603  					}
   604  				}
   605  			}
   606  			if strings.HasPrefix(t, "map[") { // map
   607  				// Lazy: assume there are no nested [] in the map key type.
   608  				if kt, vt, ok := strings.Cut(t[len("map["):], "]"); ok {
   609  					for _, e := range n.Elts {
   610  						if kv, ok := e.(*ast.KeyValueExpr); ok {
   611  							if typeof[kv.Key] == "" {
   612  								typeof[kv.Key] = kt
   613  							}
   614  							if typeof[kv.Value] == "" {
   615  								typeof[kv.Value] = vt
   616  							}
   617  						}
   618  					}
   619  				}
   620  			}
   621  			if typ := cfg.Type[t]; typ != nil && len(typ.Field) > 0 { // struct
   622  				for _, e := range n.Elts {
   623  					if kv, ok := e.(*ast.KeyValueExpr); ok {
   624  						if ft := typ.Field[fmt.Sprintf("%s", kv.Key)]; ft != "" {
   625  							if typeof[kv.Value] == "" {
   626  								typeof[kv.Value] = ft
   627  							}
   628  						}
   629  					}
   630  				}
   631  			}
   632  
   633  		case *ast.ParenExpr:
   634  			// (x) has type of x.
   635  			typeof[n] = typeof[n.X]
   636  
   637  		case *ast.RangeStmt:
   638  			t := expand(typeof[n.X])
   639  			if t == "" {
   640  				return
   641  			}
   642  			var key, value string
   643  			if t == "string" {
   644  				key, value = "int", "rune"
   645  			} else if strings.HasPrefix(t, "[") {
   646  				key = "int"
   647  				_, value, _ = strings.Cut(t, "]")
   648  			} else if strings.HasPrefix(t, "map[") {
   649  				if k, v, ok := strings.Cut(t[len("map["):], "]"); ok {
   650  					key, value = k, v
   651  				}
   652  			}
   653  			changed := false
   654  			if n.Key != nil && key != "" {
   655  				changed = true
   656  				set(n.Key, key, n.Tok == token.DEFINE)
   657  			}
   658  			if n.Value != nil && value != "" {
   659  				changed = true
   660  				set(n.Value, value, n.Tok == token.DEFINE)
   661  			}
   662  			// Ugly failure of vision: already type-checked body.
   663  			// Do it again now that we have that type info.
   664  			if changed {
   665  				typecheck1(cfg, n.Body, typeof, assign)
   666  			}
   667  
   668  		case *ast.TypeSwitchStmt:
   669  			// Type of variable changes for each case in type switch,
   670  			// but go/parser generates just one variable.
   671  			// Repeat type check for each case with more precise
   672  			// type information.
   673  			as, ok := n.Assign.(*ast.AssignStmt)
   674  			if !ok {
   675  				return
   676  			}
   677  			varx, ok := as.Lhs[0].(*ast.Ident)
   678  			if !ok {
   679  				return
   680  			}
   681  			t := typeof[varx]
   682  			for _, cas := range n.Body.List {
   683  				cas := cas.(*ast.CaseClause)
   684  				if len(cas.List) == 1 {
   685  					// Variable has specific type only when there is
   686  					// exactly one type in the case list.
   687  					if tt := typeof[cas.List[0]]; isType(tt) {
   688  						tt = getType(tt)
   689  						typeof[varx] = tt
   690  						typeof[varx.Obj] = tt
   691  						typecheck1(cfg, cas.Body, typeof, assign)
   692  					}
   693  				}
   694  			}
   695  			// Restore t.
   696  			typeof[varx] = t
   697  			typeof[varx.Obj] = t
   698  
   699  		case *ast.ReturnStmt:
   700  			if len(curfn) == 0 {
   701  				// Probably can't happen.
   702  				return
   703  			}
   704  			f := curfn[len(curfn)-1]
   705  			res := n.Results
   706  			if f.Results != nil {
   707  				t := split(typeof[f.Results])
   708  				for i := 0; i < len(res) && i < len(t); i++ {
   709  					set(res[i], t[i], false)
   710  				}
   711  			}
   712  
   713  		case *ast.BinaryExpr:
   714  			// Propagate types across binary ops that require two args of the same type.
   715  			switch n.Op {
   716  			case token.EQL, token.NEQ: // TODO: more cases. This is enough for the cftype fix.
   717  				if typeof[n.X] != "" && typeof[n.Y] == "" {
   718  					typeof[n.Y] = typeof[n.X]
   719  				}
   720  				if typeof[n.X] == "" && typeof[n.Y] != "" {
   721  					typeof[n.X] = typeof[n.Y]
   722  				}
   723  			}
   724  		}
   725  	}
   726  	walkBeforeAfter(f, before, after)
   727  }
   728  
   729  // Convert between function type strings and lists of types.
   730  // Using strings makes this a little harder, but it makes
   731  // a lot of the rest of the code easier. This will all go away
   732  // when we can use go/typechecker directly.
   733  
   734  // splitFunc splits "func(x,y,z) (a,b,c)" into ["x", "y", "z"] and ["a", "b", "c"].
   735  func splitFunc(s string) (in, out []string) {
   736  	if !strings.HasPrefix(s, "func(") {
   737  		return nil, nil
   738  	}
   739  
   740  	i := len("func(") // index of beginning of 'in' arguments
   741  	nparen := 0
   742  	for j := i; j < len(s); j++ {
   743  		switch s[j] {
   744  		case '(':
   745  			nparen++
   746  		case ')':
   747  			nparen--
   748  			if nparen < 0 {
   749  				// found end of parameter list
   750  				out := strings.TrimSpace(s[j+1:])
   751  				if len(out) >= 2 && out[0] == '(' && out[len(out)-1] == ')' {
   752  					out = out[1 : len(out)-1]
   753  				}
   754  				return split(s[i:j]), split(out)
   755  			}
   756  		}
   757  	}
   758  	return nil, nil
   759  }
   760  
   761  // joinFunc is the inverse of splitFunc.
   762  func joinFunc(in, out []string) string {
   763  	outs := ""
   764  	if len(out) == 1 {
   765  		outs = " " + out[0]
   766  	} else if len(out) > 1 {
   767  		outs = " (" + join(out) + ")"
   768  	}
   769  	return "func(" + join(in) + ")" + outs
   770  }
   771  
   772  // split splits "int, float" into ["int", "float"] and splits "" into [].
   773  func split(s string) []string {
   774  	out := []string{}
   775  	i := 0 // current type being scanned is s[i:j].
   776  	nparen := 0
   777  	for j := 0; j < len(s); j++ {
   778  		switch s[j] {
   779  		case ' ':
   780  			if i == j {
   781  				i++
   782  			}
   783  		case '(':
   784  			nparen++
   785  		case ')':
   786  			nparen--
   787  			if nparen < 0 {
   788  				// probably can't happen
   789  				return nil
   790  			}
   791  		case ',':
   792  			if nparen == 0 {
   793  				if i < j {
   794  					out = append(out, s[i:j])
   795  				}
   796  				i = j + 1
   797  			}
   798  		}
   799  	}
   800  	if nparen != 0 {
   801  		// probably can't happen
   802  		return nil
   803  	}
   804  	if i < len(s) {
   805  		out = append(out, s[i:])
   806  	}
   807  	return out
   808  }
   809  
   810  // join is the inverse of split.
   811  func join(x []string) string {
   812  	return strings.Join(x, ", ")
   813  }
   814  

View as plain text