Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
checker: add defer at the end of function checker (#901)
Detects uneeded defer at the end of function.
- Loading branch information
1 parent
c46ab3c
commit 3062653
Showing
4 changed files
with
181 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package checkers | ||
|
||
import ( | ||
"go/ast" | ||
|
||
"github.com/go-lintpack/lintpack" | ||
"github.com/go-lintpack/lintpack/astwalk" | ||
"github.com/go-toolsmith/astfmt" | ||
) | ||
|
||
func init() { | ||
var info lintpack.CheckerInfo | ||
info.Name = "deferAtTheEnd" | ||
info.Tags = []string{"diagnostic", "experimental"} | ||
info.Summary = "Detects calls to defer at the end of function" | ||
info.Before = ` | ||
func() { | ||
defer os.Remove(filename) | ||
}` | ||
info.After = ` | ||
func() { | ||
os.Remove(filename) | ||
}` | ||
|
||
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker { | ||
return astwalk.WalkerForFuncDecl(&deferAtTheEnd{ctx: ctx}) | ||
}) | ||
} | ||
|
||
type deferAtTheEnd struct { | ||
astwalk.WalkHandler | ||
ctx *lintpack.CheckerContext | ||
} | ||
|
||
func (c *deferAtTheEnd) 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 | ||
}) | ||
} | ||
|
||
func (c *deferAtTheEnd) checkDeferBeforeReturn(funcDecl *ast.BlockStmt) { | ||
retIndex := len(funcDecl.List) | ||
for i, stmt := range funcDecl.List { | ||
retStmt, ok := stmt.(*ast.ReturnStmt) | ||
if !ok { | ||
continue | ||
} | ||
if containsCallExpr(retStmt) { | ||
continue | ||
} | ||
retIndex = i | ||
break | ||
|
||
} | ||
if retIndex == 0 { | ||
return | ||
} | ||
|
||
if deferStmt, ok := funcDecl.List[retIndex-1].(*ast.DeferStmt); ok { | ||
c.warn(deferStmt) | ||
} | ||
} | ||
|
||
func containsCallExpr(retStmt *ast.ReturnStmt) bool { | ||
for _, expr := range retStmt.Results { | ||
if _, ok := expr.(*ast.CallExpr); ok { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
func (c *deferAtTheEnd) warn(deferStmt *ast.DeferStmt) { | ||
s := astfmt.Sprint(deferStmt) | ||
if fnlit, ok := deferStmt.Call.Fun.(*ast.FuncLit); ok { | ||
// To avoid long and multi-line warning messages, | ||
// collapse the function literals. | ||
s = "defer " + astfmt.Sprint(fnlit.Type) + "{...}(...)" | ||
} | ||
c.ctx.Warn(deferStmt, "%s is placed just before return", s) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package checker_test | ||
|
||
func foo_1() { | ||
foo_2() | ||
return | ||
} | ||
|
||
func foo_2() int { | ||
func() {}() | ||
return 0 | ||
} | ||
|
||
func foo_3() { | ||
func() {}() | ||
} | ||
|
||
func foo_4() { | ||
func() { | ||
foo_1() | ||
return | ||
}() | ||
} | ||
|
||
func foo_5() { | ||
defer func() { | ||
defer foo_1() | ||
foo_1() | ||
return | ||
}() | ||
foo1() | ||
return | ||
} | ||
|
||
func foo_6() int { | ||
defer func() {}() | ||
return foo_2() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package checker_test | ||
|
||
func foo1() { | ||
/*! defer foo2() is placed just before return */ | ||
defer foo2() | ||
return | ||
} | ||
|
||
func foo2() int { | ||
/*! defer func(){...}(...) is placed just before return */ | ||
defer func() {}() | ||
return 0 | ||
} | ||
|
||
func foo3() { | ||
/*! defer func(){...}(...) is placed just before return */ | ||
defer func() {}() | ||
} | ||
|
||
func foo4() { | ||
/*! defer func(){...}(...) is placed just before return */ | ||
defer func() { | ||
/*! defer foo1() is placed just before return */ | ||
defer foo1() | ||
return | ||
}() | ||
} | ||
|
||
func foo5() { | ||
func() { | ||
/*! defer foo1() is placed just before return */ | ||
defer foo1() | ||
return | ||
}() | ||
foo1() | ||
return | ||
} | ||
|
||
func foo6() { | ||
/*! defer func(){...}(...) is placed just before return */ | ||
defer func() { | ||
for { | ||
/*! defer foo1() is placed just before return */ | ||
defer foo1() | ||
return | ||
} | ||
}() | ||
return | ||
} | ||
|
||
func foo7() { | ||
if true { | ||
/*! defer foo1() is placed just before return */ | ||
defer foo1() | ||
return | ||
} | ||
return | ||
} |