//go:build ignore // +build ignore package main import ( "fmt" "go/ast" "go/parser" "go/token" "log" "os" "path/filepath" "strings" ) func main() { if len(os.Args) < 2 { fmt.Println("Usage: go run cleanup_unused.go ") os.Exit(1) } rootDir := os.Args[1] err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if !strings.HasSuffix(path, ".go") || strings.Contains(path, "_test.go") { return nil } // Skip vendor and generated files if strings.Contains(path, "vendor/") || strings.Contains(path, "generated/") { return nil } cleanupFile(path) return nil }) if err != nil { log.Fatal(err) } } func cleanupFile(filename string) { fset := token.NewFileSet() node, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) if err != nil { log.Printf("Error parsing %s: %v", filename, err) return } // Find all exported identifiers exportedIdents := make(map[string]bool) ast.Inspect(node, func(n ast.Node) bool { switch x := n.(type) { case *ast.GenDecl: for _, spec := range x.Specs { switch s := spec.(type) { case *ast.TypeSpec: if ast.IsExported(s.Name.Name) { exportedIdents[s.Name.Name] = true } case *ast.ValueSpec: for _, name := range s.Names { if ast.IsExported(name.Name) { exportedIdents[name.Name] = true } } } } case *ast.FuncDecl: if x.Recv == nil && ast.IsExported(x.Name.Name) { exportedIdents[x.Name.Name] = true } } return true }) // Find all usages usedIdents := make(map[string]bool) ast.Inspect(node, func(n ast.Node) bool { switch x := n.(type) { case *ast.Ident: if x.Name != "_" && ast.IsExported(x.Name) { usedIdents[x.Name] = true } case *ast.SelectorExpr: // x.Sel is always an *ast.Ident in SelectorExpr usedIdents[x.Sel.Name] = true } return true }) // Find unused exports var unusedExports []string for ident := range exportedIdents { if !usedIdents[ident] { unusedExports = append(unusedExports, ident) } } if len(unusedExports) > 0 { fmt.Printf("Found %d unused exports in %s: %v\n", len(unusedExports), filename, unusedExports) // For now, just report. In a real implementation, we'd modify the AST // and rewrite the file to remove unused exports } }