Source file src/go/types/generate_test.go

     1  // Copyright 2023 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  // This file implements a custom generator to create various go/types
     6  // source files from the corresponding types2 files.
     7  
     8  package types_test
     9  
    10  import (
    11  	"bytes"
    12  	"flag"
    13  	"fmt"
    14  	"go/ast"
    15  	"go/format"
    16  	"go/parser"
    17  	"go/token"
    18  	"internal/diff"
    19  	"os"
    20  	"path/filepath"
    21  	"runtime"
    22  	"strings"
    23  	"testing"
    24  )
    25  
    26  var filesToWrite = flag.String("write", "", `go/types files to generate, or "all" for all files`)
    27  
    28  const (
    29  	srcDir = "/src/cmd/compile/internal/types2/"
    30  	dstDir = "/src/go/types/"
    31  )
    32  
    33  // TestGenerate verifies that generated files in go/types match their types2
    34  // counterpart. If -write is set, this test actually writes the expected
    35  // content to go/types; otherwise, it just compares with the existing content.
    36  func TestGenerate(t *testing.T) {
    37  	// If filesToWrite is set, write the generated content to disk.
    38  	// In the special case of "all", write all files in filemap.
    39  	write := *filesToWrite != ""
    40  	var files []string // files to process
    41  	if *filesToWrite != "" && *filesToWrite != "all" {
    42  		files = strings.Split(*filesToWrite, ",")
    43  	} else {
    44  		for file := range filemap {
    45  			files = append(files, file)
    46  		}
    47  	}
    48  
    49  	for _, filename := range files {
    50  		generate(t, filename, write)
    51  	}
    52  }
    53  
    54  func generate(t *testing.T, filename string, write bool) {
    55  	// parse src (cmd/compile/internal/types2)
    56  	srcFilename := filepath.FromSlash(runtime.GOROOT() + srcDir + filename)
    57  	file, err := parser.ParseFile(fset, srcFilename, nil, parser.ParseComments|parser.SkipObjectResolution)
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	// fix package name
    63  	file.Name.Name = strings.ReplaceAll(file.Name.Name, "types2", "types")
    64  
    65  	// rewrite AST as needed
    66  	if action := filemap[filename]; action != nil {
    67  		action(file)
    68  	}
    69  
    70  	// format AST
    71  	var buf bytes.Buffer
    72  	rel, _ := filepath.Rel(dstDir, srcDir)
    73  	fmt.Fprintf(&buf, "// Code generated by \"go test -run=Generate -write=all\"; DO NOT EDIT.\n")
    74  	fmt.Fprintf(&buf, "// Source: %s/%s\n\n", filepath.ToSlash(rel), filename)
    75  	if err := format.Node(&buf, fset, file); err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	generatedContent := buf.Bytes()
    79  
    80  	// read dst (go/types)
    81  	dstFilename := filepath.FromSlash(runtime.GOROOT() + dstDir + filename)
    82  	onDiskContent, err := os.ReadFile(dstFilename)
    83  	if err != nil {
    84  		t.Fatalf("reading %q: %v", filename, err)
    85  	}
    86  
    87  	// compare on-disk dst with buffer generated from src.
    88  	if d := diff.Diff(filename+" (on disk in "+dstDir+")", onDiskContent, filename+" (generated from "+srcDir+")", generatedContent); d != nil {
    89  		if write {
    90  			t.Logf("applying change:\n%s", d)
    91  			if err := os.WriteFile(dstFilename, generatedContent, 0o644); err != nil {
    92  				t.Fatalf("writing %q: %v", filename, err)
    93  			}
    94  		} else {
    95  			t.Errorf("file on disk in %s is stale:\n%s", dstDir, d)
    96  		}
    97  	}
    98  }
    99  
   100  type action func(in *ast.File)
   101  
   102  var filemap = map[string]action{
   103  	"alias.go": fixTokenPos,
   104  	"alias_test.go": func(f *ast.File) {
   105  		renameImportPath(f, `"cmd/compile/internal/types2"->"go/types"`)
   106  		renameIdents(f, "types2->types")
   107  	},
   108  	"assignments.go": func(f *ast.File) {
   109  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   110  		renameSelectorExprs(f, "syntax.Name->ast.Ident", "ident.Value->ident.Name", "ast.Pos->token.Pos") // must happen before renaming identifiers
   111  		renameIdents(f, "syntax->ast", "poser->positioner", "nopos->noposn")
   112  	},
   113  	"array.go":          nil,
   114  	"api_predicates.go": nil,
   115  	"basic.go":          nil,
   116  	"builtins.go": func(f *ast.File) {
   117  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   118  		renameIdents(f, "syntax->ast")
   119  		renameSelectors(f, "ArgList->Args")
   120  		fixSelValue(f)
   121  		fixAtPosCall(f)
   122  	},
   123  	"builtins_test.go": func(f *ast.File) {
   124  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`, `"cmd/compile/internal/types2"->"go/types"`)
   125  		renameSelectorExprs(f, "syntax.Name->ast.Ident", "p.Value->p.Name") // must happen before renaming identifiers
   126  		renameIdents(f, "syntax->ast")
   127  	},
   128  	"chan.go":         nil,
   129  	"const.go":        fixTokenPos,
   130  	"context.go":      nil,
   131  	"context_test.go": nil,
   132  	"conversions.go":  nil,
   133  	"cycles.go": func(f *ast.File) {
   134  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   135  		renameSelectorExprs(f, "syntax.Name->ast.Ident", "rhs.Value->rhs.Name")
   136  		renameSelectors(f, "Trace->_Trace")
   137  	},
   138  	"errors_test.go":  func(f *ast.File) { renameIdents(f, "nopos->noposn") },
   139  	"errsupport.go":   nil,
   140  	"gccgosizes.go":   nil,
   141  	"gcsizes.go":      func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
   142  	"hilbert_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"->"go/types"`) },
   143  	"infer.go":        func(f *ast.File) { fixTokenPos(f); fixInferSig(f) },
   144  	"initorder.go":    nil,
   145  	// "initorder.go": fixErrErrorfCall, // disabled for now due to unresolved error_ use implications for gopls
   146  	"instantiate.go":      func(f *ast.File) { fixTokenPos(f); fixCheckErrorfCall(f); fixSprintf(f) },
   147  	"instantiate_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"->"go/types"`) },
   148  	"literals.go": func(f *ast.File) {
   149  		insertImportPath(f, `"go/token"`)
   150  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   151  		renameSelectorExprs(f,
   152  			"syntax.IntLit->token.INT", "syntax.FloatLit->token.FLOAT", "syntax.ImagLit->token.IMAG",
   153  			"syntax.Name->ast.Ident", "key.Value->key.Name", "atyp.Elem->atyp.Elt") // must happen before renaming identifiers
   154  		renameIdents(f, "syntax->ast")
   155  		renameSelectors(f, "ElemList->Elts")
   156  	},
   157  	"lookup.go":    fixTokenPos,
   158  	"main_test.go": nil,
   159  	"map.go":       nil,
   160  	"mono.go": func(f *ast.File) {
   161  		fixTokenPos(f)
   162  		insertImportPath(f, `"go/ast"`)
   163  		renameSelectorExprs(f, "syntax.Expr->ast.Expr")
   164  	},
   165  	"named.go":  func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
   166  	"object.go": func(f *ast.File) { fixTokenPos(f); renameIdents(f, "NewTypeNameLazy->_NewTypeNameLazy") },
   167  	// TODO(gri) needs adjustments for TestObjectString - disabled for now
   168  	// "object_test.go": func(f *ast.File) { renameImportPath(f, `"cmd/compile/internal/types2"->"go/types"`) },
   169  	"objset.go": nil,
   170  	"operand.go": func(f *ast.File) {
   171  		insertImportPath(f, `"go/token"`)
   172  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   173  		renameSelectorExprs(f,
   174  			"syntax.Pos->token.Pos", "syntax.LitKind->token.Token",
   175  			"syntax.IntLit->token.INT", "syntax.FloatLit->token.FLOAT",
   176  			"syntax.ImagLit->token.IMAG", "syntax.RuneLit->token.CHAR",
   177  			"syntax.StringLit->token.STRING") // must happen before renaming identifiers
   178  		renameIdents(f, "syntax->ast")
   179  	},
   180  	"package.go":    nil,
   181  	"pointer.go":    nil,
   182  	"predicates.go": nil,
   183  	"range.go": func(f *ast.File) {
   184  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   185  		renameSelectorExprs(f, "syntax.Name->ast.Ident", "syntax.ForStmt->ast.RangeStmt", "ident.Value->ident.Name") // must happen before renaming identifiers
   186  		renameIdents(f, "syntax->ast", "poser->positioner")
   187  	},
   188  	"recording.go": func(f *ast.File) {
   189  		renameImportPath(f, `"cmd/compile/internal/syntax"->"go/ast"`)
   190  		renameSelectorExprs(f, "syntax.Name->ast.Ident") // must happen before renaming identifiers
   191  		renameIdents(f, "syntax->ast")
   192  		fixAtPosCall(f)
   193  	},
   194  	"scope.go":         func(f *ast.File) { fixTokenPos(f); renameIdents(f, "InsertLazy->_InsertLazy") },
   195  	"selection.go":     nil,
   196  	"sizes.go":         func(f *ast.File) { renameIdents(f, "IsSyncAtomicAlign64->_IsSyncAtomicAlign64") },
   197  	"slice.go":         nil,
   198  	"subst.go":         func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
   199  	"termlist.go":      nil,
   200  	"termlist_test.go": nil,
   201  	"trie.go":          nil,
   202  	"trie_test.go":     nil,
   203  	"tuple.go":         nil,
   204  	"typelists.go":     nil,
   205  	"typeset.go":       func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
   206  	"typeparam.go":     nil,
   207  	"typeterm_test.go": nil,
   208  	"typeterm.go":      nil,
   209  	"typestring.go":    nil,
   210  	"under.go":         nil,
   211  	"unify.go":         fixSprintf,
   212  	"universe.go":      fixGlobalTypVarDecl,
   213  	"util_test.go":     fixTokenPos,
   214  	"validtype.go":     func(f *ast.File) { fixTokenPos(f); renameSelectors(f, "Trace->_Trace") },
   215  	"version.go":       func(f *ast.File) { renameIdents(f, "poser->positioner") },
   216  }
   217  
   218  // TODO(gri) We should be able to make these rewriters more configurable/composable.
   219  //           For now this is a good starting point.
   220  
   221  // A renameMap maps old strings to new strings.
   222  type renameMap map[string]string
   223  
   224  // makeRenameMap returns a renameMap populates from renames entries of the form "from->to".
   225  func makeRenameMap(renames ...string) renameMap {
   226  	m := make(renameMap)
   227  	for _, r := range renames {
   228  		s := strings.Split(r, "->")
   229  		if len(s) != 2 {
   230  			panic("invalid rename entry: " + r)
   231  		}
   232  		m[s[0]] = s[1]
   233  	}
   234  	return m
   235  }
   236  
   237  // rename renames the given string s if a corresponding rename exists in m.
   238  func (m renameMap) rename(s *string) {
   239  	if r, ok := m[*s]; ok {
   240  		*s = r
   241  	}
   242  }
   243  
   244  // renameSel renames a selector expression of the form a.x to b.x (where a, b are identifiers)
   245  // if m contains the ("a.x" : "b.y") key-value pair.
   246  func (m renameMap) renameSel(n *ast.SelectorExpr) {
   247  	if a, _ := n.X.(*ast.Ident); a != nil {
   248  		a_x := a.Name + "." + n.Sel.Name
   249  		if r, ok := m[a_x]; ok {
   250  			b_y := strings.Split(r, ".")
   251  			if len(b_y) != 2 {
   252  				panic("invalid selector expression: " + r)
   253  			}
   254  			a.Name = b_y[0]
   255  			n.Sel.Name = b_y[1]
   256  		}
   257  	}
   258  }
   259  
   260  // renameIdents renames identifiers: each renames entry is of the form "from->to".
   261  // Note: This doesn't change the use of the identifiers in comments.
   262  func renameIdents(f *ast.File, renames ...string) {
   263  	m := makeRenameMap(renames...)
   264  	ast.Inspect(f, func(n ast.Node) bool {
   265  		switch n := n.(type) {
   266  		case *ast.Ident:
   267  			m.rename(&n.Name)
   268  			return false
   269  		}
   270  		return true
   271  	})
   272  }
   273  
   274  // renameSelectors is like renameIdents but only looks at selectors.
   275  func renameSelectors(f *ast.File, renames ...string) {
   276  	m := makeRenameMap(renames...)
   277  	ast.Inspect(f, func(n ast.Node) bool {
   278  		switch n := n.(type) {
   279  		case *ast.SelectorExpr:
   280  			m.rename(&n.Sel.Name)
   281  			return false
   282  		}
   283  		return true
   284  	})
   285  
   286  }
   287  
   288  // renameSelectorExprs is like renameIdents but only looks at selector expressions.
   289  // Each renames entry must be of the form "x.a->y.b".
   290  func renameSelectorExprs(f *ast.File, renames ...string) {
   291  	m := makeRenameMap(renames...)
   292  	ast.Inspect(f, func(n ast.Node) bool {
   293  		switch n := n.(type) {
   294  		case *ast.SelectorExpr:
   295  			m.renameSel(n)
   296  			return false
   297  		}
   298  		return true
   299  	})
   300  }
   301  
   302  // renameImportPath is like renameIdents but renames import paths.
   303  func renameImportPath(f *ast.File, renames ...string) {
   304  	m := makeRenameMap(renames...)
   305  	ast.Inspect(f, func(n ast.Node) bool {
   306  		switch n := n.(type) {
   307  		case *ast.ImportSpec:
   308  			if n.Path.Kind != token.STRING {
   309  				panic("invalid import path")
   310  			}
   311  			m.rename(&n.Path.Value)
   312  			return false
   313  		}
   314  		return true
   315  	})
   316  }
   317  
   318  // insertImportPath inserts the given import path.
   319  // There must be at least one import declaration present already.
   320  func insertImportPath(f *ast.File, path string) {
   321  	for _, d := range f.Decls {
   322  		if g, _ := d.(*ast.GenDecl); g != nil && g.Tok == token.IMPORT {
   323  			g.Specs = append(g.Specs, &ast.ImportSpec{Path: &ast.BasicLit{ValuePos: g.End(), Kind: token.STRING, Value: path}})
   324  			return
   325  		}
   326  	}
   327  	panic("no import declaration present")
   328  }
   329  
   330  // fixTokenPos changes imports of "cmd/compile/internal/syntax" to "go/token",
   331  // uses of syntax.Pos to token.Pos, and calls to x.IsKnown() to x.IsValid().
   332  func fixTokenPos(f *ast.File) {
   333  	m := makeRenameMap(`"cmd/compile/internal/syntax"->"go/token"`, "syntax.Pos->token.Pos", "IsKnown->IsValid")
   334  	ast.Inspect(f, func(n ast.Node) bool {
   335  		switch n := n.(type) {
   336  		case *ast.ImportSpec:
   337  			// rewrite import path "cmd/compile/internal/syntax" to "go/token"
   338  			if n.Path.Kind != token.STRING {
   339  				panic("invalid import path")
   340  			}
   341  			m.rename(&n.Path.Value)
   342  			return false
   343  		case *ast.SelectorExpr:
   344  			// rewrite syntax.Pos to token.Pos
   345  			m.renameSel(n)
   346  		case *ast.CallExpr:
   347  			// rewrite x.IsKnown() to x.IsValid()
   348  			if fun, _ := n.Fun.(*ast.SelectorExpr); fun != nil && len(n.Args) == 0 {
   349  				m.rename(&fun.Sel.Name)
   350  				return false
   351  			}
   352  		}
   353  		return true
   354  	})
   355  }
   356  
   357  // fixSelValue updates the selector x.Sel.Value to x.Sel.Name.
   358  func fixSelValue(f *ast.File) {
   359  	ast.Inspect(f, func(n ast.Node) bool {
   360  		switch n := n.(type) {
   361  		case *ast.SelectorExpr:
   362  			if n.Sel.Name == "Value" {
   363  				if selx, _ := n.X.(*ast.SelectorExpr); selx != nil && selx.Sel.Name == "Sel" {
   364  					n.Sel.Name = "Name"
   365  					return false
   366  				}
   367  			}
   368  		}
   369  		return true
   370  	})
   371  }
   372  
   373  // fixInferSig updates the Checker.infer signature to use a positioner instead of a token.Position
   374  // as first argument, renames the argument from "pos" to "posn", and updates a few internal uses of
   375  // "pos" to "posn" and "posn.Pos()" respectively.
   376  func fixInferSig(f *ast.File) {
   377  	ast.Inspect(f, func(n ast.Node) bool {
   378  		switch n := n.(type) {
   379  		case *ast.FuncDecl:
   380  			if n.Name.Name == "infer" {
   381  				// rewrite (pos token.Pos, ...) to (posn positioner, ...)
   382  				par := n.Type.Params.List[0]
   383  				if len(par.Names) == 1 && par.Names[0].Name == "pos" {
   384  					par.Names[0] = newIdent(par.Names[0].Pos(), "posn")
   385  					par.Type = newIdent(par.Type.Pos(), "positioner")
   386  					return true
   387  				}
   388  			}
   389  		case *ast.CallExpr:
   390  			if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil {
   391  				switch selx.Sel.Name {
   392  				case "renameTParams":
   393  					// rewrite check.renameTParams(pos, ... ) to check.renameTParams(posn.Pos(), ... )
   394  					if isIdent(n.Args[0], "pos") {
   395  						pos := n.Args[0].Pos()
   396  						fun := &ast.SelectorExpr{X: newIdent(pos, "posn"), Sel: newIdent(pos, "Pos")}
   397  						arg := &ast.CallExpr{Fun: fun, Lparen: pos, Args: nil, Ellipsis: token.NoPos, Rparen: pos}
   398  						n.Args[0] = arg
   399  						return false
   400  					}
   401  				case "addf":
   402  					// rewrite err.addf(pos, ...) to err.addf(posn, ...)
   403  					if isIdent(n.Args[0], "pos") {
   404  						pos := n.Args[0].Pos()
   405  						arg := newIdent(pos, "posn")
   406  						n.Args[0] = arg
   407  						return false
   408  					}
   409  				case "allowVersion":
   410  					// rewrite check.allowVersion(pos, ...) to check.allowVersion(posn, ...)
   411  					if isIdent(n.Args[0], "pos") {
   412  						pos := n.Args[0].Pos()
   413  						arg := newIdent(pos, "posn")
   414  						n.Args[0] = arg
   415  						return false
   416  					}
   417  				}
   418  			}
   419  		}
   420  		return true
   421  	})
   422  }
   423  
   424  // fixAtPosCall updates calls of the form atPos(x) to x.Pos() in argument lists of (check).dump calls.
   425  // TODO(gri) can we avoid this and just use atPos consistently in go/types and types2?
   426  func fixAtPosCall(f *ast.File) {
   427  	ast.Inspect(f, func(n ast.Node) bool {
   428  		switch n := n.(type) {
   429  		case *ast.CallExpr:
   430  			if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil && selx.Sel.Name == "dump" {
   431  				for i, arg := range n.Args {
   432  					if call, _ := arg.(*ast.CallExpr); call != nil {
   433  						// rewrite xxx.dump(..., atPos(x), ...) to xxx.dump(..., x.Pos(), ...)
   434  						if isIdent(call.Fun, "atPos") {
   435  							pos := call.Args[0].Pos()
   436  							fun := &ast.SelectorExpr{X: call.Args[0], Sel: newIdent(pos, "Pos")}
   437  							n.Args[i] = &ast.CallExpr{Fun: fun, Lparen: pos, Rparen: pos}
   438  							return false
   439  						}
   440  					}
   441  				}
   442  			}
   443  		}
   444  		return true
   445  	})
   446  }
   447  
   448  // fixErrErrorfCall updates calls of the form err.addf(obj, ...) to err.addf(obj.Pos(), ...).
   449  func fixErrErrorfCall(f *ast.File) {
   450  	ast.Inspect(f, func(n ast.Node) bool {
   451  		switch n := n.(type) {
   452  		case *ast.CallExpr:
   453  			if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil {
   454  				if isIdent(selx.X, "err") {
   455  					switch selx.Sel.Name {
   456  					case "errorf":
   457  						// rewrite err.addf(obj, ... ) to err.addf(obj.Pos(), ... )
   458  						if ident, _ := n.Args[0].(*ast.Ident); ident != nil && ident.Name == "obj" {
   459  							pos := n.Args[0].Pos()
   460  							fun := &ast.SelectorExpr{X: ident, Sel: newIdent(pos, "Pos")}
   461  							n.Args[0] = &ast.CallExpr{Fun: fun, Lparen: pos, Rparen: pos}
   462  							return false
   463  						}
   464  					}
   465  				}
   466  			}
   467  		}
   468  		return true
   469  	})
   470  }
   471  
   472  // fixCheckErrorfCall updates calls of the form check.errorf(pos, ...) to check.errorf(atPos(pos), ...).
   473  func fixCheckErrorfCall(f *ast.File) {
   474  	ast.Inspect(f, func(n ast.Node) bool {
   475  		switch n := n.(type) {
   476  		case *ast.CallExpr:
   477  			if selx, _ := n.Fun.(*ast.SelectorExpr); selx != nil {
   478  				if isIdent(selx.X, "check") {
   479  					switch selx.Sel.Name {
   480  					case "errorf":
   481  						// rewrite check.errorf(pos, ... ) to check.errorf(atPos(pos), ... )
   482  						if ident := asIdent(n.Args[0], "pos"); ident != nil {
   483  							pos := n.Args[0].Pos()
   484  							fun := newIdent(pos, "atPos")
   485  							n.Args[0] = &ast.CallExpr{Fun: fun, Lparen: pos, Args: []ast.Expr{ident}, Rparen: pos}
   486  							return false
   487  						}
   488  					}
   489  				}
   490  			}
   491  		}
   492  		return true
   493  	})
   494  }
   495  
   496  // fixGlobalTypVarDecl changes the global Typ variable from an array to a slice
   497  // (in types2 we use an array for efficiency, in go/types it's a slice and we
   498  // cannot change that).
   499  func fixGlobalTypVarDecl(f *ast.File) {
   500  	ast.Inspect(f, func(n ast.Node) bool {
   501  		switch n := n.(type) {
   502  		case *ast.ValueSpec:
   503  			// rewrite type Typ = [...]Type{...} to type Typ = []Type{...}
   504  			if len(n.Names) == 1 && n.Names[0].Name == "Typ" && len(n.Values) == 1 {
   505  				n.Values[0].(*ast.CompositeLit).Type.(*ast.ArrayType).Len = nil
   506  				return false
   507  			}
   508  		}
   509  		return true
   510  	})
   511  }
   512  
   513  // fixSprintf adds an extra nil argument for the *token.FileSet parameter in sprintf calls.
   514  func fixSprintf(f *ast.File) {
   515  	ast.Inspect(f, func(n ast.Node) bool {
   516  		switch n := n.(type) {
   517  		case *ast.CallExpr:
   518  			if isIdent(n.Fun, "sprintf") && len(n.Args) >= 4 /* ... args */ {
   519  				n.Args = insert(n.Args, 1, newIdent(n.Args[1].Pos(), "nil"))
   520  				return false
   521  			}
   522  		}
   523  		return true
   524  	})
   525  }
   526  
   527  // asIdent returns x as *ast.Ident if it is an identifier with the given name.
   528  func asIdent(x ast.Node, name string) *ast.Ident {
   529  	if ident, _ := x.(*ast.Ident); ident != nil && ident.Name == name {
   530  		return ident
   531  	}
   532  	return nil
   533  }
   534  
   535  // isIdent reports whether x is an identifier with the given name.
   536  func isIdent(x ast.Node, name string) bool {
   537  	return asIdent(x, name) != nil
   538  }
   539  
   540  // newIdent returns a new identifier with the given position and name.
   541  func newIdent(pos token.Pos, name string) *ast.Ident {
   542  	id := ast.NewIdent(name)
   543  	id.NamePos = pos
   544  	return id
   545  }
   546  
   547  // insert inserts x at list[at] and moves the remaining elements up.
   548  func insert(list []ast.Expr, at int, x ast.Expr) []ast.Expr {
   549  	list = append(list, nil)
   550  	copy(list[at+1:], list[at:])
   551  	list[at] = x
   552  	return list
   553  }
   554  

View as plain text