Denis Isaev ed0b551070 Fix linting of preprocessed files
Preprocessed files like .qtpl.go quicktemplate Go files can have
//line directives. They map to a source .qtpl file.
This commit fixes linting of such files:
1. don't fail on AST cache loading
2. output Go filename not .qtpl or similar

Also, here we update golint to the upstream version.

Relates: #316, #466, #467, #468
2019-04-20 21:50:26 +03:00

163 lines
3.2 KiB
Go

package astcache
import (
"go/ast"
"go/parser"
"go/token"
"path/filepath"
"golang.org/x/tools/go/packages"
"github.com/golangci/golangci-lint/pkg/fsutils"
"github.com/golangci/golangci-lint/pkg/logutils"
)
type File struct {
F *ast.File
Fset *token.FileSet
Name string
Err error
}
type Cache struct {
m map[string]*File // map from absolute file path to file data
s []*File
log logutils.Log
}
func NewCache(log logutils.Log) *Cache {
return &Cache{
m: map[string]*File{},
log: log,
}
}
func (c Cache) ParsedFilenames() []string {
var keys []string
for k := range c.m {
keys = append(keys, k)
}
return keys
}
func (c Cache) normalizeFilename(filename string) string {
absPath := func() string {
if filepath.IsAbs(filename) {
return filepath.Clean(filename)
}
absFilename, err := filepath.Abs(filename)
if err != nil {
c.log.Warnf("Can't abs-ify filename %s: %s", filename, err)
return filename
}
return absFilename
}()
ret, err := fsutils.EvalSymlinks(absPath)
if err != nil {
c.log.Warnf("Failed to eval symlinks for %s: %s", absPath, err)
return absPath
}
return ret
}
func (c Cache) Get(filename string) *File {
return c.m[c.normalizeFilename(filename)]
}
func (c Cache) GetAllValidFiles() []*File {
return c.s
}
func (c *Cache) prepareValidFiles() {
files := make([]*File, 0, len(c.m))
for _, f := range c.m {
if f.Err != nil || f.F == nil {
continue
}
files = append(files, f)
}
c.s = files
}
func LoadFromFilenames(log logutils.Log, filenames ...string) *Cache {
c := NewCache(log)
fset := token.NewFileSet()
for _, filename := range filenames {
c.parseFile(filename, fset)
}
c.prepareValidFiles()
return c
}
func LoadFromPackages(pkgs []*packages.Package, log logutils.Log) (*Cache, error) {
c := NewCache(log)
for _, pkg := range pkgs {
c.loadFromPackage(pkg)
}
c.prepareValidFiles()
return c, nil
}
func (c *Cache) extractFilenamesForAstFile(fset *token.FileSet, f *ast.File) []string {
var ret []string
// false ignores //line comments: name can be incorrect for generated files with //line directives
// mapping e.g. from .rl to .go files.
pos := fset.PositionFor(f.Pos(), false)
if pos.Filename != "" {
ret = append(ret, pos.Filename)
}
return ret
}
func (c *Cache) loadFromPackage(pkg *packages.Package) {
for _, f := range pkg.Syntax {
for _, filename := range c.extractFilenamesForAstFile(pkg.Fset, f) {
filePath := c.normalizeFilename(filename)
c.m[filePath] = &File{
F: f,
Fset: pkg.Fset,
Name: filePath,
}
}
}
// some Go files sometimes aren't present in pkg.Syntax
fset := token.NewFileSet() // can't use pkg.Fset: it will overwrite offsets by preprocessed files
for _, filePath := range pkg.GoFiles {
filePath = c.normalizeFilename(filePath)
if c.m[filePath] == nil {
c.parseFile(filePath, fset)
}
}
}
func (c *Cache) parseFile(filePath string, fset *token.FileSet) {
if fset == nil {
fset = token.NewFileSet()
}
filePath = c.normalizeFilename(filePath)
// comments needed by e.g. golint
f, err := parser.ParseFile(fset, filePath, nil, parser.ParseComments)
c.m[filePath] = &File{
F: f,
Fset: fset,
Err: err,
Name: filePath,
}
if err != nil {
c.log.Warnf("Can't parse AST of %s: %s", filePath, err)
}
}