Repository: bombsimon/wsl
Pull request #87 (2020-05-13 21:35:15)

Declaration cuddle and multiline error
Extend support for allow cuddle declaration. (#83)
Don't force multiline error cuddling. (#86)

coveralls: 
[![Coverage Status](https://coveralls.io/builds/30771876/badge)](https://coveralls.io/builds/30771876)

Coverage decreased (-0.04%) to 89.396% when pulling **16c82817a19f614f907707600cb35051b57794cd on features** into **526db04f31d23e21ea64dd47c8639fb106999820 on master**.

grongor: ... looks like you found the time to do it first :D Sorry (I've been busy lately :disappointed:) and thanks :)
bombsimon: Yeah I was hoping to get a few more done but I lost a bit of the motivation. :) At least this seems to resolve all the examples we discussed. I'll try to merge this locally and later bump the upstream in `golangci-lint` but I want to keep the PRs down as much as possible and collect a few more issues at he same time. If you're eager to try this out feel fre to build your own `golangci-lint` from master while waiting! :) 

Patch:
diff --git a/wsl.go b/wsl.go
index afbd485..6744dc2 100644
--- a/wsl.go
+++ b/wsl.go
@@ -295,6 +295,7 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 		}
 
 		previousStatement := statements[i-1]
+		previousStatementIsMultiline := p.nodeStart(previousStatement) != p.nodeEnd(previousStatement)
 		cuddledWithLastStmt := p.nodeEnd(previousStatement) == p.nodeStart(stmt)-1
 
 		// If we're not cuddled and we don't need to enforce err-check cuddling
@@ -303,6 +304,11 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 			continue
 		}
 
+		// We don't force error cuddling for multilines. (#86)
+		if p.config.ForceCuddleErrCheckAndAssign && previousStatementIsMultiline && !cuddledWithLastStmt {
+			continue
+		}
+
 		// Extract assigned variables on the line above
 		// which is the only thing we allow cuddling with. If the assignment is
 		// made over multiple lines we should not allow cuddling.
@@ -485,6 +491,10 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 				continue
 			}
 
+			if _, ok := previousStatement.(*ast.DeclStmt); ok && p.config.AllowCuddleDeclaration {
+				continue
+			}
+
 			// If the assignment is from a type or variable called on the line
 			// above we can allow it by setting AllowAssignAndCallCuddle to
 			// true.
@@ -505,6 +515,10 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 		case *ast.ExprStmt:
 			switch previousStatement.(type) {
 			case *ast.DeclStmt, *ast.ReturnStmt:
+				if p.config.AllowAssignAndCallCuddle && p.config.AllowCuddleDeclaration {
+					continue
+				}
+
 				p.addError(t.Pos(), reasonExpressionCuddledWithDeclOrRet)
 			case *ast.IfStmt, *ast.RangeStmt, *ast.SwitchStmt:
 				p.addError(t.Pos(), reasonExpressionCuddledWithBlock)


Test patch:
diff --git a/wsl_test.go b/wsl_test.go
index 0c5820a..dfc9367 100644
--- a/wsl_test.go
+++ b/wsl_test.go
@@ -1621,6 +1621,30 @@ func TestWithConfig(t *testing.T) {
 			}`),
 			expectedErrorStrings: []string{},
 		},
+		{
+			description: "err-check cuddle enforcement multiline",
+			customConfig: &Configuration{
+				AllowAssignAndCallCuddle:     false,
+				ForceCuddleErrCheckAndAssign: true,
+				ErrorVariableNames:           []string{"err"},
+			},
+			code: []byte(`package main
+
+			import "errors"
+
+			func main() {
+				err := db.
+					Where("foo = ?", bar).
+					Preload("More").
+					Find(&x).
+					Error
+
+				if err != nil {
+					panic(err)
+				}
+			}`),
+			expectedErrorStrings: []string{},
+		},
 		{
 			description: "allow separated leading comment",
 			customConfig: &Configuration{
@@ -1718,6 +1742,56 @@ func TestWithConfig(t *testing.T) {
 			}`),
 			expectedErrorStrings: []string{reasonOnlyCuddleIfWithAssign},
 		},
+		{
+			description: "allow cuddling of declaration (#83)",
+			code: []byte(`package main
+
+			func main() {
+				var first = 1
+				var second = 2
+				third := 3
+
+				var config Configuration
+				if err := conf.Load(config); err != nil {
+					panic(err)
+				}
+
+				var config Configuration
+				conf.Load(&config)
+
+				var config Configuration
+				err = json.Unmarshal(body, &config)
+
+				var notUsed bool
+				err = json.Unmarshal(body, &something)
+
+				var x = 1
+				if x > 0 {
+					// ...
+				}
+
+				// This one fails because we're not using y in the if statement.
+				var y = 1
+				if first > 0 {
+					// ...
+				}
+
+				// This one fails because we cuddled too many
+				var z = 1
+				var zz = 2
+				if zz > z {
+					// ...
+				}
+			}`),
+			customConfig: &Configuration{
+				AllowAssignAndCallCuddle: true,
+				AllowCuddleDeclaration:   true,
+			},
+			expectedErrorStrings: []string{
+				reasonOnlyCuddleWithUsedAssign,
+				reasonOnlyOneCuddle,
+			},
+		},
 	}
 
 	for _, tc := range cases {