.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / analysis / report / report.go
1 package report
2
3 import (
4         "bytes"
5         "go/ast"
6         "go/printer"
7         "go/token"
8         "path/filepath"
9         "strconv"
10         "strings"
11
12         "honnef.co/go/tools/analysis/facts"
13
14         "golang.org/x/tools/go/analysis"
15         "golang.org/x/tools/go/ast/astutil"
16 )
17
18 type Options struct {
19         ShortRange      bool
20         FilterGenerated bool
21         Fixes           []analysis.SuggestedFix
22         Related         []analysis.RelatedInformation
23 }
24
25 type Option func(*Options)
26
27 func ShortRange() Option {
28         return func(opts *Options) {
29                 opts.ShortRange = true
30         }
31 }
32
33 func FilterGenerated() Option {
34         return func(opts *Options) {
35                 opts.FilterGenerated = true
36         }
37 }
38
39 func Fixes(fixes ...analysis.SuggestedFix) Option {
40         return func(opts *Options) {
41                 opts.Fixes = append(opts.Fixes, fixes...)
42         }
43 }
44
45 func Related(node Positioner, message string) Option {
46         return func(opts *Options) {
47                 pos, end := getRange(node, opts.ShortRange)
48                 r := analysis.RelatedInformation{
49                         Pos:     pos,
50                         End:     end,
51                         Message: message,
52                 }
53                 opts.Related = append(opts.Related, r)
54         }
55 }
56
57 type Positioner interface {
58         Pos() token.Pos
59 }
60
61 type fullPositioner interface {
62         Pos() token.Pos
63         End() token.Pos
64 }
65
66 type sourcer interface {
67         Source() ast.Node
68 }
69
70 // shortRange returns the position and end of the main component of an
71 // AST node. For nodes that have no body, the short range is identical
72 // to the node's Pos and End. For nodes that do have a body, the short
73 // range excludes the body.
74 func shortRange(node ast.Node) (pos, end token.Pos) {
75         switch node := node.(type) {
76         case *ast.File:
77                 return node.Pos(), node.Name.End()
78         case *ast.CaseClause:
79                 return node.Pos(), node.Colon + 1
80         case *ast.CommClause:
81                 return node.Pos(), node.Colon + 1
82         case *ast.DeferStmt:
83                 return node.Pos(), node.Defer + token.Pos(len("defer"))
84         case *ast.ExprStmt:
85                 return shortRange(node.X)
86         case *ast.ForStmt:
87                 if node.Post != nil {
88                         return node.For, node.Post.End()
89                 } else if node.Cond != nil {
90                         return node.For, node.Cond.End()
91                 } else if node.Init != nil {
92                         // +1 to catch the semicolon, for gofmt'ed code
93                         return node.Pos(), node.Init.End() + 1
94                 } else {
95                         return node.Pos(), node.For + token.Pos(len("for"))
96                 }
97         case *ast.FuncDecl:
98                 return node.Pos(), node.Type.End()
99         case *ast.FuncLit:
100                 return node.Pos(), node.Type.End()
101         case *ast.GoStmt:
102                 if _, ok := astutil.Unparen(node.Call.Fun).(*ast.FuncLit); ok {
103                         return node.Pos(), node.Go + token.Pos(len("go"))
104                 } else {
105                         return node.Pos(), node.End()
106                 }
107         case *ast.IfStmt:
108                 return node.Pos(), node.Cond.End()
109         case *ast.RangeStmt:
110                 return node.Pos(), node.X.End()
111         case *ast.SelectStmt:
112                 return node.Pos(), node.Pos() + token.Pos(len("select"))
113         case *ast.SwitchStmt:
114                 if node.Tag != nil {
115                         return node.Pos(), node.Tag.End()
116                 } else if node.Init != nil {
117                         // +1 to catch the semicolon, for gofmt'ed code
118                         return node.Pos(), node.Init.End() + 1
119                 } else {
120                         return node.Pos(), node.Pos() + token.Pos(len("switch"))
121                 }
122         case *ast.TypeSwitchStmt:
123                 return node.Pos(), node.Assign.End()
124         default:
125                 return node.Pos(), node.End()
126         }
127 }
128
129 func getRange(node Positioner, short bool) (pos, end token.Pos) {
130         switch node := node.(type) {
131         case sourcer:
132                 s := node.Source()
133                 if short {
134                         return shortRange(s)
135                 }
136                 return s.Pos(), s.End()
137         case fullPositioner:
138                 if short {
139                         return shortRange(node)
140                 }
141                 return node.Pos(), node.End()
142         default:
143                 return node.Pos(), token.NoPos
144         }
145 }
146
147 func Report(pass *analysis.Pass, node Positioner, message string, opts ...Option) {
148         cfg := &Options{}
149         for _, opt := range opts {
150                 opt(cfg)
151         }
152
153         file := DisplayPosition(pass.Fset, node.Pos()).Filename
154         if cfg.FilterGenerated {
155                 m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
156                 if _, ok := m[file]; ok {
157                         return
158                 }
159         }
160
161         pos, end := getRange(node, cfg.ShortRange)
162         d := analysis.Diagnostic{
163                 Pos:            pos,
164                 End:            end,
165                 Message:        message,
166                 SuggestedFixes: cfg.Fixes,
167                 Related:        cfg.Related,
168         }
169         pass.Report(d)
170 }
171
172 func Render(pass *analysis.Pass, x interface{}) string {
173         var buf bytes.Buffer
174         if err := printer.Fprint(&buf, pass.Fset, x); err != nil {
175                 panic(err)
176         }
177         return buf.String()
178 }
179
180 func RenderArgs(pass *analysis.Pass, args []ast.Expr) string {
181         var ss []string
182         for _, arg := range args {
183                 ss = append(ss, Render(pass, arg))
184         }
185         return strings.Join(ss, ", ")
186 }
187
188 func DisplayPosition(fset *token.FileSet, p token.Pos) token.Position {
189         if p == token.NoPos {
190                 return token.Position{}
191         }
192
193         // Only use the adjusted position if it points to another Go file.
194         // This means we'll point to the original file for cgo files, but
195         // we won't point to a YACC grammar file.
196         pos := fset.PositionFor(p, false)
197         adjPos := fset.PositionFor(p, true)
198
199         if filepath.Ext(adjPos.Filename) == ".go" {
200                 return adjPos
201         }
202
203         return pos
204 }
205
206 func Ordinal(n int) string {
207         suffix := "th"
208         if n < 10 || n > 20 {
209                 switch n % 10 {
210                 case 0:
211                         suffix = "th"
212                 case 1:
213                         suffix = "st"
214                 case 2:
215                         suffix = "nd"
216                 case 3:
217                         suffix = "rd"
218                 default:
219                         suffix = "th"
220                 }
221         }
222
223         return strconv.Itoa(n) + suffix
224 }