Skip to content

Commit 2b587b6

Browse files
committed
Load AST for fast linters in different way.
Use build.Import instead of manual parser.ParseFile and paths traversal. It allows: 1. support build tags for all linters. 2. analyze files only for current GOOS/GOARCH: less false-positives. 3. analyze xtest packages (*_test) by golint: upstream golint and gometalinter can't do it! And don't break analysis on the first xtest package like it was before. 4. proper handling of xtest packages for linters like goconst where package boundary is important: less false-positives is expected. Also: 1. reuse AST parsing for golint and goconst: minor speedup. 2. allow to specify path (not only name) regexp for --skip-files and --skip-dirs 3. add more default exclude filters for golint about commits: `(comment on exported (method|function)|should have( a package)? comment|comment should be of the form)` 4. print skipped dir in verbose (-v) mode 5. refactor per-linter tests: declare arguments in comments, run only one linter and in combination with slow linter
1 parent f5a9bbb commit 2b587b6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+754
-511
lines changed

.golangci.example.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ run:
66
build-tags:
77
- mytag
88
skip-dirs:
9-
- external_libs
9+
- src/external_libs
10+
- autogenerated_by_my_lib
1011
skip-files:
1112
- ".*\\.pb\\.go$"
13+
- lib/bad.go
1214

1315
output:
1416
format: colored-line-number

Gopkg.lock

+8-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ test:
33
golangci-lint run -v
44
golangci-lint run --fast --no-config -v
55
golangci-lint run --no-config -v
6-
golangci-lint run --fast --no-config -v ./test/testdata/typecheck.go
7-
go test -v -race ./...
6+
go test -v ./...
87

98
assets:
109
svg-term --cast=183662 --out docs/demo.svg --window --width 110 --height 30 --from 2000 --to 20000 --profile Dracula --term iterm2

README.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -235,14 +235,14 @@ Flags:
235235
--print-issued-lines Print lines of code with issue (default true)
236236
--print-linter-name Print linter name in issue line (default true)
237237
--issues-exit-code int Exit code when issues were found (default 1)
238-
--build-tags strings Build tags (not all linters support them)
238+
--build-tags strings Build tags
239239
--deadline duration Deadline for total work (default 1m0s)
240240
--tests Analyze tests (*_test.go) (default true)
241241
--print-resources-usage Print avg and max memory usage of golangci-lint and total time
242242
-c, --config PATH Read config from file path PATH
243243
--no-config Don't read config
244-
--skip-dirs strings Regexps of directory names to skip
245-
--skip-files strings Regexps of file names to skip
244+
--skip-dirs strings Regexps of directories to skip
245+
--skip-files strings Regexps of files to skip
246246
-E, --enable strings Enable specific linter
247247
-D, --disable strings Disable specific linter
248248
--enable-all Enable all linters
@@ -255,7 +255,7 @@ Flags:
255255
- Error return value of .((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv). is not checked
256256
257257
# golint: Annoying issue about not having a comment. The rare codebase has such comments
258-
- (should have comment|comment on exported method|should have a package comment)
258+
- (comment on exported (method|function)|should have( a package)? comment|comment should be of the form)
259259
260260
# golint: False positive when tests are defined in package 'test'
261261
- func name will be used as test\.Test.* by other packages, and that stutters; consider calling this

pkg/commands/run.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
6565
rc := &cfg.Run
6666
fs.IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code",
6767
1, wh("Exit code when issues were found"))
68-
fs.StringSliceVar(&rc.BuildTags, "build-tags", nil, wh("Build tags (not all linters support them)"))
68+
fs.StringSliceVar(&rc.BuildTags, "build-tags", nil, wh("Build tags"))
6969
fs.DurationVar(&rc.Deadline, "deadline", time.Minute, wh("Deadline for total work"))
7070
fs.BoolVar(&rc.AnalyzeTests, "tests", true, wh("Analyze tests (*_test.go)"))
7171
fs.BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false, wh("Print avg and max memory usage of golangci-lint and total time"))
7272
fs.StringVarP(&rc.Config, "config", "c", "", wh("Read config from file path `PATH`"))
7373
fs.BoolVar(&rc.NoConfig, "no-config", false, wh("Don't read config"))
74-
fs.StringSliceVar(&rc.SkipDirs, "skip-dirs", nil, wh("Regexps of directory names to skip"))
75-
fs.StringSliceVar(&rc.SkipFiles, "skip-files", nil, wh("Regexps of file names to skip"))
74+
fs.StringSliceVar(&rc.SkipDirs, "skip-dirs", nil, wh("Regexps of directories to skip"))
75+
fs.StringSliceVar(&rc.SkipFiles, "skip-files", nil, wh("Regexps of files to skip"))
7676

7777
// Linters settings config
7878
lsc := &cfg.LintersSettings

pkg/config/config.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ var DefaultExcludePatterns = []ExcludePattern{
2828
Why: "Almost all programs ignore errors on these functions and in most cases it's ok",
2929
},
3030
{
31-
Pattern: "(should have comment|comment on exported method|should have a package comment)",
31+
Pattern: "(comment on exported (method|function)|should have( a package)? comment|comment should be of the form)",
3232
Linter: "golint",
3333
Why: "Annoying issue about not having a comment. The rare codebase has such comments",
3434
},

