Home | History | Annotate | Download | only in vet
      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 main
      6 
      7 import (
      8 	"go/ast"
      9 	"go/token"
     10 )
     11 
     12 func 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)
     21 func 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
     53 func (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