github.com/Johnny2210/revive@v1.0.8-0.20210625134200-febf37ccd0f5/rule/unreachable-code.go (about)

     1  package rule
     2  
     3  import (
     4  	"go/ast"
     5  
     6  	"github.com/mgechev/revive/lint"
     7  )
     8  
     9  // UnreachableCodeRule lints unreachable code.
    10  type UnreachableCodeRule struct{}
    11  
    12  // Apply applies the rule to given file.
    13  func (r *UnreachableCodeRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
    14  	var failures []lint.Failure
    15  	onFailure := func(failure lint.Failure) {
    16  		failures = append(failures, failure)
    17  	}
    18  
    19  	var branchingFunctions = map[string]map[string]bool{
    20  		"os": {"Exit": true},
    21  		"log": {
    22  			"Fatal":   true,
    23  			"Fatalf":  true,
    24  			"Fatalln": true,
    25  			"Panic":   true,
    26  			"Panicf":  true,
    27  			"Panicln": true,
    28  		},
    29  	}
    30  
    31  	w := lintUnreachableCode{onFailure, branchingFunctions}
    32  	ast.Walk(w, file.AST)
    33  	return failures
    34  }
    35  
    36  // Name returns the rule name.
    37  func (r *UnreachableCodeRule) Name() string {
    38  	return "unreachable-code"
    39  }
    40  
    41  type lintUnreachableCode struct {
    42  	onFailure          func(lint.Failure)
    43  	branchingFunctions map[string]map[string]bool
    44  }
    45  
    46  func (w lintUnreachableCode) Visit(node ast.Node) ast.Visitor {
    47  	blk, ok := node.(*ast.BlockStmt)
    48  	if !ok {
    49  		return w
    50  	}
    51  
    52  	if len(blk.List) < 2 {
    53  		return w
    54  	}
    55  loop:
    56  	for i, stmt := range blk.List[:len(blk.List)-1] {
    57  		// println("iterating ", len(blk.List))
    58  		next := blk.List[i+1]
    59  		if _, ok := next.(*ast.LabeledStmt); ok {
    60  			continue // skip if next statement is labeled
    61  		}
    62  
    63  		switch s := stmt.(type) {
    64  		case *ast.ReturnStmt:
    65  			w.onFailure(newUnreachableCodeFailure(s))
    66  			break loop
    67  		case *ast.BranchStmt:
    68  			token := s.Tok.String()
    69  			if token != "fallthrough" {
    70  				w.onFailure(newUnreachableCodeFailure(s))
    71  				break loop
    72  			}
    73  		case *ast.ExprStmt:
    74  			ce, ok := s.X.(*ast.CallExpr)
    75  			if !ok {
    76  				continue
    77  			}
    78  			// it's a function call
    79  			fc, ok := ce.Fun.(*ast.SelectorExpr)
    80  			if !ok {
    81  				continue
    82  			}
    83  
    84  			id, ok := fc.X.(*ast.Ident)
    85  
    86  			if !ok {
    87  				continue
    88  			}
    89  			fn := fc.Sel.Name
    90  			pkg := id.Name
    91  			if !w.branchingFunctions[pkg][fn] { // it isn't a call to a branching function
    92  				continue
    93  			}
    94  
    95  			if _, ok := next.(*ast.ReturnStmt); ok { // return statement needed to satisfy function signature
    96  				continue
    97  			}
    98  
    99  			w.onFailure(newUnreachableCodeFailure(s))
   100  			break loop
   101  		}
   102  	}
   103  
   104  	return w
   105  }
   106  
   107  func newUnreachableCodeFailure(node ast.Node) lint.Failure {
   108  	return lint.Failure{
   109  		Confidence: 1,
   110  		Node:       node,
   111  		Category:   "logic",
   112  		Failure:    "unreachable code after this statement",
   113  	}
   114  }