Simplify gcov_histogram as it's used only for ARCS counters.
[official-gcc.git] / libgo / go / cmd / vet / httpresponse.go
blob791d11d5bdef047349a8fd9c5a8ff6f6360fe568
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.
8 package main
10 import (
11 "go/ast"
12 "go/types"
15 func init() {
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()
30 if len(stmts) < 2 {
31 return // the call to the http function is the last statement of the block.
34 asg, ok := stmts[0].(*ast.AssignStmt)
35 if !ok {
36 return // the first statement is not assignment.
38 resp := rootIdent(asg.Lhs[0])
39 if resp == nil {
40 return // could not find the http.Response in the assignment.
43 def, ok := stmts[1].(*ast.DeferStmt)
44 if !ok {
45 return // the following statement is not a defer.
47 root := rootIdent(def.Call.Fun)
48 if root == nil {
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)
63 if sig == nil {
64 return false // the call is not on of the form x.f()
67 res := sig.Results()
68 if res.Len() != 2 {
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
79 if typ == nil {
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) {
106 case *ast.BlockStmt:
107 f.block = n
108 case ast.Stmt:
109 f.stmt = n
111 if f.node.Pos() == node.Pos() && f.node.End() == node.End() {
112 return nil // found
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 {
120 if f.stmt == v {
121 return f.block.List[i:]
124 return nil
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)
132 case *ast.Ident:
133 return n
134 default:
135 return nil