|
| 1 | +// Copyright 2024 the libevm authors. |
| 2 | +// |
| 3 | +// The libevm additions to go-ethereum are free software: you can redistribute |
| 4 | +// them and/or modify them under the terms of the GNU Lesser General Public License |
| 5 | +// as published by the Free Software Foundation, either version 3 of the License, |
| 6 | +// or (at your option) any later version. |
| 7 | +// |
| 8 | +// The libevm additions are distributed in the hope that they will be useful, |
| 9 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser |
| 11 | +// General Public License for more details. |
| 12 | +// |
| 13 | +// You should have received a copy of the GNU Lesser General Public License |
| 14 | +// along with the go-ethereum library. If not, see |
| 15 | +// <http://www.gnu.org/licenses/>. |
| 16 | + |
| 17 | +// The internalise command modifies Go files in place, making exported methods |
| 18 | +// internal. |
| 19 | +// |
| 20 | +// Usage: |
| 21 | +// |
| 22 | +// internalise -file <filepath> <type>.<method> [<type>.<method> [...]] |
| 23 | +// |
| 24 | +// For example, with file foo.go containing declarations: |
| 25 | +// |
| 26 | +// func (f *Foo) Bar() { ... } |
| 27 | +// |
| 28 | +// func (Foo) Baz() { ... } |
| 29 | +// |
| 30 | +// running |
| 31 | +// |
| 32 | +// internalise -file foo.go Foo.Bar Foo.Baz |
| 33 | +// |
| 34 | +// results in |
| 35 | +// |
| 36 | +// func (f *Foo) bar() { ... } |
| 37 | +// |
| 38 | +// func (Foo) baz() { ... } |
| 39 | +package main |
| 40 | + |
| 41 | +import ( |
| 42 | + "flag" |
| 43 | + "fmt" |
| 44 | + "go/ast" |
| 45 | + "go/format" |
| 46 | + "go/parser" |
| 47 | + "go/token" |
| 48 | + "os" |
| 49 | + "strings" |
| 50 | +) |
| 51 | + |
| 52 | +func main() { |
| 53 | + file := flag.String("file", "", "File to modify") |
| 54 | + flag.Parse() |
| 55 | + |
| 56 | + if err := run(*file, flag.Args()...); err != nil { |
| 57 | + fmt.Fprint(os.Stderr, err) |
| 58 | + os.Exit(1) |
| 59 | + } |
| 60 | +} |
| 61 | + |
| 62 | +func run(fileName string, args ...string) error { |
| 63 | + methods := make(map[string]map[string]struct{}) |
| 64 | + for _, a := range args { |
| 65 | + a = strings.TrimPrefix(strings.TrimSpace(a), "*") |
| 66 | + parts := strings.Split(a, ".") |
| 67 | + if len(parts) != 2 { |
| 68 | + return fmt.Errorf("invalid method identifier %q", a) |
| 69 | + } |
| 70 | + typ, fn := parts[0], parts[1] |
| 71 | + if _, ok := methods[typ]; !ok { |
| 72 | + methods[typ] = make(map[string]struct{}) |
| 73 | + } |
| 74 | + methods[typ][fn] = struct{}{} |
| 75 | + } |
| 76 | + |
| 77 | + fset := token.NewFileSet() |
| 78 | + mode := parser.SkipObjectResolution | parser.ParseComments |
| 79 | + parsed, err := parser.ParseFile(fset, fileName, nil, mode) |
| 80 | + if err != nil { |
| 81 | + return fmt.Errorf("parser.ParseFile(%q): %v", fileName, err) |
| 82 | + } |
| 83 | + |
| 84 | + for _, d := range parsed.Decls { |
| 85 | + fn, ok := d.(*ast.FuncDecl) |
| 86 | + if !ok || fn.Recv.NumFields() != 1 { |
| 87 | + continue |
| 88 | + } |
| 89 | + |
| 90 | + var typ string |
| 91 | + switch t := fn.Recv.List[0].Type.(type) { |
| 92 | + case *ast.Ident: |
| 93 | + typ = t.Name |
| 94 | + case *ast.StarExpr: |
| 95 | + typ = t.X.(*ast.Ident).Name //nolint:forcetypeassert // Invariant of valid Go method |
| 96 | + } |
| 97 | + |
| 98 | + name := &fn.Name.Name |
| 99 | + if _, ok := methods[typ][*name]; !ok { |
| 100 | + continue |
| 101 | + } |
| 102 | + if n := []rune(*name); n[0] >= 'A' && n[0] <= 'Z' { |
| 103 | + n[0] += 'a' - 'A' |
| 104 | + *name = string(n) |
| 105 | + } |
| 106 | + } |
| 107 | + |
| 108 | + // Since we're not creating, the zero perm/mode is ignored. |
| 109 | + f, err := os.OpenFile(fileName, os.O_TRUNC|os.O_WRONLY, 0) //nolint:gosec // Variable file is under our direct control in go:generate |
| 110 | + if err != nil { |
| 111 | + return fmt.Errorf("os.OpenFile(%q, [write-only, truncate]): %v", fileName, err) |
| 112 | + } |
| 113 | + if err := format.Node(f, fset, parsed); err != nil { |
| 114 | + return fmt.Errorf("format.Node(%T): %v", parsed, err) |
| 115 | + } |
| 116 | + return f.Close() |
| 117 | +} |
0 commit comments