Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
defer func(file *os.File) {
1// SPDX-License-Identifier: MIT23package main45import (6 "bytes"7 _ "embed"8 "fmt"9 "go/format"10 "maps"11 "os"12 "regexp"13 "slices"14 "strconv"15 "strings"16 "text/template"1718 "github.com/boyter/scc/v4/processor"19 jsoniter "github.com/json-iterator/go"20)2122const (23 constantsFile = "./processor/constants.go"24 languagesListFile = "./LANGUAGES.md"25)2627var json = jsoniter.ConfigCompatibleWithStandardLibrary2829//go:embed languages.tmpl30var langTemplate string3132// loadLanguages reads and validates all languages*.json files in the current33// folder, returning the merged language database. Both generated artifacts are34// produced from this single freshly parsed map so that one generation pass is35// fully consistent. Previously generateLanguagesList read the languageDatabase36// compiled into this binary, which is the *old* constants.go (compiled before37// this run rewrote it), so LANGUAGES.md always lagged the JSON by one pass.38func loadLanguages() (map[string]processor.Language, error) {39 files, _ := os.ReadDir(".")40 langs := map[string]processor.Language{}4142 for _, f := range files {43 if strings.HasPrefix(f.Name(), "languages") && strings.HasSuffix(f.Name(), ".json") {44 file, err := os.Open(f.Name())45 if err != nil {46 return nil, fmt.Errorf("failed to open file '%s': %v", f.Name(), err)47 }4849 data := map[string]processor.Language{}5051 // validate the json by decoding into an empty struct52 if err := json.NewDecoder(file).Decode(&data); err != nil {53 _ = file.Close()54 return nil, fmt.Errorf("failed to validate json in file '%s': %v", f.Name(), err)55 }56 _ = file.Close()5758 // validate that every regex heuristic compiles ahead of time so a59 // broken pattern fails the build rather than at runtime60 for name, lang := range data {61 for _, h := range lang.Heuristics {62 if _, err := regexp.Compile(h.Pattern); err != nil {63 return nil, fmt.Errorf("invalid heuristic regex %q for language '%s' in file '%s': %v", h.Pattern, name, f.Name(), err)64 }65 }66 }6768 maps.Insert(langs, maps.All(data))69 }70 }7172 return langs, nil73}7475// encodes the language database as string literals in constants.go76func generateConstants(langs map[string]processor.Language) error {77 buf := &bytes.Buffer{}7879 t, err := template.New("codeGenerator").Funcs(template.FuncMap{80 "quote": strconv.Quote,81 }).Parse(langTemplate)82 if err != nil {83 return fmt.Errorf("failed to parse template file: %v", err)84 }8586 if err := t.Execute(buf, langs); err != nil {87 return fmt.Errorf("failed to execute template file: %v", err)88 }8990 source, err := format.Source(buf.Bytes())91 if err != nil {92 return fmt.Errorf("failed to format code: %v", err)93 }9495 out, err := os.OpenFile(constantsFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)96 if err != nil {97 return fmt.Errorf("failed to open constants file: %v", err)98 }99 defer func(file *os.File) {100 _ = file.Close()101 }(out)102103 if _, err := out.Write(source); err != nil {104 return fmt.Errorf("failed to write constants file %v", err)105 }106107 return nil108}109110// generateLanguagesList writes LANGUAGES.md from the freshly parsed language111// map. It mirrors the formatting of processor.PrintLanguages but works on the112// passed in data rather than the compiled-in languageDatabase so a single113// generation pass stays consistent with constants.go.114func generateLanguagesList(langs map[string]processor.Language) error {115 out, err := os.OpenFile(languagesListFile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644)116 if err != nil {117 return fmt.Errorf("failed to open languages list file: %v", err)118 }119 defer func(file *os.File) {120 _ = file.Close()121 }(out)122123 names := make([]string, 0, len(langs))124 for name := range langs {125 names = append(names, name)126 }127 slices.SortFunc(names, func(a, b string) int {128 return strings.Compare(strings.ToLower(a), strings.ToLower(b))129 })130131 _, _ = out.WriteString("```\n")132 for _, name := range names {133 exts := append(append([]string{}, langs[name].Extensions...), langs[name].FileNames...)134 _, _ = fmt.Fprintf(out, "%s (%s)\n", name, strings.Join(exts, ","))135 }136 _, _ = out.WriteString("```\n")137138 return nil139}140141func main() {142 langs, err := loadLanguages()143 if err != nil {144 fmt.Fprintf(os.Stderr, "failed to load languages: %v\n", err)145 os.Exit(1)146 }147 if err := generateConstants(langs); err != nil {148 fmt.Fprintf(os.Stderr, "failed to generate constants: %v\n", err)149 os.Exit(1)150 }151 if err := generateLanguagesList(langs); err != nil {152 fmt.Fprintf(os.Stderr, "failed to generate languages list: %v\n", err)153 os.Exit(1)154 }155}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.