Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/atomic/atomic.go

     1  // Copyright 2013 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package atomic
     6  
     7  import (
     8  	_ "embed"
     9  	"go/ast"
    10  	"go/token"
    11  
    12  	"golang.org/x/tools/go/analysis"
    13  	"golang.org/x/tools/go/analysis/passes/inspect"
    14  	"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
    15  	"golang.org/x/tools/go/ast/inspector"
    16  	"golang.org/x/tools/go/types/typeutil"
    17  )
    18  
    19  //go:embed doc.go
    20  var doc string
    21  
    22  var Analyzer = &analysis.Analyzer{
    23  	Name:             "atomic",
    24  	Doc:              analysisutil.MustExtractDoc(doc, "atomic"),
    25  	URL:              "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/atomic",
    26  	Requires:         []*analysis.Analyzer{inspect.Analyzer},
    27  	RunDespiteErrors: true,
    28  	Run:              run,
    29  }
    30  
    31  func run(pass *analysis.Pass) (interface{}, error) {
    32  	if !analysisutil.Imports(pass.Pkg, "sync/atomic") {
    33  		return nil, nil // doesn't directly import sync/atomic
    34  	}
    35  
    36  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    37  
    38  	nodeFilter := []ast.Node{
    39  		(*ast.AssignStmt)(nil),
    40  	}
    41  	inspect.Preorder(nodeFilter, func(node ast.Node) {
    42  		n := node.(*ast.AssignStmt)
    43  		if len(n.Lhs) != len(n.Rhs) {
    44  			return
    45  		}
    46  		if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
    47  			return
    48  		}
    49  
    50  		for i, right := range n.Rhs {
    51  			call, ok := right.(*ast.CallExpr)
    52  			if !ok {
    53  				continue
    54  			}
    55  			fn := typeutil.StaticCallee(pass.TypesInfo, call)
    56  			if analysisutil.IsFunctionNamed(fn, "sync/atomic", "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr") {
    57  				checkAtomicAddAssignment(pass, n.Lhs[i], call)
    58  			}
    59  		}
    60  	})
    61  	return nil, nil
    62  }
    63  
    64  // checkAtomicAddAssignment walks the atomic.Add* method calls checking
    65  // for assigning the return value to the same variable being used in the
    66  // operation
    67  func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.CallExpr) {
    68  	if len(call.Args) != 2 {
    69  		return
    70  	}
    71  	arg := call.Args[0]
    72  	broken := false
    73  
    74  	gofmt := func(e ast.Expr) string { return analysisutil.Format(pass.Fset, e) }
    75  
    76  	if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
    77  		broken = gofmt(left) == gofmt(uarg.X)
    78  	} else if star, ok := left.(*ast.StarExpr); ok {
    79  		broken = gofmt(star.X) == gofmt(arg)
    80  	}
    81  
    82  	if broken {
    83  		pass.ReportRangef(left, "direct assignment to atomic value")
    84  	}
    85  }
    86  

View as plain text