pkg/fsutils/fsutils.go

-130
Original file line numberDiff line numberDiff line change
@@ -1,139 +1,9 @@
11
package fsutils
22

33
import (
4-
"context"
5-
"fmt"
6-
"go/build"
74
"os"
8-
"path"
9-
"path/filepath"
10-
"strings"
11-
"time"
12-
13-
"github.com/sirupsen/logrus"
145
)
156

16-
var stdExcludeDirRegexps = []string{
17-
"^vendor$", "^third_party$",
18-
"^testdata$", "^examples$",
19-
"^Godeps$",
20-
"^builtin$",
21-
}
22-
23-
func GetProjectRoot() string {
24-
return path.Join(build.Default.GOPATH, "src", "github.com", "golangci", "golangci-worker")
25-
}
26-
27-
type ProjectPaths struct {
28-
Files []string
29-
Dirs []string
30-
IsDirsRun bool
31-
}
32-
33-
func (p ProjectPaths) MixedPaths() []string {
34-
if p.IsDirsRun {
35-
return p.Dirs
36-
}
37-
38-
return p.Files
39-
}
40-
41-
func (p ProjectPaths) FilesGrouppedByDirs() [][]string {
42-
dirToFiles := map[string][]string{}
43-
for _, f := range p.Files {
44-
dir := filepath.Dir(f)
45-
dirToFiles[dir] = append(dirToFiles[dir], f)
46-
}
47-
48-
ret := [][]string{}
49-
for _, files := range dirToFiles {
50-
ret = append(ret, files)
51-
}
52-
return ret
53-
}
54-
55-
func processPaths(root string, paths []string, maxPaths int) ([]string, error) {
56-
if len(paths) > maxPaths {
57-
logrus.Warnf("Gofmt: got too much paths (%d), analyze first %d", len(paths), maxPaths)
58-
paths = paths[:maxPaths]
59-
}
60-
61-
ret := []string{}
62-
for _, p := range paths {
63-
if !filepath.IsAbs(p) {
64-
ret = append(ret, p)
65-
continue
66-
}
67-
68-
relPath, err := filepath.Rel(root, p)
69-
if err != nil {
70-
return nil, fmt.Errorf("can't get relative path for path %s and root %s: %s",
71-
p, root, err)
72-
}
73-
ret = append(ret, relPath)
74-
}
75-
76-
return ret, nil
77-
}
78-
79-
func processResolvedPaths(paths *PathResolveResult) (*ProjectPaths, error) {
80-
root, err := os.Getwd()
81-
if err != nil {
82-
return nil, fmt.Errorf("can't get working dir: %s", err)
83-
}
84-
85-
files, err := processPaths(root, paths.Files(), 10000)
86-
if err != nil {
87-
return nil, fmt.Errorf("can't process resolved files: %s", err)
88-
}
89-
90-
dirs, err := processPaths(root, paths.Dirs(), 1000)
91-
if err != nil {
92-
return nil, fmt.Errorf("can't process resolved dirs: %s", err)
93-
}
94-
95-
for i := range dirs {
96-
dir := dirs[i]
97-
if dir != "." && !filepath.IsAbs(dir) {
98-
dirs[i] = "./" + dir
99-
}
100-
}
101-
102-
return &ProjectPaths{
103-
Files: files,
104-
Dirs: dirs,
105-
IsDirsRun: len(dirs) != 0,
106-
}, nil
107-
}
108-
109-
func GetPathsForAnalysis(ctx context.Context, inputPaths []string, includeTests bool, skipDirRegexps []string) (ret *ProjectPaths, err error) {
110-
defer func(startedAt time.Time) {
111-
if ret != nil {
112-
logrus.Infof("Found paths for analysis for %s: %s", time.Since(startedAt), ret.MixedPaths())
113-
}
114-
}(time.Now())
115-
116-
for _, path := range inputPaths {
117-
if strings.HasSuffix(path, ".go") && len(inputPaths) != 1 {
118-
return nil, fmt.Errorf("specific files for analysis are allowed only if one file is set")
119-
}
120-
}
121-
122-
// TODO: don't analyze skipped files also, when be able to do it
123-
excludeDirs := append([]string{}, stdExcludeDirRegexps...)
124-
excludeDirs = append(excludeDirs, skipDirRegexps...)
125-
pr, err := NewPathResolver(excludeDirs, []string{".go"}, includeTests)
126-
if err != nil {
127-
return nil, fmt.Errorf("can't make path resolver: %s", err)
128-
}
129-
paths, err := pr.Resolve(inputPaths...)
130-
if err != nil {
131-
return nil, fmt.Errorf("can't resolve paths %v: %s", inputPaths, err)
132-
}
133-
134-
return processResolvedPaths(paths)
135-
}
136-
1377
func IsDir(filename string) bool {
1388
fi, err := os.Stat(filename)
1399
return err == nil && fi.IsDir()

0 commit comments

Comments
 (0)