#12: add TypeCheck linter to report compilation errors
This commit is contained in:
parent
5b00f5bf63
commit
b361146df8
@ -166,6 +166,7 @@ golangci-lint linters
|
|||||||
- [varcheck](https://github.com/opennota/check): Finds unused global variables and constants
|
- [varcheck](https://github.com/opennota/check): Finds unused global variables and constants
|
||||||
- [ineffassign](https://github.com/gordonklaus/ineffassign): Detects when assignments to existing variables are not used
|
- [ineffassign](https://github.com/gordonklaus/ineffassign): Detects when assignments to existing variables are not used
|
||||||
- [deadcode](https://github.com/remyoudompheng/go-misc/tree/master/deadcode): Finds unused code
|
- [deadcode](https://github.com/remyoudompheng/go-misc/tree/master/deadcode): Finds unused code
|
||||||
|
- typecheck: Like the front-end of a Go compiler, parses and type-checks Go code. Similar to [gotype](https://godoc.org/golang.org/x/tools/cmd/gotype).
|
||||||
|
|
||||||
## Disabled By Default Linters (`-E/--enable`)
|
## Disabled By Default Linters (`-E/--enable`)
|
||||||
- [golint](https://github.com/golang/lint): Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
|
- [golint](https://github.com/golang/lint): Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
|
||||||
|
@ -17,7 +17,7 @@ func (e *Executor) initRoot() {
|
|||||||
Long: `Smart, fast linters runner. Run it in cloud for every GitHub pull request on https://golangci.com`,
|
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) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if err := cmd.Help(); err != nil {
|
if err := cmd.Help(); err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||||
@ -33,10 +33,10 @@ func (e *Executor) initRoot() {
|
|||||||
if e.cfg.Run.CPUProfilePath != "" {
|
if e.cfg.Run.CPUProfilePath != "" {
|
||||||
f, err := os.Create(e.cfg.Run.CPUProfilePath)
|
f, err := os.Create(e.cfg.Run.CPUProfilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
if err := pprof.StartCPUProfile(f); err != nil {
|
if err := pprof.StartCPUProfile(f); err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -47,11 +47,11 @@ func (e *Executor) initRoot() {
|
|||||||
if e.cfg.Run.MemProfilePath != "" {
|
if e.cfg.Run.MemProfilePath != "" {
|
||||||
f, err := os.Create(e.cfg.Run.MemProfilePath)
|
f, err := os.Create(e.cfg.Run.MemProfilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
runtime.GC() // get up-to-date statistics
|
runtime.GC() // get up-to-date statistics
|
||||||
if err := pprof.WriteHeapProfile(f); err != nil {
|
if err := pprof.WriteHeapProfile(f); err != nil {
|
||||||
log.Fatal("could not write memory profile: ", err)
|
logrus.Fatal("could not write memory profile: ", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
"go/token"
|
"go/token"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
@ -245,7 +246,26 @@ func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan resul
|
|||||||
return runner.Run(ctx, linters, lintCtx), nil
|
return runner.Run(ctx, linters, lintCtx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
|
||||||
|
savedStdout, savedStderr = os.Stdout, os.Stderr
|
||||||
|
devNull, err := os.Open(os.DevNull)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Warnf("can't open null device %q: %s", os.DevNull, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Stdout, os.Stderr = devNull, devNull
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
|
func (e *Executor) runAndPrint(ctx context.Context, args []string) error {
|
||||||
|
// Don't allow linters and loader to print anything
|
||||||
|
log.SetOutput(ioutil.Discard)
|
||||||
|
savedStdout, savedStderr := setOutputToDevNull()
|
||||||
|
defer func() {
|
||||||
|
os.Stdout, os.Stderr = savedStdout, savedStderr
|
||||||
|
}()
|
||||||
|
|
||||||
issues, err := e.runAnalysis(ctx, args)
|
issues, err := e.runAnalysis(ctx, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -288,11 +308,11 @@ func (e *Executor) executeRun(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if e.cfg.Output.PrintWelcomeMessage {
|
if e.cfg.Output.PrintWelcomeMessage {
|
||||||
fmt.Println("Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
|
fmt.Fprintln(printers.StdOut, "Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := e.runAndPrint(ctx, args); err != nil {
|
if err := e.runAndPrint(ctx, args); err != nil {
|
||||||
log.Print(err)
|
logrus.Warnf("running error: %s", err)
|
||||||
if e.exitCode == 0 {
|
if e.exitCode == 0 {
|
||||||
e.exitCode = exitCodeIfFailure
|
e.exitCode = exitCodeIfFailure
|
||||||
}
|
}
|
||||||
@ -305,11 +325,11 @@ func (e *Executor) parseConfig(cmd *cobra.Command) {
|
|||||||
if err == pflag.ErrHelp {
|
if err == pflag.ErrHelp {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Fatalf("Can't parse args: %s", err)
|
logrus.Fatalf("Can't parse args: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
if err := viper.BindPFlags(cmd.Flags()); err != nil {
|
||||||
log.Fatalf("Can't bind cobra's flags to viper: %s", err)
|
logrus.Fatalf("Can't bind cobra's flags to viper: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.SetEnvPrefix("GOLANGCI")
|
viper.SetEnvPrefix("GOLANGCI")
|
||||||
@ -318,7 +338,7 @@ func (e *Executor) parseConfig(cmd *cobra.Command) {
|
|||||||
|
|
||||||
configFile := e.cfg.Run.Config
|
configFile := e.cfg.Run.Config
|
||||||
if e.cfg.Run.NoConfig && configFile != "" {
|
if e.cfg.Run.NoConfig && configFile != "" {
|
||||||
log.Fatal("can't combine option --config and --no-config")
|
logrus.Fatal("can't combine option --config and --no-config")
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.cfg.Run.NoConfig {
|
if e.cfg.Run.NoConfig {
|
||||||
@ -342,15 +362,15 @@ func (e *Executor) parseConfigImpl() {
|
|||||||
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Fatalf("Can't read viper config: %s", err)
|
logrus.Fatalf("Can't read viper config: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := viper.Unmarshal(&e.cfg); err != nil {
|
if err := viper.Unmarshal(&e.cfg); err != nil {
|
||||||
log.Fatalf("Can't unmarshal config by viper: %s", err)
|
logrus.Fatalf("Can't unmarshal config by viper: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := e.validateConfig(&commandLineConfig); err != nil {
|
if err := e.validateConfig(&commandLineConfig); err != nil {
|
||||||
log.Fatal(err)
|
logrus.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
|
|||||||
newLinterConfig(golinters.Goconst{}).WithPresets(PresetStyle).WithSpeed(9),
|
newLinterConfig(golinters.Goconst{}).WithPresets(PresetStyle).WithSpeed(9),
|
||||||
newLinterConfig(golinters.Deadcode{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
|
newLinterConfig(golinters.Deadcode{}).WithFullImport().WithPresets(PresetUnused).WithSpeed(10),
|
||||||
newLinterConfig(golinters.Gocyclo{}).WithPresets(PresetComplexity).WithSpeed(8),
|
newLinterConfig(golinters.Gocyclo{}).WithPresets(PresetComplexity).WithSpeed(8),
|
||||||
|
newLinterConfig(golinters.TypeCheck{}).WithFullImport().WithPresets(PresetBugs).WithSpeed(10),
|
||||||
|
|
||||||
newLinterConfig(golinters.Gofmt{}).WithPresets(PresetFormatting).WithSpeed(7),
|
newLinterConfig(golinters.Gofmt{}).WithPresets(PresetFormatting).WithSpeed(7),
|
||||||
newLinterConfig(golinters.Gofmt{UseGoimports: true}).WithPresets(PresetFormatting).WithSpeed(5),
|
newLinterConfig(golinters.Gofmt{UseGoimports: true}).WithPresets(PresetFormatting).WithSpeed(5),
|
||||||
@ -128,9 +129,10 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
|
|||||||
|
|
||||||
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
|
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
|
||||||
disabled := map[string]bool{
|
disabled := map[string]bool{
|
||||||
"gocyclo": true,
|
golinters.Gocyclo{}.Name(): true, // annoying
|
||||||
"dupl": true,
|
golinters.Dupl{}.Name(): true, // annoying
|
||||||
"maligned": true,
|
golinters.Maligned{}.Name(): true, // rarely usable
|
||||||
|
golinters.TypeCheck{}.Name(): true, // annoying because of different building envs
|
||||||
}
|
}
|
||||||
return enableLinterConfigs(lcs, func(lc *LinterConfig) bool {
|
return enableLinterConfigs(lcs, func(lc *LinterConfig) bool {
|
||||||
return !disabled[lc.Linter.Name()]
|
return !disabled[lc.Linter.Name()]
|
||||||
@ -138,16 +140,17 @@ func GetAllSupportedLinterConfigs() []LinterConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enabled := map[string]bool{
|
enabled := map[string]bool{
|
||||||
"govet": true,
|
golinters.Govet{}.Name(): true,
|
||||||
"errcheck": true,
|
golinters.Errcheck{}.Name(): true,
|
||||||
"staticcheck": true,
|
golinters.Megacheck{StaticcheckEnabled: true}.Name(): true,
|
||||||
"unused": true,
|
golinters.Megacheck{UnusedEnabled: true}.Name(): true,
|
||||||
"gosimple": true,
|
golinters.Megacheck{GosimpleEnabled: true}.Name(): true,
|
||||||
"gas": true,
|
golinters.Gas{}.Name(): true,
|
||||||
"structcheck": true,
|
golinters.Structcheck{}.Name(): true,
|
||||||
"varcheck": true,
|
golinters.Varcheck{}.Name(): true,
|
||||||
"ineffassign": true,
|
golinters.Ineffassign{}.Name(): true,
|
||||||
"deadcode": true,
|
golinters.Deadcode{}.Name(): true,
|
||||||
|
golinters.TypeCheck{}.Name(): true,
|
||||||
}
|
}
|
||||||
return enableLinterConfigs(lcs, func(lc *LinterConfig) bool {
|
return enableLinterConfigs(lcs, func(lc *LinterConfig) bool {
|
||||||
return enabled[lc.Linter.Name()]
|
return enabled[lc.Linter.Name()]
|
||||||
|
@ -31,7 +31,6 @@ func runGoErrchk(c *exec.Cmd, t *testing.T) {
|
|||||||
const testdataDir = "testdata"
|
const testdataDir = "testdata"
|
||||||
|
|
||||||
var testdataWithIssuesDir = filepath.Join(testdataDir, "with_issues")
|
var testdataWithIssuesDir = filepath.Join(testdataDir, "with_issues")
|
||||||
var testdataNotCompilingDir = filepath.Join(testdataDir, "not_compiles")
|
|
||||||
|
|
||||||
const binName = "golangci-lint"
|
const binName = "golangci-lint"
|
||||||
|
|
||||||
@ -72,12 +71,6 @@ func testOneSource(t *testing.T, sourcePath string) {
|
|||||||
runGoErrchk(cmd, t)
|
runGoErrchk(cmd, t)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotCompilingProgram(t *testing.T) {
|
|
||||||
installBinary(t)
|
|
||||||
err := exec.Command(binName, "run", "--enable-all", testdataNotCompilingDir).Run()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func chdir(b *testing.B, dir string) {
|
func chdir(b *testing.B, dir string) {
|
||||||
if err := os.Chdir(dir); err != nil {
|
if err := os.Chdir(dir); err != nil {
|
||||||
b.Fatalf("can't chdir to %s: %s", dir, err)
|
b.Fatalf("can't chdir to %s: %s", dir, err)
|
||||||
|
@ -32,7 +32,7 @@ func (g Golint) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, erro
|
|||||||
issues = append(issues, i...)
|
issues = append(issues, i...)
|
||||||
}
|
}
|
||||||
if lintErr != nil {
|
if lintErr != nil {
|
||||||
logrus.Warnf("golint: %s", lintErr)
|
logrus.Infof("golint: %s", lintErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return issues, nil
|
return issues, nil
|
||||||
|
76
pkg/golinters/typecheck.go
Normal file
76
pkg/golinters/typecheck.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package golinters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"go/token"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golangci/golangci-lint/pkg/result"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TypeCheck struct{}
|
||||||
|
|
||||||
|
func (TypeCheck) Name() string {
|
||||||
|
return "typecheck"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (TypeCheck) Desc() string {
|
||||||
|
return "Like the front-end of a Go compiler, parses and type-checks Go code"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lint TypeCheck) parseError(err error) *result.Issue {
|
||||||
|
// file:line(<optional>:colon): message
|
||||||
|
parts := strings.Split(err.Error(), ":")
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
file := parts[0]
|
||||||
|
line, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var column int
|
||||||
|
var message string
|
||||||
|
if len(parts) == 3 { // no column
|
||||||
|
message = parts[2]
|
||||||
|
} else {
|
||||||
|
column, err = strconv.Atoi(parts[2])
|
||||||
|
if err == nil { // column was parsed
|
||||||
|
message = strings.Join(parts[3:], ":")
|
||||||
|
} else {
|
||||||
|
message = strings.Join(parts[2:], ":")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message = strings.TrimSpace(message)
|
||||||
|
if message == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &result.Issue{
|
||||||
|
Pos: token.Position{
|
||||||
|
Filename: file,
|
||||||
|
Line: line,
|
||||||
|
Column: column,
|
||||||
|
},
|
||||||
|
Text: message,
|
||||||
|
FromLinter: lint.Name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lint TypeCheck) Run(ctx context.Context, lintCtx *Context) ([]result.Issue, error) {
|
||||||
|
var res []result.Issue
|
||||||
|
for _, pkg := range lintCtx.Program.InitialPackages() {
|
||||||
|
for _, err := range pkg.Errors {
|
||||||
|
i := lint.parseError(err)
|
||||||
|
if i != nil {
|
||||||
|
res = append(res, *i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
59
pkg/golinters/typecheck_test.go
Normal file
59
pkg/golinters/typecheck_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package golinters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseError(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
in, out string
|
||||||
|
good bool
|
||||||
|
}{
|
||||||
|
{"f.go:1:2: text", "", true},
|
||||||
|
{"f.go:1:2: text: with: colons", "", true},
|
||||||
|
|
||||||
|
{"f.go:1:2:text wo leading space", "f.go:1:2: text wo leading space", true},
|
||||||
|
|
||||||
|
{"f.go:1:2:", "", false},
|
||||||
|
{"f.go:1:2: ", "", false},
|
||||||
|
|
||||||
|
{"f.go:1:2", "f.go:1: 2", true},
|
||||||
|
{"f.go:1: text no column", "", true},
|
||||||
|
{"f.go:1: text no column: but with colon", "", true},
|
||||||
|
{"f.go:1:text no column", "f.go:1: text no column", true},
|
||||||
|
|
||||||
|
{"f.go: no line", "", false},
|
||||||
|
{"f.go: 1: text", "", false},
|
||||||
|
|
||||||
|
{"f.go:", "", false},
|
||||||
|
{"f.go", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
lint := TypeCheck{}
|
||||||
|
for _, c := range cases {
|
||||||
|
i := lint.parseError(errors.New(c.in))
|
||||||
|
if !c.good {
|
||||||
|
assert.Nil(t, i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.NotNil(t, i)
|
||||||
|
|
||||||
|
pos := fmt.Sprintf("%s:%d", i.FilePath(), i.Line())
|
||||||
|
if i.Pos.Column != 0 {
|
||||||
|
pos += fmt.Sprintf(":%d", i.Pos.Column)
|
||||||
|
}
|
||||||
|
out := fmt.Sprintf("%s: %s", pos, i.Text)
|
||||||
|
expOut := c.out
|
||||||
|
if expOut == "" {
|
||||||
|
expOut = c.in
|
||||||
|
}
|
||||||
|
assert.Equal(t, expOut, out)
|
||||||
|
|
||||||
|
assert.Equal(t, "typecheck", i.FromLinter)
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,6 @@ func (JSON) Print(issues <-chan result.Issue) (bool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
fmt.Fprint(stdOut, string(outputJSON))
|
fmt.Fprint(StdOut, string(outputJSON))
|
||||||
return len(allIssues) != 0, nil
|
return len(allIssues) != 0, nil
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ func (p *Text) Print(issues <-chan result.Issue) (bool, error) {
|
|||||||
|
|
||||||
if issuesN == 0 {
|
if issuesN == 0 {
|
||||||
outStr := p.SprintfColored(color.FgGreen, "Congrats! No issues were found.")
|
outStr := p.SprintfColored(color.FgGreen, "Congrats! No issues were found.")
|
||||||
fmt.Fprintln(stdOut, outStr)
|
fmt.Fprintln(StdOut, outStr)
|
||||||
} else {
|
} else {
|
||||||
logrus.Infof("Found %d issues", issuesN)
|
logrus.Infof("Found %d issues", issuesN)
|
||||||
}
|
}
|
||||||
@ -105,7 +105,7 @@ func (p Text) printIssue(i *result.Issue) {
|
|||||||
if i.Pos.Column != 0 {
|
if i.Pos.Column != 0 {
|
||||||
pos += fmt.Sprintf(":%d", i.Pos.Column)
|
pos += fmt.Sprintf(":%d", i.Pos.Column)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(stdOut, "%s: %s\n", pos, text)
|
fmt.Fprintf(StdOut, "%s: %s\n", pos, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Text) printIssuedLines(i *result.Issue, lines linesCache) {
|
func (p Text) printIssuedLines(i *result.Issue, lines linesCache) {
|
||||||
@ -123,7 +123,7 @@ func (p Text) printIssuedLines(i *result.Issue, lines linesCache) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lineStr = string(bytes.Trim(lines[zeroIndexedLine], "\r"))
|
lineStr = string(bytes.Trim(lines[zeroIndexedLine], "\r"))
|
||||||
fmt.Fprintln(stdOut, lineStr)
|
fmt.Fprintln(StdOut, lineStr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,5 +146,5 @@ func (p Text) printUnderLinePointer(i *result.Issue, line string) {
|
|||||||
prefix += strings.Repeat(" ", spacesCount)
|
prefix += strings.Repeat(" ", spacesCount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintf(stdOut, "%s%s\n", prefix, p.SprintfColored(color.FgYellow, "^"))
|
fmt.Fprintf(StdOut, "%s%s\n", prefix, p.SprintfColored(color.FgYellow, "^"))
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,4 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
var stdOut = os.NewFile(uintptr(syscall.Stdout), "/dev/stdout") // was set to /dev/null
|
var StdOut = os.NewFile(uintptr(syscall.Stdout), "/dev/stdout") // was set to /dev/null
|
||||||
|
@ -27,6 +27,7 @@ func (p MaxPerFileFromLinter) Name() string {
|
|||||||
var maxPerFileFromLinterConfig = map[string]int{
|
var maxPerFileFromLinterConfig = map[string]int{
|
||||||
golinters.Gofmt{}.Name(): 1,
|
golinters.Gofmt{}.Name(): 1,
|
||||||
golinters.Gofmt{UseGoimports: true}.Name(): 1,
|
golinters.Gofmt{UseGoimports: true}.Name(): 1,
|
||||||
|
golinters.TypeCheck{}.Name(): 3,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *MaxPerFileFromLinter) Process(issues []result.Issue) ([]result.Issue, error) {
|
func (p *MaxPerFileFromLinter) Process(issues []result.Issue) ([]result.Issue, error) {
|
||||||
|
@ -3,7 +3,6 @@ package pkg
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@ -107,7 +106,6 @@ func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *golinters.Contex
|
|||||||
lintResultsCh := make(chan lintRes, len(linters))
|
lintResultsCh := make(chan lintRes, len(linters))
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
savedStdout, savedStderr := setOutputToDevNull() // Don't allow linters to print anything
|
|
||||||
workersFinishTimes := make([]time.Time, lintCtx.Cfg.Run.Concurrency)
|
workersFinishTimes := make([]time.Time, lintCtx.Cfg.Run.Concurrency)
|
||||||
|
|
||||||
for i := 0; i < lintCtx.Cfg.Run.Concurrency; i++ {
|
for i := 0; i < lintCtx.Cfg.Run.Concurrency; i++ {
|
||||||
@ -129,7 +127,6 @@ func (r *SimpleRunner) runWorkers(ctx context.Context, lintCtx *golinters.Contex
|
|||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
close(lintResultsCh)
|
close(lintResultsCh)
|
||||||
os.Stdout, os.Stderr = savedStdout, savedStderr
|
|
||||||
|
|
||||||
logWorkersStat(workersFinishTimes)
|
logWorkersStat(workersFinishTimes)
|
||||||
}()
|
}()
|
||||||
@ -190,18 +187,6 @@ func collectIssues(ctx context.Context, resCh <-chan lintRes) <-chan result.Issu
|
|||||||
return retIssues
|
return retIssues
|
||||||
}
|
}
|
||||||
|
|
||||||
func setOutputToDevNull() (savedStdout, savedStderr *os.File) {
|
|
||||||
savedStdout, savedStderr = os.Stdout, os.Stderr
|
|
||||||
devNull, err := os.Open(os.DevNull)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Warnf("can't open null device %q: %s", os.DevNull, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Stdout, os.Stderr = devNull, devNull
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SimpleRunner) Run(ctx context.Context, linters []Linter, lintCtx *golinters.Context) <-chan result.Issue {
|
func (r SimpleRunner) Run(ctx context.Context, linters []Linter, lintCtx *golinters.Context) <-chan result.Issue {
|
||||||
lintResultsCh := r.runWorkers(ctx, lintCtx, linters)
|
lintResultsCh := r.runWorkers(ctx, lintCtx, linters)
|
||||||
processedLintResultsCh := r.processLintResults(ctx, lintResultsCh)
|
processedLintResultsCh := r.processLintResults(ctx, lintResultsCh)
|
||||||
@ -228,7 +213,7 @@ func (r *SimpleRunner) processIssues(ctx context.Context, issues []result.Issue,
|
|||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Warnf("Can't process result by %s processor: %s", p.Name(), err)
|
logrus.Infof("Can't process result by %s processor: %s", p.Name(), err)
|
||||||
} else {
|
} else {
|
||||||
issues = newIssues
|
issues = newIssues
|
||||||
}
|
}
|
||||||
|
3
pkg/testdata/not_compiles/main.go
vendored
3
pkg/testdata/not_compiles/main.go
vendored
@ -1,3 +0,0 @@
|
|||||||
package p
|
|
||||||
|
|
||||||
func F {
|
|
4
pkg/testdata/with_issues/typecheck.go
vendored
Normal file
4
pkg/testdata/with_issues/typecheck.go
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package testdata
|
||||||
|
|
||||||
|
fun NotCompiles() { // ERROR "expected declaration, found 'IDENT' fun"
|
||||||
|
}
|
8
pkg/testdata/with_issues/typecheck_many_issues.go
vendored
Normal file
8
pkg/testdata/with_issues/typecheck_many_issues.go
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package testdata
|
||||||
|
|
||||||
|
func TypeCheckBadCalls() {
|
||||||
|
typecheckNotExists1.F1() // ERROR "undeclared name: typecheckNotExists1"
|
||||||
|
typecheckNotExists2.F2() // ERROR "undeclared name: typecheckNotExists2"
|
||||||
|
typecheckNotExists3.F3() // ERROR "undeclared name: typecheckNotExists3"
|
||||||
|
typecheckNotExists4.F4()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user