Skip to content

Commit 084e09b

Browse files
guseggerthacdias
authored andcommitted
feat: add deprecator
This utility adds deprecation comments to all exported types in a given module. It's intended to be used by Boxo maintainers when moving repos into Boxo. For example, if your module is "github.com/ipfs/go-unixfs", then checkout that repo and, in its root, run: deprecator --path github.com/ipfs/boxo/ipld/unixfs Then on the func "github.com/ipfs/go-unixfs/mod.NewDagModifier", the deprecator will add the following comment: // Deprecated: use github.com/ipfs/boxo/ipld/unixfs/mod.NewDagModifier
1 parent 9b8e63b commit 084e09b

3 files changed

Lines changed: 222 additions & 0 deletions

File tree

cmd/deprecator/go.mod

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
module github.com/ipfs/boxo/cmd/deprecator
2+
3+
go 1.19
4+
5+
require (
6+
github.com/dave/dst v0.27.2
7+
github.com/urfave/cli/v2 v2.25.3
8+
)
9+
10+
require (
11+
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
12+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
13+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
14+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
15+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
16+
golang.org/x/tools v0.1.12 // indirect
17+
)

cmd/deprecator/go.sum

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
2+
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
3+
github.com/dave/dst v0.27.2 h1:4Y5VFTkhGLC1oddtNwuxxe36pnyLxMFXT51FOzH8Ekc=
4+
github.com/dave/dst v0.27.2/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc=
5+
github.com/dave/jennifer v1.5.0 h1:HmgPN93bVDpkQyYbqhCHj5QlgvUkvEOzMyEvKLgCRrg=
6+
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
7+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
8+
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
9+
github.com/urfave/cli/v2 v2.25.3 h1:VJkt6wvEBOoSjPFQvOkv6iWIrsJyCrKGtCtxXWwmGeY=
10+
github.com/urfave/cli/v2 v2.25.3/go.mod h1:GHupkWPMM0M/sj1a2b4wUrWBPzazNrIjouW6fmdJLxc=
11+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
12+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
13+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
14+
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
15+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
16+
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
17+
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
18+
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=

