golang.org/x/tools@v0.21.0/go/analysis/passes/atomic/atomic.go (about) 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 }