Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / config / config.go
1 package config
2
3 import (
4         "bytes"
5         "fmt"
6         "go/ast"
7         "go/token"
8         "os"
9         "path/filepath"
10         "reflect"
11         "strings"
12
13         "github.com/BurntSushi/toml"
14         "golang.org/x/tools/go/analysis"
15 )
16
17 // Dir looks at a list of absolute file names, which should make up a
18 // single package, and returns the path of the directory that may
19 // contain a staticcheck.conf file. It returns the empty string if no
20 // such directory could be determined, for example because all files
21 // were located in Go's build cache.
22 func Dir(files []string) string {
23         if len(files) == 0 {
24                 return ""
25         }
26         cache, err := os.UserCacheDir()
27         if err != nil {
28                 cache = ""
29         }
30         var path string
31         for _, p := range files {
32                 // FIXME(dh): using strings.HasPrefix isn't technically
33                 // correct, but it should be good enough for now.
34                 if cache != "" && strings.HasPrefix(p, cache) {
35                         // File in the build cache of the standard Go build system
36                         continue
37                 }
38                 path = p
39                 break
40         }
41
42         if path == "" {
43                 // The package only consists of generated files.
44                 return ""
45         }
46
47         dir := filepath.Dir(path)
48         return dir
49 }
50
51 func dirAST(files []*ast.File, fset *token.FileSet) string {
52         names := make([]string, len(files))
53         for i, f := range files {
54                 names[i] = fset.PositionFor(f.Pos(), true).Filename
55         }
56         return Dir(names)
57 }
58
59 var Analyzer = &analysis.Analyzer{
60         Name: "config",
61         Doc:  "loads configuration for the current package tree",
62         Run: func(pass *analysis.Pass) (interface{}, error) {
63                 dir := dirAST(pass.Files, pass.Fset)
64                 if dir == "" {
65                         cfg := DefaultConfig
66                         return &cfg, nil
67                 }
68                 cfg, err := Load(dir)
69                 if err != nil {
70                         return nil, fmt.Errorf("error loading staticcheck.conf: %s", err)
71                 }
72                 return &cfg, nil
73         },
74         RunDespiteErrors: true,
75         ResultType:       reflect.TypeOf((*Config)(nil)),
76 }
77
78 func For(pass *analysis.Pass) *Config {
79         return pass.ResultOf[Analyzer].(*Config)
80 }
81
82 func mergeLists(a, b []string) []string {
83         out := make([]string, 0, len(a)+len(b))
84         for _, el := range b {
85                 if el == "inherit" {
86                         out = append(out, a...)
87                 } else {
88                         out = append(out, el)
89                 }
90         }
91
92         return out
93 }
94
95 func normalizeList(list []string) []string {
96         if len(list) > 1 {
97                 nlist := make([]string, 0, len(list))
98                 nlist = append(nlist, list[0])
99                 for i, el := range list[1:] {
100                         if el != list[i] {
101                                 nlist = append(nlist, el)
102                         }
103                 }
104                 list = nlist
105         }
106
107         for _, el := range list {
108                 if el == "inherit" {
109                         // This should never happen, because the default config
110                         // should not use "inherit"
111                         panic(`unresolved "inherit"`)
112                 }
113         }
114
115         return list
116 }
117
118 func (cfg Config) Merge(ocfg Config) Config {
119         if ocfg.Checks != nil {
120                 cfg.Checks = mergeLists(cfg.Checks, ocfg.Checks)
121         }
122         if ocfg.Initialisms != nil {
123                 cfg.Initialisms = mergeLists(cfg.Initialisms, ocfg.Initialisms)
124         }
125         if ocfg.DotImportWhitelist != nil {
126                 cfg.DotImportWhitelist = mergeLists(cfg.DotImportWhitelist, ocfg.DotImportWhitelist)
127         }
128         if ocfg.HTTPStatusCodeWhitelist != nil {
129                 cfg.HTTPStatusCodeWhitelist = mergeLists(cfg.HTTPStatusCodeWhitelist, ocfg.HTTPStatusCodeWhitelist)
130         }
131         return cfg
132 }
133
134 type Config struct {
135         // TODO(dh): this implementation makes it impossible for external
136         // clients to add their own checkers with configuration. At the
137         // moment, we don't really care about that; we don't encourage
138         // that people use this package. In the future, we may. The
139         // obvious solution would be using map[string]interface{}, but
140         // that's obviously subpar.
141
142         Checks                  []string `toml:"checks"`
143         Initialisms             []string `toml:"initialisms"`
144         DotImportWhitelist      []string `toml:"dot_import_whitelist"`
145         HTTPStatusCodeWhitelist []string `toml:"http_status_code_whitelist"`
146 }
147
148 func (c Config) String() string {
149         buf := &bytes.Buffer{}
150
151         fmt.Fprintf(buf, "Checks: %#v\n", c.Checks)
152         fmt.Fprintf(buf, "Initialisms: %#v\n", c.Initialisms)
153         fmt.Fprintf(buf, "DotImportWhitelist: %#v\n", c.DotImportWhitelist)
154         fmt.Fprintf(buf, "HTTPStatusCodeWhitelist: %#v", c.HTTPStatusCodeWhitelist)
155
156         return buf.String()
157 }
158
159 var DefaultConfig = Config{
160         Checks: []string{"all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022"},
161         Initialisms: []string{
162                 "ACL", "API", "ASCII", "CPU", "CSS", "DNS",
163                 "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID",
164                 "IP", "JSON", "QPS", "RAM", "RPC", "SLA",
165                 "SMTP", "SQL", "SSH", "TCP", "TLS", "TTL",
166                 "UDP", "UI", "GID", "UID", "UUID", "URI",
167                 "URL", "UTF8", "VM", "XML", "XMPP", "XSRF",
168                 "XSS", "SIP", "RTP", "AMQP", "DB", "TS",
169         },
170         DotImportWhitelist:      []string{},
171         HTTPStatusCodeWhitelist: []string{"200", "400", "404", "500"},
172 }
173
174 const ConfigName = "staticcheck.conf"
175
176 func parseConfigs(dir string) ([]Config, error) {
177         var out []Config
178
179         // TODO(dh): consider stopping at the GOPATH/module boundary
180         for dir != "" {
181                 f, err := os.Open(filepath.Join(dir, ConfigName))
182                 if os.IsNotExist(err) {
183                         ndir := filepath.Dir(dir)
184                         if ndir == dir {
185                                 break
186                         }
187                         dir = ndir
188                         continue
189                 }
190                 if err != nil {
191                         return nil, err
192                 }
193                 var cfg Config
194                 _, err = toml.DecodeReader(f, &cfg)
195                 f.Close()
196                 if err != nil {
197                         return nil, err
198                 }
199                 out = append(out, cfg)
200                 ndir := filepath.Dir(dir)
201                 if ndir == dir {
202                         break
203                 }
204                 dir = ndir
205         }
206         out = append(out, DefaultConfig)
207         if len(out) < 2 {
208                 return out, nil
209         }
210         for i := 0; i < len(out)/2; i++ {
211                 out[i], out[len(out)-1-i] = out[len(out)-1-i], out[i]
212         }
213         return out, nil
214 }
215
216 func mergeConfigs(confs []Config) Config {
217         if len(confs) == 0 {
218                 // This shouldn't happen because we always have at least a
219                 // default config.
220                 panic("trying to merge zero configs")
221         }
222         if len(confs) == 1 {
223                 return confs[0]
224         }
225         conf := confs[0]
226         for _, oconf := range confs[1:] {
227                 conf = conf.Merge(oconf)
228         }
229         return conf
230 }
231
232 func Load(dir string) (Config, error) {
233         confs, err := parseConfigs(dir)
234         if err != nil {
235                 return Config{}, err
236         }
237         conf := mergeConfigs(confs)
238
239         conf.Checks = normalizeList(conf.Checks)
240         conf.Initialisms = normalizeList(conf.Initialisms)
241         conf.DotImportWhitelist = normalizeList(conf.DotImportWhitelist)
242         conf.HTTPStatusCodeWhitelist = normalizeList(conf.HTTPStatusCodeWhitelist)
243
244         return conf, nil
245 }