github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/simple/s1023/s1023.go (about)

     1  package s1023
     2  
     3  import (
     4  	"go/ast"
     5  	"go/token"
     6  
     7  	"github.com/amarpal/go-tools/analysis/code"
     8  	"github.com/amarpal/go-tools/analysis/facts/generated"
     9  	"github.com/amarpal/go-tools/analysis/lint"
    10  	"github.com/amarpal/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.Documentation{
    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  }