Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / passes / atomicalign / atomicalign.go
1 // Copyright 2019 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 atomicalign defines an Analyzer that checks for non-64-bit-aligned
6 // arguments to sync/atomic functions. On non-32-bit platforms, those functions
7 // panic if their argument variables are not 64-bit aligned. It is therefore
8 // the caller's responsibility to arrange for 64-bit alignment of such variables.
9 // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG
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 // 64-bit platform
35         }
36         if !analysisutil.Imports(pass.Pkg, "sync/atomic") {
37                 return nil, nil // doesn't directly import sync/atomic
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                         // For all the listed functions, the expression to check is always the first function argument.
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         // Checks the argument is made of the address operator (&) applied to
77         // to a struct field (as opposed to a variable as the first word of
78         // uint64 and int64 variables can be relied upon to be 64-bit aligned.
79         unary, ok := arg.(*ast.UnaryExpr)
80         if !ok || unary.Op != token.AND {
81                 return
82         }
83
84         // Retrieve the types.Struct in order to get the offset of the
85         // atomically accessed field.
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                         // We're done, this is the field we were looking for,
107                         // no need to fill the fields slice further.
108                         offset = pass.TypesSizes.Offsetsof(fields)[i]
109                         break
110                 }
111         }
112         if offset&7 == 0 {
113                 return // 64-bit aligned
114         }
115
116         pass.ReportRangef(arg, "address of non 64-bit aligned field .%s passed to atomic.%s", tvar.Name(), funcName)
117 }