src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

generated.go (1896B)


      1 package generated
      2 
      3 import (
      4 	"bufio"
      5 	"bytes"
      6 	"io"
      7 	"os"
      8 	"reflect"
      9 	"strings"
     10 
     11 	"golang.org/x/tools/go/analysis"
     12 )
     13 
     14 type Generator int
     15 
     16 // A list of known generators we can detect
     17 const (
     18 	Unknown Generator = iota
     19 	Goyacc
     20 	Cgo
     21 	Stringer
     22 	ProtocGenGo
     23 )
     24 
     25 var (
     26 	// used by cgo before Go 1.11
     27 	oldCgo = []byte("// Created by cgo - DO NOT EDIT")
     28 	prefix = []byte("// Code generated ")
     29 	suffix = []byte(" DO NOT EDIT.")
     30 	nl     = []byte("\n")
     31 	crnl   = []byte("\r\n")
     32 )
     33 
     34 func isGenerated(path string) (Generator, bool) {
     35 	f, err := os.Open(path)
     36 	if err != nil {
     37 		return 0, false
     38 	}
     39 	defer f.Close()
     40 	br := bufio.NewReader(f)
     41 	for {
     42 		s, err := br.ReadBytes('\n')
     43 		if err != nil && err != io.EOF {
     44 			return 0, false
     45 		}
     46 		s = bytes.TrimSuffix(s, crnl)
     47 		s = bytes.TrimSuffix(s, nl)
     48 		if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) {
     49 			if len(s)-len(suffix) < len(prefix) {
     50 				return Unknown, true
     51 			}
     52 
     53 			text := string(s[len(prefix) : len(s)-len(suffix)])
     54 			switch text {
     55 			case "by goyacc.":
     56 				return Goyacc, true
     57 			case "by cmd/cgo;":
     58 				return Cgo, true
     59 			case "by protoc-gen-go.":
     60 				return ProtocGenGo, true
     61 			}
     62 			if strings.HasPrefix(text, `by "stringer `) {
     63 				return Stringer, true
     64 			}
     65 			if strings.HasPrefix(text, `by goyacc `) {
     66 				return Goyacc, true
     67 			}
     68 
     69 			return Unknown, true
     70 		}
     71 		if bytes.Equal(s, oldCgo) {
     72 			return Cgo, true
     73 		}
     74 		if err == io.EOF {
     75 			break
     76 		}
     77 	}
     78 	return 0, false
     79 }
     80 
     81 var Analyzer = &analysis.Analyzer{
     82 	Name: "isgenerated",
     83 	Doc:  "annotate file names that have been code generated",
     84 	Run: func(pass *analysis.Pass) (any, error) {
     85 		m := map[string]Generator{}
     86 		for _, f := range pass.Files {
     87 			path := pass.Fset.PositionFor(f.Pos(), false).Filename
     88 			g, ok := isGenerated(path)
     89 			if ok {
     90 				m[path] = g
     91 			}
     92 		}
     93 		return m, nil
     94 	},
     95 	RunDespiteErrors: true,
     96 	ResultType:       reflect.TypeFor[map[string]Generator](),
     97 }