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