github.com/shulhan/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  }