#45: fix no results for gocyclo

This commit is contained in:
golangci 2018-05-30 19:54:29 +03:00
parent 80a5ff2eff
commit ef81b998ed
6 changed files with 193 additions and 52 deletions

View File

@ -1,16 +1,21 @@
package astcache package astcache
import ( import (
"fmt"
"go/ast" "go/ast"
"go/parser" "go/parser"
"go/token" "go/token"
"os"
"path/filepath"
"github.com/sirupsen/logrus"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
type File struct { type File struct {
F *ast.File F *ast.File
Fset *token.FileSet Fset *token.FileSet
Name string
err error err error
} }
@ -34,22 +39,40 @@ func (c *Cache) prepareValidFiles() {
c.s = files c.s = files
} }
func LoadFromProgram(prog *loader.Program) *Cache { func LoadFromProgram(prog *loader.Program) (*Cache, error) {
c := &Cache{ c := &Cache{
m: map[string]*File{}, m: map[string]*File{},
} }
root, err := os.Getwd()
if err != nil {
return nil, fmt.Errorf("can't get working dir: %s", err)
}
for _, pkg := range prog.InitialPackages() { for _, pkg := range prog.InitialPackages() {
for _, f := range pkg.Files { for _, f := range pkg.Files {
pos := prog.Fset.Position(0) pos := prog.Fset.Position(f.Pos())
c.m[pos.Filename] = &File{ if pos.Filename == "" {
continue
}
relPath, err := filepath.Rel(root, pos.Filename)
if err != nil {
logrus.Warnf("Can't get relative path for %s and %s: %s",
root, pos.Filename, err)
continue
}
c.m[relPath] = &File{
F: f, F: f,
Fset: prog.Fset, Fset: prog.Fset,
Name: relPath,
} }
} }
} }
c.prepareValidFiles() c.prepareValidFiles()
return c return c, nil
} }
func LoadFromFiles(files []string) *Cache { func LoadFromFiles(files []string) *Cache {
@ -63,6 +86,7 @@ func LoadFromFiles(files []string) *Cache {
F: f, F: f,
Fset: fset, Fset: fset,
err: err, err: err,
Name: filePath,
} }
} }

View File

@ -12,17 +12,7 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func (e *Executor) initRoot() { func (e *Executor) persistentPostRun(cmd *cobra.Command, args []string) {
rootCmd := &cobra.Command{
Use: "golangci-lint",
Short: "golangci-lint is a smart linters runner.",
Long: `Smart, fast linters runner. Run it in cloud for every GitHub pull request on https://golangci.com`,
Run: func(cmd *cobra.Command, args []string) {
if err := cmd.Help(); err != nil {
logrus.Fatal(err)
}
},
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if e.cfg.Run.PrintVersion { if e.cfg.Run.PrintVersion {
fmt.Fprintf(printers.StdOut, "golangci-lint has version %s built from %s on %s\n", e.version, e.commit, e.date) fmt.Fprintf(printers.StdOut, "golangci-lint has version %s built from %s on %s\n", e.version, e.commit, e.date)
os.Exit(0) os.Exit(0)
@ -46,8 +36,9 @@ func (e *Executor) initRoot() {
logrus.Fatal(err) logrus.Fatal(err)
} }
} }
}, }
PersistentPostRun: func(cmd *cobra.Command, args []string) {
func (e *Executor) persistentPreRun(cmd *cobra.Command, args []string) {
if e.cfg.Run.CPUProfilePath != "" { if e.cfg.Run.CPUProfilePath != "" {
pprof.StopCPUProfile() pprof.StopCPUProfile()
} }
@ -63,7 +54,20 @@ func (e *Executor) initRoot() {
} }
os.Exit(e.exitCode) os.Exit(e.exitCode)
}
func (e *Executor) initRoot() {
rootCmd := &cobra.Command{
Use: "golangci-lint",
Short: "golangci-lint is a smart linters runner.",
Long: `Smart, fast linters runner. Run it in cloud for every GitHub pull request on https://golangci.com`,
Run: func(cmd *cobra.Command, args []string) {
if err := cmd.Help(); err != nil {
logrus.Fatal(err)
}
}, },
PersistentPreRun: e.persistentPostRun,
PersistentPostRun: e.persistentPreRun,
} }
rootCmd.PersistentFlags().BoolVarP(&e.cfg.Run.IsVerbose, "verbose", "v", false, "verbose output") rootCmd.PersistentFlags().BoolVarP(&e.cfg.Run.IsVerbose, "verbose", "v", false, "verbose output")
rootCmd.PersistentFlags().StringVar(&e.cfg.Run.CPUProfilePath, "cpu-profile-path", "", "Path to CPU profile output file") rootCmd.PersistentFlags().StringVar(&e.cfg.Run.CPUProfilePath, "cpu-profile-path", "", "Path to CPU profile output file")

View File

@ -254,7 +254,10 @@ func buildLintCtx(ctx context.Context, linters []pkg.Linter, cfg *config.Config)
var astCache *astcache.Cache var astCache *astcache.Cache
if prog != nil { if prog != nil {
astCache = astcache.LoadFromProgram(prog) astCache, err = astcache.LoadFromProgram(prog)
if err != nil {
return nil, err
}
} else { } else {
astCache = astcache.LoadFromFiles(paths.Files) astCache = astcache.LoadFromFiles(paths.Files)
} }

