blob: a45c41fe71c77b9258aab453d60cccc352dd857c [file]
// Copyright 2021 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.
//go:build ignore
// copytermlist.go copies the term list algorithm from GOROOT/src/go/types.
//
// Usage:
//
// Requires running in GOPATH mode (GO111MODULE=off):to avoid adding
// the dependency on golang.org/x/tools in go.mod.
// 1. Check out golang.org/x/tools at $GOPATH/src/golang.org/x/tools.
// 2. Run: GO111MODULE=off go run copytermlist.go
package main
import (
"bytes"
"fmt"
"go/ast"
"go/format"
"go/parser"
"go/token"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"golang.org/x/tools/go/ast/astutil"
)
func main() {
if err := doCopy(); err != nil {
fmt.Fprintf(os.Stderr, "error copying from go/types: %v", err)
os.Exit(1)
}
}
func doCopy() error {
dir := filepath.Join(runtime.GOROOT(), "src", "go", "types")
for _, name := range []string{"typeterm.go", "termlist.go"} {
path := filepath.Join(dir, name)
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
return err
}
file.Name.Name = "typeparams"
file.Doc = &ast.CommentGroup{List: []*ast.Comment{&ast.Comment{Text: "DO NOT MODIFY"}}}
var needImport bool
selectorType := reflect.TypeOf((*ast.SelectorExpr)(nil))
astutil.Apply(file, func(c *astutil.Cursor) bool {
if id, _ := c.Node().(*ast.Ident); id != nil {
// Check if this ident should be qualified with types. For simplicity,
// assume the copied files do not themselves contain any exported
// symbols.
// As a simple heuristic, just verify that the ident may be replaced by
// a selector.
if !token.IsExported(id.Name) {
return false
}
v := reflect.TypeOf(c.Parent()).Elem() // ast nodes are all pointers
field, ok := v.FieldByName(c.Name())
if !ok {
panic("missing field")
}
t := field.Type
if c.Index() > 0 { // => t is a slice
t = t.Elem()
}
if !selectorType.AssignableTo(t) {
return false
}
needImport = true
c.Replace(&ast.SelectorExpr{
X: &ast.Ident{NamePos: id.Pos(), Name: "types"},
Sel: &ast.Ident{NamePos: id.Pos(), Name: id.Name},
})
}
return true
}, nil)
if needImport {
astutil.AddImport(fset, file, "go/types")
}
var b bytes.Buffer
if err := format.Node(&b, fset, file); err != nil {
return err
}
// Rewrite code gen byline and source info.
var res bytes.Buffer
var skippingHeader = true
var wroteFirstContentLine bool
for line := range strings.SplitSeq(b.String(), "\n") {
if skippingHeader {
trimmed := strings.TrimSpace(line)
if strings.Contains(trimmed, "DO NOT EDIT") && strings.Contains(trimmed, "Code generated") {
res.WriteString("// Code generated by copytermlist.go DO NOT EDIT.\n")
continue
}
if strings.HasPrefix(trimmed, "// Source:") {
res.WriteString("// Source: GOROOT/src/go/types/" + name + "\n\n")
continue
}
if trimmed == "" {
continue // Skip leading blank lines until we hit the first actual comment/code
}
skippingHeader = false
}
// Handle line endings to avoid duplicate trailing newlines at the end of the file
if !wroteFirstContentLine {
res.WriteString(line)
wroteFirstContentLine = true
} else {
res.WriteString("\n")
res.WriteString(line)
}
}
if err := os.WriteFile(name, res.Bytes(), 0644); err != nil {
return err
}
}
return nil
}