github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/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 defines an Analyzer that checks for common mistakes 6 // using the sync/atomic package. 7 package atomic 8 9 import ( 10 "go/ast" 11 "go/token" 12 "go/types" 13 14 "github.com/powerman/golang-tools/go/analysis" 15 "github.com/powerman/golang-tools/go/analysis/passes/inspect" 16 "github.com/powerman/golang-tools/go/analysis/passes/internal/analysisutil" 17 "github.com/powerman/golang-tools/go/ast/inspector" 18 ) 19 20 const Doc = `check for common mistakes using the sync/atomic package 21 22 The atomic checker looks for assignment statements of the form: 23 24 x = atomic.AddUint64(&x, 1) 25 26 which are not atomic.` 27 28 var Analyzer = &analysis.Analyzer{ 29 Name: "atomic", 30 Doc: Doc, 31 Requires: []*analysis.Analyzer{inspect.Analyzer}, 32 RunDespiteErrors: true, 33 Run: run, 34 } 35 36 func run(pass *analysis.Pass) (interface{}, error) { 37 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 38 39 nodeFilter := []ast.Node{ 40 (*ast.AssignStmt)(nil), 41 } 42 inspect.Preorder(nodeFilter, func(node ast.Node) { 43 n := node.(*ast.AssignStmt) 44 if len(n.Lhs) != len(n.Rhs) { 45 return 46 } 47 if len(n.Lhs) == 1 && n.Tok == token.DEFINE { 48 return 49 } 50 51 for i, right := range n.Rhs { 52 call, ok := right.(*ast.CallExpr) 53 if !ok { 54 continue 55 } 56 sel, ok := call.Fun.(*ast.SelectorExpr) 57 if !ok { 58 continue 59 } 60 pkgIdent, _ := sel.X.(*ast.Ident) 61 pkgName, ok := pass.TypesInfo.Uses[pkgIdent].(*types.PkgName) 62 if !ok || pkgName.Imported().Path() != "sync/atomic" { 63 continue 64 } 65 66 switch sel.Sel.Name { 67 case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": 68 checkAtomicAddAssignment(pass, n.Lhs[i], call) 69 } 70 } 71 }) 72 return nil, nil 73 } 74 75 // checkAtomicAddAssignment walks the atomic.Add* method calls checking 76 // for assigning the return value to the same variable being used in the 77 // operation 78 func checkAtomicAddAssignment(pass *analysis.Pass, left ast.Expr, call *ast.CallExpr) { 79 if len(call.Args) != 2 { 80 return 81 } 82 arg := call.Args[0] 83 broken := false 84 85 gofmt := func(e ast.Expr) string { return analysisutil.Format(pass.Fset, e) } 86 87 if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { 88 broken = gofmt(left) == gofmt(uarg.X) 89 } else if star, ok := left.(*ast.StarExpr); ok { 90 broken = gofmt(star.X) == gofmt(arg) 91 } 92 93 if broken { 94 pass.ReportRangef(left, "direct assignment to atomic value") 95 } 96 }