Repository: bombsimon/wsl
Pull request #116 (2023-02-05 18:00:15)

Allow more cuddling with with go routines
Don't be as strict with cuddling go stmts. A lot of the time a go routine will be called on a type called or assigned on the line above or on an assignment on the line above will be used in the go routine call.

Relates to #100 


Patch:
diff --git a/wsl.go b/wsl.go
index 01cc9dc..29f75b6 100644
--- a/wsl.go
+++ b/wsl.go
@@ -247,7 +247,6 @@ func (p *Processor) ProcessFiles(filenames []string) ([]Result, []string) {
 func (p *Processor) process(filename string, data []byte) {
 	fileSet := token.NewFileSet()
 	file, err := parser.ParseFile(fileSet, filename, data, parser.ParseComments)
-
 	// If the file is not parsable let's add a syntax error and move on.
 	if err != nil {
 		p.result = append(p.result, Result{
@@ -335,7 +334,7 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 		var calledOnLineAbove []string
 
 		// Check if the previous statement spans over multiple lines.
-		var cuddledWithMultiLineAssignment = cuddledWithLastStmt && p.nodeStart(previousStatement) != p.nodeStart(stmt)-1
+		cuddledWithMultiLineAssignment := cuddledWithLastStmt && p.nodeStart(previousStatement) != p.nodeStart(stmt)-1
 
 		// Ensure previous line is not a multi line assignment and if not get
 		// rightAndLeftHandSide assigned variables.
@@ -672,6 +671,22 @@ func (p *Processor) parseBlockStatements(statements []ast.Stmt) {
 				continue
 			}
 
+			if c, ok := t.Call.Fun.(*ast.SelectorExpr); ok {
+				goCallArgs := append(p.findLHS(c.X), p.findRHS(c.X)...)
+
+				if atLeastOneInListsMatch(calledOnLineAbove, goCallArgs) {
+					continue
+				}
+			}
+
+			if c, ok := t.Call.Fun.(*ast.FuncLit); ok {
+				goCallArgs := append(p.findLHS(c.Body), p.findRHS(c.Body)...)
+
+				if atLeastOneInListsMatch(assignedOnLineAbove, goCallArgs) {
+					continue
+				}
+			}
+
 			if !atLeastOneInListsMatch(rightAndLeftHandSide, assignedOnLineAbove) {
 				p.addError(t.Pos(), reasonGoFuncWithoutAssign)
 			}
@@ -1207,7 +1222,7 @@ func (p *Processor) nodeStart(node ast.Node) int {
 }
 
 func (p *Processor) nodeEnd(node ast.Node) int {
-	var line = p.fileSet.Position(node.End()).Line
+	line := p.fileSet.Position(node.End()).Line
 
 	if isEmptyLabeledStmt(node) {
 		return p.fileSet.Position(node.Pos()).Line


Test patch:
diff --git a/wsl_test.go b/wsl_test.go
index 10b9a3d..9cc5685 100644
--- a/wsl_test.go
+++ b/wsl_test.go
@@ -1231,6 +1231,59 @@ func TestShouldAddEmptyLines(t *testing.T) {
 				}()
 			}`),
 		},
+		{
+			description: "go stmt can be cuddled if used in call",
+			code: []byte(`package main
+
+			func main() {
+				wg := sync.WaitGroup{}
+				go func(wg) {
+					defer wg.Done()
+
+					fmt.Println("cuddling correctly")
+				}()
+
+				one := 1
+				go func(wg) {
+					defer wg.Done()
+
+					fmt.Println("cuddling faulty")
+				}()
+
+				two := 2
+				go someFunc(two)
+
+				wg.Wait()
+
+				_ = one
+				_ = two
+			}`),
+			expectedErrorStrings: []string{"go statements can only invoke functions assigned on line above"},
+		},
+		{
+			description: "calling go stmt on type used above should be ok",
+			code: []byte(`package main
+
+			type A struct {
+				wg sync.WaitGrop
+			}
+
+			func (a *A) doWork() {
+				fmt.Println("doing work")
+				a.wg.Done()
+			}
+
+			func main() {
+				a := A{
+					wg: sync.WaitGroup{},
+				}
+
+				a.wg.Add(1)
+				go a.doWork()
+
+				wg.Done()
+			}`),
+		},
 	}
 
 	for _, tc := range cases {
@@ -1574,7 +1627,7 @@ func TestWithConfig(t *testing.T) {
 				once.Do(func() {
 					err = ProduceError()
 				})
-			
+
 				if err != nil {
 					return nil, err
 				}
@@ -1905,7 +1958,7 @@ func TestWithConfig(t *testing.T) {
 				c := 3
 				b = 2
 
-				// should fail 
+				// should fail
 				err := DoSomething()
 				if err != nil {
 					b = 4