Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix false positives for unnecessaryDefer. #926 #927

Merged
merged 1 commit into from May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions checkers/testdata/unnecessaryDefer/negative_tests.go
Expand Up @@ -35,3 +35,21 @@ func foo_6() int {
defer func() {}()
return foo_2()
}

func foo_7() int {
if true {
defer func() {}()
}
return 0
}

func foo_8() int {
if true {
foo_1()
foo_2()
foo_3()

defer func() {}()
}
return 0
}
41 changes: 32 additions & 9 deletions checkers/unnecessaryDefer_checker.go
Expand Up @@ -31,26 +31,44 @@ func() {

type unnecessaryDeferChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
ctx *lintpack.CheckerContext
isFunc bool
}

// Visit implements the ast.Visitor. This visitor keeps track of the block
// statement belongs to a function or any other block. If the block is not a
// function and ends with a defer statement that should be OK since it's
// defering the outer function.
func (c *unnecessaryDeferChecker) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
case *ast.FuncDecl, *ast.FuncLit:
c.isFunc = true
case *ast.BlockStmt:
c.checkDeferBeforeReturn(n)
default:
c.isFunc = false
}

return c
}

func (c *unnecessaryDeferChecker) VisitFuncDecl(funcDecl *ast.FuncDecl) {
ast.Inspect(funcDecl.Body, func(n ast.Node) bool {
funDecl, ok := n.(*ast.BlockStmt)
if ok {
c.checkDeferBeforeReturn(funDecl)
}
return true
})
// We always start as a function (*ast.FuncDecl.Body passed)
c.isFunc = true

ast.Walk(c, funcDecl.Body)
}

func (c *unnecessaryDeferChecker) checkDeferBeforeReturn(funcDecl *ast.BlockStmt) {
// Check if we have an explicit return or if it's just the end of the scope.
explicitReturn := false
retIndex := len(funcDecl.List)
for i, stmt := range funcDecl.List {
retStmt, ok := stmt.(*ast.ReturnStmt)
if !ok {
continue
}
explicitReturn = true
if lintutil.ContainsNode(retStmt, astp.IsCallExpr) {
continue
}
Expand All @@ -63,7 +81,12 @@ func (c *unnecessaryDeferChecker) checkDeferBeforeReturn(funcDecl *ast.BlockStmt
}

if deferStmt, ok := funcDecl.List[retIndex-1].(*ast.DeferStmt); ok {
c.warn(deferStmt)
// If the block is a function and ending with return or if we have an
// explicit return in any other block we should warn about
// unnecessary defer.
if c.isFunc || explicitReturn {
c.warn(deferStmt)
}
}
}

Expand Down
5 changes: 0 additions & 5 deletions go.sum
Expand Up @@ -38,11 +38,6 @@ github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c h1:JoUA0uz
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8 h1:DvnesvLtRPQOvaUbfXfh0tpMHg29by0H7F2U+QIkSu8=
github.com/quasilyte/go-ruleguard v0.1.2-0.20200318202121-b00d7a75d3d8/go.mod h1:CGFX09Ci3pq9QZdj86B+VGIdNj4VyCo2iPOGS9esB/k=
github.com/quasilyte/regex v0.0.0-20200402210348-d7167c1cb489 h1:MaRo2gFtvdQHnxOMUhKxyWAbaRXn7siZJOD4zKrYeQU=
github.com/quasilyte/regex/syntax v0.0.0-20200402210348-d7167c1cb489 h1:Wg/knSPrrEsisZKcWBk8cGbiroiNk7v6jeHZAcAfzz4=
github.com/quasilyte/regex/syntax v0.0.0-20200402210348-d7167c1cb489/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/regex/syntax v0.0.0-20200405164239-f6e043421647 h1:ST0AU0FGuLuKkS3RlulTWjV4pBelyTH7wBLA9/SYAUE=
github.com/quasilyte/regex/syntax v0.0.0-20200405164239-f6e043421647/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 h1:L8QM9bvf68pVdQ3bCFZMDmnt9yqcMBro1pC7F+IPYMY=
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down