cmd/deprecator/main.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"go/parser"
8+
"go/token"
9+
"io"
10+
"io/fs"
11+
"log"
12+
"os"
13+
"os/exec"
14+
"path/filepath"
15+
"strings"
16+
17+
"github.com/dave/dst"
18+
"github.com/dave/dst/decorator"
19+
"github.com/urfave/cli/v2"
20+
)
21+
22+
func main() {
23+
app := &cli.App{
24+
Name: "deprecator",
25+
Usage: "Adds deprecation comments to all exported types in the module, with pointers to a new location. You should run this in your module root.",
26+
Flags: []cli.Flag{
27+
&cli.StringFlag{
28+
Name: "path",
29+
Usage: "the new package path that the deprecated message should point users to",
30+
Required: true,
31+
},
32+
},
33+
Action: func(ctx *cli.Context) error {
34+
newPkgPath := ctx.String("path")
35+
wd, err := os.Getwd()
36+
if err != nil {
37+
return fmt.Errorf("getting working dir: %w", err)
38+
}
39+
fileToPackage, err := buildFileToPackage(wd)
40+
if err != nil {
41+
return fmt.Errorf("building mapping of files to packages: %w", err)
42+
}
43+
44+
modPath, err := getModulePath(wd)
45+
if err != nil {
46+
return fmt.Errorf("finding current module path: %w", err)
47+
}
48+
49+
fset := token.NewFileSet()
50+
return filepath.Walk(wd, func(path string, info fs.FileInfo, err error) error {
51+
if err != nil {
52+
return err
53+
}
54+
if info.IsDir() || filepath.Ext(path) != ".go" {
55+
return nil
56+
}
57+
58+
addComment := func(name string, decs *dst.Decorations) {
59+
oldPkg := fileToPackage[path]
60+
newPkg := strings.Replace(oldPkg, modPath, newPkgPath, 1)
61+
newSym := newPkg + "." + name
62+
comment := fmt.Sprintf("// Deprecated: use %s", newSym)
63+
if len(decs.All()) > 0 {
64+
decs.Append("//")
65+
}
66+
decs.Append(comment)
67+
}
68+
69+
file, err := decorator.ParseFile(fset, path, nil, parser.ParseComments)
70+
if err != nil {
71+
return fmt.Errorf("parsing %s: %w", path, err)
72+
}
73+
74+
if _, ok := fileToPackage[path]; !ok {
75+
// this happens in the case of e.g. test files, which we want to skip
76+
return nil
77+
}
78+
79+
// process the AST, adding comments where necessary
80+
dst.Inspect(file, func(n dst.Node) bool { return inspectASTNode(addComment, n) })
81+
82+
outFile, err := os.Create(path)
83+
if err != nil {
84+
return fmt.Errorf("creating %s to write: %w", path, err)
85+
}
86+
defer outFile.Close()
87+
err = decorator.Fprint(outFile, file)
88+
if err != nil {
89+
return fmt.Errorf("writing %s: %w", path, err)
90+
}
91+
92+
return nil
93+
})
94+
},
95+
}
96+
err := app.Run(os.Args)
97+
if err != nil {
98+
log.Fatal(err)
99+
}
100+
}
101+
102+
type pkg struct {
103+
Dir string
104+
ImportPath string
105+
GoFiles []string
106+
}
107+
108+
func inspectASTNode(addComment func(string, *dst.Decorations), n dst.Node) bool {
109+
switch x := n.(type) {
110+
case *dst.GenDecl:
111+
if x.Tok == token.CONST || x.Tok == token.VAR || x.Tok == token.TYPE {
112+
for _, spec := range x.Specs {
113+
switch s := spec.(type) {
114+
case *dst.ValueSpec:
115+
// if parenthesized, put a comment above each exported type in the group
116+
if x.Lparen {
117+
for _, name := range s.Names {
118+
if !name.IsExported() {
119+
continue
120+
}
121+
addComment(name.Name, &s.Decs.Start)
122+
}
123+
} else {
124+
name := s.Names[0]
125+
if !name.IsExported() {
126+
continue
127+
}
128+
addComment(name.Name, &x.Decs.Start)
129+
}
130+
case *dst.TypeSpec:
131+
name := s.Name
132+
if !name.IsExported() {
133+
continue
134+
}
135+
addComment(name.Name, &x.Decs.Start)
136+
}
137+
}
138+
}
139+
case *dst.FuncDecl:
140+
// don't add notices to methods
141+
if x.Name.IsExported() && x.Recv == nil {
142+
addComment(x.Name.Name, &x.Decs.Start)
143+
}
144+
}
145+
return true
146+
147+
}
148+
149+
func getModulePath(dir string) (string, error) {
150+
cmd := exec.Command("go", "list", "-m")
151+
cmd.Dir = dir
152+
stdout := &bytes.Buffer{}
153+
cmd.Stdout = stdout
154+
err := cmd.Run()
155+
if err != nil {
156+
return "", err
157+
}
158+
return strings.TrimSpace(stdout.String()), nil
159+
}
160+
161+
func buildFileToPackage(dir string) (map[string]string, error) {
162+
cmd := exec.Command("go", "list", "-json", "./...")
163+
cmd.Dir = dir
164+
stdout := &bytes.Buffer{}
165+
stderr := &bytes.Buffer{}
166+
cmd.Stdout = stdout
167+
cmd.Stderr = stderr
168+
err := cmd.Run()
169+
if err != nil {
170+
return nil, err
171+
}
172+
dec := json.NewDecoder(stdout)
173+
fileToPackage := map[string]string{}
174+
for {
175+
var p pkg
176+
err := dec.Decode(&p)
177+
if err == io.EOF {
178+
return fileToPackage, nil
179+
}
180+
if err != nil {
181+
return nil, err
182+
}
183+
for _, f := range p.GoFiles {
184+
fileToPackage[filepath.Join(p.Dir, f)] = p.ImportPath
185+
}
186+
}
187+
}

0 commit comments

Comments
 (0)