Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / go / analysis / passes / httpresponse / httpresponse.go
1 // Copyright 2016 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Package httpresponse defines an Analyzer that checks for mistakes
6 // using HTTP responses.
7 package httpresponse
8
9 import (
10         "go/ast"
11         "go/types"
12
13         "golang.org/x/tools/go/analysis"
14         "golang.org/x/tools/go/analysis/passes/inspect"
15         "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
16         "golang.org/x/tools/go/ast/inspector"
17 )
18
19 const Doc = `check for mistakes using HTTP responses
20
21 A common mistake when using the net/http package is to defer a function
22 call to close the http.Response Body before checking the error that
23 determines whether the response is valid:
24
25         resp, err := http.Head(url)
26         defer resp.Body.Close()
27         if err != nil {
28                 log.Fatal(err)
29         }
30         // (defer statement belongs here)
31
32 This checker helps uncover latent nil dereference bugs by reporting a
33 diagnostic for such mistakes.`
34
35 var Analyzer = &analysis.Analyzer{
36         Name:     "httpresponse",
37         Doc:      Doc,
38         Requires: []*analysis.Analyzer{inspect.Analyzer},
39         Run:      run,
40 }
41
42 func run(pass *analysis.Pass) (interface{}, error) {
43         inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
44
45         // Fast path: if the package doesn't import net/http,
46         // skip the traversal.
47         if !analysisutil.Imports(pass.Pkg, "net/http") {
48                 return nil, nil
49         }
50
51         nodeFilter := []ast.Node{
52                 (*ast.CallExpr)(nil),
53         }
54         inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
55                 if !push {
56                         return true
57                 }
58                 call := n.(*ast.CallExpr)
59                 if !isHTTPFuncOrMethodOnClient(pass.TypesInfo, call) {
60                         return true // the function call is not related to this check.
61                 }
62
63                 // Find the innermost containing block, and get the list
64                 // of statements starting with the one containing call.
65                 stmts := restOfBlock(stack)
66                 if len(stmts) < 2 {
67                         return true // the call to the http function is the last statement of the block.
68                 }
69
70                 asg, ok := stmts[0].(*ast.AssignStmt)
71                 if !ok {
72                         return true // the first statement is not assignment.
73                 }
74                 resp := rootIdent(asg.Lhs[0])
75                 if resp == nil {
76                         return true // could not find the http.Response in the assignment.
77                 }
78
79                 def, ok := stmts[1].(*ast.DeferStmt)
80                 if !ok {
81                         return true // the following statement is not a defer.
82                 }
83                 root := rootIdent(def.Call.Fun)
84                 if root == nil {
85                         return true // could not find the receiver of the defer call.
86                 }
87
88                 if resp.Obj == root.Obj {
89                         pass.ReportRangef(root, "using %s before checking for errors", resp.Name)
90                 }
91                 return true
92         })
93         return nil, nil
94 }
95
96 // isHTTPFuncOrMethodOnClient checks whether the given call expression is on
97 // either a function of the net/http package or a method of http.Client that
98 // returns (*http.Response, error).
99 func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
100         fun, _ := expr.Fun.(*ast.SelectorExpr)
101         sig, _ := info.Types[fun].Type.(*types.Signature)
102         if sig == nil {
103                 return false // the call is not of the form x.f()
104         }
105
106         res := sig.Results()
107         if res.Len() != 2 {
108                 return false // the function called does not return two values.
109         }
110         if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "net/http", "Response") {
111                 return false // the first return type is not *http.Response.
112         }
113
114         errorType := types.Universe.Lookup("error").Type()
115         if !types.Identical(res.At(1).Type(), errorType) {
116                 return false // the second return type is not error
117         }
118
119         typ := info.Types[fun.X].Type
120         if typ == nil {
121                 id, ok := fun.X.(*ast.Ident)
122                 return ok && id.Name == "http" // function in net/http package.
123         }
124
125         if isNamedType(typ, "net/http", "Client") {
126                 return true // method on http.Client.
127         }
128         ptr, ok := typ.(*types.Pointer)
129         return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
130 }
131
132 // restOfBlock, given a traversal stack, finds the innermost containing
133 // block and returns the suffix of its statements starting with the
134 // current node (the last element of stack).
135 func restOfBlock(stack []ast.Node) []ast.Stmt {
136         for i := len(stack) - 1; i >= 0; i-- {
137                 if b, ok := stack[i].(*ast.BlockStmt); ok {
138                         for j, v := range b.List {
139                                 if v == stack[i+1] {
140                                         return b.List[j:]
141                                 }
142                         }
143                         break
144                 }
145         }
146         return nil
147 }
148
149 // rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
150 func rootIdent(n ast.Node) *ast.Ident {
151         switch n := n.(type) {
152         case *ast.SelectorExpr:
153                 return rootIdent(n.X)
154         case *ast.Ident:
155                 return n
156         default:
157                 return nil
158         }
159 }
160
161 // isNamedType reports whether t is the named type path.name.
162 func isNamedType(t types.Type, path, name string) bool {
163         n, ok := t.(*types.Named)
164         if !ok {
165                 return false
166         }
167         obj := n.Obj()
168         return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
169 }