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.
5 // This file contains the check for http.Response values being used before
6 // checking for errors.
16 register("httpresponse",
17 "check errors are checked before using an http Response",
18 checkHTTPResponse
, callExpr
)
21 func checkHTTPResponse(f
*File
, node ast
.Node
) {
22 call
:= node
.(*ast
.CallExpr
)
23 if !isHTTPFuncOrMethodOnClient(f
, call
) {
24 return // the function call is not related to this check.
27 finder
:= &blockStmtFinder
{node
: call
}
28 ast
.Walk(finder
, f
.file
)
29 stmts
:= finder
.stmts()
31 return // the call to the http function is the last statement of the block.
34 asg
, ok
:= stmts
[0].(*ast
.AssignStmt
)
36 return // the first statement is not assignment.
38 resp
:= rootIdent(asg
.Lhs
[0])
40 return // could not find the http.Response in the assignment.
43 def
, ok
:= stmts
[1].(*ast
.DeferStmt
)
45 return // the following statement is not a defer.
47 root
:= rootIdent(def
.Call
.Fun
)
49 return // could not find the receiver of the defer call.
52 if resp
.Obj
== root
.Obj
{
53 f
.Badf(root
.Pos(), "using %s before checking for errors", resp
.Name
)
57 // isHTTPFuncOrMethodOnClient checks whether the given call expression is on
58 // either a function of the net/http package or a method of http.Client that
59 // returns (*http.Response, error).
60 func isHTTPFuncOrMethodOnClient(f
*File
, expr
*ast
.CallExpr
) bool {
61 fun
, _
:= expr
.Fun
.(*ast
.SelectorExpr
)
62 sig
, _
:= f
.pkg
.types
[fun
].Type
.(*types
.Signature
)
64 return false // the call is not on of the form x.f()
69 return false // the function called does not return two values.
71 if ptr
, ok
:= res
.At(0).Type().(*types
.Pointer
); !ok ||
!isNamedType(ptr
.Elem(), "net/http", "Response") {
72 return false // the first return type is not *http.Response.
74 if !types
.Identical(res
.At(1).Type().Underlying(), errorType
) {
75 return false // the second return type is not error
78 typ
:= f
.pkg
.types
[fun
.X
].Type
80 id
, ok
:= fun
.X
.(*ast
.Ident
)
81 return ok
&& id
.Name
== "http" // function in net/http package.
84 if isNamedType(typ
, "net/http", "Client") {
85 return true // method on http.Client.
87 ptr
, ok
:= typ
.(*types
.Pointer
)
88 return ok
&& isNamedType(ptr
.Elem(), "net/http", "Client") // method on *http.Client.
91 // blockStmtFinder is an ast.Visitor that given any ast node can find the
92 // statement containing it and its succeeding statements in the same block.
93 type blockStmtFinder
struct {
94 node ast
.Node
// target of search
95 stmt ast
.Stmt
// innermost statement enclosing argument to Visit
96 block
*ast
.BlockStmt
// innermost block enclosing argument to Visit.
99 // Visit finds f.node performing a search down the ast tree.
100 // It keeps the last block statement and statement seen for later use.
101 func (f
*blockStmtFinder
) Visit(node ast
.Node
) ast
.Visitor
{
102 if node
== nil || f
.node
.Pos() < node
.Pos() || f
.node
.End() > node
.End() {
103 return nil // not here
105 switch n
:= node
.(type) {
111 if f
.node
.Pos() == node
.Pos() && f
.node
.End() == node
.End() {
114 return f
// keep looking
117 // stmts returns the statements of f.block starting from the one including f.node.
118 func (f
*blockStmtFinder
) stmts() []ast
.Stmt
{
119 for i
, v
:= range f
.block
.List
{
121 return f
.block
.List
[i
:]
127 // rootIdent finds the root identifier x in a chain of selections x.y.z, or nil if not found.
128 func rootIdent(n ast
.Node
) *ast
.Ident
{
129 switch n
:= n
.(type) {
130 case *ast
.SelectorExpr
:
131 return rootIdent(n
.X
)