54
pkg/commands/run_test.go Normal file
View File

@ -0,0 +1,54 @@
package commands
import (
"context"
"testing"
"github.com/golangci/golangci-lint/pkg"
"github.com/golangci/golangci-lint/pkg/astcache"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/fsutils"
"github.com/golangci/golangci-lint/pkg/golinters"
"github.com/stretchr/testify/assert"
)
func TestASTCacheLoading(t *testing.T) {
ctx := context.Background()
linters := []pkg.Linter{golinters.Errcheck{}}
inputPaths := []string{"./...", "./", "./run.go", "run.go"}
for _, inputPath := range inputPaths {
paths, err := fsutils.GetPathsForAnalysis(ctx, []string{inputPath}, true)
assert.NoError(t, err)
assert.NotEmpty(t, paths.Files)
prog, _, err := loadWholeAppIfNeeded(ctx, linters, &config.Run{
AnalyzeTests: true,
}, paths)
assert.NoError(t, err)
astCacheFromProg, err := astcache.LoadFromProgram(prog)
assert.NoError(t, err)
astCacheFromFiles := astcache.LoadFromFiles(paths.Files)
filesFromProg := astCacheFromProg.GetAllValidFiles()
filesFromFiles := astCacheFromFiles.GetAllValidFiles()
if len(filesFromProg) != len(filesFromFiles) {
t.Logf("files: %s", paths.Files)
t.Logf("from prog:")
for _, f := range filesFromProg {
t.Logf("%+v", *f)
}
t.Logf("from files:")
for _, f := range filesFromFiles {
t.Logf("%+v", *f)
}
t.Fatalf("lengths differ")
}
if len(filesFromProg) != len(paths.Files) {
t.Fatalf("filesFromProg differ from path.Files")
}
}
}

View File

@ -294,9 +294,7 @@ func GetAllLintersForPreset(p string) []Linter {
return ret return ret
} }
func getEnabledLintersSet(cfg *config.Config) map[string]Linter { // nolint:gocyclo func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []Linter) map[string]Linter { // nolint:gocyclo
lcfg := &cfg.Linters
resultLintersSet := map[string]Linter{} resultLintersSet := map[string]Linter{}
switch { switch {
case len(lcfg.Presets) != 0: case len(lcfg.Presets) != 0:
@ -306,7 +304,7 @@ func getEnabledLintersSet(cfg *config.Config) map[string]Linter { // nolint:gocy
case lcfg.DisableAll: case lcfg.DisableAll:
break break
default: default:
resultLintersSet = lintersToMap(getAllEnabledByDefaultLinters()) resultLintersSet = lintersToMap(enabledByDefaultLinters)
} }
// --presets can only add linters to default set // --presets can only add linters to default set
@ -332,12 +330,24 @@ func getEnabledLintersSet(cfg *config.Config) map[string]Linter { // nolint:gocy
} }
for _, name := range lcfg.Disable { for _, name := range lcfg.Disable {
if name == "megacheck" {
for _, ln := range getAllMegacheckSubLinterNames() {
delete(resultLintersSet, ln)
}
}
delete(resultLintersSet, name) delete(resultLintersSet, name)
} }
return resultLintersSet return resultLintersSet
} }
func getAllMegacheckSubLinterNames() []string {
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
return []string{unusedName, gosimpleName, staticcheckName}
}
func optimizeLintersSet(linters map[string]Linter) { func optimizeLintersSet(linters map[string]Linter) {
unusedName := golinters.Megacheck{UnusedEnabled: true}.Name() unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name() gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
@ -375,7 +385,7 @@ func GetEnabledLinters(cfg *config.Config) ([]Linter, error) {
return nil, err return nil, err
} }
resultLintersSet := getEnabledLintersSet(cfg) resultLintersSet := getEnabledLintersSet(&cfg.Linters, getAllEnabledByDefaultLinters())
optimizeLintersSet(resultLintersSet) optimizeLintersSet(resultLintersSet)
var resultLinters []Linter var resultLinters []Linter

View File

@ -8,6 +8,7 @@ import (
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime" "runtime"
"sort"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -370,3 +371,48 @@ func BenchmarkWithGometalinter(b *testing.B) {
compare(b, runGometalinter, runGolangciLint, bc.name, "", lc/1000) compare(b, runGometalinter, runGolangciLint, bc.name, "", lc/1000)
} }
} }
func TestGetEnabledLintersSet(t *testing.T) {
type cs struct {
cfg config.Linters
name string // test case name
def []string // enabled by default linters
exp []string // alphabetically ordered enabled linter names
}
cases := []cs{
{
cfg: config.Linters{
Disable: []string{"megacheck"},
},
name: "disable all linters from megacheck",
def: getAllMegacheckSubLinterNames(),
},
{
cfg: config.Linters{
Disable: []string{"staticcheck"},
},
name: "disable only staticcheck",
def: getAllMegacheckSubLinterNames(),
exp: []string{"gosimple", "unused"},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
defaultLinters := []Linter{}
for _, ln := range c.def {
defaultLinters = append(defaultLinters, getLinterByName(ln))
}
els := getEnabledLintersSet(&c.cfg, defaultLinters)
var enabledLinters []string
for ln := range els {
enabledLinters = append(enabledLinters, ln)
}
sort.Strings(enabledLinters)
sort.Strings(c.exp)
assert.Equal(t, c.exp, enabledLinters)
})
}
}