.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / analysis / lint / lint.go
1 // Package lint provides abstractions on top of go/analysis.
2 package lint
3
4 import (
5         "errors"
6         "flag"
7         "fmt"
8         "go/ast"
9         "go/build"
10         "go/token"
11         "strconv"
12         "strings"
13
14         "golang.org/x/tools/go/analysis"
15 )
16
17 type Documentation struct {
18         Title      string
19         Text       string
20         Since      string
21         NonDefault bool
22         Options    []string
23 }
24
25 func (doc *Documentation) String() string {
26         b := &strings.Builder{}
27         fmt.Fprintf(b, "%s\n\n", doc.Title)
28         if doc.Text != "" {
29                 fmt.Fprintf(b, "%s\n\n", doc.Text)
30         }
31         fmt.Fprint(b, "Available since\n    ")
32         if doc.Since == "" {
33                 fmt.Fprint(b, "unreleased")
34         } else {
35                 fmt.Fprintf(b, "%s", doc.Since)
36         }
37         if doc.NonDefault {
38                 fmt.Fprint(b, ", non-default")
39         }
40         fmt.Fprint(b, "\n")
41         if len(doc.Options) > 0 {
42                 fmt.Fprintf(b, "\nOptions\n")
43                 for _, opt := range doc.Options {
44                         fmt.Fprintf(b, "    %s", opt)
45                 }
46                 fmt.Fprint(b, "\n")
47         }
48         return b.String()
49 }
50
51 func newVersionFlag() flag.Getter {
52         tags := build.Default.ReleaseTags
53         v := tags[len(tags)-1][2:]
54         version := new(VersionFlag)
55         if err := version.Set(v); err != nil {
56                 panic(fmt.Sprintf("internal error: %s", err))
57         }
58         return version
59 }
60
61 type VersionFlag int
62
63 func (v *VersionFlag) String() string {
64         return fmt.Sprintf("1.%d", *v)
65 }
66
67 func (v *VersionFlag) Set(s string) error {
68         if len(s) < 3 {
69                 return errors.New("invalid Go version")
70         }
71         if s[0] != '1' {
72                 return errors.New("invalid Go version")
73         }
74         if s[1] != '.' {
75                 return errors.New("invalid Go version")
76         }
77         i, err := strconv.Atoi(s[2:])
78         *v = VersionFlag(i)
79         return err
80 }
81
82 func (v *VersionFlag) Get() interface{} {
83         return int(*v)
84 }
85
86 func InitializeAnalyzers(docs map[string]*Documentation, analyzers map[string]*analysis.Analyzer) map[string]*analysis.Analyzer {
87         out := make(map[string]*analysis.Analyzer, len(analyzers))
88         for k, v := range analyzers {
89                 vc := *v
90                 out[k] = &vc
91
92                 vc.Name = k
93                 doc, ok := docs[k]
94                 if !ok {
95                         panic(fmt.Sprintf("missing documentation for check %s", k))
96                 }
97                 vc.Doc = fmt.Sprintf("%s\nOnline documentation\n    https://staticcheck.io/docs/checks#%s", doc.String(), k)
98                 if vc.Flags.Usage == nil {
99                         fs := flag.NewFlagSet("", flag.PanicOnError)
100                         fs.Var(newVersionFlag(), "go", "Target Go version")
101                         vc.Flags = *fs
102                 }
103         }
104         return out
105 }
106
107 // ExhaustiveTypeSwitch panics when called. It can be used to ensure
108 // that type switches are exhaustive.
109 func ExhaustiveTypeSwitch(v interface{}) {
110         panic(fmt.Sprintf("internal error: unhandled case %T", v))
111 }
112
113 // A directive is a comment of the form '//lint:<command>
114 // [arguments...]'. It represents instructions to the static analysis
115 // tool.
116 type Directive struct {
117         Command   string
118         Arguments []string
119         Directive *ast.Comment
120         Node      ast.Node
121 }
122
123 func parseDirective(s string) (cmd string, args []string) {
124         if !strings.HasPrefix(s, "//lint:") {
125                 return "", nil
126         }
127         s = strings.TrimPrefix(s, "//lint:")
128         fields := strings.Split(s, " ")
129         return fields[0], fields[1:]
130 }
131
132 func ParseDirectives(files []*ast.File, fset *token.FileSet) []Directive {
133         var dirs []Directive
134         for _, f := range files {
135                 // OPT(dh): in our old code, we skip all the commentmap work if we
136                 // couldn't find any directives, benchmark if that's actually
137                 // worth doing
138                 cm := ast.NewCommentMap(fset, f, f.Comments)
139                 for node, cgs := range cm {
140                         for _, cg := range cgs {
141                                 for _, c := range cg.List {
142                                         if !strings.HasPrefix(c.Text, "//lint:") {
143                                                 continue
144                                         }
145                                         cmd, args := parseDirective(c.Text)
146                                         d := Directive{
147                                                 Command:   cmd,
148                                                 Arguments: args,
149                                                 Directive: c,
150                                                 Node:      node,
151                                         }
152                                         dirs = append(dirs, d)
153                                 }
154                         }
155                 }
156         }
157         return dirs
158 }