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 / facts / purity.go
1 package facts
2
3 import (
4         "go/types"
5         "reflect"
6
7         "golang.org/x/tools/go/analysis"
8         "honnef.co/go/tools/functions"
9         "honnef.co/go/tools/internal/passes/buildir"
10         "honnef.co/go/tools/ir"
11 )
12
13 type IsPure struct{}
14
15 func (*IsPure) AFact()           {}
16 func (d *IsPure) String() string { return "is pure" }
17
18 type PurityResult map[*types.Func]*IsPure
19
20 var Purity = &analysis.Analyzer{
21         Name:       "fact_purity",
22         Doc:        "Mark pure functions",
23         Run:        purity,
24         Requires:   []*analysis.Analyzer{buildir.Analyzer},
25         FactTypes:  []analysis.Fact{(*IsPure)(nil)},
26         ResultType: reflect.TypeOf(PurityResult{}),
27 }
28
29 var pureStdlib = map[string]struct{}{
30         "errors.New":                      {},
31         "fmt.Errorf":                      {},
32         "fmt.Sprintf":                     {},
33         "fmt.Sprint":                      {},
34         "sort.Reverse":                    {},
35         "strings.Map":                     {},
36         "strings.Repeat":                  {},
37         "strings.Replace":                 {},
38         "strings.Title":                   {},
39         "strings.ToLower":                 {},
40         "strings.ToLowerSpecial":          {},
41         "strings.ToTitle":                 {},
42         "strings.ToTitleSpecial":          {},
43         "strings.ToUpper":                 {},
44         "strings.ToUpperSpecial":          {},
45         "strings.Trim":                    {},
46         "strings.TrimFunc":                {},
47         "strings.TrimLeft":                {},
48         "strings.TrimLeftFunc":            {},
49         "strings.TrimPrefix":              {},
50         "strings.TrimRight":               {},
51         "strings.TrimRightFunc":           {},
52         "strings.TrimSpace":               {},
53         "strings.TrimSuffix":              {},
54         "(*net/http.Request).WithContext": {},
55 }
56
57 func purity(pass *analysis.Pass) (interface{}, error) {
58         seen := map[*ir.Function]struct{}{}
59         irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg
60         var check func(fn *ir.Function) (ret bool)
61         check = func(fn *ir.Function) (ret bool) {
62                 if fn.Object() == nil {
63                         // TODO(dh): support closures
64                         return false
65                 }
66                 if pass.ImportObjectFact(fn.Object(), new(IsPure)) {
67                         return true
68                 }
69                 if fn.Pkg != irpkg {
70                         // Function is in another package but wasn't marked as
71                         // pure, ergo it isn't pure
72                         return false
73                 }
74                 // Break recursion
75                 if _, ok := seen[fn]; ok {
76                         return false
77                 }
78
79                 seen[fn] = struct{}{}
80                 defer func() {
81                         if ret {
82                                 pass.ExportObjectFact(fn.Object(), &IsPure{})
83                         }
84                 }()
85
86                 if functions.IsStub(fn) {
87                         return false
88                 }
89
90                 if _, ok := pureStdlib[fn.Object().(*types.Func).FullName()]; ok {
91                         return true
92                 }
93
94                 if fn.Signature.Results().Len() == 0 {
95                         // A function with no return values is empty or is doing some
96                         // work we cannot see (for example because of build tags);
97                         // don't consider it pure.
98                         return false
99                 }
100
101                 for _, param := range fn.Params {
102                         // TODO(dh): this may not be strictly correct. pure code
103                         // can, to an extent, operate on non-basic types.
104                         if _, ok := param.Type().Underlying().(*types.Basic); !ok {
105                                 return false
106                         }
107                 }
108
109                 // Don't consider external functions pure.
110                 if fn.Blocks == nil {
111                         return false
112                 }
113                 checkCall := func(common *ir.CallCommon) bool {
114                         if common.IsInvoke() {
115                                 return false
116                         }
117                         builtin, ok := common.Value.(*ir.Builtin)
118                         if !ok {
119                                 if common.StaticCallee() != fn {
120                                         if common.StaticCallee() == nil {
121                                                 return false
122                                         }
123                                         if !check(common.StaticCallee()) {
124                                                 return false
125                                         }
126                                 }
127                         } else {
128                                 switch builtin.Name() {
129                                 case "len", "cap":
130                                 default:
131                                         return false
132                                 }
133                         }
134                         return true
135                 }
136                 for _, b := range fn.Blocks {
137                         for _, ins := range b.Instrs {
138                                 switch ins := ins.(type) {
139                                 case *ir.Call:
140                                         if !checkCall(ins.Common()) {
141                                                 return false
142                                         }
143                                 case *ir.Defer:
144                                         if !checkCall(&ins.Call) {
145                                                 return false
146                                         }
147                                 case *ir.Select:
148                                         return false
149                                 case *ir.Send:
150                                         return false
151                                 case *ir.Go:
152                                         return false
153                                 case *ir.Panic:
154                                         return false
155                                 case *ir.Store:
156                                         return false
157                                 case *ir.FieldAddr:
158                                         return false
159                                 case *ir.Alloc:
160                                         return false
161                                 case *ir.Load:
162                                         return false
163                                 }
164                         }
165                 }
166                 return true
167         }
168         for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
169                 check(fn)
170         }
171
172         out := PurityResult{}
173         for _, fact := range pass.AllObjectFacts() {
174                 out[fact.Object.(*types.Func)] = fact.Fact.(*IsPure)
175         }
176         return out, nil
177 }