github.com/songshiyun/revive@v1.1.5-0.20220323112655-f8433a19b3c5/rule/atomic.go (about)

     1  package rule
     2  
     3  import (
     4  	"go/ast"
     5  	"go/token"
     6  	"go/types"
     7  
     8  	"github.com/songshiyun/revive/lint"
     9  )
    10  
    11  // AtomicRule lints given else constructs.
    12  type AtomicRule struct{}
    13  
    14  // Apply applies the rule to given file.
    15  func (r *AtomicRule) Apply(file *lint.File, _ lint.Arguments) []lint.Failure {
    16  	var failures []lint.Failure
    17  	walker := atomic{
    18  		pkgTypesInfo: file.Pkg.TypesInfo,
    19  		onFailure: func(failure lint.Failure) {
    20  			failures = append(failures, failure)
    21  		},
    22  	}
    23  
    24  	ast.Walk(walker, file.AST)
    25  
    26  	return failures
    27  }
    28  
    29  // Name returns the rule name.
    30  func (r *AtomicRule) Name() string {
    31  	return "atomic"
    32  }
    33  
    34  type atomic struct {
    35  	pkgTypesInfo *types.Info
    36  	onFailure    func(lint.Failure)
    37  }
    38  
    39  func (w atomic) Visit(node ast.Node) ast.Visitor {
    40  	n, ok := node.(*ast.AssignStmt)
    41  	if !ok {
    42  		return w
    43  	}
    44  
    45  	if len(n.Lhs) != len(n.Rhs) {
    46  		return nil // skip assignment sub-tree
    47  	}
    48  	if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
    49  		return nil // skip assignment sub-tree
    50  	}
    51  
    52  	for i, right := range n.Rhs {
    53  		call, ok := right.(*ast.CallExpr)
    54  		if !ok {
    55  			continue
    56  		}
    57  		sel, ok := call.Fun.(*ast.SelectorExpr)
    58  		if !ok {
    59  			continue
    60  		}
    61  		pkgIdent, _ := sel.X.(*ast.Ident)
    62  		if w.pkgTypesInfo != nil {
    63  			pkgName, ok := w.pkgTypesInfo.Uses[pkgIdent].(*types.PkgName)
    64  			if !ok || pkgName.Imported().Path() != "sync/atomic" {
    65  				continue
    66  			}
    67  		}
    68  
    69  		switch sel.Sel.Name {
    70  		case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
    71  			left := n.Lhs[i]
    72  			if len(call.Args) != 2 {
    73  				continue
    74  			}
    75  			arg := call.Args[0]
    76  			broken := false
    77  
    78  			if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
    79  				broken = gofmt(left) == gofmt(uarg.X)
    80  			} else if star, ok := left.(*ast.StarExpr); ok {
    81  				broken = gofmt(star.X) == gofmt(arg)
    82  			}
    83  
    84  			if broken {
    85  				w.onFailure(lint.Failure{
    86  					Confidence: 1,
    87  					Failure:    "direct assignment to atomic value",
    88  					Node:       n,
    89  				})
    90  			}
    91  		}
    92  	}
    93  	return w
    94  }