--- /dev/null
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "go/ast"
+ "go/token"
+ "sync/atomic"
+)
+
+// checkAtomicAssignment walks the assignment statement checking for comomon
+// mistaken usage of atomic package, such as: x = atomic.AddUint64(&x, 1)
+func (f *File) checkAtomicAssignment(n *ast.AssignStmt) {
+ if !*vetAtomic && !*vetAll {
+ return
+ }
+
+ if len(n.Lhs) != len(n.Rhs) {
+ return
+ }
+
+ for i, right := range n.Rhs {
+ call, ok := right.(*ast.CallExpr)
+ if !ok {
+ continue
+ }
+ sel, ok := call.Fun.(*ast.SelectorExpr)
+ if !ok {
+ continue
+ }
+ pkg, ok := sel.X.(*ast.Ident)
+ if !ok || pkg.Name != "atomic" {
+ continue
+ }
+
+ switch sel.Sel.Name {
+ case "AddInt32", "AddInt64", "AddUint32", "AddUint64", "AddUintptr":
+ f.checkAtomicAddAssignment(n.Lhs[i], call)
+ }
+ }
+}
+
+// checkAtomicAddAssignment walks the atomic.Add* method calls checking for assigning the return value
+// to the same variable being used in the operation
+func (f *File) checkAtomicAddAssignment(left ast.Expr, call *ast.CallExpr) {
+ arg := call.Args[0]
+ broken := false
+
+ if uarg, ok := arg.(*ast.UnaryExpr); ok && uarg.Op == token.AND {
+ broken = f.gofmt(left) == f.gofmt(uarg.X)
+ } else if star, ok := left.(*ast.StarExpr); ok {
+ broken = f.gofmt(star.X) == f.gofmt(arg)
+ }
+
+ if broken {
+ f.Warn(left.Pos(), "direct assignment to atomic value")
+ }
+}
+
+type Counter uint64
+
+func BadAtomicAssignmentUsedInTests() {
+ x := uint64(1)
+ x = atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
+ _, x = 10, atomic.AddUint64(&x, 1) // ERROR "direct assignment to atomic value"
+ x, _ = atomic.AddUint64(&x, 1), 10 // ERROR "direct assignment to atomic value"
+
+ y := &x
+ *y = atomic.AddUint64(y, 1) // ERROR "direct assignment to atomic value"
+
+ var su struct{ Counter uint64 }
+ su.Counter = atomic.AddUint64(&su.Counter, 1) // ERROR "direct assignment to atomic value"
+ z1 := atomic.AddUint64(&su.Counter, 1)
+ _ = z1 // Avoid err "z declared and not used"
+
+ var sp struct{ Counter *uint64 }
+ *sp.Counter = atomic.AddUint64(sp.Counter, 1) // ERROR "direct assignment to atomic value"
+ z2 := atomic.AddUint64(sp.Counter, 1)
+ _ = z2 // Avoid err "z declared and not used"
+
+ au := []uint64{10, 20}
+ au[0] = atomic.AddUint64(&au[0], 1) // ERROR "direct assignment to atomic value"
+ au[1] = atomic.AddUint64(&au[0], 1)
+
+ ap := []*uint64{&au[0], &au[1]}
+ *ap[0] = atomic.AddUint64(ap[0], 1) // ERROR "direct assignment to atomic value"
+ *ap[1] = atomic.AddUint64(ap[0], 1)
+}
"fmt"
"go/ast"
"go/parser"
+ "go/printer"
"go/token"
"io"
"os"
vetStructTags = flag.Bool("structtags", false, "check that struct field tags have canonical format")
vetUntaggedLiteral = flag.Bool("composites", false, "check that composite literals used type-tagged elements")
vetRangeLoops = flag.Bool("rangeloops", false, "check that range loop variables are used correctly")
+ vetAtomic = flag.Bool("atomic", false, "check for common mistaken usages of the sync/atomic package")
)
// setExit sets the value for os.Exit when it is called, later. It
// Visit implements the ast.Visitor interface.
func (f *File) Visit(node ast.Node) ast.Visitor {
switch n := node.(type) {
+ case *ast.AssignStmt:
+ f.walkAssignStmt(n)
case *ast.CallExpr:
f.walkCallExpr(n)
case *ast.CompositeLit:
return f
}
+// walkCall walks an assignment statement
+func (f *File) walkAssignStmt(stmt *ast.AssignStmt) {
+ f.checkAtomicAssignment(stmt)
+}
+
// walkCall walks a call expression.
func (f *File) walkCall(call *ast.CallExpr, name string) {
f.checkFmtPrintfCall(call, name)
func (f *File) walkRangeStmt(n *ast.RangeStmt) {
checkRangeLoop(f, n)
}
+
+// goFmt returns a string representation of the expression
+func (f *File) gofmt(x ast.Expr) string {
+ f.b.Reset()
+ printer.Fprint(&f.b, f.fset, x)
+ return f.b.String()
+}