s1023.go raw

   1  package s1023
   2  
   3  import (
   4  	"go/ast"
   5  	"go/token"
   6  
   7  	"honnef.co/go/tools/analysis/code"
   8  	"honnef.co/go/tools/analysis/facts/generated"
   9  	"honnef.co/go/tools/analysis/lint"
  10  	"honnef.co/go/tools/analysis/report"
  11  
  12  	"golang.org/x/tools/go/analysis"
  13  	"golang.org/x/tools/go/analysis/passes/inspect"
  14  )
  15  
  16  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  17  	Analyzer: &analysis.Analyzer{
  18  		Name:     "S1023",
  19  		Run:      run,
  20  		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
  21  	},
  22  	Doc: &lint.RawDocumentation{
  23  		Title: `Omit redundant control flow`,
  24  		Text: `Functions that have no return value do not need a return statement as
  25  the final statement of the function.
  26  
  27  Switches in Go do not have automatic fallthrough, unlike languages
  28  like C. It is not necessary to have a break statement as the final
  29  statement in a case block.`,
  30  		Since:   "2017.1",
  31  		MergeIf: lint.MergeIfAny,
  32  	},
  33  })
  34  
  35  var Analyzer = SCAnalyzer.Analyzer
  36  
  37  func run(pass *analysis.Pass) (interface{}, error) {
  38  	fn1 := func(node ast.Node) {
  39  		clause := node.(*ast.CaseClause)
  40  		if len(clause.Body) < 2 {
  41  			return
  42  		}
  43  		branch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt)
  44  		if !ok || branch.Tok != token.BREAK || branch.Label != nil {
  45  			return
  46  		}
  47  		report.Report(pass, branch, "redundant break statement", report.FilterGenerated())
  48  	}
  49  	fn2 := func(node ast.Node) {
  50  		var ret *ast.FieldList
  51  		var body *ast.BlockStmt
  52  		switch x := node.(type) {
  53  		case *ast.FuncDecl:
  54  			ret = x.Type.Results
  55  			body = x.Body
  56  		case *ast.FuncLit:
  57  			ret = x.Type.Results
  58  			body = x.Body
  59  		default:
  60  			lint.ExhaustiveTypeSwitch(node)
  61  		}
  62  		// if the func has results, a return can't be redundant.
  63  		// similarly, if there are no statements, there can be
  64  		// no return.
  65  		if ret != nil || body == nil || len(body.List) < 1 {
  66  			return
  67  		}
  68  		rst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt)
  69  		if !ok {
  70  			return
  71  		}
  72  		// we don't need to check rst.Results as we already
  73  		// checked x.Type.Results to be nil.
  74  		report.Report(pass, rst, "redundant return statement", report.FilterGenerated())
  75  	}
  76  	code.Preorder(pass, fn1, (*ast.CaseClause)(nil))
  77  	code.Preorder(pass, fn2, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))
  78  	return nil, nil
  79  }
  80