1
0
mirror of https://github.com/mgechev/revive.git synced 2025-02-17 14:01:01 +02:00

New rule: unreachable-code (#38)

* 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

* [WIP] fix multiple file test

* draft solution for detecting confusing-names through multiple files

* [WIP] confusing-name multiple files

* clean-up

* draft working version

* cleaner version + more informative messages

* adds check on struct field names

* fix config.go

* clean master

* new ADS rule: newerr

* ADS-print working version

* ads-print final version

* ads-lost-err working version

* confusing-namming: fix tests

* removes ads-* rules

* rule unreachable-code

* unreachable-code refactored version

* skip corner case of mandatory return
This commit is contained in:
chavacava 2018-07-16 23:23:47 +02:00 committed by Minko Gechev
parent 439cef2893
commit db6c522a37
5 changed files with 164 additions and 0 deletions

View File

@ -220,6 +220,7 @@ List of all available rules. The rules ported from `golint` are left unchanged a
| `confusing-results` | n/a | Suggests to name potentially confusing function results | no | no |
| `deep-exit` | n/a | Looks for program exits in funcs other than `main()` or `init()` | no | no |
| `unused-parameter` | n/a | Suggests to rename or remove unused function parameters | no | no |
| `unreachable-code` | n/a | Warns on unreachable code | no | no |
## Available Formatters

View File

@ -55,6 +55,7 @@ var allRules = append([]lint.Rule{
&rule.ConfusingResultsRule{},
&rule.DeepExitRule{},
&rule.UnusedParamRule{},
&rule.UnreachableCodeRule{},
}, defaultRules...)
var allFormatters = []lint.Formatter{

View File

@ -0,0 +1,37 @@
package fixtures
import (
"fmt"
"log"
"os"
)
func foo() int {
log.Fatalf("%s", "About to fail") // ignore
return 0 // MATCH /unreachable code after this statement/
return 1
Println("unreachable")
}
func f() {
fmt.Println("Hello, playground")
if true {
return // MATCH /unreachable code after this statement/
Println("unreachable")
os.Exit(2) // ignore
Println("also unreachable")
}
return // MATCH /unreachable code after this statement/
fmt.Println("Bye, playground")
}
func g() {
fmt.Println("Hello, playground")
if true {
return // ignore if next stmt is labeled
label:
os.Exit(2) // ignore
}
fmt.Println("Bye, playground")
}

114
rule/unreachable-code.go Normal file
View File

@ -0,0 +1,114 @@
package rule
import (
"go/ast"
"github.com/mgechev/revive/lint"
)
// UnreachableCodeRule lints unreachable code.
type UnreachableCodeRule struct{}
// Apply applies the rule to given file.
func (r *UnreachableCodeRule) Apply(file *lint.File, arguments lint.Arguments) []lint.Failure {
var failures []lint.Failure
onFailure := func(failure lint.Failure) {
failures = append(failures, failure)
}
var branchingFunctions = map[string]map[string]bool{
"os": map[string]bool{"Exit": true},
"log": map[string]bool{
"Fatal": true,
"Fatalf": true,
"Fatalln": true,
"Panic": true,
"Panicf": true,
"Panicln": true,
},
}
w := lintUnreachableCode{onFailure, branchingFunctions}
ast.Walk(w, file.AST)
return failures
}
// Name returns the rule name.
func (r *UnreachableCodeRule) Name() string {
return "unreachable-code"
}
type lintUnreachableCode struct {
onFailure func(lint.Failure)
branchingFunctions map[string]map[string]bool
}
func (w lintUnreachableCode) Visit(node ast.Node) ast.Visitor {
blk, ok := node.(*ast.BlockStmt)
if !ok {
return w
}
if len(blk.List) < 2 {
return w
}
loop:
for i, stmt := range blk.List[:len(blk.List)-1] {
// println("iterating ", len(blk.List))
next := blk.List[i+1]
if _, ok := next.(*ast.LabeledStmt); ok {
continue // skip if next statement is labeled
}
switch s := stmt.(type) {
case *ast.ReturnStmt:
w.onFailure(newUnreachableCodeFailure(s))
break loop
case *ast.BranchStmt:
token := s.Tok.String()
if token != "fallthrough" {
w.onFailure(newUnreachableCodeFailure(s))
break loop
}
case *ast.ExprStmt:
ce, ok := s.X.(*ast.CallExpr)
if !ok {
continue
}
// it's a function call
fc, ok := ce.Fun.(*ast.SelectorExpr)
if !ok {
continue
}
id, ok := fc.X.(*ast.Ident)
if !ok {
continue
}
fn := fc.Sel.Name
pkg := id.Name
if !w.branchingFunctions[pkg][fn] { // it isn't a call to a branching function
continue
}
if _, ok := next.(*ast.ReturnStmt); ok { // return statement needed to satisfy function signature
continue
}
w.onFailure(newUnreachableCodeFailure(s))
break loop
}
}
return w
}
func newUnreachableCodeFailure(node ast.Node) lint.Failure {
return lint.Failure{
Confidence: 1,
Node: node,
Category: "logic",
Failure: "unreachable code after this statement",
}
}

View File

@ -0,0 +1,11 @@
package test
import (
"testing"
"github.com/mgechev/revive/rule"
)
func TestUnreachableCode(t *testing.T) {
testRule(t, "unreachable-code", &rule.UnreachableCodeRule{})
}