package main import ( "log" "io/ioutil" "os" "path/filepath" "regexp" "strings" sitter "github.com/smacker/go-tree-sitter" "github.com/smacker/go-tree-sitter/ruby" ) const NUM_WORKERS = 32 func gatherFiles(root string, ch chan string) { defer close(ch) i := 0 err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { if err != nil { log.Printf("Error reading directory %s: %s\n", root, err) return nil } i += 1 name := info.Name() if strings.HasSuffix(name, ".rb") { ch <- path } return nil }) log.Printf("Checked %d files\n", i) if err != nil { log.Fatalf("Error reading directory %s: %s\n", root, err) return } } func findTypedLine(path string, tree *sitter.Tree, buf []byte) *string { r := regexp.MustCompile("# typed: (ignore|false|true|strict|strong|__STDLIB_INTERNAL)") root := tree.RootNode() for i := 0; i < int(root.ChildCount()); i++ { n := root.Child(i) if n.Type() == "comment" { matches := r.FindStringSubmatch(n.Content(buf)) if matches == nil { continue } return &matches[1] } } return nil } func worker(ch chan string, finished chan int) { parser := sitter.NewParser() parser.SetLanguage(ruby.GetLanguage()) for { path, more := <-ch if !more { finished <- -1 return } buf, err := ioutil.ReadFile(path) if err != nil { log.Printf("Error reading %s: %s\n", path, err) continue } node := parser.Parse(nil, buf) sigil := findTypedLine(path, node, buf) if sigil == nil { log.Printf("File %s: missing typed sigil", path) } else if *sigil == "false" || *sigil == "ignore" { log.Printf("File %s: expected at least `true`, but got `%s`", path, *sigil) } finished <- 1 } } func main() { ch := make(chan string) go gatherFiles(".", ch) finished := make(chan int) for i := 0; i < NUM_WORKERS; i++ { go worker(ch, finished) } all_files := 0 done := 0 for done < NUM_WORKERS { n := <- finished if n == -1 { done += 1 } else { all_files += 1 } } log.Printf("Parsed %d files\n", all_files) }