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

     1  package rule
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/mgechev/revive/lint"
     6  	"go/ast"
     7  )
     8  
     9  // FlagParamRule lints given else constructs.
    10  type FlagParamRule struct{}
    11  
    12  // Apply applies the rule to given file.
    13  func (r *FlagParamRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
    14  	var failures []lint.Failure
    15  
    16  	onFailure := func(failure lint.Failure) {
    17  		failures = append(failures, failure)
    18  	}
    19  
    20  	w := lintFlagParamRule{onFailure: onFailure}
    21  	ast.Walk(w, file.AST)
    22  	return failures
    23  }
    24  
    25  // Name returns the rule name.
    26  func (r *FlagParamRule) Name() string {
    27  	return "flag-parameter"
    28  }
    29  
    30  type lintFlagParamRule struct {
    31  	onFailure func(lint.Failure)
    32  }
    33  
    34  func (w lintFlagParamRule) Visit(node ast.Node) ast.Visitor {
    35  	fd, ok := node.(*ast.FuncDecl)
    36  	if !ok {
    37  		return w
    38  	}
    39  
    40  	if fd.Body == nil {
    41  		return nil // skip whole function declaration
    42  	}
    43  
    44  	for _, p := range fd.Type.Params.List {
    45  		t := p.Type
    46  
    47  		id, ok := t.(*ast.Ident)
    48  		if !ok {
    49  			continue
    50  		}
    51  
    52  		if id.Name != "bool" {
    53  			continue
    54  		}
    55  
    56  		cv := conditionVisitor{p.Names, fd, w}
    57  		ast.Walk(cv, fd.Body)
    58  	}
    59  
    60  	return w
    61  }
    62  
    63  type conditionVisitor struct {
    64  	ids    []*ast.Ident
    65  	fd     *ast.FuncDecl
    66  	linter lintFlagParamRule
    67  }
    68  
    69  func (w conditionVisitor) Visit(node ast.Node) ast.Visitor {
    70  	ifStmt, ok := node.(*ast.IfStmt)
    71  	if !ok {
    72  		return w
    73  	}
    74  
    75  	fselect := func(n ast.Node) bool {
    76  		ident, ok := n.(*ast.Ident)
    77  		if !ok {
    78  			return false
    79  		}
    80  
    81  		for _, id := range w.ids {
    82  			if ident.Name == id.Name {
    83  				return true
    84  			}
    85  		}
    86  
    87  		return false
    88  	}
    89  
    90  	uses := pick(ifStmt.Cond, fselect, nil)
    91  
    92  	if len(uses) < 1 {
    93  		return w
    94  	}
    95  
    96  	w.linter.onFailure(lint.Failure{
    97  		Confidence: 1,
    98  		Node:       w.fd.Type.Params,
    99  		Category:   "bad practice",
   100  		Failure:    fmt.Sprintf("parameter '%s' seems to be a control flag, avoid control coupling", uses[0]),
   101  	})
   102  
   103  	return nil
   104  }