-
Notifications
You must be signed in to change notification settings - Fork 113
/
exitAfterDefer_checker.go
76 lines (68 loc) · 1.99 KB
/
exitAfterDefer_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package checkers
import (
"go/ast"
"github.com/go-lintpack/lintpack"
"github.com/go-lintpack/lintpack/astwalk"
"github.com/go-toolsmith/astfmt"
"github.com/go-toolsmith/astp"
"golang.org/x/tools/go/ast/astutil"
)
func init() {
var info lintpack.CheckerInfo
info.Name = "exitAfterDefer"
info.Tags = []string{"diagnostic", "experimental"}
info.Summary = "Detects calls to exit/fatal inside functions that use defer"
info.Before = `
defer os.Remove(filename)
if bad {
log.Fatalf("something bad happened")
}`
info.After = `
defer os.Remove(filename)
if bad {
log.Printf("something bad happened")
return
}`
collection.AddChecker(&info, func(ctx *lintpack.CheckerContext) lintpack.FileWalker {
return astwalk.WalkerForFuncDecl(&exitAfterDeferChecker{ctx: ctx})
})
}
type exitAfterDeferChecker struct {
astwalk.WalkHandler
ctx *lintpack.CheckerContext
}
func (c *exitAfterDeferChecker) VisitFuncDecl(fn *ast.FuncDecl) {
// TODO(Quasilyte): handle goto and other kinds of flow that break
// the algorithm below that expects the latter statement to be
// executed after the ones that come before it.
var deferStmt *ast.DeferStmt
pre := func(cur *astutil.Cursor) bool {
// Don't recurse into local anonymous functions.
return !astp.IsFuncLit(cur.Node())
}
post := func(cur *astutil.Cursor) bool {
switch n := cur.Node().(type) {
case *ast.DeferStmt:
deferStmt = n
case *ast.CallExpr:
if deferStmt != nil {
switch qualifiedName(n.Fun) {
case "log.Fatal", "log.Fatalf", "log.Fatalln", "os.Exit":
c.warn(n, deferStmt)
return false
}
}
}
return true
}
astutil.Apply(fn.Body, pre, post)
}
func (c *exitAfterDeferChecker) warn(cause *ast.CallExpr, 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(cause, "%s clutters `%s`", cause.Fun, s)
}