github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/add-check.go (about) 1 //go:build ignore 2 3 package main 4 5 import ( 6 "bytes" 7 "go/format" 8 "log" 9 "os" 10 "os/exec" 11 "path/filepath" 12 "regexp" 13 "strings" 14 "text/template" 15 ) 16 17 var tmpl = ` 18 package {{.lname}} 19 20 import ( 21 "github.com/amarpal/go-tools/analysis/lint" 22 23 "golang.org/x/tools/go/analysis" 24 ) 25 26 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 27 Analyzer: &analysis.Analyzer{ 28 Name: "{{.name}}", 29 Run: run, 30 Requires: []*analysis.Analyzer{}, 31 }, 32 Doc: &lint.Documentation{ 33 Title: "", 34 Text: {{.emptyRaw}}, 35 {{- if .quickfix }} 36 Before: {{.emptyRaw}}, 37 After: {{.emptyRaw}}, 38 {{- end }} 39 Since: "Unreleased", 40 Severity: lint.SeverityWarning, 41 }, 42 }) 43 44 var Analyzer = SCAnalyzer.Analyzer 45 46 func run(pass *analysis.Pass) (interface{}, error) { 47 return nil, nil 48 } 49 ` 50 51 func main() { 52 log.SetFlags(0) 53 54 var t template.Template 55 if _, err := t.Parse(tmpl); err != nil { 56 log.Fatalln("couldn't parse template:", err) 57 } 58 59 if len(os.Args) != 2 { 60 log.Fatalf("Usage: %s <new check's name>", os.Args[0]) 61 } 62 63 name := os.Args[1] 64 checkRe := regexp.MustCompile(`^([A-Za-z]+)\d{4}$`) 65 parts := checkRe.FindStringSubmatch(name) 66 if parts == nil { 67 log.Fatalf("invalid check name %q", name) 68 } 69 70 var catDir string 71 prefix := strings.ToUpper(parts[1]) 72 switch prefix { 73 case "SA": 74 catDir = "staticcheck" 75 case "S": 76 catDir = "simple" 77 case "ST": 78 catDir = "stylecheck" 79 case "QF": 80 catDir = "quickfix" 81 default: 82 log.Fatalf("unknown check prefix %q", prefix) 83 } 84 85 lname := strings.ToLower(name) 86 dir := filepath.Join(catDir, lname) 87 dst := filepath.Join(dir, lname+".go") 88 89 mkdirp(dir) 90 91 buf := bytes.NewBuffer(nil) 92 vars := map[string]any{ 93 "name": name, 94 "lname": lname, 95 "emptyRaw": "``", 96 "quickfix": prefix == "QF", 97 } 98 99 if err := t.Execute(buf, vars); err != nil { 100 log.Fatalf("couldn't generate %s: %s", dst, err) 101 } 102 103 b, err := format.Source(buf.Bytes()) 104 if err != nil { 105 log.Fatalf("couldn't gofmt %s: %s", dst, err) 106 } 107 108 writeFile(dst, b) 109 110 testdata := filepath.Join(dir, "testdata", "src", "example.com", "pkg") 111 mkdirp(testdata) 112 writeFile(filepath.Join(testdata, "pkg.go"), []byte("package pkg\n")) 113 114 out, err := exec.Command("go", "generate", "./...").CombinedOutput() 115 if err != nil { 116 log.Printf("could not run 'go generate ./...': %s", err) 117 log.Println("Output:") 118 log.Fatalln(string(out)) 119 } 120 121 flags := []string{ 122 "add", 123 "--intent-to-add", 124 "--verbose", 125 126 filepath.Join(dir, lname+"_test.go"), 127 filepath.Join(testdata, "pkg.go"), 128 dst, 129 } 130 cmd := exec.Command("git", flags...) 131 cmd.Stdout = os.Stdout 132 cmd.Stderr = os.Stderr 133 if err := cmd.Run(); err != nil { 134 log.Fatalln("could not run 'git add':", err) 135 } 136 } 137 138 func writeFile(path string, data []byte) { 139 if err := os.WriteFile(path, data, 0677); err != nil { 140 log.Fatalf("couldn't write %s: %s", path, err) 141 } 142 } 143 144 func mkdirp(path string) { 145 if err := os.MkdirAll(path, 0777); err != nil { 146 log.Fatalf("couldn't create directory %s: %s", path, err) 147 } 148 }