From 750309d1574e9a4afc32346364f9e4ae36955692 Mon Sep 17 00:00:00 2001
From: Eugene Simonov <esimonov@users.noreply.github.com>
Date: Thu, 21 Jan 2021 00:07:43 +0200
Subject: [PATCH] Add ifshort linter (#1587)

---
 .golangci.example.yml         |  6 ++++++
 go.mod                        |  1 +
 go.sum                        |  3 +++
 pkg/config/config.go          |  6 ++++++
 pkg/golinters/ifshort.go      | 17 +++++++++++++++++
 pkg/lint/lintersdb/manager.go |  3 +++
 test/testdata/ifshort.go      | 11 +++++++++++
 7 files changed, 47 insertions(+)
 create mode 100644 pkg/golinters/ifshort.go
 create mode 100644 test/testdata/ifshort.go

diff --git a/.golangci.example.yml b/.golangci.example.yml
index 45a28dbcc944..026d195ff01a 100644
--- a/.golangci.example.yml
+++ b/.golangci.example.yml
@@ -293,6 +293,12 @@ linters-settings:
     packages-with-error-message:
       # specify an error message to output when a blacklisted package is used
       - github.com/sirupsen/logrus: "logging is allowed only by logutils.Log"
+  ifshort:
+    # Maximum length of variable declaration measured in number of lines, after which linter won't suggest using short syntax. 
+    # Has higher priority than max-decl-chars.
+    max-decl-lines: 1
+    # Maximum length of variable declaration measured in number of characters, after which linter won't suggest using short syntax.
+    max-decl-chars: 30
   lll:
     # max line length, lines longer will be reported. Default is 120.
     # '\t' is counted as 1 character by default, and can be changed with the tab-width option
diff --git a/go.mod b/go.mod
index 505631ecd2f2..7b9c6616a261 100644
--- a/go.mod
+++ b/go.mod
@@ -11,6 +11,7 @@ require (
 	github.com/bombsimon/wsl/v3 v3.1.0
 	github.com/daixiang0/gci v0.2.8
 	github.com/denis-tingajkin/go-header v0.4.2
+	github.com/esimonov/ifshort v1.0.0
 	github.com/fatih/color v1.10.0
 	github.com/go-critic/go-critic v0.5.3
 	github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b
diff --git a/go.sum b/go.sum
index 761cd0318ac4..e53660385b14 100644
--- a/go.sum
+++ b/go.sum
@@ -59,6 +59,8 @@ github.com/denis-tingajkin/go-header v0.4.2 h1:jEeSF4sdv8/3cT/WY8AgDHUoItNSoEZ7q
 github.com/denis-tingajkin/go-header v0.4.2/go.mod h1:eLRHAVXzE5atsKAnNRDB90WHCFFnBUn4RN0nRcs1LJA=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/esimonov/ifshort v1.0.0 h1:mcOSoOMVtL4tJyyDTakunR+KFQUywLLAVesiWleGPHU=
+github.com/esimonov/ifshort v1.0.0/go.mod h1:yZqNJUrNn20K8Q9n2CrjTKYyVEmX209Hgu+M1LBpeZE=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
 github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
@@ -579,6 +581,7 @@ golang.org/x/tools v0.0.0-20201001104356-43ebab892c4c/go.mod h1:z6u4i615ZeAfBE4X
 golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
 golang.org/x/tools v0.0.0-20201011145850-ed2f50202694/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201030010431-2feb2bb1ff51/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394 h1:O3VD5Fds21mB1WVRTbkiz/HTXESx6Jql5ucPZi69oiM=
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 4281f75f28bd..2e95c164deb2 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -270,6 +270,7 @@ type LintersSettings struct {
 	Makezero    MakezeroSettings
 	Thelper     ThelperSettings
 	Forbidigo   ForbidigoSettings
+	Ifshort     IfshortSettings
 	Predeclared PredeclaredSettings
 
 	Custom map[string]CustomLinterSettings
@@ -408,6 +409,11 @@ type ThelperSettings struct {
 	} `mapstructure:"benchmark"`
 }
 
+type IfshortSettings struct {
+	MaxDeclLines int `mapstructure:"max-decl-lines"`
+	MaxDeclChars int `mapstructure:"max-decl-chars"`
+}
+
 type ForbidigoSettings struct {
 	Forbid []string `mapstructure:"forbid"`
 }
diff --git a/pkg/golinters/ifshort.go b/pkg/golinters/ifshort.go
new file mode 100644
index 000000000000..cb54a2fcbb96
--- /dev/null
+++ b/pkg/golinters/ifshort.go
@@ -0,0 +1,17 @@
+package golinters
+
+import (
+	"github.com/esimonov/ifshort/pkg/analyzer"
+	"golang.org/x/tools/go/analysis"
+
+	"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
+)
+
+func NewIfshort() *goanalysis.Linter {
+	return goanalysis.NewLinter(
+		"ifshort",
+		"Checks that your code uses short syntax for if-statements whenever possible",
+		[]*analysis.Analyzer{analyzer.Analyzer},
+		nil,
+	).WithLoadMode(goanalysis.LoadModeSyntax)
+}
diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go
index 510b834b32bb..fb399617cc64 100644
--- a/pkg/lint/lintersdb/manager.go
+++ b/pkg/lint/lintersdb/manager.go
@@ -344,6 +344,9 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
 		linter.NewConfig(golinters.NewForbidigo()).
 			WithPresets(linter.PresetStyle).
 			WithURL("https://github.com/ashanbrown/forbidigo"),
+		linter.NewConfig(golinters.NewIfshort()).
+			WithPresets(linter.PresetStyle).
+			WithURL("https://github.com/esimonov/ifshort"),
 		linter.NewConfig(golinters.NewPredeclared(predeclaredCfg)).
 			WithPresets(linter.PresetStyle).
 			WithURL("https://github.com/nishanths/predeclared"),
diff --git a/test/testdata/ifshort.go b/test/testdata/ifshort.go
new file mode 100644
index 000000000000..448636f0cd6f
--- /dev/null
+++ b/test/testdata/ifshort.go
@@ -0,0 +1,11 @@
+//args: -Eifshort
+package testdata
+
+func DontUseShortSyntaxWhenPossible() {
+	getValue := func() interface{} { return nil }
+
+	v := getValue() // ERROR "variable 'v' is only used in the if-statement .*"
+	if v != nil {
+		return
+	}
+}