...
1
2
3
4
5
6
7
8
9
10 package atomicalign
11
12 import (
13 "go/ast"
14 "go/token"
15 "go/types"
16
17 "golang.org/x/tools/go/analysis"
18 "golang.org/x/tools/go/analysis/passes/inspect"
19 "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
20 "golang.org/x/tools/go/ast/inspector"
21 )
22
23 const Doc = "check for non-64-bits-aligned arguments to sync/atomic functions"
24
25 var Analyzer = &analysis.Analyzer{
26 Name: "atomicalign",
27 Doc: Doc,
28 Requires: []*analysis.Analyzer{inspect.Analyzer},
29 Run: run,
30 }
31
32 func run(pass *analysis.Pass) (interface{}, error) {
33 if 8*pass.TypesSizes.Sizeof(types.Typ[types.Uintptr]) == 64 {
34 return nil, nil
35 }
36 if !analysisutil.Imports(pass.Pkg, "sync/atomic") {
37 return nil, nil
38 }
39
40 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
41 nodeFilter := []ast.Node{
42 (*ast.CallExpr)(nil),
43 }
44
45 inspect.Preorder(nodeFilter, func(node ast.Node) {
46 call := node.(*ast.CallExpr)
47 sel, ok := call.Fun.(*ast.SelectorExpr)
48 if !ok {
49 return
50 }
51 pkgIdent, ok := sel.X.(*ast.Ident)
52 if !ok {
53 return
54 }
55 pkgName, ok := pass.TypesInfo.Uses[pkgIdent].(*types.PkgName)
56 if !ok || pkgName.Imported().Path() != "sync/atomic" {
57 return
58 }
59
60 switch sel.Sel.Name {
61 case "AddInt64", "AddUint64",
62 "LoadInt64", "LoadUint64",
63 "StoreInt64", "StoreUint64",
64 "SwapInt64", "SwapUint64",
65 "CompareAndSwapInt64", "CompareAndSwapUint64":
66
67
68 check64BitAlignment(pass, sel.Sel.Name, call.Args[0])
69 }
70 })
71
72 return nil, nil
73 }
74
75 func check64BitAlignment(pass *analysis.Pass, funcName string, arg ast.Expr) {
76
77
78
79 unary, ok := arg.(*ast.UnaryExpr)
80 if !ok || unary.Op != token.AND {
81 return
82 }
83
84
85
86 sel, ok := unary.X.(*ast.SelectorExpr)
87 if !ok {
88 return
89 }
90 tvar, ok := pass.TypesInfo.Selections[sel].Obj().(*types.Var)
91 if !ok || !tvar.IsField() {
92 return
93 }
94
95 stype, ok := pass.TypesInfo.Types[sel.X].Type.Underlying().(*types.Struct)
96 if !ok {
97 return
98 }
99
100 var offset int64
101 var fields []*types.Var
102 for i := 0; i < stype.NumFields(); i++ {
103 f := stype.Field(i)
104 fields = append(fields, f)
105 if f == tvar {
106
107
108 offset = pass.TypesSizes.Offsetsof(fields)[i]
109 break
110 }
111 }
112 if offset&7 == 0 {
113 return
114 }
115
116 pass.ReportRangef(arg, "address of non 64-bit aligned field .%s passed to atomic.%s", tvar.Name(), funcName)
117 }
118
View as plain text