Skip to content

Commit

Permalink
fix: literal calls case (#909)
Browse files Browse the repository at this point in the history
  • Loading branch information
mfederowicz authored Sep 30, 2023
1 parent cdb8268 commit fb5bbe7
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 10 deletions.
30 changes: 22 additions & 8 deletions rule/unconditional-recursion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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) {
Expand All @@ -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
}
Expand All @@ -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)
Expand All @@ -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
Expand Down
16 changes: 14 additions & 2 deletions testdata/unconditional-recursion.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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/
}()
}

0 comments on commit fb5bbe7

Please sign in to comment.