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