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 / report / report.go
1 package report
2
3 import (
4         "bytes"
5         "go/ast"
6         "go/printer"
7         "go/token"
8         "strings"
9
10         "golang.org/x/tools/go/analysis"
11         "golang.org/x/tools/go/ast/astutil"
12         "honnef.co/go/tools/facts"
13         "honnef.co/go/tools/lint"
14 )
15
16 type Options struct {
17         ShortRange      bool
18         FilterGenerated bool
19         Fixes           []analysis.SuggestedFix
20         Related         []analysis.RelatedInformation
21 }
22
23 type Option func(*Options)
24
25 func ShortRange() Option {
26         return func(opts *Options) {
27                 opts.ShortRange = true
28         }
29 }
30
31 func FilterGenerated() Option {
32         return func(opts *Options) {
33                 opts.FilterGenerated = true
34         }
35 }
36
37 func Fixes(fixes ...analysis.SuggestedFix) Option {
38         return func(opts *Options) {
39                 opts.Fixes = append(opts.Fixes, fixes...)
40         }
41 }
42
43 func Related(node Positioner, message string) Option {
44         return func(opts *Options) {
45                 pos, end := getRange(node, opts.ShortRange)
46                 r := analysis.RelatedInformation{
47                         Pos:     pos,
48                         End:     end,
49                         Message: message,
50                 }
51                 opts.Related = append(opts.Related, r)
52         }
53 }
54
55 type Positioner interface {
56         Pos() token.Pos
57 }
58
59 type fullPositioner interface {
60         Pos() token.Pos
61         End() token.Pos
62 }
63
64 type sourcer interface {
65         Source() ast.Node
66 }
67
68 // shortRange returns the position and end of the main component of an
69 // AST node. For nodes that have no body, the short range is identical
70 // to the node's Pos and End. For nodes that do have a body, the short
71 // range excludes the body.
72 func shortRange(node ast.Node) (pos, end token.Pos) {
73         switch node := node.(type) {
74         case *ast.File:
75                 return node.Pos(), node.Name.End()
76         case *ast.CaseClause:
77                 return node.Pos(), node.Colon + 1
78         case *ast.CommClause:
79                 return node.Pos(), node.Colon + 1
80         case *ast.DeferStmt:
81                 return node.Pos(), node.Defer + token.Pos(len("defer"))
82         case *ast.ExprStmt:
83                 return shortRange(node.X)
84         case *ast.ForStmt:
85                 if node.Post != nil {
86                         return node.For, node.Post.End()
87                 } else if node.Cond != nil {
88                         return node.For, node.Cond.End()
89                 } else if node.Init != nil {
90                         // +1 to catch the semicolon, for gofmt'ed code
91                         return node.Pos(), node.Init.End() + 1
92                 } else {
93                         return node.Pos(), node.For + token.Pos(len("for"))
94                 }
95         case *ast.FuncDecl:
96                 return node.Pos(), node.Type.End()
97         case *ast.FuncLit:
98                 return node.Pos(), node.Type.End()
99         case *ast.GoStmt:
100                 if _, ok := astutil.Unparen(node.Call.Fun).(*ast.FuncLit); ok {
101                         return node.Pos(), node.Go + token.Pos(len("go"))
102                 } else {
103                         return node.Pos(), node.End()
104                 }
105         case *ast.IfStmt:
106                 return node.Pos(), node.Cond.End()
107         case *ast.RangeStmt:
108                 return node.Pos(), node.X.End()
109         case *ast.SelectStmt:
110                 return node.Pos(), node.Pos() + token.Pos(len("select"))
111         case *ast.SwitchStmt:
112                 if node.Tag != nil {
113                         return node.Pos(), node.Tag.End()
114                 } else if node.Init != nil {
115                         // +1 to catch the semicolon, for gofmt'ed code
116                         return node.Pos(), node.Init.End() + 1
117                 } else {
118                         return node.Pos(), node.Pos() + token.Pos(len("switch"))
119                 }
120         case *ast.TypeSwitchStmt:
121                 return node.Pos(), node.Assign.End()
122         default:
123                 return node.Pos(), node.End()
124         }
125 }
126
127 func getRange(node Positioner, short bool) (pos, end token.Pos) {
128         switch node := node.(type) {
129         case sourcer:
130                 s := node.Source()
131                 if short {
132                         return shortRange(s)
133                 }
134                 return s.Pos(), s.End()
135         case fullPositioner:
136                 if short {
137                         return shortRange(node)
138                 }
139                 return node.Pos(), node.End()
140         default:
141                 return node.Pos(), token.NoPos
142         }
143 }
144
145 func Report(pass *analysis.Pass, node Positioner, message string, opts ...Option) {
146         cfg := &Options{}
147         for _, opt := range opts {
148                 opt(cfg)
149         }
150
151         file := lint.DisplayPosition(pass.Fset, node.Pos()).Filename
152         if cfg.FilterGenerated {
153                 m := pass.ResultOf[facts.Generated].(map[string]facts.Generator)
154                 if _, ok := m[file]; ok {
155                         return
156                 }
157         }
158
159         pos, end := getRange(node, cfg.ShortRange)
160         d := analysis.Diagnostic{
161                 Pos:            pos,
162                 End:            end,
163                 Message:        message,
164                 SuggestedFixes: cfg.Fixes,
165                 Related:        cfg.Related,
166         }
167         pass.Report(d)
168 }
169
170 func Render(pass *analysis.Pass, x interface{}) string {
171         var buf bytes.Buffer
172         if err := printer.Fprint(&buf, pass.Fset, x); err != nil {
173                 panic(err)
174         }
175         return buf.String()
176 }
177
178 func RenderArgs(pass *analysis.Pass, args []ast.Expr) string {
179         var ss []string
180         for _, arg := range args {
181                 ss = append(ss, Render(pass, arg))
182         }
183         return strings.Join(ss, ", ")
184 }