group of fixes after running on golang source code

This commit is contained in:
golangci 2018-05-08 17:13:16 +03:00
parent 6f384926cf
commit d993d3a264
31 changed files with 331 additions and 103 deletions

4
Gopkg.lock generated
View File

@ -101,7 +101,7 @@
"lib/cfg",
"lib/whitelist"
]
revision = "31bebf17d90d40b728c41fecdd5acf5fb8654fc7"
revision = "1a9ab8120ec96a014df3afab2d6c47f7e12d8928"
[[projects]]
branch = "master"
@ -140,7 +140,7 @@
"golangci",
"internal/errcheck"
]
revision = "7a3d63cfc6fbd9e46f2e551f9b8d1e9c69bffab1"
revision = "d2c0791055c651c4bb6798ee1aacbd27ad377aa8"
source = "github.com/golangci/errcheck"
[[projects]]

View File

@ -1,2 +1,2 @@
test:
go test -v ./...
go test -v -race ./...

View File

@ -3,6 +3,7 @@ package commands
import (
"fmt"
"os"
"strings"
"github.com/fatih/color"
"github.com/golangci/golangci-lint/pkg"
@ -20,7 +21,7 @@ func (e *Executor) initLinters() {
func printLinterConfigs(lcs []pkg.LinterConfig) {
for _, lc := range lcs {
fmt.Printf("%s: %s\n", color.YellowString(lc.Linter.Name()), lc.Desc)
fmt.Printf("%s: %s\n", color.YellowString(lc.Linter.Name()), lc.Linter.Desc())
}
}
@ -39,5 +40,15 @@ func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
color.Red("\nDisabled by default linters:\n")
printLinterConfigs(disabledLCs)
color.Green("\nLinters presets:")
for _, p := range pkg.AllPresets() {
linters := pkg.GetAllLintersForPreset(p)
linterNames := []string{}
for _, linter := range linters {
linterNames = append(linterNames, linter.Name())
}
fmt.Printf("%s: %s\n", color.YellowString(p), strings.Join(linterNames, ", "))
}
os.Exit(0)
}

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"go/build"
"go/token"
"log"
"strings"
"time"
@ -38,16 +39,16 @@ func (e *Executor) initRun() {
fmt.Sprintf("Format of output: %s", strings.Join(config.OutFormats, "|")))
runCmd.Flags().BoolVar(&rc.PrintIssuedLine, "print-issued-lines", true, "Print lines of code with issue")
runCmd.Flags().BoolVar(&rc.PrintLinterName, "print-linter-name", true, "Print linter name in issue line")
runCmd.Flags().BoolVar(&rc.PrintWelcomeMessage, "print-welcome", true, "Print welcome message")
runCmd.Flags().IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code",
1, "Exit code when issues were found")
runCmd.Flags().StringSliceVar(&rc.BuildTags, "build-tags", []string{}, "Build tags (not all linters support them)")
runCmd.Flags().BoolVar(&rc.Errcheck.CheckClose, "errcheck.check-close", false, "Errcheck: check missed error checks on .Close() calls")
runCmd.Flags().BoolVar(&rc.Errcheck.CheckTypeAssertions, "errcheck.check-type-assertions", false, "Errcheck: check for ignored type assertion results")
runCmd.Flags().BoolVar(&rc.Errcheck.CheckAssignToBlank, "errcheck.check-blank", false, "Errcheck: check for errors assigned to blank identifier: _ = errFunc()")
runCmd.Flags().BoolVar(&rc.Govet.CheckShadowing, "govet.check-shadowing", true, "Govet: check for shadowed variables")
runCmd.Flags().BoolVar(&rc.Govet.CheckShadowing, "govet.check-shadowing", false, "Govet: check for shadowed variables")
runCmd.Flags().Float64Var(&rc.Golint.MinConfidence, "golint.min-confidence", 0.8, "Golint: minimum confidence of a problem to print it")
@ -90,6 +91,9 @@ func (e *Executor) initRun() {
runCmd.Flags().StringVar(&rc.DiffFromRevision, "new-from-rev", "", "Show only new issues created after git revision `REV`")
runCmd.Flags().StringVar(&rc.DiffPatchFilePath, "new-from-patch", "", "Show only new issues created in git patch with file path `PATH`")
runCmd.Flags().BoolVar(&rc.AnalyzeTests, "tests", false, "Analyze tests (*_test.go)")
runCmd.Flags().StringSliceVarP(&rc.Presets, "presets", "p", []string{},
fmt.Sprintf("Enable presets (%s) of linters. Run 'golangci-lint linters' to see them. This option implies option --disable-all", strings.Join(pkg.AllPresets(), "|")))
}
func isFullImportNeeded(linters []pkg.Linter) bool {
@ -208,10 +212,15 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (chan result.
if len(excludePatterns) != 0 {
excludeTotalPattern = fmt.Sprintf("(%s)", strings.Join(excludePatterns, "|"))
}
fset := token.NewFileSet()
if lintCtx.Program != nil {
fset = lintCtx.Program.Fset
}
runner := pkg.SimpleRunner{
Processors: []processors.Processor{
processors.NewExclude(excludeTotalPattern),
processors.NewNolint(lintCtx.Program.Fset),
processors.NewCgo(),
processors.NewNolint(fset),
processors.NewUniqByLine(),
processors.NewDiff(e.cfg.Run.Diff, e.cfg.Run.DiffFromRevision, e.cfg.Run.DiffPatchFilePath),
processors.NewMaxPerFileFromLinter(),
@ -231,6 +240,10 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
logrus.Infof("Run took %s", time.Since(startedAt))
}(time.Now())
if e.cfg.Run.PrintWelcomeMessage {
fmt.Println("Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
}
f := func() error {
issues, err := e.runAnalysis(ctx, args)
if err != nil {

View File

@ -15,6 +15,7 @@ const (
var OutFormats = []string{OutFormatColoredLineNumber, OutFormatLineNumber, OutFormatJSON}
var DefaultExcludePatterns = []string{
"Error return value of `(os\\.Std(out|err)\\.Write|.*\\.Close)` is not checked",
"should have comment",
"comment on exported method",
"G104", // disable what errcheck does: it reports on Close etc
@ -36,11 +37,11 @@ type Run struct { // nolint:maligned
OutFormat string
PrintIssuedLine bool
PrintLinterName bool
PrintWelcomeMessage bool
ExitCodeIfIssuesFound int
Errcheck struct {
CheckClose bool
CheckTypeAssertions bool
CheckAssignToBlank bool
}
@ -83,6 +84,8 @@ type Run struct { // nolint:maligned
EnableAllLinters bool
DisableAllLinters bool
Presets []string
ExcludePatterns []string
UseDefaultExcludes bool

View File

@ -3,6 +3,7 @@ package pkg
import (
"context"
"fmt"
"strings"
"sync"
"github.com/golangci/golangci-lint/pkg/config"
@ -10,12 +11,61 @@ import (
"github.com/sirupsen/logrus"
)
const (
PresetFormatting = "format"
PresetComplexity = "complexity"
PresetStyle = "style"
PresetBugs = "bugs"
PresetUnused = "unused"
PresetPerformance = "performance"
)
func AllPresets() []string {
return []string{PresetBugs, PresetUnused, PresetFormatting, PresetStyle, PresetComplexity, PresetPerformance}
}
func allPresetsSet() map[string]bool {
ret := map[string]bool{}
for _, p := range AllPresets() {
ret[p] = true
}
return ret
}
type LinterConfig struct {
Desc string
Linter Linter
EnabledByDefault bool
DoesFullImport bool
NeedsSSARepr bool
InPresets []string
}
func (lc LinterConfig) WithFullImport() LinterConfig {
lc.DoesFullImport = true
return lc
}
func (lc LinterConfig) WithSSA() LinterConfig {
lc.DoesFullImport = true
lc.NeedsSSARepr = true
return lc
}
func (lc LinterConfig) WithPresets(presets ...string) LinterConfig {
lc.InPresets = presets
return lc
}
func (lc LinterConfig) WithDisabledByDefault() LinterConfig {
lc.EnabledByDefault = false
return lc
}
func newLinterConfig(linter Linter) LinterConfig {
return LinterConfig{
Linter: linter,
EnabledByDefault: true,
}
}
var nameToLC map[string]LinterConfig
@ -37,46 +87,26 @@ func GetLinterConfig(name string) *LinterConfig {
return &lc
}
func enabledByDefault(linter Linter, desc string, doesFullImport, needsSSARepr bool) LinterConfig {
return LinterConfig{
EnabledByDefault: true,
Linter: linter,
Desc: desc,
DoesFullImport: doesFullImport,
NeedsSSARepr: needsSSARepr,
}
}
func disabledByDefault(linter Linter, desc string, doesFullImport, needsSSARepr bool) LinterConfig {
return LinterConfig{
EnabledByDefault: false,
Linter: linter,
Desc: desc,
DoesFullImport: doesFullImport,
NeedsSSARepr: needsSSARepr,
}
}
func GetAllSupportedLinterConfigs() []LinterConfig {
return []LinterConfig{
enabledByDefault(golinters.Govet{}, "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string", false, false),
enabledByDefault(golinters.Errcheck{}, "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases", true, false),
enabledByDefault(golinters.Golint{}, "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes", false, false),
enabledByDefault(golinters.Megacheck{}, "Megacheck: 3 sub-linters in one: staticcheck, gosimple and unused", true, true),
enabledByDefault(golinters.Gas{}, "Inspects source code for security problems", true, false),
enabledByDefault(golinters.Structcheck{}, "Finds unused struct fields", true, false),
enabledByDefault(golinters.Varcheck{}, "Finds unused global variables and constants", true, false),
enabledByDefault(golinters.Interfacer{}, "Linter that suggests narrower interface types", true, true),
enabledByDefault(golinters.Unconvert{}, "Remove unnecessary type conversions", true, false),
enabledByDefault(golinters.Ineffassign{}, "Detects when assignments to existing variables are not used", false, false),
enabledByDefault(golinters.Dupl{}, "Tool for code clone detection", false, false),
enabledByDefault(golinters.Goconst{}, "Finds repeated strings that could be replaced by a constant", false, false),
enabledByDefault(golinters.Deadcode{}, "Finds unused code", true, false),
enabledByDefault(golinters.Gocyclo{}, "Computes and checks the cyclomatic complexity of functions", false, false),
newLinterConfig(golinters.Govet{}).WithPresets(PresetBugs),
newLinterConfig(golinters.Errcheck{}).WithFullImport().WithPresets(PresetBugs),
newLinterConfig(golinters.Golint{}).WithDisabledByDefault().WithPresets(PresetStyle),
newLinterConfig(golinters.Megacheck{}).WithSSA().WithPresets(PresetBugs, PresetUnused, PresetStyle),
newLinterConfig(golinters.Gas{}).WithFullImport().WithPresets(PresetBugs),
newLinterConfig(golinters.Structcheck{}).WithFullImport().WithPresets(PresetUnused),
newLinterConfig(golinters.Varcheck{}).WithFullImport().WithPresets(PresetUnused),
newLinterConfig(golinters.Interfacer{}).WithDisabledByDefault().WithSSA().WithPresets(PresetStyle),
newLinterConfig(golinters.Unconvert{}).WithDisabledByDefault().WithFullImport().WithPresets(PresetStyle),
newLinterConfig(golinters.Ineffassign{}).WithPresets(PresetUnused),
newLinterConfig(golinters.Dupl{}).WithDisabledByDefault().WithPresets(PresetStyle),
newLinterConfig(golinters.Goconst{}).WithDisabledByDefault().WithPresets(PresetStyle),
newLinterConfig(golinters.Deadcode{}).WithFullImport().WithPresets(PresetUnused),
newLinterConfig(golinters.Gocyclo{}).WithDisabledByDefault().WithPresets(PresetComplexity),
disabledByDefault(golinters.Gofmt{}, "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification", false, false),
disabledByDefault(golinters.Gofmt{UseGoimports: true}, "Goimports does everything that gofmt does. Additionally it checks unused imports", false, false),
disabledByDefault(golinters.Maligned{}, "Tool to detect Go structs that would take less memory if their fields were sorted", true, false),
newLinterConfig(golinters.Gofmt{}).WithDisabledByDefault().WithPresets(PresetFormatting),
newLinterConfig(golinters.Gofmt{UseGoimports: true}).WithDisabledByDefault().WithPresets(PresetFormatting),
newLinterConfig(golinters.Maligned{}).WithFullImport().WithDisabledByDefault().WithPresets(PresetPerformance),
}
}
@ -132,6 +162,17 @@ func validateEnabledDisabledLintersConfig(cfg *config.Run) error {
}
}
allPresets := allPresetsSet()
for _, p := range cfg.Presets {
if !allPresets[p] {
return fmt.Errorf("no such preset %q: only next presets exist: (%s)", p, strings.Join(AllPresets(), "|"))
}
}
if len(cfg.Presets) != 0 && cfg.EnableAllLinters {
return fmt.Errorf("--presets is incompatible with --enable-all")
}
if cfg.EnableAllLinters && cfg.DisableAllLinters {
return fmt.Errorf("--enable-all and --disable-all options must not be combined")
}
@ -164,12 +205,29 @@ func validateEnabledDisabledLintersConfig(cfg *config.Run) error {
return nil
}
func GetAllLintersForPreset(p string) []Linter {
ret := []Linter{}
for _, lc := range GetAllSupportedLinterConfigs() {
for _, ip := range lc.InPresets {
if p == ip {
ret = append(ret, lc.Linter)
break
}
}
}
return ret
}
func GetEnabledLinters(ctx context.Context, cfg *config.Run) ([]Linter, error) {
if err := validateEnabledDisabledLintersConfig(cfg); err != nil {
return nil, err
}
resultLintersSet := map[string]Linter{}
switch {
case len(cfg.Presets) != 0:
break // imply --disable-all
case cfg.EnableAllLinters:
resultLintersSet = lintersToMap(getAllSupportedLinters())
case cfg.DisableAllLinters:
@ -182,6 +240,32 @@ func GetEnabledLinters(ctx context.Context, cfg *config.Run) ([]Linter, error) {
resultLintersSet[name] = getLinterByName(name)
}
// XXX: hacks because of sub-linters in megacheck
megacheckWasEnabledByUser := resultLintersSet["megacheck"] != nil
if !megacheckWasEnabledByUser {
cfg.Megacheck.EnableGosimple = false
cfg.Megacheck.EnableStaticcheck = false
cfg.Megacheck.EnableUnused = false
}
for _, p := range cfg.Presets {
for _, linter := range GetAllLintersForPreset(p) {
resultLintersSet[linter.Name()] = linter
}
if !megacheckWasEnabledByUser {
if p == PresetBugs {
cfg.Megacheck.EnableStaticcheck = true
}
if p == PresetStyle {
cfg.Megacheck.EnableGosimple = true
}
if p == PresetUnused {
cfg.Megacheck.EnableUnused = true
}
}
}
for _, name := range cfg.DisabledLinters {
delete(resultLintersSet, name)
}
@ -192,7 +276,10 @@ func GetEnabledLinters(ctx context.Context, cfg *config.Run) ([]Linter, error) {
resultLinters = append(resultLinters, linter)
resultLinterNames = append(resultLinterNames, name)
}
logrus.Infof("Enabled linters: %s", resultLinterNames)
logrus.Infof("Active linters: %s", resultLinterNames)
if len(cfg.Presets) != 0 {
logrus.Infof("Active presets: %s", cfg.Presets)
}
return resultLinters, nil
}

View File

@ -56,6 +56,8 @@ func testOneSource(t *testing.T, sourcePath string) {
"--print-issued-lines=false",
"--print-linter-name=false",
"--out-format=line-number",
"--print-welcome=false",
"--govet.check-shadowing=true",
sourcePath)
runGoErrchk(cmd, t)
}

View File

@ -14,6 +14,10 @@ func (Deadcode) Name() string {
return "deadcode"
}
func (Deadcode) Desc() string {
return "Finds unused code"
}
func (d Deadcode) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues, err := deadcodeAPI.Run(lintCtx.Program)
if err != nil {

View File

@ -15,6 +15,10 @@ func (Dupl) Name() string {
return "dupl"
}
func (Dupl) Desc() string {
return "Tool for code clone detection"
}
func (d Dupl) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues, err := duplAPI.Run(lintCtx.Paths.Files, lintCtx.RunCfg().Dupl.Threshold)
if err != nil {

View File

@ -3,7 +3,6 @@ package golinters
import (
"context"
"fmt"
"strings"
"github.com/golangci/golangci-lint/pkg/result"
errcheckAPI "github.com/kisielk/errcheck/golangci"
@ -15,6 +14,10 @@ func (Errcheck) Name() string {
return "errcheck"
}
func (Errcheck) Desc() string {
return "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases"
}
func (e Errcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
errCfg := &lintCtx.RunCfg().Errcheck
issues, err := errcheckAPI.Run(lintCtx.Program, errCfg.CheckAssignToBlank, errCfg.CheckTypeAssertions)
@ -24,10 +27,6 @@ func (e Errcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, er
var res []result.Issue
for _, i := range issues {
if !errCfg.CheckClose && strings.HasSuffix(i.FuncName, ".Close") {
continue
}
var text string
if i.FuncName != "" {
text = fmt.Sprintf("Error return value of %s is not checked", formatCode(i.FuncName, lintCtx.RunCfg()))

View File

@ -19,6 +19,10 @@ func (Gas) Name() string {
return "gas"
}
func (Gas) Desc() string {
return "Inspects source code for security problems"
}
func (lint Gas) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
gasConfig := gas.NewConfig()
enabledRules := rules.Generate()

View File

@ -14,8 +14,15 @@ func (Goconst) Name() string {
return "goconst"
}
func (Goconst) Desc() string {
return "Finds repeated strings that could be replaced by a constant"
}
func (lint Goconst) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues, err := goconstAPI.Run(lintCtx.Paths.Files, true,
var goconstIssues []goconstAPI.Issue
// TODO: make it cross-package: pass package names inside goconst
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
issues, err := goconstAPI.Run(files, true,
lintCtx.RunCfg().Goconst.MinStringLen,
lintCtx.RunCfg().Goconst.MinOccurrencesCount,
)
@ -23,8 +30,11 @@ func (lint Goconst) Run(ctx context.Context, lintCtx *Context) ([]result.Issue,
return nil, err
}
goconstIssues = append(goconstIssues, issues...)
}
var res []result.Issue
for _, i := range issues {
for _, i := range goconstIssues {
textBegin := fmt.Sprintf("string %s has %d occurrences", formatCode(i.Str, lintCtx.RunCfg()), i.OccurencesCount)
var textEnd string
if i.MatchingConst == "" {

View File

@ -14,6 +14,10 @@ func (Gocyclo) Name() string {
return "gocyclo"
}
func (Gocyclo) Desc() string {
return "Computes and checks the cyclomatic complexity of functions"
}
func (g Gocyclo) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
stats := gocycloAPI.Run(lintCtx.Paths.MixedPaths())

View File

@ -25,6 +25,14 @@ func (g Gofmt) Name() string {
return "gofmt"
}
func (g Gofmt) Desc() string {
if g.UseGoimports {
return "Goimports does everything that gofmt does. Additionally it checks unused imports"
}
return "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification"
}
func getFirstDeletedAndAddedLineNumberInHunk(h *diff.Hunk) (int, int, error) {
lines := bytes.Split(h.Body, []byte{'\n'})
lineNumber := int(h.OrigStartLine - 1)

View File

@ -15,6 +15,10 @@ func (Golint) Name() string {
return "golint"
}
func (Golint) Desc() string {
return "Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes"
}
func (g Golint) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
var issues []result.Issue
for _, pkgFiles := range lintCtx.Paths.FilesGrouppedByDirs() {

View File

@ -13,14 +13,23 @@ func (Govet) Name() string {
return "govet"
}
func (Govet) Desc() string {
return "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string"
}
func (g Govet) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues, err := govetAPI.Run(lintCtx.Paths.MixedPaths(), lintCtx.RunCfg().BuildTags, lintCtx.RunCfg().Govet.CheckShadowing)
// TODO: check .S asm files: govet can do it if pass dirs
var govetIssues []govetAPI.Issue
for _, files := range lintCtx.Paths.FilesGrouppedByDirs() {
issues, err := govetAPI.Run(files, lintCtx.RunCfg().Govet.CheckShadowing)
if err != nil {
return nil, err
}
govetIssues = append(govetIssues, issues...)
}
var res []result.Issue
for _, i := range issues {
for _, i := range govetIssues {
res = append(res, result.Issue{
Pos: i.Pos,
Text: i.Message,

View File

@ -14,6 +14,10 @@ func (Ineffassign) Name() string {
return "ineffassign"
}
func (Ineffassign) Desc() string {
return "Detects when assignments to existing variables are not used"
}
func (lint Ineffassign) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues := ineffassignAPI.Run(lintCtx.Paths.Files)

View File

@ -14,6 +14,10 @@ func (Interfacer) Name() string {
return "interfacer"
}
func (Interfacer) Desc() string {
return "Linter that suggests narrower interface types"
}
func (lint Interfacer) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
c := new(check.Checker)
c.Program(lintCtx.Program)

View File

@ -14,6 +14,10 @@ func (Maligned) Name() string {
return "maligned"
}
func (Maligned) Desc() string {
return "Tool to detect Go structs that would take less memory if their fields were sorted"
}
func (m Maligned) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues := malignedAPI.Run(lintCtx.Program)

View File

@ -13,6 +13,10 @@ func (Megacheck) Name() string {
return "megacheck"
}
func (Megacheck) Desc() string {
return "Megacheck: 3 sub-linters in one: staticcheck, gosimple and unused"
}
func (m Megacheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
c := lintCtx.RunCfg().Megacheck
issues := megacheckAPI.Run(lintCtx.Program, lintCtx.LoaderConfig, lintCtx.SSAProgram, c.EnableStaticcheck, c.EnableGosimple, c.EnableUnused)

View File

@ -14,6 +14,10 @@ func (Structcheck) Name() string {
return "structcheck"
}
func (Structcheck) Desc() string {
return "Finds unused struct fields"
}
func (s Structcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues := structcheckAPI.Run(lintCtx.Program, lintCtx.RunCfg().Structcheck.CheckExportedFields)

View File

@ -13,6 +13,10 @@ func (Unconvert) Name() string {
return "unconvert"
}
func (Unconvert) Desc() string {
return "Remove unnecessary type conversions"
}
func (lint Unconvert) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
positions := unconvertAPI.Run(lintCtx.Program)
var res []result.Issue

View File

@ -14,6 +14,10 @@ func (Varcheck) Name() string {
return "varcheck"
}
func (Varcheck) Desc() string {
return "Finds unused global variables and constants"
}
func (v Varcheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
issues := varcheckAPI.Run(lintCtx.Program, lintCtx.RunCfg().Varcheck.CheckExportedFields)

View File

@ -10,4 +10,5 @@ import (
type Linter interface {
Run(ctx context.Context, lintCtx *golinters.Context) ([]result.Issue, error)
Name() string
Desc() string
}

View File

@ -0,0 +1,30 @@
package processors
import (
"strings"
"github.com/golangci/golangci-lint/pkg/result"
)
type Cgo struct {
}
var _ Processor = Cgo{}
func NewCgo() *Cgo {
return &Cgo{}
}
func (p Cgo) Name() string {
return "cgo"
}
func (p Cgo) Process(issues []result.Issue) ([]result.Issue, error) {
return filterIssues(issues, func(i *result.Issue) bool {
// some linters (.e.g gas, deadcode) return incorrect filepaths for cgo issues,
// it breaks next processing, so skip them
return !strings.HasSuffix(i.FilePath(), "/C")
}), nil
}
func (Cgo) Finish() {}

View File

@ -1,6 +1,7 @@
package processors
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
@ -43,7 +44,7 @@ func (p *Nolint) shouldPassIssue(i *result.Issue) (bool, error) {
if comments == nil {
file, err := parser.ParseFile(p.fset, i.FilePath(), nil, parser.ParseComments)
if err != nil {
return true, err
return false, fmt.Errorf("can't parse file %s", i.FilePath())
}
comments = extractFileComments(p.fset, file.Comments...)

View File

@ -1,6 +1,10 @@
package processors
import "github.com/golangci/golangci-lint/pkg/result"
import (
"fmt"
"github.com/golangci/golangci-lint/pkg/result"
)
func filterIssues(issues []result.Issue, filter func(i *result.Issue) bool) []result.Issue {
retIssues := make([]result.Issue, 0, len(issues))
@ -18,8 +22,9 @@ func filterIssuesErr(issues []result.Issue, filter func(i *result.Issue) (bool,
for _, i := range issues {
ok, err := filter(&i)
if err != nil {
return nil, err
return nil, fmt.Errorf("can't filter issue %#v: %s", i, err)
}
if ok {
retIssues = append(retIssues, i)
}

View File

@ -34,11 +34,7 @@ func (r *SimpleRunner) runLinter(ctx context.Context, linter Linter, lintCtx *go
startedAt := time.Now()
res, err = linter.Run(ctx, lintCtx)
if err == nil && len(res) != 0 {
res = r.processIssues(ctx, res)
}
logrus.Infof("worker #%d: linter %s took %s and found %d issues", i, linter.Name(),
logrus.Infof("worker #%d: linter %s took %s and found %d issues (before processing them)", i, linter.Name(),
time.Since(startedAt), len(res))
return
}
@ -120,6 +116,11 @@ func (r SimpleRunner) runGo(ctx context.Context, linters []Linter, lintCtx *goli
}
finishedN++
if len(res.issues) != 0 {
res.issues = r.processIssues(ctx, res.issues)
}
for _, i := range res.issues {
retIssues <- i
}

View File

@ -32,3 +32,8 @@ func IgnoreCloseInDeferMissingErrHandling() {
panic(resp)
}
func IgnoreStdxWrite() {
os.Stdout.Write([]byte{})
os.Stderr.Write([]byte{})
}

View File

@ -1,9 +1,7 @@
package govet
import (
"fmt"
"go/token"
"os"
"strings"
)
@ -14,7 +12,7 @@ type Issue struct {
var foundIssues []Issue
func Run(paths, buildTags []string, checkShadowing bool) ([]Issue, error) {
func Run(files []string, checkShadowing bool) ([]Issue, error) {
foundIssues = nil
if checkShadowing {
@ -26,37 +24,16 @@ func Run(paths, buildTags []string, checkShadowing bool) ([]Issue, error) {
}
}
tagList = buildTags
initPrintFlags()
initUnusedFlags()
for _, name := range paths {
// Is it a directory?
fi, err := os.Stat(name)
if err != nil {
warnf("error walking tree: %s", err)
continue
}
if fi.IsDir() {
dirsRun = true
} else {
filesRun = true
for _, name := range files {
if !strings.HasSuffix(name, "_test.go") {
includesNonTest = true
}
}
}
if dirsRun && filesRun {
return nil, fmt.Errorf("can't mix dirs and files")
}
if dirsRun {
for _, name := range paths {
doPackageDir(name)
}
return foundIssues, nil
}
if doPackage(paths, nil) == nil {
if doPackage(files, nil) == nil {
return nil, nil
}

View File

@ -226,6 +226,18 @@ type visitor struct {
errors []UncheckedError
}
func getSelName(sel *ast.SelectorExpr) string {
if ident, ok := sel.X.(*ast.Ident); ok {
return fmt.Sprintf("%s.%s", ident.Name, sel.Sel.Name)
}
if s, ok := sel.X.(*ast.SelectorExpr); ok {
return fmt.Sprintf("%s.%s", getSelName(s), sel.Sel.Name)
}
return ""
}
func (v *visitor) fullName(call *ast.CallExpr) (string, bool) {
if ident, ok := call.Fun.(*ast.Ident); ok {
return ident.Name, true
@ -235,6 +247,12 @@ func (v *visitor) fullName(call *ast.CallExpr) (string, bool) {
if !ok {
return "", false
}
name := getSelName(sel)
if name != "" {
return name, true
}
fn, ok := v.pkg.ObjectOf(sel.Sel).(*types.Func)
if !ok {
// Shouldn't happen, but be paranoid