From fb5bbe72e706ab0cfd5e7135032a52096c66aed2 Mon Sep 17 00:00:00 2001 From: Marcin Federowicz Date: Sat, 30 Sep 2023 10:18:17 +0200 Subject: [PATCH] fix: literal calls case (#909) --- rule/unconditional-recursion.go | 30 +++++++++++++++++++++-------- testdata/unconditional-recursion.go | 16 +++++++++++++-- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/rule/unconditional-recursion.go b/rule/unconditional-recursion.go index bad907533..7e73b72d4 100644 --- a/rule/unconditional-recursion.go +++ b/rule/unconditional-recursion.go @@ -45,8 +45,9 @@ type funcStatus struct { } type lintUnconditionalRecursionRule struct { - onFailure func(lint.Failure) - currentFunc *funcStatus + onFailure func(lint.Failure) + currentFunc *funcStatus + inGoStatement bool } // Visit will traverse the file AST. @@ -68,9 +69,13 @@ func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { default: rec = n.Recv.List[0].Names[0] } - w.currentFunc = &funcStatus{&funcDesc{rec, n.Name}, false} case *ast.CallExpr: + // check if call arguments has a recursive call + for _, arg := range n.Args { + ast.Walk(w, arg) + } + var funcID *ast.Ident var selector *ast.Ident switch c := n.Fun.(type) { @@ -84,6 +89,9 @@ func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { return nil } funcID = c.Sel + case *ast.FuncLit: + ast.Walk(w, c.Body) // analyze the body of the function literal + return nil default: return w } @@ -93,11 +101,12 @@ func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { w.currentFunc.funcDesc.equal(&funcDesc{selector, funcID}) { w.onFailure(lint.Failure{ Category: "logic", - Confidence: 1, + Confidence: 0.8, Node: n, Failure: "unconditional recursive call", }) } + return nil case *ast.IfStmt: w.updateFuncStatus(n.Body) w.updateFuncStatus(n.Else) @@ -115,16 +124,21 @@ func (w lintUnconditionalRecursionRule) Visit(node ast.Node) ast.Visitor { w.updateFuncStatus(n.Body) return nil case *ast.GoStmt: - for _, a := range n.Call.Args { - ast.Walk(w, a) // check if arguments have a recursive call - } - return nil // recursive async call is not an issue + w.inGoStatement = true + ast.Walk(w, n.Call) + w.inGoStatement = false + return nil case *ast.ForStmt: if n.Cond != nil { return nil } // unconditional loop return w + case *ast.FuncLit: + if w.inGoStatement { + return w + } + return nil // literal call (closure) is not necessarily an issue } return w diff --git a/testdata/unconditional-recursion.go b/testdata/unconditional-recursion.go index b6267691a..a35e9cc4e 100644 --- a/testdata/unconditional-recursion.go +++ b/testdata/unconditional-recursion.go @@ -134,8 +134,8 @@ func ur10() { ur10() } -func ur11() { - go ur11() +func ur11() { // this pattern produces "infinite" number of goroutines + go ur11() // MATCH /unconditional recursive call/ } func ur12() { @@ -187,3 +187,15 @@ func (*fooType) BarFunc() { func (_ *fooType) BazFunc() { BazFunc() } + +// Tests for #902 +func falsePositiveFuncLiteral() { + _ = foo(func() { + falsePositiveFuncLiteral() + }) +} +func nr902() { + go func() { + nr902() // MATCH /unconditional recursive call/ + }() +}