summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/mgechev/revive/rule/deep-exit.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/mgechev/revive/rule/deep-exit.go')
-rw-r--r--vendor/github.com/mgechev/revive/rule/deep-exit.go94
1 files changed, 94 insertions, 0 deletions
diff --git a/vendor/github.com/mgechev/revive/rule/deep-exit.go b/vendor/github.com/mgechev/revive/rule/deep-exit.go
new file mode 100644
index 0000000000..f49e93dd47
--- /dev/null
+++ b/vendor/github.com/mgechev/revive/rule/deep-exit.go
@@ -0,0 +1,94 @@
+package rule
+
+import (
+ "fmt"
+ "go/ast"
+
+ "github.com/mgechev/revive/lint"
+)
+
+// DeepExitRule lints program exit at functions other than main or init.
+type DeepExitRule struct{}
+
+// Apply applies the rule to given file.
+func (r *DeepExitRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
+ var failures []lint.Failure
+ onFailure := func(failure lint.Failure) {
+ failures = append(failures, failure)
+ }
+
+ var exitFunctions = map[string]map[string]bool{
+ "os": map[string]bool{"Exit": true},
+ "syscall": map[string]bool{"Exit": true},
+ "log": map[string]bool{
+ "Fatal": true,
+ "Fatalf": true,
+ "Fatalln": true,
+ "Panic": true,
+ "Panicf": true,
+ "Panicln": true,
+ },
+ }
+
+ w := lintDeepExit{onFailure, exitFunctions, file.IsTest()}
+ ast.Walk(w, file.AST)
+ return failures
+}
+
+// Name returns the rule name.
+func (r *DeepExitRule) Name() string {
+ return "deep-exit"
+}
+
+type lintDeepExit struct {
+ onFailure func(lint.Failure)
+ exitFunctions map[string]map[string]bool
+ isTestFile bool
+}
+
+func (w lintDeepExit) Visit(node ast.Node) ast.Visitor {
+ if fd, ok := node.(*ast.FuncDecl); ok {
+ if w.mustIgnore(fd) {
+ return nil // skip analysis of this function
+ }
+
+ return w
+ }
+
+ se, ok := node.(*ast.ExprStmt)
+ if !ok {
+ return w
+ }
+ ce, ok := se.X.(*ast.CallExpr)
+ if !ok {
+ return w
+ }
+
+ fc, ok := ce.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return w
+ }
+ id, ok := fc.X.(*ast.Ident)
+ if !ok {
+ return w
+ }
+
+ fn := fc.Sel.Name
+ pkg := id.Name
+ if w.exitFunctions[pkg] != nil && w.exitFunctions[pkg][fn] { // it's a call to an exit function
+ w.onFailure(lint.Failure{
+ Confidence: 1,
+ Node: ce,
+ Category: "bad practice",
+ Failure: fmt.Sprintf("calls to %s.%s only in main() or init() functions", pkg, fn),
+ })
+ }
+
+ return w
+}
+
+func (w *lintDeepExit) mustIgnore(fd *ast.FuncDecl) bool {
+ fn := fd.Name.Name
+
+ return fn == "init" || fn == "main" || (w.isTestFile && fn == "TestMain")
+}