github.com/ferretdb/golangci-lint@v1.10.1/pkg/lint/astcache/astcache.go (about) 1 package astcache 2 3 import ( 4 "go/ast" 5 "go/parser" 6 "go/token" 7 "path/filepath" 8 9 "github.com/golangci/golangci-lint/pkg/fsutils" 10 "github.com/golangci/golangci-lint/pkg/goutils" 11 "github.com/golangci/golangci-lint/pkg/logutils" 12 "golang.org/x/tools/go/loader" 13 ) 14 15 type File struct { 16 F *ast.File 17 Fset *token.FileSet 18 Name string 19 Err error 20 } 21 22 type Cache struct { 23 m map[string]*File 24 s []*File 25 log logutils.Log 26 } 27 28 func NewCache(log logutils.Log) *Cache { 29 return &Cache{ 30 m: map[string]*File{}, 31 log: log, 32 } 33 } 34 35 func (c Cache) Get(filename string) *File { 36 return c.m[filepath.Clean(filename)] 37 } 38 39 func (c Cache) GetOrParse(filename string) *File { 40 f := c.m[filename] 41 if f != nil { 42 return f 43 } 44 45 c.log.Infof("Parse AST for file %s on demand", filename) 46 c.parseFile(filename, nil) 47 return c.m[filename] 48 } 49 50 func (c Cache) GetAllValidFiles() []*File { 51 return c.s 52 } 53 54 func (c *Cache) prepareValidFiles() { 55 files := make([]*File, 0, len(c.m)) 56 for _, f := range c.m { 57 if f.Err != nil || f.F == nil { 58 continue 59 } 60 files = append(files, f) 61 } 62 c.s = files 63 } 64 65 func LoadFromProgram(prog *loader.Program, log logutils.Log) (*Cache, error) { 66 c := NewCache(log) 67 68 for _, pkg := range prog.InitialPackages() { 69 for _, f := range pkg.Files { 70 pos := prog.Fset.Position(f.Pos()) 71 if pos.Filename == "" { 72 continue 73 } 74 75 if goutils.IsCgoFilename(pos.Filename) { 76 continue 77 } 78 79 path, err := fsutils.ShortestRelPath(pos.Filename, "") 80 if err != nil { 81 c.log.Warnf("Can't get relative path for %s: %s", 82 pos.Filename, err) 83 continue 84 } 85 86 c.m[path] = &File{ 87 F: f, 88 Fset: prog.Fset, 89 Name: path, 90 } 91 } 92 } 93 94 c.prepareValidFiles() 95 return c, nil 96 } 97 98 func (c *Cache) parseFile(filePath string, fset *token.FileSet) { 99 if fset == nil { 100 fset = token.NewFileSet() 101 } 102 103 f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments) // comments needed by e.g. golint 104 c.m[filePath] = &File{ 105 F: f, 106 Fset: fset, 107 Err: err, 108 Name: filePath, 109 } 110 if err != nil { 111 c.log.Warnf("Can't parse AST of %s: %s", filePath, err) 112 } 113 } 114 115 func LoadFromFiles(files []string, log logutils.Log) (*Cache, error) { //nolint:unparam 116 c := NewCache(log) 117 118 fset := token.NewFileSet() 119 for _, filePath := range files { 120 filePath = filepath.Clean(filePath) 121 c.parseFile(filePath, fset) 122 } 123 124 c.prepareValidFiles() 125 return c, nil 126 }