Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / analysis / passes / composite / composite.go
1 // Copyright 2012 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 composite defines an Analyzer that checks for unkeyed
6 // composite literals.
7 package composite
8
9 import (
10         "go/ast"
11         "go/types"
12         "strings"
13
14         "golang.org/x/tools/go/analysis"
15         "golang.org/x/tools/go/analysis/passes/inspect"
16         "golang.org/x/tools/go/ast/inspector"
17 )
18
19 const Doc = `check for unkeyed composite literals
20
21 This analyzer reports a diagnostic for composite literals of struct
22 types imported from another package that do not use the field-keyed
23 syntax. Such literals are fragile because the addition of a new field
24 (even if unexported) to the struct will cause compilation to fail.
25
26 As an example,
27
28         err = &net.DNSConfigError{err}
29
30 should be replaced by:
31
32         err = &net.DNSConfigError{Err: err}
33 `
34
35 var Analyzer = &analysis.Analyzer{
36         Name:             "composites",
37         Doc:              Doc,
38         Requires:         []*analysis.Analyzer{inspect.Analyzer},
39         RunDespiteErrors: true,
40         Run:              run,
41 }
42
43 var whitelist = true
44
45 func init() {
46         Analyzer.Flags.BoolVar(&whitelist, "whitelist", whitelist, "use composite white list; for testing only")
47 }
48
49 // runUnkeyedLiteral checks if a composite literal is a struct literal with
50 // unkeyed fields.
51 func run(pass *analysis.Pass) (interface{}, error) {
52         inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
53
54         nodeFilter := []ast.Node{
55                 (*ast.CompositeLit)(nil),
56         }
57         inspect.Preorder(nodeFilter, func(n ast.Node) {
58                 cl := n.(*ast.CompositeLit)
59
60                 typ := pass.TypesInfo.Types[cl].Type
61                 if typ == nil {
62                         // cannot determine composite literals' type, skip it
63                         return
64                 }
65                 typeName := typ.String()
66                 if whitelist && unkeyedLiteral[typeName] {
67                         // skip whitelisted types
68                         return
69                 }
70                 under := typ.Underlying()
71                 for {
72                         ptr, ok := under.(*types.Pointer)
73                         if !ok {
74                                 break
75                         }
76                         under = ptr.Elem().Underlying()
77                 }
78                 if _, ok := under.(*types.Struct); !ok {
79                         // skip non-struct composite literals
80                         return
81                 }
82                 if isLocalType(pass, typ) {
83                         // allow unkeyed locally defined composite literal
84                         return
85                 }
86
87                 // check if the CompositeLit contains an unkeyed field
88                 allKeyValue := true
89                 for _, e := range cl.Elts {
90                         if _, ok := e.(*ast.KeyValueExpr); !ok {
91                                 allKeyValue = false
92                                 break
93                         }
94                 }
95                 if allKeyValue {
96                         // all the composite literal fields are keyed
97                         return
98                 }
99
100                 pass.ReportRangef(cl, "%s composite literal uses unkeyed fields", typeName)
101         })
102         return nil, nil
103 }
104
105 func isLocalType(pass *analysis.Pass, typ types.Type) bool {
106         switch x := typ.(type) {
107         case *types.Struct:
108                 // struct literals are local types
109                 return true
110         case *types.Pointer:
111                 return isLocalType(pass, x.Elem())
112         case *types.Named:
113                 // names in package foo are local to foo_test too
114                 return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
115         }
116         return false
117 }