From a71b4d42e374cc169a4562aa774170c4f35670cc Mon Sep 17 00:00:00 2001 From: David Braley Date: Tue, 29 Oct 2019 17:54:49 -0400 Subject: [PATCH 01/15] rebase --- pkg/config/config.go | 25 ++++++++++++-- pkg/lint/lintersdb/enabled_set.go | 56 +++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 1b8de67585fb..5f86b886c872 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -299,6 +299,24 @@ var defaultLintersSettings = LintersSettings{ }, } +type PluginLintersSettings struct { + PluginLinters []PluginLinter `mapstructure:"plugin-linters"` +} + +type PluginLinter struct { + Name string + Path string +} + +func (cfg Config) GetPluginLinterNames() []string { + l := len(cfg.PluginLintersSettings.PluginLinters) + names := make([]string, l) + for i, linter := range cfg.PluginLintersSettings.PluginLinters { + names[i] = linter.Name + } + return names +} + type Linters struct { Enable []string Disable []string @@ -379,9 +397,10 @@ type Config struct { PrintWelcomeMessage bool `mapstructure:"print-welcome"` } - LintersSettings LintersSettings `mapstructure:"linters-settings"` - Linters Linters - Issues Issues + LintersSettings LintersSettings `mapstructure:"linters-settings"` + PluginLintersSettings PluginLintersSettings `mapstructure:"plugin-linters-settings"` + Linters Linters + Issues Issues InternalTest bool // Option is used only for testing golangci-lint code, don't use it } diff --git a/pkg/lint/lintersdb/enabled_set.go b/pkg/lint/lintersdb/enabled_set.go index 73e3bcfed29e..d30b378feb29 100644 --- a/pkg/lint/lintersdb/enabled_set.go +++ b/pkg/lint/lintersdb/enabled_set.go @@ -1,6 +1,9 @@ package lintersdb import ( + "fmt" + "golang.org/x/tools/go/analysis" + "plugin" "sort" "github.com/golangci/golangci-lint/pkg/config" @@ -73,9 +76,62 @@ func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*lint } } + // This isn't quite the right place to do this, but proves it can be done + pluginLinters := es.cfg.PluginLintersSettings.PluginLinters + for _, pluginLinter := range pluginLinters { + analyzer, err := es.GetAnalyzer(pluginLinter.Path) + if err != nil { + es.log.Errorf("Unable to load custom analyzer %s:%s, %v", + pluginLinter.Name, + pluginLinter.Path, + err) + } else { + es.log.Infof("Loaded %s: %s", pluginLinter.Path, analyzer.Name) + customLinter := goanalysis.NewLinter( + analyzer.Name, + analyzer.Doc, + []*analysis.Analyzer{analyzer}, + nil) + resultLintersSet[analyzer.Name] = &linter.Config{ + Linter: customLinter, + EnabledByDefault: true, + LoadMode: 0, + InPresets: nil, + AlternativeNames: nil, + OriginalURL: "", + CanAutoFix: false, + IsSlow: false, + } + } + } + // + return resultLintersSet } +type AnalyzerPlugin interface { + GetAnalyzer() *analysis.Analyzer +} + +func (es EnabledSet) GetAnalyzer(path string) (*analysis.Analyzer, error) { + plug, err := plugin.Open(path) + if err != nil { + return nil, err + } + + symbol, err := plug.Lookup("AnalyzerPlugin") + if err != nil { + return nil, err + } + + analyzerPlugin, ok := symbol.(AnalyzerPlugin) + if !ok { + return nil, fmt.Errorf("plugin %s does not abide by 'AnalyzerPlugin' interface", path) + } + + return analyzerPlugin.GetAnalyzer(), nil +} + func (es EnabledSet) Get(optimize bool) ([]*linter.Config, error) { if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil { return nil, err From 4be5882d50cf159790ed3fe9853aa9469f41708e Mon Sep 17 00:00:00 2001 From: David Braley Date: Wed, 30 Oct 2019 15:32:10 -0400 Subject: [PATCH 02/15] Closer to what I want --- .golangci.yml | 6 ++++ pkg/config/config.go | 9 +++++ pkg/lint/lintersdb/enabled_set.go | 58 ++++++++++++++++++------------- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 2a1eec7a3f7c..2685354e0fe3 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -48,6 +48,12 @@ linters-settings: funlen: lines: 100 statements: 50 +# custom: +# example-1: +# path: /example-linter.so +# enabled: true +# original-url: github.com/dbraley/example-linter +# slow: false linters: # please, do not use `enable-all`: it's deprecated and will be removed soon. diff --git a/pkg/config/config.go b/pkg/config/config.go index 5f86b886c872..85808619f2dd 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -190,6 +190,8 @@ type LintersSettings struct { Godox GodoxSettings Dogsled DogsledSettings Gocognit GocognitSettings + + Custom map[string]CustomLinterSettings } type GovetSettings struct { @@ -308,6 +310,13 @@ type PluginLinter struct { Path string } +type CustomLinterSettings struct { + Path string + Enabled bool + OriginalUrl string `mapstructure:"original-url"` + Slow bool +} + func (cfg Config) GetPluginLinterNames() []string { l := len(cfg.PluginLintersSettings.PluginLinters) names := make([]string, l) diff --git a/pkg/lint/lintersdb/enabled_set.go b/pkg/lint/lintersdb/enabled_set.go index d30b378feb29..302f32629254 100644 --- a/pkg/lint/lintersdb/enabled_set.go +++ b/pkg/lint/lintersdb/enabled_set.go @@ -77,43 +77,51 @@ func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*lint } // This isn't quite the right place to do this, but proves it can be done - pluginLinters := es.cfg.PluginLintersSettings.PluginLinters - for _, pluginLinter := range pluginLinters { - analyzer, err := es.GetAnalyzer(pluginLinter.Path) + for name, settings := range es.cfg.LintersSettings.Custom { + linterConfig, err := es.loadCustomLinterConfig(name, settings) + if err != nil { es.log.Errorf("Unable to load custom analyzer %s:%s, %v", - pluginLinter.Name, - pluginLinter.Path, + name, + settings.Path, err) } else { - es.log.Infof("Loaded %s: %s", pluginLinter.Path, analyzer.Name) - customLinter := goanalysis.NewLinter( - analyzer.Name, - analyzer.Doc, - []*analysis.Analyzer{analyzer}, - nil) - resultLintersSet[analyzer.Name] = &linter.Config{ - Linter: customLinter, - EnabledByDefault: true, - LoadMode: 0, - InPresets: nil, - AlternativeNames: nil, - OriginalURL: "", - CanAutoFix: false, - IsSlow: false, - } + resultLintersSet[linterConfig.Name()] = linterConfig } } - // return resultLintersSet } +func (es EnabledSet) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { + analyzer, err := es.GetAnalyzerPlugin(settings.Path) + if err != nil { + return nil, err + } else { + es.log.Infof("Loaded %s: %s", settings.Path, analyzer.GetLinterName()) + customLinter := goanalysis.NewLinter( + analyzer.GetLinterName(), + analyzer.GetLinterDesc(), + analyzer.GetAnalyzers(), + nil) + linterConfig := linter.NewConfig(customLinter) + linterConfig.EnabledByDefault = settings.Enabled + linterConfig.IsSlow = settings.Slow + linterConfig.WithURL(settings.OriginalUrl) + if name != linterConfig.Name() { + es.log.Warnf("Configuration linter name %s doesn't match plugin linter name %s", name, linterConfig.Name()) + } + return linterConfig, nil + } +} + type AnalyzerPlugin interface { - GetAnalyzer() *analysis.Analyzer + GetLinterName() string + GetLinterDesc() string + GetAnalyzers() []*analysis.Analyzer } -func (es EnabledSet) GetAnalyzer(path string) (*analysis.Analyzer, error) { +func (es EnabledSet) GetAnalyzerPlugin(path string) (AnalyzerPlugin, error) { plug, err := plugin.Open(path) if err != nil { return nil, err @@ -129,7 +137,7 @@ func (es EnabledSet) GetAnalyzer(path string) (*analysis.Analyzer, error) { return nil, fmt.Errorf("plugin %s does not abide by 'AnalyzerPlugin' interface", path) } - return analyzerPlugin.GetAnalyzer(), nil + return analyzerPlugin, nil } func (es EnabledSet) Get(optimize bool) ([]*linter.Config, error) { From b835e43e6fd1f5f836c71d23779b2765f36283d8 Mon Sep 17 00:00:00 2001 From: David Braley Date: Wed, 30 Oct 2019 16:43:55 -0400 Subject: [PATCH 03/15] Remove unnecessary configuration --- pkg/config/config.go | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 85808619f2dd..340171db1fb7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -301,15 +301,6 @@ var defaultLintersSettings = LintersSettings{ }, } -type PluginLintersSettings struct { - PluginLinters []PluginLinter `mapstructure:"plugin-linters"` -} - -type PluginLinter struct { - Name string - Path string -} - type CustomLinterSettings struct { Path string Enabled bool @@ -318,10 +309,11 @@ type CustomLinterSettings struct { } func (cfg Config) GetPluginLinterNames() []string { - l := len(cfg.PluginLintersSettings.PluginLinters) + customLinterSettings := cfg.LintersSettings.Custom + l := len(customLinterSettings) names := make([]string, l) - for i, linter := range cfg.PluginLintersSettings.PluginLinters { - names[i] = linter.Name + for name := range customLinterSettings { + names = append(names, name) } return names } @@ -406,10 +398,9 @@ type Config struct { PrintWelcomeMessage bool `mapstructure:"print-welcome"` } - LintersSettings LintersSettings `mapstructure:"linters-settings"` - PluginLintersSettings PluginLintersSettings `mapstructure:"plugin-linters-settings"` - Linters Linters - Issues Issues + LintersSettings LintersSettings `mapstructure:"linters-settings"` + Linters Linters + Issues Issues InternalTest bool // Option is used only for testing golangci-lint code, don't use it } From f86a8e2a6d72f8c3f458fe42bb111aa2f278fbc9 Mon Sep 17 00:00:00 2001 From: David Braley Date: Fri, 1 Nov 2019 10:35:21 -0400 Subject: [PATCH 04/15] Pretty close I think Still needs unit tests & custom linters don't show up when running `golangci-lint linters` unless they're enabled on the command line --- Makefile | 6 ++ pkg/commands/executor.go | 4 +- pkg/lint/lintersdb/enabled_set.go | 64 --------------------- pkg/lint/lintersdb/enabled_set_test.go | 2 +- pkg/lint/lintersdb/manager.go | 80 +++++++++++++++++++++++++- pkg/result/processors/nolint_test.go | 2 +- scripts/gen_readme/main.go | 4 +- test/enabled_linters_test.go | 8 +-- 8 files changed, 94 insertions(+), 76 deletions(-) diff --git a/Makefile b/Makefile index 365751a9ef17..f1e61eb9e6f8 100644 --- a/Makefile +++ b/Makefile @@ -107,3 +107,9 @@ go.sum: go.mod .PHONY: vendor vendor: go.mod go.sum go mod vendor + +unexport GOFLAGS +vendor_free_build: FORCE + go env + go build -o golangci-lint ./cmd/golangci-lint +.PHONY: vendor_free_build diff --git a/pkg/commands/executor.go b/pkg/commands/executor.go index 975a3c507588..c82e70f7edd6 100644 --- a/pkg/commands/executor.go +++ b/pkg/commands/executor.go @@ -62,7 +62,7 @@ func NewExecutor(version, commit, date string) *Executor { version: version, commit: commit, date: date, - DBManager: lintersdb.NewManager(nil), + DBManager: lintersdb.NewManager(nil, nil), debugf: logutils.Debug("exec"), } @@ -114,7 +114,7 @@ func NewExecutor(version, commit, date string) *Executor { } // recreate after getting config - e.DBManager = lintersdb.NewManager(e.cfg) + e.DBManager = lintersdb.NewManager(e.cfg, e.log).WithCustomLinters() e.cfg.LintersSettings.Gocritic.InferEnabledChecks(e.log) if err = e.cfg.LintersSettings.Gocritic.Validate(e.log); err != nil { diff --git a/pkg/lint/lintersdb/enabled_set.go b/pkg/lint/lintersdb/enabled_set.go index 302f32629254..73e3bcfed29e 100644 --- a/pkg/lint/lintersdb/enabled_set.go +++ b/pkg/lint/lintersdb/enabled_set.go @@ -1,9 +1,6 @@ package lintersdb import ( - "fmt" - "golang.org/x/tools/go/analysis" - "plugin" "sort" "github.com/golangci/golangci-lint/pkg/config" @@ -76,70 +73,9 @@ func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*lint } } - // This isn't quite the right place to do this, but proves it can be done - for name, settings := range es.cfg.LintersSettings.Custom { - linterConfig, err := es.loadCustomLinterConfig(name, settings) - - if err != nil { - es.log.Errorf("Unable to load custom analyzer %s:%s, %v", - name, - settings.Path, - err) - } else { - resultLintersSet[linterConfig.Name()] = linterConfig - } - } - return resultLintersSet } -func (es EnabledSet) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { - analyzer, err := es.GetAnalyzerPlugin(settings.Path) - if err != nil { - return nil, err - } else { - es.log.Infof("Loaded %s: %s", settings.Path, analyzer.GetLinterName()) - customLinter := goanalysis.NewLinter( - analyzer.GetLinterName(), - analyzer.GetLinterDesc(), - analyzer.GetAnalyzers(), - nil) - linterConfig := linter.NewConfig(customLinter) - linterConfig.EnabledByDefault = settings.Enabled - linterConfig.IsSlow = settings.Slow - linterConfig.WithURL(settings.OriginalUrl) - if name != linterConfig.Name() { - es.log.Warnf("Configuration linter name %s doesn't match plugin linter name %s", name, linterConfig.Name()) - } - return linterConfig, nil - } -} - -type AnalyzerPlugin interface { - GetLinterName() string - GetLinterDesc() string - GetAnalyzers() []*analysis.Analyzer -} - -func (es EnabledSet) GetAnalyzerPlugin(path string) (AnalyzerPlugin, error) { - plug, err := plugin.Open(path) - if err != nil { - return nil, err - } - - symbol, err := plug.Lookup("AnalyzerPlugin") - if err != nil { - return nil, err - } - - analyzerPlugin, ok := symbol.(AnalyzerPlugin) - if !ok { - return nil, fmt.Errorf("plugin %s does not abide by 'AnalyzerPlugin' interface", path) - } - - return analyzerPlugin, nil -} - func (es EnabledSet) Get(optimize bool) ([]*linter.Config, error) { if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil { return nil, err diff --git a/pkg/lint/lintersdb/enabled_set_test.go b/pkg/lint/lintersdb/enabled_set_test.go index b2eaf383d344..f9b6393f71e3 100644 --- a/pkg/lint/lintersdb/enabled_set_test.go +++ b/pkg/lint/lintersdb/enabled_set_test.go @@ -91,7 +91,7 @@ func TestGetEnabledLintersSet(t *testing.T) { }, } - m := NewManager(nil) + m := NewManager(nil, nil) es := NewEnabledSet(m, NewValidator(m), nil, nil) for _, c := range cases { c := c diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index f2cd2bd57ce0..220a6962932a 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -1,7 +1,14 @@ package lintersdb import ( + "fmt" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/logutils" + "github.com/golangci/golangci-lint/pkg/report" + "github.com/prometheus/common/log" + "golang.org/x/tools/go/analysis" "os" + "plugin" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters" @@ -11,10 +18,11 @@ import ( type Manager struct { nameToLCs map[string][]*linter.Config cfg *config.Config + log logutils.Log } -func NewManager(cfg *config.Config) *Manager { - m := &Manager{cfg: cfg} +func NewManager(cfg *config.Config, log logutils.Log) *Manager { + m := &Manager{cfg: cfg, log: log} nameToLCs := make(map[string][]*linter.Config) for _, lc := range m.GetAllSupportedLinterConfigs() { for _, name := range lc.AllNames() { @@ -26,6 +34,27 @@ func NewManager(cfg *config.Config) *Manager { return m } +func (m *Manager) WithCustomLinters() *Manager { + if m.log == nil { + m.log = report.NewLogWrapper(logutils.NewStderrLog(""), &report.Data{}) + } + if m.cfg != nil { + for name, settings := range m.cfg.LintersSettings.Custom { + lc, err := m.loadCustomLinterConfig(name, settings) + + if err != nil { + log.Errorf("Unable to load custom analyzer %s:%s, %v", + name, + settings.Path, + err) + } else { + m.nameToLCs[name] = append(m.nameToLCs[name], lc) + } + } + } + return m +} + func (Manager) AllPresets() []string { return []string{linter.PresetBugs, linter.PresetComplexity, linter.PresetFormatting, linter.PresetPerformance, linter.PresetStyle, linter.PresetUnused} @@ -264,3 +293,50 @@ func (m Manager) GetAllLinterConfigsForPreset(p string) []*linter.Config { return ret } + +func (m Manager) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { + analyzer, err := m.getAnalyzerPlugin(settings.Path) + if err != nil { + return nil, err + } else { + m.log.Infof("Loaded %s: %s", settings.Path, analyzer.GetLinterName()) + customLinter := goanalysis.NewLinter( + analyzer.GetLinterName(), + analyzer.GetLinterDesc(), + analyzer.GetAnalyzers(), + nil) + linterConfig := linter.NewConfig(customLinter) + linterConfig.EnabledByDefault = settings.Enabled + linterConfig.IsSlow = settings.Slow + linterConfig.WithURL(settings.OriginalUrl) + if name != linterConfig.Name() { + m.log.Warnf("Configuration linter name %s doesn't match plugin linter name %s", name, linterConfig.Name()) + } + return linterConfig, nil + } +} + +type AnalyzerPlugin interface { + GetLinterName() string + GetLinterDesc() string + GetAnalyzers() []*analysis.Analyzer +} + +func (m Manager) getAnalyzerPlugin(path string) (AnalyzerPlugin, error) { + plug, err := plugin.Open(path) + if err != nil { + return nil, err + } + + symbol, err := plug.Lookup("AnalyzerPlugin") + if err != nil { + return nil, err + } + + analyzerPlugin, ok := symbol.(AnalyzerPlugin) + if !ok { + return nil, fmt.Errorf("plugin %s does not abide by 'AnalyzerPlugin' interface", path) + } + + return analyzerPlugin, nil +} diff --git a/pkg/result/processors/nolint_test.go b/pkg/result/processors/nolint_test.go index a9f245fe72d5..b547fbd0b87f 100644 --- a/pkg/result/processors/nolint_test.go +++ b/pkg/result/processors/nolint_test.go @@ -31,7 +31,7 @@ func newNolint2FileIssue(line int) result.Issue { } func newTestNolintProcessor(log logutils.Log) *Nolint { - return NewNolint(log, lintersdb.NewManager(nil)) + return NewNolint(log, lintersdb.NewManager(nil, nil)) } func getMockLog() *logutils.MockLog { diff --git a/scripts/gen_readme/main.go b/scripts/gen_readme/main.go index e3b30a3bb3d6..358582866e4a 100644 --- a/scripts/gen_readme/main.go +++ b/scripts/gen_readme/main.go @@ -114,7 +114,7 @@ func buildTemplateContext() (map[string]interface{}, error) { func getLintersListMarkdown(enabled bool) string { var neededLcs []*linter.Config - lcs := lintersdb.NewManager(nil).GetAllSupportedLinterConfigs() + lcs := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() for _, lc := range lcs { if lc.EnabledByDefault == enabled { neededLcs = append(neededLcs, lc) @@ -139,7 +139,7 @@ func getLintersListMarkdown(enabled bool) string { func getThanksList() string { var lines []string addedAuthors := map[string]bool{} - for _, lc := range lintersdb.NewManager(nil).GetAllSupportedLinterConfigs() { + for _, lc := range lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() { if lc.OriginalURL == "" { continue } diff --git a/test/enabled_linters_test.go b/test/enabled_linters_test.go index 436d42b29b0d..0ade56b04dc3 100644 --- a/test/enabled_linters_test.go +++ b/test/enabled_linters_test.go @@ -21,7 +21,7 @@ func inSlice(s []string, v string) bool { } func getEnabledByDefaultFastLintersExcept(except ...string) []string { - m := lintersdb.NewManager(nil) + m := lintersdb.NewManager(nil, nil) ebdl := m.GetAllEnabledByDefaultLinters() ret := []string{} for _, lc := range ebdl { @@ -38,7 +38,7 @@ func getEnabledByDefaultFastLintersExcept(except ...string) []string { } func getAllFastLintersWith(with ...string) []string { - linters := lintersdb.NewManager(nil).GetAllSupportedLinterConfigs() + linters := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() ret := append([]string{}, with...) for _, lc := range linters { if lc.IsSlowLinter() { @@ -51,7 +51,7 @@ func getAllFastLintersWith(with ...string) []string { } func getEnabledByDefaultLinters() []string { - ebdl := lintersdb.NewManager(nil).GetAllEnabledByDefaultLinters() + ebdl := lintersdb.NewManager(nil, nil).GetAllEnabledByDefaultLinters() ret := []string{} for _, lc := range ebdl { ret = append(ret, lc.Name()) @@ -61,7 +61,7 @@ func getEnabledByDefaultLinters() []string { } func getEnabledByDefaultFastLintersWith(with ...string) []string { - ebdl := lintersdb.NewManager(nil).GetAllEnabledByDefaultLinters() + ebdl := lintersdb.NewManager(nil, nil).GetAllEnabledByDefaultLinters() ret := append([]string{}, with...) for _, lc := range ebdl { if lc.IsSlowLinter() { From b9c6f81dae69d4b06927d003ef010ca38ae84015 Mon Sep 17 00:00:00 2001 From: David Braley Date: Fri, 1 Nov 2019 17:16:42 -0400 Subject: [PATCH 05/15] Small fixes --- Makefile | 1 - pkg/lint/lintersdb/manager.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f1e61eb9e6f8..3506eccd292d 100644 --- a/Makefile +++ b/Makefile @@ -110,6 +110,5 @@ vendor: go.mod go.sum unexport GOFLAGS vendor_free_build: FORCE - go env go build -o golangci-lint ./cmd/golangci-lint .PHONY: vendor_free_build diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 220a6962932a..67228d59d016 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -304,7 +304,7 @@ func (m Manager) loadCustomLinterConfig(name string, settings config.CustomLinte analyzer.GetLinterName(), analyzer.GetLinterDesc(), analyzer.GetAnalyzers(), - nil) + nil).WithLoadMode(goanalysis.LoadModeTypesInfo) linterConfig := linter.NewConfig(customLinter) linterConfig.EnabledByDefault = settings.Enabled linterConfig.IsSlow = settings.Slow From c45ef5abc76921e977f757c158dc9b6ae5ec743d Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 09:23:06 -0500 Subject: [PATCH 06/15] Update documentation --- .golangci.example.yml | 15 +++++++++++ .golangci.yml | 6 ----- README.tmpl.md | 60 +++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 4 +++ 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/.golangci.example.yml b/.golangci.example.yml index 7735dc1b607c..4faafa516bc6 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -228,6 +228,21 @@ linters-settings: # Allow declarations (var) to be cuddled. allow-cuddle-declarations: false + # The custom section can be used to define linter plugins to be loaded at runtime. See README doc + # for more info. + custom: + # Each custom linter should have a name. This name should match with what the linter reports for itself. + example: + # The path to the plugin *.so. Required for each custom linter + path: /path/to/example.so + # If the linter should be enabled by default. Options in the `linters` + # section override this, including `disable-all`. Optional, false by default. + enabled: true + # Intended to point to the repo location of the linter. Optional, just for documentation purposes. + original-url: github.com/dbraley/example-linter + # Indicates if this linter should be considered slow. Optional, false by default. + slow: false + linters: enable: - megacheck diff --git a/.golangci.yml b/.golangci.yml index 2685354e0fe3..2a1eec7a3f7c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -48,12 +48,6 @@ linters-settings: funlen: lines: 100 statements: 50 -# custom: -# example-1: -# path: /example-linter.so -# enabled: true -# original-url: github.com/dbraley/example-linter -# slow: false linters: # please, do not use `enable-all`: it's deprecated and will be removed soon. diff --git a/README.tmpl.md b/README.tmpl.md index bb11b99a788b..50bb58d2ad26 100644 --- a/README.tmpl.md +++ b/README.tmpl.md @@ -453,6 +453,66 @@ than the default and have more strict settings: {{.GolangciYaml}} ``` +## Custom Linters +Some people and organizations may choose to have custom made linters run as a part of golangci-lint. That functionality +is supported through go's plugin library. + +### Create a Copy of `golangci-lint` that Can Run with Plugins +In order to use plugins, you'll need a golangci-lint executable that can run them. The normal version of this project +is built with the vendors option, which breaks plugins that have overlapping dependencies. + +1. Download the `i473` branch of https://github.com/dbraley/golangci-lint/tree/i473 +2. From that projects root directory, run `make vendor_free_build` +3. Copy the `golangci-lint` executable that was created to your path, project, or other location + +### Configure Your Project for Linting +If you already have a linter plugin available, you can follow these steps to define it's usage in a projects +`.golangci.yml` file. An example linter can be found at https://github.com/dbraley/example-linter. If you're looking for +instructions on how to configure your own custom linter, they can be found further down. + +1. If the project you want to lint does not have one already, copy the https://github.com/dbraley/golangci-lint/blob/i473/.golangci.yml to the root directory. +2. Adjust the yaml to appropriate `linters-settings:custom` entries as so: +``` +linters-settings: + custom: + example: # If this doesn't match the linters name definition, it will warn you but still run + path: /example.so # Adjust this the location of the plugin + enabled: true # This determines if the linter is run by default + original-url: github.com/dbraley/example-linter # This is just documentation for custom linters + slow: false # Set this to true to observe `--fast` option +``` +3. If your `.golangci.yml` file has `linters:disable-all` set to true, you'll also need to add your linter to the `linters:enable` seciont: +``` +linters: + enable: + # ... + - varcheck + - whitespace + # Custom linters + - example +``` +4. Alternately, you can leave it disabled and turn it on via command line only: `golangci-lint run -Eexample` +5. If everything works correctly, you should see the linter show up as enabled by running `golangci-lint linters`. This linter will look for `// TODO: ` in front of functions, so you can also add that to your code and see the errors. +6. You can also see the linter get loaded with the `-v` option for either `run` or `linters` + +### To Configure Your Own `golang.org/x/tools/go/analysis` Based Linter + +Your Linter must implement one or more `analysis.Analyzer` structs. +Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. + +You'll also want to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define: +1. A variable of type `analyzerPlugin`. The type `analyzerPlugin` can be defined as a string, struct, whatever. +``` +type analyzerPlugin struct{} +var AnalyzerPlugin analyzerPlugin +``` +2. A function with signature `func (*analyzerPlugin) GetLinterName() string` +3. A function with signature `func (*analyzerPlugin) GetLinterDesc() string` +4. A function with signature `func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer` + +From the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` +file that can be copied into your project or another well known location for usage in golangci-lint. + ## False Positives False positives are inevitable, but we did our best to reduce their count. For example, we have a default enabled set of [exclude patterns](#command-line-options). If a false positive occurred you have the following choices: diff --git a/go.mod b/go.mod index 263d2c880699..bdbd9e388488 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b github.com/pkg/errors v0.8.1 + github.com/prometheus/common v0.4.0 github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada // v2.19.8 github.com/sirupsen/logrus v1.4.2 diff --git a/go.sum b/go.sum index 01daee958033..2f99c089aefb 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,9 @@ github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -182,6 +184,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -297,6 +300,7 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From bd80105aca55595ba4a6014b2c7a87df042e8ac6 Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 09:28:04 -0500 Subject: [PATCH 07/15] Commit generated README.md --- README.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/README.md b/README.md index 1b7e0a7d0665..1bda5e013676 100644 --- a/README.md +++ b/README.md @@ -828,6 +828,21 @@ linters-settings: # Allow declarations (var) to be cuddled. allow-cuddle-declarations: false + # The custom section can be used to define linter plugins to be loaded at runtime. See README doc + # for more info. + custom: + # Each custom linter should have a name. This name should match with what the linter reports for itself. + example: + # The path to the plugin *.so. Required for each custom linter + path: /path/to/example.so + # If the linter should be enabled by default. Options in the `linters` + # section override this, including `disable-all`. Optional, false by default. + enabled: true + # Intended to point to the repo location of the linter. Optional, just for documentation purposes. + original-url: github.com/dbraley/example-linter + # Indicates if this linter should be considered slow. Optional, false by default. + slow: false + linters: enable: - megacheck @@ -1027,6 +1042,66 @@ service: - echo "here I can run custom commands, but no preparation needed for this repo" ``` +## Custom Linters +Some people and organizations may choose to have custom made linters run as a part of golangci-lint. That functionality +is supported through go's plugin library. + +### Create a Copy of `golangci-lint` that Can Run with Plugins +In order to use plugins, you'll need a golangci-lint executable that can run them. The normal version of this project +is built with the vendors option, which breaks plugins that have overlapping dependencies. + +1. Download the `i473` branch of https://github.com/dbraley/golangci-lint/tree/i473 +2. From that projects root directory, run `make vendor_free_build` +3. Copy the `golangci-lint` executable that was created to your path, project, or other location + +### Configure Your Project for Linting +If you already have a linter plugin available, you can follow these steps to define it's usage in a projects +`.golangci.yml` file. An example linter can be found at https://github.com/dbraley/example-linter. If you're looking for +instructions on how to configure your own custom linter, they can be found further down. + +1. If the project you want to lint does not have one already, copy the https://github.com/dbraley/golangci-lint/blob/i473/.golangci.yml to the root directory. +2. Adjust the yaml to appropriate `linters-settings:custom` entries as so: +``` +linters-settings: + custom: + example: # If this doesn't match the linters name definition, it will warn you but still run + path: /example.so # Adjust this the location of the plugin + enabled: true # This determines if the linter is run by default + original-url: github.com/dbraley/example-linter # This is just documentation for custom linters + slow: false # Set this to true to observe `--fast` option +``` +3. If your `.golangci.yml` file has `linters:disable-all` set to true, you'll also need to add your linter to the `linters:enable` seciont: +``` +linters: + enable: + # ... + - varcheck + - whitespace + # Custom linters + - example +``` +4. Alternately, you can leave it disabled and turn it on via command line only: `golangci-lint run -Eexample` +5. If everything works correctly, you should see the linter show up as enabled by running `golangci-lint linters`. This linter will look for `// TODO: ` in front of functions, so you can also add that to your code and see the errors. +6. You can also see the linter get loaded with the `-v` option for either `run` or `linters` + +### To Configure Your Own `golang.org/x/tools/go/analysis` Based Linter + +Your Linter must implement one or more `analysis.Analyzer` structs. +Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. + +You'll also want to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define: +1. A variable of type `analyzerPlugin`. The type `analyzerPlugin` can be defined as a string, struct, whatever. +``` +type analyzerPlugin struct{} +var AnalyzerPlugin analyzerPlugin +``` +2. A function with signature `func (*analyzerPlugin) GetLinterName() string` +3. A function with signature `func (*analyzerPlugin) GetLinterDesc() string` +4. A function with signature `func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer` + +From the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` +file that can be copied into your project or another well known location for usage in golangci-lint. + ## False Positives False positives are inevitable, but we did our best to reduce their count. For example, we have a default enabled set of [exclude patterns](#command-line-options). If a false positive occurred you have the following choices: From f2fc003c7fc47df11c1e38face8387e2ad4becfb Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 09:32:41 -0500 Subject: [PATCH 08/15] Roll back accidentally changed go.mod and go.sum --- go.mod | 1 - go.sum | 4 ---- 2 files changed, 5 deletions(-) diff --git a/go.mod b/go.mod index bdbd9e388488..263d2c880699 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b github.com/pkg/errors v0.8.1 - github.com/prometheus/common v0.4.0 github.com/securego/gosec v0.0.0-20191002120514-e680875ea14d github.com/shirou/gopsutil v0.0.0-20190901111213-e4ec7b275ada // v2.19.8 github.com/sirupsen/logrus v1.4.2 diff --git a/go.sum b/go.sum index 2f99c089aefb..01daee958033 100644 --- a/go.sum +++ b/go.sum @@ -6,9 +6,7 @@ github.com/OpenPeeDeeP/depguard v1.0.1 h1:VlW4R6jmBIv3/u1JNlawEvJMM4J+dPORPaZasQ github.com/OpenPeeDeeP/depguard v1.0.1/go.mod h1:xsIw86fROiiwelg+jB2uM9PiKihMMmUx/1V+TNhjQvM= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -184,7 +182,6 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -300,7 +297,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From a70cc0e71670696779c237fd4fa6b1cfedd2e73f Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 09:45:23 -0500 Subject: [PATCH 09/15] Get rid of unnecessary function GetPluginLinterNames --- pkg/config/config.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index 340171db1fb7..c30562fb8f8b 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -308,16 +308,6 @@ type CustomLinterSettings struct { Slow bool } -func (cfg Config) GetPluginLinterNames() []string { - customLinterSettings := cfg.LintersSettings.Custom - l := len(customLinterSettings) - names := make([]string, l) - for name := range customLinterSettings { - names = append(names, name) - } - return names -} - type Linters struct { Enable []string Disable []string From 2b444e6c98c74c75353cd11e20a5f2c57a6209e5 Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 10:06:45 -0500 Subject: [PATCH 10/15] Cleanup --- pkg/config/config.go | 2 +- pkg/lint/lintersdb/manager.go | 32 +++++++++++++++----------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index c30562fb8f8b..35d314d9cf38 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -304,7 +304,7 @@ var defaultLintersSettings = LintersSettings{ type CustomLinterSettings struct { Path string Enabled bool - OriginalUrl string `mapstructure:"original-url"` + OriginalURL string `mapstructure:"original-url"` Slow bool } diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 67228d59d016..0116bdfb60e8 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -5,7 +5,6 @@ import ( "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/report" - "github.com/prometheus/common/log" "golang.org/x/tools/go/analysis" "os" "plugin" @@ -43,7 +42,7 @@ func (m *Manager) WithCustomLinters() *Manager { lc, err := m.loadCustomLinterConfig(name, settings) if err != nil { - log.Errorf("Unable to load custom analyzer %s:%s, %v", + m.log.Errorf("Unable to load custom analyzer %s:%s, %v", name, settings.Path, err) @@ -298,22 +297,21 @@ func (m Manager) loadCustomLinterConfig(name string, settings config.CustomLinte analyzer, err := m.getAnalyzerPlugin(settings.Path) if err != nil { return nil, err - } else { - m.log.Infof("Loaded %s: %s", settings.Path, analyzer.GetLinterName()) - customLinter := goanalysis.NewLinter( - analyzer.GetLinterName(), - analyzer.GetLinterDesc(), - analyzer.GetAnalyzers(), - nil).WithLoadMode(goanalysis.LoadModeTypesInfo) - linterConfig := linter.NewConfig(customLinter) - linterConfig.EnabledByDefault = settings.Enabled - linterConfig.IsSlow = settings.Slow - linterConfig.WithURL(settings.OriginalUrl) - if name != linterConfig.Name() { - m.log.Warnf("Configuration linter name %s doesn't match plugin linter name %s", name, linterConfig.Name()) - } - return linterConfig, nil } + m.log.Infof("Loaded %s: %s", settings.Path, analyzer.GetLinterName()) + customLinter := goanalysis.NewLinter( + analyzer.GetLinterName(), + analyzer.GetLinterDesc(), + analyzer.GetAnalyzers(), + nil).WithLoadMode(goanalysis.LoadModeTypesInfo) + linterConfig := linter.NewConfig(customLinter) + linterConfig.EnabledByDefault = settings.Enabled + linterConfig.IsSlow = settings.Slow + linterConfig.WithURL(settings.OriginalURL) + if name != linterConfig.Name() { + m.log.Warnf("Configuration linter name %s doesn't match plugin linter name %s", name, linterConfig.Name()) + } + return linterConfig, nil } type AnalyzerPlugin interface { From f82a6399168d97ddfc651137d052fec9ce1bc967 Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 10:23:27 -0500 Subject: [PATCH 11/15] I think this fixes 'File is not goimports-ed with -local' type errors... --- pkg/lint/lintersdb/manager.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 0116bdfb60e8..66e19f6bd814 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -2,16 +2,16 @@ package lintersdb import ( "fmt" - "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" - "github.com/golangci/golangci-lint/pkg/logutils" - "github.com/golangci/golangci-lint/pkg/report" - "golang.org/x/tools/go/analysis" "os" "plugin" "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/logutils" + "github.com/golangci/golangci-lint/pkg/report" + "golang.org/x/tools/go/analysis" ) type Manager struct { From 7e0a42045175c8e2c8a7e8757404bae81cfe3a1e Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 10:32:28 -0500 Subject: [PATCH 12/15] One more time, now with correct sorting --- pkg/lint/lintersdb/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 66e19f6bd814..f504bd5c4d06 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -2,6 +2,7 @@ package lintersdb import ( "fmt" + "golang.org/x/tools/go/analysis" "os" "plugin" @@ -11,7 +12,6 @@ import ( "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/report" - "golang.org/x/tools/go/analysis" ) type Manager struct { From 1cd09ac66e3aba01df4d942c1ce54cc2e5487697 Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 4 Nov 2019 10:37:39 -0500 Subject: [PATCH 13/15] Yeah, sure one more go at this. --- pkg/lint/lintersdb/manager.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index f504bd5c4d06..261064217b4b 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -2,10 +2,11 @@ package lintersdb import ( "fmt" - "golang.org/x/tools/go/analysis" "os" "plugin" + "golang.org/x/tools/go/analysis" + "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" From 329c6e054e20e7476d4d93161c5c03f49216cd27 Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 11 Nov 2019 12:23:37 -0500 Subject: [PATCH 14/15] Yeah, sure one more go at this. --- .golangci.example.yml | 13 ++++----- README.tmpl.md | 51 +++++++++++++---------------------- pkg/config/config.go | 3 +-- pkg/lint/lintersdb/manager.go | 15 ++++------- 4 files changed, 30 insertions(+), 52 deletions(-) diff --git a/.golangci.example.yml b/.golangci.example.yml index 4faafa516bc6..1a824bcb7bd8 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -231,17 +231,14 @@ linters-settings: # The custom section can be used to define linter plugins to be loaded at runtime. See README doc # for more info. custom: - # Each custom linter should have a name. This name should match with what the linter reports for itself. + # Each custom linter should have a unique name. example: - # The path to the plugin *.so. Required for each custom linter + # The path to the plugin *.so. Can be absolute or local. Required for each custom linter path: /path/to/example.so - # If the linter should be enabled by default. Options in the `linters` - # section override this, including `disable-all`. Optional, false by default. - enabled: true + # The description of the linter. Optional, just for documentation purposes. + description: This is an example usage of a plugin linter. # Intended to point to the repo location of the linter. Optional, just for documentation purposes. - original-url: github.com/dbraley/example-linter - # Indicates if this linter should be considered slow. Optional, false by default. - slow: false + original-url: github.com/golangci/example-linter linters: enable: diff --git a/README.tmpl.md b/README.tmpl.md index 50bb58d2ad26..23fa13e6e111 100644 --- a/README.tmpl.md +++ b/README.tmpl.md @@ -461,56 +461,43 @@ is supported through go's plugin library. In order to use plugins, you'll need a golangci-lint executable that can run them. The normal version of this project is built with the vendors option, which breaks plugins that have overlapping dependencies. -1. Download the `i473` branch of https://github.com/dbraley/golangci-lint/tree/i473 -2. From that projects root directory, run `make vendor_free_build` +1. Download https://github.com/golangci/golangci-lint +2. From the projects root directory, run `make vendor_free_build` 3. Copy the `golangci-lint` executable that was created to your path, project, or other location ### Configure Your Project for Linting If you already have a linter plugin available, you can follow these steps to define it's usage in a projects -`.golangci.yml` file. An example linter can be found at https://github.com/dbraley/example-linter. If you're looking for +`.golangci.yml` file. An example linter can be found at [here](https://github.com/golangci/example-plugin-linter). If you're looking for instructions on how to configure your own custom linter, they can be found further down. -1. If the project you want to lint does not have one already, copy the https://github.com/dbraley/golangci-lint/blob/i473/.golangci.yml to the root directory. +1. If the project you want to lint does not have one already, copy the [.golangci.yml](https://github.com/golangci/golangci-lint/blob/master/.golangci.yml) to the root directory. 2. Adjust the yaml to appropriate `linters-settings:custom` entries as so: ``` linters-settings: custom: - example: # If this doesn't match the linters name definition, it will warn you but still run - path: /example.so # Adjust this the location of the plugin - enabled: true # This determines if the linter is run by default - original-url: github.com/dbraley/example-linter # This is just documentation for custom linters - slow: false # Set this to true to observe `--fast` option + example: + path: /example.so + description: The description of the linter + original-url: github.com/golangci/example-linter ``` -3. If your `.golangci.yml` file has `linters:disable-all` set to true, you'll also need to add your linter to the `linters:enable` seciont: -``` -linters: - enable: - # ... - - varcheck - - whitespace - # Custom linters - - example -``` -4. Alternately, you can leave it disabled and turn it on via command line only: `golangci-lint run -Eexample` -5. If everything works correctly, you should see the linter show up as enabled by running `golangci-lint linters`. This linter will look for `// TODO: ` in front of functions, so you can also add that to your code and see the errors. -6. You can also see the linter get loaded with the `-v` option for either `run` or `linters` -### To Configure Your Own `golang.org/x/tools/go/analysis` Based Linter +That is all the configuration that is required to run a custom linter in your project. Custom linters are enabled by default, +but abide by the same rules as other linters. If the `.golang.yml` file specifies `linters:disable-all: true` -Your Linter must implement one or more `analysis.Analyzer` structs. +### To Create Your Own Custom `golang.org/x/tools/go/analysis` Based Linter + +Your linter must implement one or more `analysis.Analyzer` structs. Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. -You'll also want to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define: -1. A variable of type `analyzerPlugin`. The type `analyzerPlugin` can be defined as a string, struct, whatever. +You'll also need to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define a variable of Named `AnalyzerPlugin`. The `AnalyzerPlugin` MUST implement the following interface: ``` -type analyzerPlugin struct{} -var AnalyzerPlugin analyzerPlugin +type AnalyzerPlugin interface { + GetAnalyzers() []*analysis.Analyzer +} ``` -2. A function with signature `func (*analyzerPlugin) GetLinterName() string` -3. A function with signature `func (*analyzerPlugin) GetLinterDesc() string` -4. A function with signature `func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer` +The type `AnalyzerPlugin` is not important, but is by convention of `type analyzerPlugin struct {}`. See [plugin/example.go](https://github.com/golangci/example-plugin-linter/plugin/example.go) for more info. -From the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` +To build the plugin, from the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` file that can be copied into your project or another well known location for usage in golangci-lint. ## False Positives diff --git a/pkg/config/config.go b/pkg/config/config.go index 35d314d9cf38..83c8d7576cf6 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -303,9 +303,8 @@ var defaultLintersSettings = LintersSettings{ type CustomLinterSettings struct { Path string - Enabled bool + Description string OriginalURL string `mapstructure:"original-url"` - Slow bool } type Linters struct { diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 261064217b4b..66e74ace8bf8 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -299,25 +299,20 @@ func (m Manager) loadCustomLinterConfig(name string, settings config.CustomLinte if err != nil { return nil, err } - m.log.Infof("Loaded %s: %s", settings.Path, analyzer.GetLinterName()) + m.log.Infof("Loaded %s: %s", settings.Path, name) customLinter := goanalysis.NewLinter( - analyzer.GetLinterName(), - analyzer.GetLinterDesc(), + name, + settings.Description, analyzer.GetAnalyzers(), nil).WithLoadMode(goanalysis.LoadModeTypesInfo) linterConfig := linter.NewConfig(customLinter) - linterConfig.EnabledByDefault = settings.Enabled - linterConfig.IsSlow = settings.Slow + linterConfig.EnabledByDefault = true + linterConfig.IsSlow = false linterConfig.WithURL(settings.OriginalURL) - if name != linterConfig.Name() { - m.log.Warnf("Configuration linter name %s doesn't match plugin linter name %s", name, linterConfig.Name()) - } return linterConfig, nil } type AnalyzerPlugin interface { - GetLinterName() string - GetLinterDesc() string GetAnalyzers() []*analysis.Analyzer } From ba0c6ddd069d8d2f0772be9ae01078761c227069 Mon Sep 17 00:00:00 2001 From: David Braley Date: Mon, 11 Nov 2019 12:44:54 -0500 Subject: [PATCH 15/15] Regenerate README.md --- README.md | 71 +++++++++++++++++++++----------------------------- README.tmpl.md | 19 +++++++++----- 2 files changed, 42 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 1bda5e013676..1f0e012f7184 100644 --- a/README.md +++ b/README.md @@ -831,17 +831,14 @@ linters-settings: # The custom section can be used to define linter plugins to be loaded at runtime. See README doc # for more info. custom: - # Each custom linter should have a name. This name should match with what the linter reports for itself. + # Each custom linter should have a unique name. example: - # The path to the plugin *.so. Required for each custom linter + # The path to the plugin *.so. Can be absolute or local. Required for each custom linter path: /path/to/example.so - # If the linter should be enabled by default. Options in the `linters` - # section override this, including `disable-all`. Optional, false by default. - enabled: true + # The description of the linter. Optional, just for documentation purposes. + description: This is an example usage of a plugin linter. # Intended to point to the repo location of the linter. Optional, just for documentation purposes. - original-url: github.com/dbraley/example-linter - # Indicates if this linter should be considered slow. Optional, false by default. - slow: false + original-url: github.com/golangci/example-linter linters: enable: @@ -1050,56 +1047,48 @@ is supported through go's plugin library. In order to use plugins, you'll need a golangci-lint executable that can run them. The normal version of this project is built with the vendors option, which breaks plugins that have overlapping dependencies. -1. Download the `i473` branch of https://github.com/dbraley/golangci-lint/tree/i473 -2. From that projects root directory, run `make vendor_free_build` +1. Download [golangci-lint](https://github.com/golangci/golangci-lint) source code +2. From the projects root directory, run `make vendor_free_build` 3. Copy the `golangci-lint` executable that was created to your path, project, or other location ### Configure Your Project for Linting If you already have a linter plugin available, you can follow these steps to define it's usage in a projects -`.golangci.yml` file. An example linter can be found at https://github.com/dbraley/example-linter. If you're looking for +`.golangci.yml` file. An example linter can be found at [here](https://github.com/golangci/example-plugin-linter). If you're looking for instructions on how to configure your own custom linter, they can be found further down. -1. If the project you want to lint does not have one already, copy the https://github.com/dbraley/golangci-lint/blob/i473/.golangci.yml to the root directory. +1. If the project you want to lint does not have one already, copy the [.golangci.yml](https://github.com/golangci/golangci-lint/blob/master/.golangci.yml) to the root directory. 2. Adjust the yaml to appropriate `linters-settings:custom` entries as so: ``` linters-settings: custom: - example: # If this doesn't match the linters name definition, it will warn you but still run - path: /example.so # Adjust this the location of the plugin - enabled: true # This determines if the linter is run by default - original-url: github.com/dbraley/example-linter # This is just documentation for custom linters - slow: false # Set this to true to observe `--fast` option + example: + path: /example.so + description: The description of the linter + original-url: github.com/golangci/example-linter ``` -3. If your `.golangci.yml` file has `linters:disable-all` set to true, you'll also need to add your linter to the `linters:enable` seciont: -``` -linters: - enable: - # ... - - varcheck - - whitespace - # Custom linters - - example -``` -4. Alternately, you can leave it disabled and turn it on via command line only: `golangci-lint run -Eexample` -5. If everything works correctly, you should see the linter show up as enabled by running `golangci-lint linters`. This linter will look for `// TODO: ` in front of functions, so you can also add that to your code and see the errors. -6. You can also see the linter get loaded with the `-v` option for either `run` or `linters` -### To Configure Your Own `golang.org/x/tools/go/analysis` Based Linter +That is all the configuration that is required to run a custom linter in your project. Custom linters are enabled by default, +but abide by the same rules as other linters. If the disable all option is specified either on command line or in +`.golang.yml` files `linters:disable-all: true`, custom linters will be disabled; they can be re-enabled by adding them +to the `linters:enable` list, or providing the enabled option on the command line, `golangci-lint run -Eexample`. -Your Linter must implement one or more `analysis.Analyzer` structs. -Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. +### To Create Your Own Custom Linter -You'll also want to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define: -1. A variable of type `analyzerPlugin`. The type `analyzerPlugin` can be defined as a string, struct, whatever. +Your linter must implement one or more `golang.org/x/tools/go/analysis.Analyzer` structs. +Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced +libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. + +You'll also need to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define a +variable of name `AnalyzerPlugin`. The `AnalyzerPlugin` instance MUST implement the following interface: ``` -type analyzerPlugin struct{} -var AnalyzerPlugin analyzerPlugin +type AnalyzerPlugin interface { + GetAnalyzers() []*analysis.Analyzer +} ``` -2. A function with signature `func (*analyzerPlugin) GetLinterName() string` -3. A function with signature `func (*analyzerPlugin) GetLinterDesc() string` -4. A function with signature `func (*analyzerPlugin) GetAnalyzers() []*analysis.Analyzer` +The type of `AnalyzerPlugin` is not important, but is by convention `type analyzerPlugin struct {}`. See +[plugin/example.go](https://github.com/golangci/example-plugin-linter/plugin/example.go) for more info. -From the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` +To build the plugin, from the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` file that can be copied into your project or another well known location for usage in golangci-lint. ## False Positives diff --git a/README.tmpl.md b/README.tmpl.md index 23fa13e6e111..e39286d6b8b9 100644 --- a/README.tmpl.md +++ b/README.tmpl.md @@ -461,7 +461,7 @@ is supported through go's plugin library. In order to use plugins, you'll need a golangci-lint executable that can run them. The normal version of this project is built with the vendors option, which breaks plugins that have overlapping dependencies. -1. Download https://github.com/golangci/golangci-lint +1. Download [golangci-lint](https://github.com/golangci/golangci-lint) source code 2. From the projects root directory, run `make vendor_free_build` 3. Copy the `golangci-lint` executable that was created to your path, project, or other location @@ -482,20 +482,25 @@ linters-settings: ``` That is all the configuration that is required to run a custom linter in your project. Custom linters are enabled by default, -but abide by the same rules as other linters. If the `.golang.yml` file specifies `linters:disable-all: true` +but abide by the same rules as other linters. If the disable all option is specified either on command line or in +`.golang.yml` files `linters:disable-all: true`, custom linters will be disabled; they can be re-enabled by adding them +to the `linters:enable` list, or providing the enabled option on the command line, `golangci-lint run -Eexample`. -### To Create Your Own Custom `golang.org/x/tools/go/analysis` Based Linter +### To Create Your Own Custom Linter -Your linter must implement one or more `analysis.Analyzer` structs. -Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. +Your linter must implement one or more `golang.org/x/tools/go/analysis.Analyzer` structs. +Your project should also use `go.mod`. All versions of libraries that overlap `golangci-lint` (including replaced +libraries) MUST be set to the same version as `golangci-lint`. You can see the versions by running `go version -m golangci-lint`. -You'll also need to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define a variable of Named `AnalyzerPlugin`. The `AnalyzerPlugin` MUST implement the following interface: +You'll also need to create a go file like `plugin/example.go`. This MUST be in the package `main`, and define a +variable of name `AnalyzerPlugin`. The `AnalyzerPlugin` instance MUST implement the following interface: ``` type AnalyzerPlugin interface { GetAnalyzers() []*analysis.Analyzer } ``` -The type `AnalyzerPlugin` is not important, but is by convention of `type analyzerPlugin struct {}`. See [plugin/example.go](https://github.com/golangci/example-plugin-linter/plugin/example.go) for more info. +The type of `AnalyzerPlugin` is not important, but is by convention `type analyzerPlugin struct {}`. See +[plugin/example.go](https://github.com/golangci/example-plugin-linter/plugin/example.go) for more info. To build the plugin, from the root project directory, run `go build -buildmode=plugin plugin/example.go`. This will create a plugin `*.so` file that can be copied into your project or another well known location for usage in golangci-lint.