...

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

View as plain text