Skip to content

Commit 1a9af12

Browse files
committed
Fix #96: support lll
1 parent 7b2a63d commit 1a9af12

File tree

17 files changed

+207
-46
lines changed

17 files changed

+207
-46
lines changed

Diff for: .golangci.example.yml

+3
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ linters-settings:
104104
# Default is to use a neutral variety of English.
105105
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
106106
locale: US
107+
lll:
108+
# max line length, lines longer will be reported. Default is 120. '\t' is counted as 1 character.
109+
line-length: 120
107110

108111
linters:
109112
enable:

Diff for: README.md

+6
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ maligned: Tool to detect Go structs that would take less memory if their fields
114114
megacheck: 3 sub-linters in one: unused, gosimple and staticcheck [fast: false]
115115
depguard: Go linter that checks if package imports are in a list of acceptable packages [fast: false]
116116
misspell: Finds commonly misspelled English words in comments [fast: true]
117+
lll: Reports long lines [fast: true]
117118
```
118119

119120
Pass `-E/--enable` to enable linter and `-D/--disable` to disable:
@@ -220,6 +221,7 @@ golangci-lint linters
220221
- [megacheck](https://github.com/dominikh/go-tools/tree/master/cmd/megacheck) - 3 sub-linters in one: unused, gosimple and staticcheck
221222
- [depguard](https://github.com/OpenPeeDeeP/depguard) - Go linter that checks if package imports are in a list of acceptable packages
222223
- [misspell](https://github.com/client9/misspell) - Finds commonly misspelled English words in comments
224+
- [lll](https://github.com/walle/lll) - Reports long lines
223225

224226
# Configuration
225227
The config file has lower priority than command-line options. If the same bool/string/int option is provided on the command-line
@@ -422,6 +424,9 @@ linters-settings:
422424
# Default is to use a neutral variety of English.
423425
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
424426
locale: US
427+
lll:
428+
# max line length, lines longer will be reported. Default is 120. '\t' is counted as 1 character.
429+
line-length: 120
425430

426431
linters:
427432
enable:
@@ -601,6 +606,7 @@ Thanks to developers and authors of used linters:
601606
- [alecthomas](https://github.com/alecthomas)
602607
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
603608
- [client9](https://github.com/client9)
609+
- [walle](https://github.com/walle)
604610

605611
# Future Plans
606612
1. Upstream all changes of forked linters.

Diff for: pkg/commands/executor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type Executor struct {
2323

2424
func NewExecutor(version, commit, date string) *Executor {
2525
e := &Executor{
26-
cfg: &config.Config{},
26+
cfg: config.NewDefault(),
2727
version: version,
2828
commit: commit,
2929
date: date,

Diff for: pkg/commands/run.go

+28-13
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ func getDefaultExcludeHelp() string {
3232
return strings.Join(parts, "\n")
3333
}
3434

35-
const welcomeMessage = "Run this tool in cloud on every github pull request in https://golangci.com for free (public repos)"
35+
const welcomeMessage = "Run this tool in cloud on every github pull " +
36+
"request in https://golangci.com for free (public repos)"
3637

3738
func wh(text string) string {
3839
return color.GreenString(text)
@@ -62,7 +63,8 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
6263
fs.StringSliceVar(&rc.BuildTags, "build-tags", nil, wh("Build tags"))
6364
fs.DurationVar(&rc.Deadline, "deadline", time.Minute, wh("Deadline for total work"))
6465
fs.BoolVar(&rc.AnalyzeTests, "tests", true, wh("Analyze tests (*_test.go)"))
65-
fs.BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false, wh("Print avg and max memory usage of golangci-lint and total time"))
66+
fs.BoolVar(&rc.PrintResourcesUsage, "print-resources-usage", false,
67+
wh("Print avg and max memory usage of golangci-lint and total time"))
6668
fs.StringVarP(&rc.Config, "config", "c", "", wh("Read config from file path `PATH`"))
6769
fs.BoolVar(&rc.NoConfig, "no-config", false, wh("Don't read config"))
6870
fs.StringSliceVar(&rc.SkipDirs, "skip-dirs", nil, wh("Regexps of directories to skip"))
@@ -75,16 +77,20 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
7577
// but when number of linters started to grow it became ovious that
7678
// we can't fill 90% of flags by linters settings: common flags became hard to find.
7779
// New linters settings should be done only through config file.
78-
fs.BoolVar(&lsc.Errcheck.CheckTypeAssertions, "errcheck.check-type-assertions", false, "Errcheck: check for ignored type assertion results")
80+
fs.BoolVar(&lsc.Errcheck.CheckTypeAssertions, "errcheck.check-type-assertions",
81+
false, "Errcheck: check for ignored type assertion results")
7982
hideFlag("errcheck.check-type-assertions")
8083

81-
fs.BoolVar(&lsc.Errcheck.CheckAssignToBlank, "errcheck.check-blank", false, "Errcheck: check for errors assigned to blank identifier: _ = errFunc()")
84+
fs.BoolVar(&lsc.Errcheck.CheckAssignToBlank, "errcheck.check-blank", false,
85+
"Errcheck: check for errors assigned to blank identifier: _ = errFunc()")
8286
hideFlag("errcheck.check-blank")
8387

84-
fs.BoolVar(&lsc.Govet.CheckShadowing, "govet.check-shadowing", false, "Govet: check for shadowed variables")
88+
fs.BoolVar(&lsc.Govet.CheckShadowing, "govet.check-shadowing", false,
89+
"Govet: check for shadowed variables")
8590
hideFlag("govet.check-shadowing")
8691

87-
fs.Float64Var(&lsc.Golint.MinConfidence, "golint.min-confidence", 0.8, "Golint: minimum confidence of a problem to print it")
92+
fs.Float64Var(&lsc.Golint.MinConfidence, "golint.min-confidence", 0.8,
93+
"Golint: minimum confidence of a problem to print it")
8894
hideFlag("golint.min-confidence")
8995

9096
fs.BoolVar(&lsc.Gofmt.Simplify, "gofmt.simplify", true, "Gofmt: simplify code")
@@ -94,7 +100,8 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
94100
30, "Minimal complexity of function to report it")
95101
hideFlag("gocyclo.min-complexity")
96102

97-
fs.BoolVar(&lsc.Maligned.SuggestNewOrder, "maligned.suggest-new", false, "Maligned: print suggested more optimal struct fields ordering")
103+
fs.BoolVar(&lsc.Maligned.SuggestNewOrder, "maligned.suggest-new", false,
104+
"Maligned: print suggested more optimal struct fields ordering")
98105
hideFlag("maligned.suggest-new")
99106

100107
fs.IntVar(&lsc.Dupl.Threshold, "dupl.threshold",
@@ -124,21 +131,29 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
124131
fs.BoolVar(&lc.EnableAll, "enable-all", false, wh("Enable all linters"))
125132
fs.BoolVar(&lc.DisableAll, "disable-all", false, wh("Disable all linters"))
126133
fs.StringSliceVarP(&lc.Presets, "presets", "p", nil,
127-
wh(fmt.Sprintf("Enable presets (%s) of linters. Run 'golangci-lint linters' to see them. This option implies option --disable-all", strings.Join(lintersdb.AllPresets(), "|"))))
134+
wh(fmt.Sprintf("Enable presets (%s) of linters. Run 'golangci-lint linters' to see "+
135+
"them. This option implies option --disable-all", strings.Join(lintersdb.AllPresets(), "|"))))
128136
fs.BoolVar(&lc.Fast, "fast", false, wh("Run only fast linters from enabled linters set"))
129137

130138
// Issues config
131139
ic := &cfg.Issues
132140
fs.StringSliceVarP(&ic.ExcludePatterns, "exclude", "e", nil, wh("Exclude issue by regexp"))
133141
fs.BoolVar(&ic.UseDefaultExcludes, "exclude-use-default", true, getDefaultExcludeHelp())
134142

135-
fs.IntVar(&ic.MaxIssuesPerLinter, "max-issues-per-linter", 50, wh("Maximum issues count per one linter. Set to 0 to disable"))
136-
fs.IntVar(&ic.MaxSameIssues, "max-same-issues", 3, wh("Maximum count of issues with the same text. Set to 0 to disable"))
143+
fs.IntVar(&ic.MaxIssuesPerLinter, "max-issues-per-linter", 50,
144+
wh("Maximum issues count per one linter. Set to 0 to disable"))
145+
fs.IntVar(&ic.MaxSameIssues, "max-same-issues", 3,
146+
wh("Maximum count of issues with the same text. Set to 0 to disable"))
137147

138148
fs.BoolVarP(&ic.Diff, "new", "n", false,
139-
wh("Show only new issues: if there are unstaged changes or untracked files, only those changes are analyzed, else only changes in HEAD~ are analyzed.\nIt's a super-useful option for integration of golangci-lint into existing large codebase.\nIt's not practical to fix all existing issues at the moment of integration: much better don't allow issues in new code"))
140-
fs.StringVar(&ic.DiffFromRevision, "new-from-rev", "", wh("Show only new issues created after git revision `REV`"))
141-
fs.StringVar(&ic.DiffPatchFilePath, "new-from-patch", "", wh("Show only new issues created in git patch with file path `PATH`"))
149+
wh("Show only new issues: if there are unstaged changes or untracked files, only those changes "+
150+
"are analyzed, else only changes in HEAD~ are analyzed.\nIt's a super-useful option for integration "+
151+
"of golangci-lint into existing large codebase.\nIt's not practical to fix all existing issues at "+
152+
"the moment of integration: much better don't allow issues in new code"))
153+
fs.StringVar(&ic.DiffFromRevision, "new-from-rev", "",
154+
wh("Show only new issues created after git revision `REV`"))
155+
fs.StringVar(&ic.DiffPatchFilePath, "new-from-patch", "",
156+
wh("Show only new issues created in git patch with file path `PATH`"))
142157

143158
}
144159

Diff for: pkg/config/config.go

+25-6
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ type ExcludePattern struct {
3030

3131
var DefaultExcludePatterns = []ExcludePattern{
3232
{
33-
Pattern: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked",
34-
Linter: "errcheck",
35-
Why: "Almost all programs ignore errors on these functions and in most cases it's ok",
33+
Pattern: "Error return value of .((os\\.)?std(out|err)\\..*|.*Close" +
34+
"|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked",
35+
Linter: "errcheck",
36+
Why: "Almost all programs ignore errors on these functions and in most cases it's ok",
3637
},
3738
{
38-
Pattern: "(comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form)",
39-
Linter: "golint",
40-
Why: "Annoying issue about not having a comment. The rare codebase has such comments",
39+
Pattern: "(comment on exported (method|function|type|const)|" +
40+
"should have( a package)? comment|comment should be of the form)",
41+
Linter: "golint",
42+
Why: "Annoying issue about not having a comment. The rare codebase has such comments",
4143
},
4244
{
4345
Pattern: "func name will be used as test\\.Test.* by other packages, and that stutters; consider calling this",
@@ -156,6 +158,17 @@ type LintersSettings struct {
156158
Misspell struct {
157159
Locale string
158160
}
161+
Lll LllSettings
162+
}
163+
164+
type LllSettings struct {
165+
LineLength int `mapstructure:"line-length"`
166+
}
167+
168+
var defaultLintersSettings = LintersSettings{
169+
Lll: LllSettings{
170+
LineLength: 120,
171+
},
159172
}
160173

161174
type Linters struct {
@@ -196,3 +209,9 @@ type Config struct { //nolint:maligned
196209

197210
InternalTest bool // Option is used only for testing golangci-lint code, don't use it
198211
}
212+
213+
func NewDefault() *Config {
214+
return &Config{
215+
LintersSettings: defaultLintersSettings,
216+
}
217+
}

Diff for: pkg/golinters/errcheck.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ func (Errcheck) Name() string {
1616
}
1717

1818
func (Errcheck) Desc() string {
19-
return "Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases"
19+
return "Errcheck is a program for checking for unchecked errors " +
20+
"in go programs. These unchecked errors can be critical bugs in some cases"
2021
}
2122

2223
func (e Errcheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {

Diff for: pkg/golinters/gofmt.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ func (g Gofmt) Desc() string {
3131
return "Goimports does everything that gofmt does. Additionally it checks unused imports"
3232
}
3333

34-
return "Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification"
34+
return "Gofmt checks whether code was gofmt-ed. By default " +
35+
"this tool runs with -s option to check for code simplification"
3536
}
3637

3738
func getFirstDeletedAndAddedLineNumberInHunk(h *diff.Hunk) (int, int, error) {

Diff for: pkg/golinters/govet.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ func (Govet) Name() string {
2525
}
2626

2727
func (Govet) Desc() string {
28-
return "Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string"
28+
return "Vet examines Go source code and reports suspicious constructs, " +
29+
"such as Printf calls whose arguments do not align with the format string"
2930
}
3031

3132
func (g Govet) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {

Diff for: pkg/golinters/lll.go

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package golinters
2+
3+
import (
4+
"bufio"
5+
"context"
6+
"fmt"
7+
"go/token"
8+
"os"
9+
"strings"
10+
"unicode/utf8"
11+
12+
"github.com/golangci/golangci-lint/pkg/lint/linter"
13+
"github.com/golangci/golangci-lint/pkg/result"
14+
)
15+
16+
type Lll struct{}
17+
18+
func (Lll) Name() string {
19+
return "lll"
20+
}
21+
22+
func (Lll) Desc() string {
23+
return "Reports long lines"
24+
}
25+
26+
func (lint Lll) getIssuesForFile(filename string, maxLineLen int) ([]result.Issue, error) {
27+
var res []result.Issue
28+
29+
f, err := os.Open(filename)
30+
if err != nil {
31+
return nil, fmt.Errorf("can't open file %s: %s", filename, err)
32+
}
33+
34+
lineNumber := 1
35+
scanner := bufio.NewScanner(f)
36+
for scanner.Scan() {
37+
line := scanner.Text()
38+
line = strings.Replace(line, "\t", " ", -1)
39+
lineLen := utf8.RuneCountInString(line)
40+
if lineLen > maxLineLen {
41+
res = append(res, result.Issue{
42+
Pos: token.Position{
43+
Filename: filename,
44+
Line: lineNumber,
45+
Column: 1,
46+
},
47+
Text: fmt.Sprintf("line is %d characters", lineLen),
48+
FromLinter: lint.Name(),
49+
})
50+
}
51+
lineNumber++
52+
}
53+
54+
if err := scanner.Err(); err != nil {
55+
return nil, fmt.Errorf("can't scan file %s: %s", filename, err)
56+
}
57+
58+
return res, nil
59+
}
60+
61+
func (lint Lll) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue, error) {
62+
var res []result.Issue
63+
for _, f := range lintCtx.PkgProgram.Files(lintCtx.Cfg.Run.AnalyzeTests) {
64+
issues, err := lint.getIssuesForFile(f, lintCtx.Settings().Lll.LineLength)
65+
if err != nil {
66+
return nil, err
67+
}
68+
res = append(res, issues...)
69+
}
70+
71+
return res, nil
72+
}

Diff for: pkg/lint/lintersdb/lintersdb.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import (
1414
)
1515

1616
func AllPresets() []string {
17-
return []string{linter.PresetBugs, linter.PresetUnused, linter.PresetFormatting, linter.PresetStyle, linter.PresetComplexity, linter.PresetPerformance}
17+
return []string{linter.PresetBugs, linter.PresetUnused, linter.PresetFormatting,
18+
linter.PresetStyle, linter.PresetComplexity, linter.PresetPerformance}
1819
}
1920

2021
func allPresetsSet() map[string]bool {
@@ -166,6 +167,10 @@ func GetAllSupportedLinterConfigs() []linter.Config {
166167
WithPresets(linter.PresetStyle).
167168
WithSpeed(7).
168169
WithURL("https://github.com/client9/misspell"),
170+
linter.NewConfig(golinters.Lll{}).
171+
WithPresets(linter.PresetStyle).
172+
WithSpeed(10).
173+
WithURL("https://github.com/walle/lll"),
169174
}
170175

171176
if os.Getenv("GOLANGCI_COM_RUN") == "1" {
@@ -175,6 +180,7 @@ func GetAllSupportedLinterConfigs() []linter.Config {
175180
golinters.Maligned{}.Name(): true, // rarely usable
176181
golinters.TypeCheck{}.Name(): true, // annoying because of different building envs
177182
golinters.Misspell{}.Name(): true, // unsure about false-positives number
183+
golinters.Lll{}.Name(): true, // annoying
178184
}
179185
return enableLinterConfigs(lcs, func(lc *linter.Config) bool {
180186
return !disabled[lc.Linter.Name()]
@@ -314,7 +320,10 @@ func GetAllLinterConfigsForPreset(p string) []linter.Config {
314320
return ret
315321
}
316322

317-
func getEnabledLintersSet(lcfg *config.Linters, enabledByDefaultLinters []linter.Config) map[string]*linter.Config { // nolint:gocyclo
323+
// nolint:gocyclo
324+
func getEnabledLintersSet(lcfg *config.Linters,
325+
enabledByDefaultLinters []linter.Config) map[string]*linter.Config {
326+
318327
resultLintersSet := map[string]*linter.Config{}
319328
switch {
320329
case len(lcfg.Presets) != 0:

Diff for: pkg/lint/load.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ func isLocalProjectAnalysis(args []string) bool {
109109
return true
110110
}
111111

112-
func getTypeCheckFuncBodies(cfg *config.Run, linters []linter.Config, pkgProg *packages.Program, log logutils.Log) func(string) bool {
112+
func getTypeCheckFuncBodies(cfg *config.Run, linters []linter.Config,
113+
pkgProg *packages.Program, log logutils.Log) func(string) bool {
114+
113115
if !isLocalProjectAnalysis(cfg.Args) {
114116
loadDebugf("analysis in nonlocal, don't optimize loading by not typechecking func bodies")
115117
return nil
@@ -155,7 +157,9 @@ func getTypeCheckFuncBodies(cfg *config.Run, linters []linter.Config, pkgProg *p
155157
}
156158
}
157159

158-
func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *config.Config, pkgProg *packages.Program, log logutils.Log) (*loader.Program, *loader.Config, error) {
160+
func loadWholeAppIfNeeded(ctx context.Context, linters []linter.Config, cfg *config.Config,
161+
pkgProg *packages.Program, log logutils.Log) (*loader.Program, *loader.Config, error) {
162+
159163
if !isFullImportNeeded(linters, cfg) {
160164
return nil, nil, nil
161165
}
@@ -255,7 +259,9 @@ func separateNotCompilingPackages(lintCtx *linter.Context) {
255259
}
256260

257261
//nolint:gocyclo
258-
func LoadContext(ctx context.Context, linters []linter.Config, cfg *config.Config, log logutils.Log) (*linter.Context, error) {
262+
func LoadContext(ctx context.Context, linters []linter.Config, cfg *config.Config,
263+
log logutils.Log) (*linter.Context, error) {
264+
259265
// Set GOROOT to have working cross-compilation: cross-compiled binaries
260266
// have invalid GOROOT. XXX: can't use runtime.GOROOT().
261267
goroot, err := goutils.DiscoverGoRoot()

Diff for: pkg/lint/runner.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,9 @@ type lintRes struct {
6666
issues []result.Issue
6767
}
6868

69-
func (r Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context, lc linter.Config) (ret []result.Issue, err error) {
69+
func (r Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context,
70+
lc linter.Config) (ret []result.Issue, err error) {
71+
7072
defer func() {
7173
if panicData := recover(); panicData != nil {
7274
err = fmt.Errorf("panic occurred: %s", panicData)
@@ -88,7 +90,9 @@ func (r Runner) runLinterSafe(ctx context.Context, lintCtx *linter.Context, lc l
8890
return issues, nil
8991
}
9092

91-
func (r Runner) runWorker(ctx context.Context, lintCtx *linter.Context, tasksCh <-chan linter.Config, lintResultsCh chan<- lintRes, name string) {
93+
func (r Runner) runWorker(ctx context.Context, lintCtx *linter.Context,
94+
tasksCh <-chan linter.Config, lintResultsCh chan<- lintRes, name string) {
95+
9296
sw := timeutils.NewStopwatch(name, r.Log)
9397
defer sw.Print()
9498

0 commit comments

Comments
 (0)