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/ast/inspector"
    15  	"golang.org/x/tools/go/types/typeutil"
    16  	"golang.org/x/tools/internal/analysis/analyzerutil"
    17  	"golang.org/x/tools/internal/astutil"
    18  	"golang.org/x/tools/internal/typesinternal"
    19  )
    20  
    21  //go:embed doc.go
    22  var doc string
    23  
    24  var Analyzer = &analysis.Analyzer{
    25  	Name:             "atomic",
    26  	Doc:              analyzerutil.MustExtractDoc(doc, "atomic"),
    27  	URL:              "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/atomic",
    28  	Requires:         []*analysis.Analyzer{inspect.Analyzer},
    29  	RunDespiteErrors: true,
    30  	Run:              run,
    31  }
    32  
    33  func run(pass *analysis.Pass) (any, error) {
    34  	if !typesinternal.Imports(pass.Pkg, "sync/atomic") {
    35  		return nil, nil // doesn't directly import sync/atomic
    36  	}
    37  
    38  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    39  
    40  	nodeFilter := []ast.Node{
    41  		(*ast.AssignStmt)(nil),
    42  	}
    43  	inspect.Preorder(nodeFilter, func(node ast.Node) {
    44  		n := node.(*ast.AssignStmt)
    45  		if len(n.Lhs) != len(n.Rhs) {
    46  			return
    47  		}
    48  		if len(n.Lhs) == 1 && n.Tok == token.DEFINE {
    49  			return
    50  		}
    51  
    52  		for i, right := range n.Rhs {
    53  			call, ok := right.(*ast.CallExpr)
    54  			if !ok {
    55  				continue
    56  			}
    57  			obj := typeutil.Callee(pass.TypesInfo, call)
    58  			if typesinternal.IsFunctionNamed(obj, "sync/atomic", "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr") {
    59  				checkAtomicAddAssignment(pass, n.Lhs[i], call)
    60  			}
    61  		}
    62  	})
    63  	return nil, nil
    64  }
    65  
    66  // checkAtomicAddAssignment walks the atomic.Add* method calls checking
    67  // for assigning the return value to the same variable being used in the
    68  // operation
    69  func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.CallExpr) {
    70  	if len(call.Args) != 2 {
    71  		return
    72  	}
    73  	arg := call.Args[0]
    74  	broken := false
    75  
    76  	gofmt := func(e ast.Expr) string { return astutil.Format(pass.Fset, e) }
    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  		pass.ReportRangef(left, "direct assignment to atomic value")
    86  	}
    87  }
    88  

View as plain text