Skip to content

Commit f1c1dbf

Browse files
authored
Feature/enable autofix on whitespace (#674)
The whitespace linter was added in #673. Enable it and fix found issues. Add auto-fixing to the whitespace linter.
1 parent c7dee2c commit f1c1dbf

File tree

15 files changed

+152
-37
lines changed

15 files changed

+152
-37
lines changed

.golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ linters:
7575
- unparam
7676
- unused
7777
- varcheck
78+
# - whitespace - TODO: enable it when golangci.com will support it.
7879

7980
# don't enable:
8081
# - depguard - until https://github.com/OpenPeeDeeP/depguard/issues/7 gets fixed

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33

44
# Build
55

6+
fast_build: FORCE
7+
go build -o golangci-lint ./cmd/golangci-lint
68
build: golangci-lint
79
clean:
810
rm -f golangci-lint test/path
911
rm -rf tools
10-
.PHONY: build clean
12+
.PHONY: fast_build build clean
1113

1214
# Test
1315

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ scopelint: Scopelint checks for unpinned variables in go programs [fast: true, a
216216
stylecheck: Stylecheck is a replacement for golint [fast: false, auto-fix: false]
217217
unconvert: Remove unnecessary type conversions [fast: true, auto-fix: false]
218218
unparam: Reports unused function parameters [fast: false, auto-fix: false]
219-
whitespace: Tool for detection of leading and trailing whitespace [fast: true, auto-fix: false]
219+
whitespace: Tool for detection of leading and trailing whitespace [fast: true, auto-fix: true]
220220
```
221221
222222
Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
@@ -925,6 +925,7 @@ linters:
925925
- unparam
926926
- unused
927927
- varcheck
928+
# - whitespace - TODO: enable it when golangci.com will support it.
928929
929930
# don't enable:
930931
# - depguard - until https://github.com/OpenPeeDeeP/depguard/issues/7 gets fixed

pkg/commands/config.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ func (e *Executor) initConfig() {
3434
}
3535
e.initRunConfiguration(pathCmd) // allow --config
3636
cmd.AddCommand(pathCmd)
37-
3837
}
3938

4039
func (e *Executor) executePathCmd(_ *cobra.Command, args []string) {

pkg/golinters/goanalysis/checker/checker.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,6 @@ func (act *action) execOnce() {
348348
// in-memory outputs of prerequisite analyzers
349349
// become inputs to this analysis pass.
350350
inputs[dep.a] = dep.result
351-
352351
} else if dep.a == act.a { // (always true)
353352
// Same analysis, different package (vertical edge):
354353
// serialized facts produced by prerequisite analysis

pkg/golinters/gocritic.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,6 @@ func (lint Gocritic) Run(ctx context.Context, lintCtx *linter.Context) ([]result
134134

135135
func (lint Gocritic) runOnPackage(lintpackCtx *lintpack.Context, checkers []*lintpack.Checker,
136136
pkgInfo *loader.PackageInfo, ret chan<- result.Issue) {
137-
138137
for _, f := range pkgInfo.Files {
139138
filename := filepath.Base(lintpackCtx.FileSet.Position(f.Pos()).Filename)
140139
lintpackCtx.SetFileInfo(filename, f)
@@ -145,7 +144,6 @@ func (lint Gocritic) runOnPackage(lintpackCtx *lintpack.Context, checkers []*lin
145144

146145
func (lint Gocritic) runOnFile(ctx *lintpack.Context, f *ast.File, checkers []*lintpack.Checker,
147146
ret chan<- result.Issue) {
148-
149147
var wg sync.WaitGroup
150148
wg.Add(len(checkers))
151149
for _, c := range checkers {

pkg/golinters/whitespace.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import (
44
"context"
55
"go/token"
66

7+
"github.com/pkg/errors"
8+
79
"github.com/golangci/golangci-lint/pkg/lint/linter"
810
"github.com/golangci/golangci-lint/pkg/result"
911

1012
"github.com/ultraware/whitespace"
1113
)
1214

13-
type Whitespace struct{}
15+
type Whitespace struct {
16+
}
1417

1518
func (Whitespace) Name() string {
1619
return "whitespace"
@@ -32,14 +35,40 @@ func (w Whitespace) Run(ctx context.Context, lintCtx *linter.Context) ([]result.
3235

3336
res := make([]result.Issue, len(issues))
3437
for k, i := range issues {
35-
res[k] = result.Issue{
38+
issue := result.Issue{
3639
Pos: token.Position{
3740
Filename: i.Pos.Filename,
3841
Line: i.Pos.Line,
3942
},
40-
Text: i.Message,
41-
FromLinter: w.Name(),
43+
Text: i.Message,
44+
FromLinter: w.Name(),
45+
Replacement: &result.Replacement{},
46+
}
47+
48+
// TODO(jirfag): return more information from Whitespace to get rid of string comparisons
49+
if i.Message == "unnecessary leading newline" {
50+
// cover two lines by the issue: opening bracket "{" (issue.Pos.Line) and following empty line
51+
issue.LineRange = &result.Range{From: issue.Pos.Line, To: issue.Pos.Line + 1}
52+
53+
bracketLine, err := lintCtx.LineCache.GetLine(issue.Pos.Filename, issue.Pos.Line)
54+
if err != nil {
55+
return nil, errors.Wrapf(err, "failed to get line %s:%d", issue.Pos.Filename, issue.Pos.Line)
56+
}
57+
issue.Replacement.NewLines = []string{bracketLine}
58+
} else {
59+
// cover two lines by the issue: closing bracket "}" (issue.Pos.Line) and preceding empty line
60+
issue.LineRange = &result.Range{From: issue.Pos.Line - 1, To: issue.Pos.Line}
61+
62+
bracketLine, err := lintCtx.LineCache.GetLine(issue.Pos.Filename, issue.Pos.Line)
63+
if err != nil {
64+
return nil, errors.Wrapf(err, "failed to get line %s:%d", issue.Pos.Filename, issue.Pos.Line)
65+
}
66+
issue.Replacement.NewLines = []string{bracketLine}
67+
68+
issue.Pos.Line-- // set in sync with LineRange.From to not break fixer and other code features
4269
}
70+
71+
res[k] = issue
4372
}
4473

4574
return res, nil

pkg/lint/lintersdb/manager.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
245245
linter.NewConfig(golinters.Whitespace{}).
246246
WithPresets(linter.PresetStyle).
247247
WithSpeed(10).
248+
WithAutoFix().
248249
WithURL("https://github.com/ultraware/whitespace"),
249250
}
250251

pkg/lint/load.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ type ContextLoader struct {
3939

4040
func NewContextLoader(cfg *config.Config, log logutils.Log, goenv *goutil.Env,
4141
lineCache *fsutils.LineCache, fileCache *fsutils.FileCache) *ContextLoader {
42-
4342
return &ContextLoader{
4443
cfg: cfg,
4544
log: log,

pkg/lint/runner.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ type Runner struct {
3131

3232
func NewRunner(astCache *astcache.Cache, cfg *config.Config, log logutils.Log, goenv *goutil.Env,
3333
lineCache *fsutils.LineCache, dbManager *lintersdb.Manager) (*Runner, error) {
34-
3534
icfg := cfg.Issues
3635
excludePatterns := icfg.ExcludePatterns
3736
if icfg.UseDefaultExcludes {
@@ -101,7 +100,6 @@ type lintRes struct {
101100

102101
func (r *Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context,
103102
lc *linter.Config) (ret []result.Issue, err error) {
104-
105103
defer func() {
106104
if panicData := recover(); panicData != nil {
107105
err = fmt.Errorf("panic occurred: %s", panicData)
@@ -125,7 +123,6 @@ func (r *Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context,
125123

126124
func (r Runner) runWorker(ctx context.Context, lintCtx *linter.Context,
127125
tasksCh <-chan *linter.Config, lintResultsCh chan<- lintRes, name string) {
128-
129126
sw := timeutils.NewStopwatch(name, r.Log)
130127
defer sw.Print()
131128

pkg/result/processors/fixer.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"sort"
99
"strings"
1010

11+
"github.com/golangci/golangci-lint/pkg/timeutils"
12+
1113
"github.com/golangci/golangci-lint/pkg/fsutils"
1214

1315
"github.com/golangci/golangci-lint/pkg/logutils"
@@ -22,10 +24,20 @@ type Fixer struct {
2224
cfg *config.Config
2325
log logutils.Log
2426
fileCache *fsutils.FileCache
27+
sw *timeutils.Stopwatch
2528
}
2629

2730
func NewFixer(cfg *config.Config, log logutils.Log, fileCache *fsutils.FileCache) *Fixer {
28-
return &Fixer{cfg: cfg, log: log, fileCache: fileCache}
31+
return &Fixer{
32+
cfg: cfg,
33+
log: log,
34+
fileCache: fileCache,
35+
sw: timeutils.NewStopwatch("fixer", log),
36+
}
37+
}
38+
39+
func (f Fixer) printStat() {
40+
f.sw.PrintStages()
2941
}
3042

3143
func (f Fixer) Process(issues <-chan result.Issue) <-chan result.Issue {
@@ -47,7 +59,11 @@ func (f Fixer) Process(issues <-chan result.Issue) <-chan result.Issue {
4759
}
4860

4961
for file, issuesToFix := range issuesToFixPerFile {
50-
if err := f.fixIssuesInFile(file, issuesToFix); err != nil {
62+
var err error
63+
f.sw.TrackStage("all", func() {
64+
err = f.fixIssuesInFile(file, issuesToFix) //nolint:scopelint
65+
})
66+
if err != nil {
5167
f.log.Errorf("Failed to fix issues in file %s: %s", file, err)
5268

5369
// show issues only if can't fix them
@@ -56,6 +72,7 @@ func (f Fixer) Process(issues <-chan result.Issue) <-chan result.Issue {
5672
}
5773
}
5874
}
75+
f.printStat()
5976
close(outCh)
6077
}()
6178

test/fix_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ func TestFix(t *testing.T) {
2424
tmpDir := filepath.Join(testdataDir, "fix.tmp")
2525
os.RemoveAll(tmpDir) // cleanup after previous runs
2626

27-
if os.Getenv("GL_KEEP_TEMP_FILES") != "1" {
27+
if os.Getenv("GL_KEEP_TEMP_FILES") == "1" {
28+
t.Logf("Temp dir for fix test: %s", tmpDir)
29+
} else {
2830
defer os.RemoveAll(tmpDir)
2931
}
3032

test/testdata/fix/in/whitespace.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//args: -Ewhitespace
2+
package p
3+
4+
import "fmt"
5+
6+
func oneLeadingNewline() {
7+
8+
fmt.Println("Hello world")
9+
}
10+
11+
func oneNewlineAtBothEnds() {
12+
13+
fmt.Println("Hello world")
14+
15+
}
16+
17+
func noNewlineFunc() {
18+
}
19+
20+
func oneNewlineFunc() {
21+
22+
}
23+
24+
func twoNewlinesFunc() {
25+
26+
27+
}
28+
29+
func noNewlineWithCommentFunc() {
30+
// some comment
31+
}
32+
33+
func oneTrailingNewlineWithCommentFunc() {
34+
// some comment
35+
36+
}
37+
38+
func oneLeadingNewlineWithCommentFunc() {
39+
40+
// some comment
41+
}
42+
43+
func twoLeadingNewlines() {
44+
45+
46+
fmt.Println("Hello world")
47+
}

test/testdata/fix/out/whitespace.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//args: -Ewhitespace
2+
package p
3+
4+
import "fmt"
5+
6+
func oneLeadingNewline() {
7+
fmt.Println("Hello world")
8+
}
9+
10+
func oneNewlineAtBothEnds() {
11+
fmt.Println("Hello world")
12+
}
13+
14+
func noNewlineFunc() {
15+
}
16+
17+
func oneNewlineFunc() {
18+
19+
}
20+
21+
func twoNewlinesFunc() {
22+
23+
24+
}
25+
26+
func noNewlineWithCommentFunc() {
27+
// some comment
28+
}
29+
30+
func oneTrailingNewlineWithCommentFunc() {
31+
// some comment
32+
33+
}
34+
35+
func oneLeadingNewlineWithCommentFunc() {
36+
37+
// some comment
38+
}
39+
40+
func twoLeadingNewlines() {
41+
42+
fmt.Println("Hello world")
43+
}

test/testdata/whitespace.go

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)