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 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