Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / go / analysis / passes / httpresponse / httpresponse.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/analysis/passes/httpresponse/httpresponse.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/golang.org/x/tools@v0.0.0-20201105173854-bc9fc8d8c4bc/go/analysis/passes/httpresponse/httpresponse.go
new file mode 100644 (file)
index 0000000..fd9e2af
--- /dev/null
@@ -0,0 +1,169 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package httpresponse defines an Analyzer that checks for mistakes
+// using HTTP responses.
+package httpresponse
+
+import (
+       "go/ast"
+       "go/types"
+
+       "golang.org/x/tools/go/analysis"
+       "golang.org/x/tools/go/analysis/passes/inspect"
+       "golang.org/x/tools/go/analysis/passes/internal/analysisutil"
+       "golang.org/x/tools/go/ast/inspector"
+)
+
+const Doc = `check for mistakes using HTTP responses
+
+A common mistake when using the net/http package is to defer a function
+call to close the http.Response Body before checking the error that
+determines whether the response is valid:
+
+       resp, err := http.Head(url)
+       defer resp.Body.Close()
+       if err != nil {
+               log.Fatal(err)
+       }
+       // (defer statement belongs here)
+
+This checker helps uncover latent nil dereference bugs by reporting a
+diagnostic for such mistakes.`
+
+var Analyzer = &analysis.Analyzer{
+       Name:     "httpresponse",
+       Doc:      Doc,
+       Requires: []*analysis.Analyzer{inspect.Analyzer},
+       Run:      run,
+}
+
+func run(pass *analysis.Pass) (interface{}, error) {
+       inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+       // Fast path: if the package doesn't import net/http,
+       // skip the traversal.
+       if !analysisutil.Imports(pass.Pkg, "net/http") {
+               return nil, nil
+       }
+
+       nodeFilter := []ast.Node{
+               (*ast.CallExpr)(nil),
+       }
+       inspect.WithStack(nodeFilter, func(n ast.Node, push bool, stack []ast.Node) bool {
+               if !push {
+                       return true
+               }
+               call := n.(*ast.CallExpr)
+               if !isHTTPFuncOrMethodOnClient(pass.TypesInfo, call) {
+                       return true // the function call is not related to this check.
+               }
+
+               // Find the innermost containing block, and get the list
+               // of statements starting with the one containing call.
+               stmts := restOfBlock(stack)
+               if len(stmts) < 2 {
+                       return true // the call to the http function is the last statement of the block.
+               }
+
+               asg, ok := stmts[0].(*ast.AssignStmt)
+               if !ok {
+                       return true // the first statement is not assignment.
+               }
+               resp := rootIdent(asg.Lhs[0])
+               if resp == nil {
+                       return true // could not find the http.Response in the assignment.
+               }
+
+               def, ok := stmts[1].(*ast.DeferStmt)
+               if !ok {
+                       return true // the following statement is not a defer.
+               }
+               root := rootIdent(def.Call.Fun)
+               if root == nil {
+                       return true // could not find the receiver of the defer call.
+               }
+
+               if resp.Obj == root.Obj {
+                       pass.ReportRangef(root, "using %s before checking for errors", resp.Name)
+               }
+               return true
+       })
+       return nil, nil
+}
+
+// isHTTPFuncOrMethodOnClient checks whether the given call expression is on
+// either a function of the net/http package or a method of http.Client that
+// returns (*http.Response, error).
+func isHTTPFuncOrMethodOnClient(info *types.Info, expr *ast.CallExpr) bool {
+       fun, _ := expr.Fun.(*ast.SelectorExpr)
+       sig, _ := info.Types[fun].Type.(*types.Signature)
+       if sig == nil {
+               return false // the call is not of the form x.f()
+       }
+
+       res := sig.Results()
+       if res.Len() != 2 {
+               return false // the function called does not return two values.
+       }
+       if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !isNamedType(ptr.Elem(), "net/http", "Response") {
+               return false // the first return type is not *http.Response.
+       }
+
+       errorType := types.Universe.Lookup("error").Type()
+       if !types.Identical(res.At(1).Type(), errorType) {
+               return false // the second return type is not error
+       }
+
+       typ := info.Types[fun.X].Type
+       if typ == nil {
+               id, ok := fun.X.(*ast.Ident)
+               return ok && id.Name == "http" // function in net/http package.
+       }
+
+       if isNamedType(typ, "net/http", "Client") {
+               return true // method on http.Client.
+       }
+       ptr, ok := typ.(*types.Pointer)
+       return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
+}
+
+// restOfBlock, given a traversal stack, finds the innermost containing
+// block and returns the suffix of its statements starting with the
+// current node (the last element of stack).
+func restOfBlock(stack []ast.Node) []ast.Stmt {
+       for i := len(stack) - 1; i >= 0; i-- {
+               if b, ok := stack[i].(*ast.BlockStmt); ok {
+                       for j, v := range b.List {
+                               if v == stack[i+1] {
+                                       return b.List[j:]
+                               }
+                       }
+                       break
+               }
+       }
+       return nil
+}
+
+// rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
+func rootIdent(n ast.Node) *ast.Ident {
+       switch n := n.(type) {
+       case *ast.SelectorExpr:
+               return rootIdent(n.X)
+       case *ast.Ident:
+               return n
+       default:
+               return nil
+       }
+}
+
+// isNamedType reports whether t is the named type path.name.
+func isNamedType(t types.Type, path, name string) bool {
+       n, ok := t.(*types.Named)
+       if !ok {
+               return false
+       }
+       obj := n.Obj()
+       return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
+}