|
@@ -0,0 +1,104 @@
|
|
|
+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)
|
|
|
+}
|