From a24cc87a062433d8b6177e455813446e7b3f148a Mon Sep 17 00:00:00 2001
From: Denis Isaev <denis@golangci.com>
Date: Sat, 1 Sep 2018 11:01:17 +0300
Subject: [PATCH] refactor lintersdb: split it into abstractions

---
 pkg/commands/executor.go                      |  17 +-
 pkg/commands/help.go                          |   7 +-
 pkg/commands/linters.go                       |   5 +-
 pkg/commands/run.go                           |  12 +-
 pkg/lint/lintersdb/enabled_set.go             | 148 ++++++++++
 ...{lintersdb_test.go => enabled_set_test.go} |   6 +-
 .../lintersdb/{lintersdb.go => manager.go}    | 255 ++----------------
 pkg/lint/lintersdb/validator.go               |  93 +++++++
 8 files changed, 291 insertions(+), 252 deletions(-)
 create mode 100644 pkg/lint/lintersdb/enabled_set.go
 rename pkg/lint/lintersdb/{lintersdb_test.go => enabled_set_test.go} (89%)
 rename pkg/lint/lintersdb/{lintersdb.go => manager.go} (50%)
 create mode 100644 pkg/lint/lintersdb/validator.go

diff --git a/pkg/commands/executor.go b/pkg/commands/executor.go
index b54aad26..ecd23a33 100644
--- a/pkg/commands/executor.go
+++ b/pkg/commands/executor.go
@@ -2,6 +2,7 @@ package commands
 
 import (
 	"github.com/golangci/golangci-lint/pkg/config"
+	"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
 	"github.com/golangci/golangci-lint/pkg/logutils"
 	"github.com/golangci/golangci-lint/pkg/report"
 	"github.com/spf13/cobra"
@@ -10,15 +11,14 @@ import (
 type Executor struct {
 	rootCmd *cobra.Command
 
-	cfg *config.Config
-
-	exitCode int
-
+	exitCode              int
 	version, commit, date string
 
-	log logutils.Log
-
-	reportData report.Data
+	cfg               *config.Config
+	log               logutils.Log
+	reportData        report.Data
+	DBManager         *lintersdb.Manager
+	EnabledLintersSet *lintersdb.EnabledSet
 }
 
 func NewExecutor(version, commit, date string) *Executor {
@@ -30,6 +30,9 @@ func NewExecutor(version, commit, date string) *Executor {
 	}
 
 	e.log = report.NewLogWrapper(logutils.NewStderrLog(""), &e.reportData)
+	e.DBManager = lintersdb.NewManager()
+	e.EnabledLintersSet = lintersdb.NewEnabledSet(e.DBManager, &lintersdb.Validator{},
+		e.log.Child("lintersdb"), e.cfg)
 
 	e.initRoot()
 	e.initRun()
diff --git a/pkg/commands/help.go b/pkg/commands/help.go
index 61357748..984b9aa8 100644
--- a/pkg/commands/help.go
+++ b/pkg/commands/help.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/fatih/color"
 	"github.com/golangci/golangci-lint/pkg/lint/linter"
-	"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
 	"github.com/golangci/golangci-lint/pkg/logutils"
 	"github.com/spf13/cobra"
 )
@@ -41,7 +40,7 @@ func printLinterConfigs(lcs []linter.Config) {
 
 func (e Executor) executeLintersHelp(cmd *cobra.Command, args []string) {
 	var enabledLCs, disabledLCs []linter.Config
-	for _, lc := range lintersdb.GetAllSupportedLinterConfigs() {
+	for _, lc := range e.DBManager.GetAllSupportedLinterConfigs() {
 		if lc.EnabledByDefault {
 			enabledLCs = append(enabledLCs, lc)
 		} else {
@@ -55,8 +54,8 @@ func (e Executor) executeLintersHelp(cmd *cobra.Command, args []string) {
 	printLinterConfigs(disabledLCs)
 
 	color.Green("\nLinters presets:")
-	for _, p := range lintersdb.AllPresets() {
-		linters := lintersdb.GetAllLinterConfigsForPreset(p)
+	for _, p := range e.DBManager.AllPresets() {
+		linters := e.DBManager.GetAllLinterConfigsForPreset(p)
 		linterNames := []string{}
 		for _, lc := range linters {
 			linterNames = append(linterNames, lc.Linter.Name())
diff --git a/pkg/commands/linters.go b/pkg/commands/linters.go
index f559d973..f544e8c0 100644
--- a/pkg/commands/linters.go
+++ b/pkg/commands/linters.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/fatih/color"
 	"github.com/golangci/golangci-lint/pkg/lint/linter"
-	"github.com/golangci/golangci-lint/pkg/lint/lintersdb"
 	"github.com/spf13/cobra"
 )
 
@@ -31,7 +30,7 @@ func IsLinterInConfigsList(name string, linters []linter.Config) bool {
 }
 
 func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
-	enabledLCs, err := lintersdb.GetEnabledLinters(e.cfg, e.log.Child("lintersdb"))
+	enabledLCs, err := e.EnabledLintersSet.Get()
 	if err != nil {
 		log.Fatalf("Can't get enabled linters: %s", err)
 	}
@@ -40,7 +39,7 @@ func (e Executor) executeLinters(cmd *cobra.Command, args []string) {
 	printLinterConfigs(enabledLCs)
 
 	var disabledLCs []linter.Config
-	for _, lc := range lintersdb.GetAllSupportedLinterConfigs() {
+	for _, lc := range e.DBManager.GetAllSupportedLinterConfigs() {
 		if !IsLinterInConfigsList(lc.Linter.Name(), enabledLCs) {
 			disabledLCs = append(disabledLCs, lc)
 		}
diff --git a/pkg/commands/run.go b/pkg/commands/run.go
index ab9e2136..49a49296 100644
--- a/pkg/commands/run.go
+++ b/pkg/commands/run.go
@@ -40,7 +40,7 @@ func wh(text string) string {
 	return color.GreenString(text)
 }
 
-func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
+func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager) {
 	hideFlag := func(name string) {
 		if err := fs.MarkHidden(name); err != nil {
 			panic(err)
@@ -137,7 +137,7 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
 	fs.BoolVar(&lc.DisableAll, "disable-all", false, wh("Disable all linters"))
 	fs.StringSliceVarP(&lc.Presets, "presets", "p", nil,
 		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(), "|"))))
+			"them. This option implies option --disable-all", strings.Join(m.AllPresets(), "|"))))
 	fs.BoolVar(&lc.Fast, "fast", false, wh("Run only fast linters from enabled linters set"))
 
 	// Issues config
@@ -167,7 +167,7 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config) {
 func (e *Executor) initRunConfiguration(cmd *cobra.Command) {
 	fs := cmd.Flags()
 	fs.SortFlags = false // sort them as they are defined here
-	initFlagSet(fs, e.cfg)
+	initFlagSet(fs, e.cfg, e.DBManager)
 
 	// init e.cfg by values from config: flags parse will see these values
 	// like the default ones. It will overwrite them only if the same option
@@ -178,7 +178,7 @@ func (e *Executor) initRunConfiguration(cmd *cobra.Command) {
 		// `changed` variable inside string slice vars will be shared.
 		// Use another config variable here, not e.cfg, to not
 		// affect main parsing by this parsing of only config option.
-		initFlagSet(fs, cfg)
+		initFlagSet(fs, cfg, e.DBManager)
 
 		// Parse max options, even force version option: don't want
 		// to get access to Executor here: it's error-prone to use
@@ -232,12 +232,12 @@ func fixSlicesFlags(fs *pflag.FlagSet) {
 func (e *Executor) runAnalysis(ctx context.Context, args []string) (<-chan result.Issue, error) {
 	e.cfg.Run.Args = args
 
-	linters, err := lintersdb.GetEnabledLinters(e.cfg, e.log.Child("lintersdb"))
+	linters, err := e.EnabledLintersSet.Get()
 	if err != nil {
 		return nil, err
 	}
 
-	for _, lc := range lintersdb.GetAllSupportedLinterConfigs() {
+	for _, lc := range e.DBManager.GetAllSupportedLinterConfigs() {
 		isEnabled := false
 		for _, linter := range linters {
 			if linter.Linter.Name() == lc.Linter.Name() {
diff --git a/pkg/lint/lintersdb/enabled_set.go b/pkg/lint/lintersdb/enabled_set.go
new file mode 100644
index 00000000..14d36168
--- /dev/null
+++ b/pkg/lint/lintersdb/enabled_set.go
@@ -0,0 +1,148 @@
+package lintersdb
+
+import (
+	"sort"
+
+	"github.com/golangci/golangci-lint/pkg/config"
+	"github.com/golangci/golangci-lint/pkg/golinters"
+	"github.com/golangci/golangci-lint/pkg/lint/linter"
+	"github.com/golangci/golangci-lint/pkg/logutils"
+)
+
+type EnabledSet struct {
+	m   *Manager
+	v   *Validator
+	log logutils.Log
+	cfg *config.Config
+}
+
+func NewEnabledSet(m *Manager, v *Validator, log logutils.Log, cfg *config.Config) *EnabledSet {
+	return &EnabledSet{
+		m:   m,
+		v:   v,
+		log: log,
+		cfg: cfg,
+	}
+}
+
+// nolint:gocyclo
+func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []linter.Config) map[string]*linter.Config {
+	resultLintersSet := map[string]*linter.Config{}
+	switch {
+	case len(lcfg.Presets) != 0:
+		break // imply --disable-all
+	case lcfg.EnableAll:
+		resultLintersSet = linterConfigsToMap(es.m.GetAllSupportedLinterConfigs())
+	case lcfg.DisableAll:
+		break
+	default:
+		resultLintersSet = linterConfigsToMap(enabledByDefaultLinters)
+	}
+
+	// --presets can only add linters to default set
+	for _, p := range lcfg.Presets {
+		for _, lc := range es.m.GetAllLinterConfigsForPreset(p) {
+			lc := lc
+			resultLintersSet[lc.Linter.Name()] = &lc
+		}
+	}
+
+	// --fast removes slow linters from current set.
+	// It should be after --presets to be able to run only fast linters in preset.
+	// It should be before --enable and --disable to be able to enable or disable specific linter.
+	if lcfg.Fast {
+		for name := range resultLintersSet {
+			if es.m.getLinterConfig(name).DoesFullImport {
+				delete(resultLintersSet, name)
+			}
+		}
+	}
+
+	for _, name := range lcfg.Enable {
+		resultLintersSet[name] = es.m.getLinterConfig(name)
+	}
+
+	for _, name := range lcfg.Disable {
+		if name == "megacheck" {
+			for _, ln := range getAllMegacheckSubLinterNames() {
+				delete(resultLintersSet, ln)
+			}
+		}
+		delete(resultLintersSet, name)
+	}
+
+	es.optimizeLintersSet(resultLintersSet)
+	return resultLintersSet
+}
+
+func getAllMegacheckSubLinterNames() []string {
+	unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
+	gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
+	staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
+	return []string{unusedName, gosimpleName, staticcheckName}
+}
+
+func (es EnabledSet) optimizeLintersSet(linters map[string]*linter.Config) {
+	unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
+	gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
+	staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
+	fullName := golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}.Name()
+	allNames := []string{unusedName, gosimpleName, staticcheckName, fullName}
+
+	megacheckCount := 0
+	for _, n := range allNames {
+		if linters[n] != nil {
+			megacheckCount++
+		}
+	}
+
+	if megacheckCount <= 1 {
+		return
+	}
+
+	isFullEnabled := linters[fullName] != nil
+	mega := golinters.Megacheck{
+		UnusedEnabled:      isFullEnabled || linters[unusedName] != nil,
+		GosimpleEnabled:    isFullEnabled || linters[gosimpleName] != nil,
+		StaticcheckEnabled: isFullEnabled || linters[staticcheckName] != nil,
+	}
+
+	for _, n := range allNames {
+		delete(linters, n)
+	}
+
+	lc := *es.m.getLinterConfig("megacheck")
+	lc.Linter = mega
+	linters[mega.Name()] = &lc
+}
+
+func (es EnabledSet) Get() ([]linter.Config, error) {
+	if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil {
+		return nil, err
+	}
+
+	resultLintersSet := es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters())
+
+	var resultLinters []linter.Config
+	for _, lc := range resultLintersSet {
+		resultLinters = append(resultLinters, *lc)
+	}
+
+	es.verbosePrintLintersStatus(resultLinters)
+
+	return resultLinters, nil
+}
+
+func (es EnabledSet) verbosePrintLintersStatus(lcs []linter.Config) {
+	var linterNames []string
+	for _, lc := range lcs {
+		linterNames = append(linterNames, lc.Linter.Name())
+	}
+	sort.StringSlice(linterNames).Sort()
+	es.log.Infof("Active %d linters: %s", len(linterNames), linterNames)
+
+	if len(es.cfg.Linters.Presets) != 0 {
+		sort.StringSlice(es.cfg.Linters.Presets).Sort()
+		es.log.Infof("Active presets: %s", es.cfg.Linters.Presets)
+	}
+}
diff --git a/pkg/lint/lintersdb/lintersdb_test.go b/pkg/lint/lintersdb/enabled_set_test.go
similarity index 89%
rename from pkg/lint/lintersdb/lintersdb_test.go
rename to pkg/lint/lintersdb/enabled_set_test.go
index 7a1205aa..33365171 100644
--- a/pkg/lint/lintersdb/lintersdb_test.go
+++ b/pkg/lint/lintersdb/enabled_set_test.go
@@ -44,13 +44,15 @@ func TestGetEnabledLintersSet(t *testing.T) {
 		},
 	}
 
+	m := NewManager()
+	es := NewEnabledSet(m, &Validator{}, nil, nil)
 	for _, c := range cases {
 		t.Run(c.name, func(t *testing.T) {
 			defaultLinters := []linter.Config{}
 			for _, ln := range c.def {
-				defaultLinters = append(defaultLinters, *getLinterConfig(ln))
+				defaultLinters = append(defaultLinters, *m.getLinterConfig(ln))
 			}
-			els := getEnabledLintersSet(&c.cfg, defaultLinters)
+			els := es.build(&c.cfg, defaultLinters)
 			var enabledLinters []string
 			for ln, lc := range els {
 				assert.Equal(t, ln, lc.Linter.Name())
diff --git a/pkg/lint/lintersdb/lintersdb.go b/pkg/lint/lintersdb/manager.go
similarity index 50%
rename from pkg/lint/lintersdb/lintersdb.go
rename to pkg/lint/lintersdb/manager.go
index d2edcb41..76b24d3d 100644
--- a/pkg/lint/lintersdb/lintersdb.go
+++ b/pkg/lint/lintersdb/manager.go
@@ -1,43 +1,42 @@
 package lintersdb
 
 import (
-	"fmt"
 	"os"
-	"sort"
-	"strings"
-	"sync"
 
-	"github.com/golangci/golangci-lint/pkg/config"
 	"github.com/golangci/golangci-lint/pkg/golinters"
 	"github.com/golangci/golangci-lint/pkg/lint/linter"
-	"github.com/golangci/golangci-lint/pkg/logutils"
 )
 
-func AllPresets() []string {
+type Manager struct {
+	nameToLC map[string]linter.Config
+}
+
+func NewManager() *Manager {
+	m := &Manager{}
+	nameToLC := make(map[string]linter.Config)
+	for _, lc := range m.GetAllSupportedLinterConfigs() {
+		nameToLC[lc.Linter.Name()] = lc
+	}
+
+	m.nameToLC = nameToLC
+	return m
+}
+
+func (Manager) AllPresets() []string {
 	return []string{linter.PresetBugs, linter.PresetUnused, linter.PresetFormatting,
 		linter.PresetStyle, linter.PresetComplexity, linter.PresetPerformance}
 }
 
-func allPresetsSet() map[string]bool {
+func (m Manager) allPresetsSet() map[string]bool {
 	ret := map[string]bool{}
-	for _, p := range AllPresets() {
+	for _, p := range m.AllPresets() {
 		ret[p] = true
 	}
 	return ret
 }
 
-var nameToLC map[string]linter.Config
-var nameToLCOnce sync.Once
-
-func getLinterConfig(name string) *linter.Config {
-	nameToLCOnce.Do(func() {
-		nameToLC = make(map[string]linter.Config)
-		for _, lc := range GetAllSupportedLinterConfigs() {
-			nameToLC[lc.Linter.Name()] = lc
-		}
-	})
-
-	lc, ok := nameToLC[name]
+func (m Manager) getLinterConfig(name string) *linter.Config {
+	lc, ok := m.nameToLC[name]
 	if !ok {
 		return nil
 	}
@@ -55,7 +54,7 @@ func enableLinterConfigs(lcs []linter.Config, isEnabled func(lc *linter.Config)
 	return ret
 }
 
-func GetAllSupportedLinterConfigs() []linter.Config {
+func (Manager) GetAllSupportedLinterConfigs() []linter.Config {
 	lcs := []linter.Config{
 		linter.NewConfig(golinters.Govet{}).
 			WithFullImport(). // TODO: depend on it's configuration here
@@ -207,9 +206,9 @@ func GetAllSupportedLinterConfigs() []linter.Config {
 	})
 }
 
-func GetAllEnabledByDefaultLinters() []linter.Config {
+func (m Manager) GetAllEnabledByDefaultLinters() []linter.Config {
 	var ret []linter.Config
-	for _, lc := range GetAllSupportedLinterConfigs() {
+	for _, lc := range m.GetAllSupportedLinterConfigs() {
 		if lc.EnabledByDefault {
 			ret = append(ret, lc)
 		}
@@ -228,89 +227,9 @@ func linterConfigsToMap(lcs []linter.Config) map[string]*linter.Config {
 	return ret
 }
 
-func validateLintersNames(cfg *config.Linters) error {
-	allNames := append([]string{}, cfg.Enable...)
-	allNames = append(allNames, cfg.Disable...)
-	for _, name := range allNames {
-		if getLinterConfig(name) == nil {
-			return fmt.Errorf("no such linter %q", name)
-		}
-	}
-
-	return nil
-}
-
-func validatePresets(cfg *config.Linters) error {
-	allPresets := allPresetsSet()
-	for _, p := range cfg.Presets {
-		if !allPresets[p] {
-			return fmt.Errorf("no such preset %q: only next presets exist: (%s)", p, strings.Join(AllPresets(), "|"))
-		}
-	}
-
-	if len(cfg.Presets) != 0 && cfg.EnableAll {
-		return fmt.Errorf("--presets is incompatible with --enable-all")
-	}
-
-	return nil
-}
-
-func validateAllDisableEnableOptions(cfg *config.Linters) error {
-	if cfg.EnableAll && cfg.DisableAll {
-		return fmt.Errorf("--enable-all and --disable-all options must not be combined")
-	}
-
-	if cfg.DisableAll {
-		if len(cfg.Enable) == 0 && len(cfg.Presets) == 0 {
-			return fmt.Errorf("all linters were disabled, but no one linter was enabled: must enable at least one")
-		}
-
-		if len(cfg.Disable) != 0 {
-			return fmt.Errorf("can't combine options --disable-all and --disable %s", cfg.Disable[0])
-		}
-	}
-
-	if cfg.EnableAll && len(cfg.Enable) != 0 && !cfg.Fast {
-		return fmt.Errorf("can't combine options --enable-all and --enable %s", cfg.Enable[0])
-	}
-
-	return nil
-}
-
-func validateDisabledAndEnabledAtOneMoment(cfg *config.Linters) error {
-	enabledLintersSet := map[string]bool{}
-	for _, name := range cfg.Enable {
-		enabledLintersSet[name] = true
-	}
-
-	for _, name := range cfg.Disable {
-		if enabledLintersSet[name] {
-			return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name)
-		}
-	}
-
-	return nil
-}
-
-func validateEnabledDisabledLintersConfig(cfg *config.Linters) error {
-	validators := []func(cfg *config.Linters) error{
-		validateLintersNames,
-		validatePresets,
-		validateAllDisableEnableOptions,
-		validateDisabledAndEnabledAtOneMoment,
-	}
-	for _, v := range validators {
-		if err := v(cfg); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-func GetAllLinterConfigsForPreset(p string) []linter.Config {
+func (m Manager) GetAllLinterConfigsForPreset(p string) []linter.Config {
 	ret := []linter.Config{}
-	for _, lc := range GetAllSupportedLinterConfigs() {
+	for _, lc := range m.GetAllSupportedLinterConfigs() {
 		for _, ip := range lc.InPresets {
 			if p == ip {
 				ret = append(ret, lc)
@@ -321,127 +240,3 @@ func GetAllLinterConfigsForPreset(p string) []linter.Config {
 
 	return ret
 }
-
-// nolint:gocyclo
-func getEnabledLintersSet(lcfg *config.Linters,
-	enabledByDefaultLinters []linter.Config) map[string]*linter.Config {
-
-	resultLintersSet := map[string]*linter.Config{}
-	switch {
-	case len(lcfg.Presets) != 0:
-		break // imply --disable-all
-	case lcfg.EnableAll:
-		resultLintersSet = linterConfigsToMap(GetAllSupportedLinterConfigs())
-	case lcfg.DisableAll:
-		break
-	default:
-		resultLintersSet = linterConfigsToMap(enabledByDefaultLinters)
-	}
-
-	// --presets can only add linters to default set
-	for _, p := range lcfg.Presets {
-		for _, lc := range GetAllLinterConfigsForPreset(p) {
-			lc := lc
-			resultLintersSet[lc.Linter.Name()] = &lc
-		}
-	}
-
-	// --fast removes slow linters from current set.
-	// It should be after --presets to be able to run only fast linters in preset.
-	// It should be before --enable and --disable to be able to enable or disable specific linter.
-	if lcfg.Fast {
-		for name := range resultLintersSet {
-			if getLinterConfig(name).DoesFullImport {
-				delete(resultLintersSet, name)
-			}
-		}
-	}
-
-	for _, name := range lcfg.Enable {
-		resultLintersSet[name] = getLinterConfig(name)
-	}
-
-	for _, name := range lcfg.Disable {
-		if name == "megacheck" {
-			for _, ln := range getAllMegacheckSubLinterNames() {
-				delete(resultLintersSet, ln)
-			}
-		}
-		delete(resultLintersSet, name)
-	}
-
-	optimizeLintersSet(resultLintersSet)
-	return resultLintersSet
-}
-
-func getAllMegacheckSubLinterNames() []string {
-	unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
-	gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
-	staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
-	return []string{unusedName, gosimpleName, staticcheckName}
-}
-
-func optimizeLintersSet(linters map[string]*linter.Config) {
-	unusedName := golinters.Megacheck{UnusedEnabled: true}.Name()
-	gosimpleName := golinters.Megacheck{GosimpleEnabled: true}.Name()
-	staticcheckName := golinters.Megacheck{StaticcheckEnabled: true}.Name()
-	fullName := golinters.Megacheck{GosimpleEnabled: true, UnusedEnabled: true, StaticcheckEnabled: true}.Name()
-	allNames := []string{unusedName, gosimpleName, staticcheckName, fullName}
-
-	megacheckCount := 0
-	for _, n := range allNames {
-		if linters[n] != nil {
-			megacheckCount++
-		}
-	}
-
-	if megacheckCount <= 1 {
-		return
-	}
-
-	isFullEnabled := linters[fullName] != nil
-	m := golinters.Megacheck{
-		UnusedEnabled:      isFullEnabled || linters[unusedName] != nil,
-		GosimpleEnabled:    isFullEnabled || linters[gosimpleName] != nil,
-		StaticcheckEnabled: isFullEnabled || linters[staticcheckName] != nil,
-	}
-
-	for _, n := range allNames {
-		delete(linters, n)
-	}
-
-	lc := *getLinterConfig("megacheck")
-	lc.Linter = m
-	linters[m.Name()] = &lc
-}
-
-func GetEnabledLinters(cfg *config.Config, log logutils.Log) ([]linter.Config, error) {
-	if err := validateEnabledDisabledLintersConfig(&cfg.Linters); err != nil {
-		return nil, err
-	}
-
-	resultLintersSet := getEnabledLintersSet(&cfg.Linters, GetAllEnabledByDefaultLinters())
-
-	var resultLinters []linter.Config
-	for _, lc := range resultLintersSet {
-		resultLinters = append(resultLinters, *lc)
-	}
-
-	verbosePrintLintersStatus(cfg, resultLinters, log)
-
-	return resultLinters, nil
-}
-
-func verbosePrintLintersStatus(cfg *config.Config, lcs []linter.Config, log logutils.Log) {
-	var linterNames []string
-	for _, lc := range lcs {
-		linterNames = append(linterNames, lc.Linter.Name())
-	}
-	sort.StringSlice(linterNames).Sort()
-	log.Infof("Active %d linters: %s", len(linterNames), linterNames)
-
-	if len(cfg.Linters.Presets) != 0 {
-		sort.StringSlice(cfg.Linters.Presets).Sort()
-		log.Infof("Active presets: %s", cfg.Linters.Presets)
-	}
-}
diff --git a/pkg/lint/lintersdb/validator.go b/pkg/lint/lintersdb/validator.go
new file mode 100644
index 00000000..4ccc5726
--- /dev/null
+++ b/pkg/lint/lintersdb/validator.go
@@ -0,0 +1,93 @@
+package lintersdb
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/golangci/golangci-lint/pkg/config"
+)
+
+type Validator struct {
+	m *Manager
+}
+
+func (v Validator) validateLintersNames(cfg *config.Linters) error {
+	allNames := append([]string{}, cfg.Enable...)
+	allNames = append(allNames, cfg.Disable...)
+	for _, name := range allNames {
+		if v.m.getLinterConfig(name) == nil {
+			return fmt.Errorf("no such linter %q", name)
+		}
+	}
+
+	return nil
+}
+
+func (v Validator) validatePresets(cfg *config.Linters) error {
+	allPresets := v.m.allPresetsSet()
+	for _, p := range cfg.Presets {
+		if !allPresets[p] {
+			return fmt.Errorf("no such preset %q: only next presets exist: (%s)",
+				p, strings.Join(v.m.AllPresets(), "|"))
+		}
+	}
+
+	if len(cfg.Presets) != 0 && cfg.EnableAll {
+		return fmt.Errorf("--presets is incompatible with --enable-all")
+	}
+
+	return nil
+}
+
+func (v Validator) validateAllDisableEnableOptions(cfg *config.Linters) error {
+	if cfg.EnableAll && cfg.DisableAll {
+		return fmt.Errorf("--enable-all and --disable-all options must not be combined")
+	}
+
+	if cfg.DisableAll {
+		if len(cfg.Enable) == 0 && len(cfg.Presets) == 0 {
+			return fmt.Errorf("all linters were disabled, but no one linter was enabled: must enable at least one")
+		}
+
+		if len(cfg.Disable) != 0 {
+			return fmt.Errorf("can't combine options --disable-all and --disable %s", cfg.Disable[0])
+		}
+	}
+
+	if cfg.EnableAll && len(cfg.Enable) != 0 && !cfg.Fast {
+		return fmt.Errorf("can't combine options --enable-all and --enable %s", cfg.Enable[0])
+	}
+
+	return nil
+}
+
+func (v Validator) validateDisabledAndEnabledAtOneMoment(cfg *config.Linters) error {
+	enabledLintersSet := map[string]bool{}
+	for _, name := range cfg.Enable {
+		enabledLintersSet[name] = true
+	}
+
+	for _, name := range cfg.Disable {
+		if enabledLintersSet[name] {
+			return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name)
+		}
+	}
+
+	return nil
+}
+
+func (v Validator) validateEnabledDisabledLintersConfig(cfg *config.Linters) error {
+	validators := []func(cfg *config.Linters) error{
+		v.validateLintersNames,
+		v.validatePresets,
+		v.validateAllDisableEnableOptions,
+		v.validateDisabledAndEnabledAtOneMoment,
+	}
+	for _, v := range validators {
+		if err := v(cfg); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}