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 5package main 6 7import ( 8 "go/ast" 9 "go/token" 10) 11 12func init() { 13 register("atomic", 14 "check for common mistaken usages of the sync/atomic package", 15 checkAtomicAssignment, 16 assignStmt) 17} 18 19// checkAtomicAssignment walks the assignment statement checking for common 20// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1) 21func checkAtomicAssignment(f *File, node ast.Node) { 22 n := node.(*ast.AssignStmt) 23 if len(n.Lhs) != len(n.Rhs) { 24 return 25 } 26 if len(n.Lhs) == 1 && n.Tok == token.DEFINE { 27 return 28 } 29 30 for i, right := range n.Rhs { 31 call, ok := right.(*ast.CallExpr) 32 if !ok { 33 continue 34 } 35 sel, ok := call.Fun.(*ast.SelectorExpr) 36 if !ok { 37 continue 38 } 39 pkg, ok := sel.X.(*ast.Ident) 40 if !ok || pkg.Name != "atomic" { 41 continue 42 } 43 44 switch sel.Sel.Name { 45 case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr": 46 f.checkAtomicAddAssignment(n.Lhs[i], call) 47 } 48 } 49} 50 51// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value 52// to the same variable being used in the operation 53func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) { 54 if len(call.Args) != 2 { 55 return 56 } 57 arg := call.Args[0] 58 broken := false 59 60 if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND { 61 broken = f.gofmt(left) == f.gofmt(uarg.X) 62 } else if star, ok := left.(*ast.StarExpr); ok { 63 broken = f.gofmt(star.X) == f.gofmt(arg) 64 } 65 66 if broken { 67 f.Bad(left.Pos(), "direct assignment to atomic value") 68 } 69} 70