1
0
mirror of https://github.com/mgechev/revive.git synced 2025-01-22 03:38:47 +02:00

New rule deep-exit (#26)

* Adds rule superfluous-else (an extension of indent-error-flow)

* Fix superfluous-else rule struct namming.

* Adds superfuous-else rule to the rules table

* Adds confusing-naming rule

* adds multifile test

* clean-up

* fix config.go

* deep-exit: first working version

* fix pbs from  @mgechev review

* deep-exit: modifies failure message
This commit is contained in:
chavacava 2018-06-26 22:21:03 +02:00 committed by Minko Gechev
parent b2532b3c33
commit 68deb55492
5 changed files with 140 additions and 0 deletions

View File

@ -213,6 +213,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
| `confusing-naming` | n/a | Warns on methods with names that differ only by capitalization | no | no |
| `get-return ` | n/a | Warns on getters that do not yield any result | no | no |
| `modifies-param` | n/a | Warns on assignments to function parameters | no | no |
| `deep-exit` | n/a | Looks for program exits in funcs other than `main()` or `init()` | no | no |
## Available Formatters

View File

@ -51,6 +51,7 @@ var allRules = append([]lint.Rule{
&rule.SuperfluousElseRule{},
&rule.GetReturnRule{},
&rule.ModifiesParamRule{},
&rule.DeepExitRule{},
}, defaultRules...)
var allFormatters = []lint.Formatter{

32
fixtures/deep-exit.go Normal file
View File

@ -0,0 +1,32 @@
package fixtures
import (
"syscall"
"log"
"os"
)
func foo0() {
os.Exit(1) // MATCH /calls to os.Exit only in main() or init() functions/
}
func init() {
log.Fatal("v ...interface{}")
}
func foo() {
log.Fatalf(1) // MATCH /calls to log.Fatalf only in main() or init() functions/
}
func main() {
log.Fatalln("v ...interface{}")
}
func bar() {
log.Fatal(1) // MATCH /calls to log.Fatal only in main() or init() functions/
}
func bar2() {
bar()
syscall.Exit(1) // MATCH /calls to syscall.Exit only in main() or init() functions/
}

95
rule/deep-exit.go Normal file
View File

@ -0,0 +1,95 @@
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, arguments 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, false}
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
ignore bool
}
func (w lintDeepExit) Visit(node ast.Node) ast.Visitor {
if stmt, ok := node.(*ast.FuncDecl); ok {
w.updateIgnore(stmt)
return w
}
if w.ignore {
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",
URL: "#deep-exit",
Failure: fmt.Sprintf("calls to %s.%s only in main() or init() functions", pkg, fn),
})
}
return w
}
func (w *lintDeepExit) updateIgnore(fd *ast.FuncDecl) {
fn := fd.Name.Name
w.ignore = (fn == "init" || fn == "main")
}

11
test/deep-exit_test.go Normal file
View File

@ -0,0 +1,11 @@
package test
import (
"testing"
"github.com/mgechev/revive/rule"
)
func TestDeepExit(t *testing.T) {
testRule(t, "deep-exit", &rule.DeepExitRule{})
}