1
0
mirror of https://github.com/golang/go synced 2024-11-19 16:44:43 -07:00

cmd/vet: do not import net/http at startup

The httpresponse.go module wants to be able to tell if a particular type t
is net/http.Response (and also net/http.Client). It does this by importing
net/http, looking up Response, and then comparing that saved type against
each t.

Instead of doing an eager import of net/http, wait until we have a type t
to ask a question about, and then just look to see if that t is http.Response.
This kind of lazy check does not require assuming that net/http is available
or will be important (perhaps the check is disabled in this run, or perhaps
other conditions that lead to the comparison are not satisfied).

Not loading these kinds of types at startup time will scale better.

Change-Id: Ibb00623901a96e725a4ff6f231e6d15127979dfd
Reviewed-on: https://go-review.googlesource.com/74353
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Russ Cox 2017-10-30 10:47:30 -04:00
parent c1e026a5f6
commit 9364c0e337
3 changed files with 16 additions and 18 deletions

View File

@ -19,10 +19,6 @@ func init() {
} }
func checkHTTPResponse(f *File, node ast.Node) { func checkHTTPResponse(f *File, node ast.Node) {
// If http.Response or http.Client are not defined, skip this check.
if httpResponseType == nil || httpClientType == nil {
return
}
call := node.(*ast.CallExpr) call := node.(*ast.CallExpr)
if !isHTTPFuncOrMethodOnClient(f, call) { if !isHTTPFuncOrMethodOnClient(f, call) {
return // the function call is not related to this check. return // the function call is not related to this check.
@ -72,7 +68,7 @@ func isHTTPFuncOrMethodOnClient(f *File, expr *ast.CallExpr) bool {
if res.Len() != 2 { if res.Len() != 2 {
return false // the function called does not return two values. return false // the function called does not return two values.
} }
if ptr, ok := res.At(0).Type().(*types.Pointer); !ok || !types.Identical(ptr.Elem(), httpResponseType) { 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. return false // the first return type is not *http.Response.
} }
if !types.Identical(res.At(1).Type().Underlying(), errorType) { if !types.Identical(res.At(1).Type().Underlying(), errorType) {
@ -85,11 +81,11 @@ func isHTTPFuncOrMethodOnClient(f *File, expr *ast.CallExpr) bool {
return ok && id.Name == "http" // function in net/http package. return ok && id.Name == "http" // function in net/http package.
} }
if types.Identical(typ, httpClientType) { if isNamedType(typ, "net/http", "Client") {
return true // method on http.Client. return true // method on http.Client.
} }
ptr, ok := typ.(*types.Pointer) ptr, ok := typ.(*types.Pointer)
return ok && types.Identical(ptr.Elem(), httpClientType) // method on *http.Client. return ok && isNamedType(ptr.Elem(), "net/http", "Client") // method on *http.Client.
} }
// blockStmtFinder is an ast.Visitor that given any ast node can find the // blockStmtFinder is an ast.Visitor that given any ast node can find the

View File

@ -161,7 +161,7 @@ func checkTest(fn *ast.FuncDecl, prefix string, report reporter) {
type reporter func(format string, args ...interface{}) type reporter func(format string, args ...interface{})
// checkTestFunctions walks Test, Benchmark and Example functions checking // checkTestFunctions walks Test, Benchmark and Example functions checking
// malformed names, wrong signatures and examples documenting inexistent // malformed names, wrong signatures and examples documenting nonexistent
// identifiers. // identifiers.
func checkTestFunctions(f *File, node ast.Node) { func checkTestFunctions(f *File, node ast.Node) {
if !strings.HasSuffix(f.name, "_test.go") { if !strings.HasSuffix(f.name, "_test.go") {

View File

@ -19,11 +19,9 @@ import (
var stdImporter types.Importer var stdImporter types.Importer
var ( var (
errorType *types.Interface errorType *types.Interface
stringerType *types.Interface // possibly nil stringerType *types.Interface // possibly nil
formatterType *types.Interface // possibly nil formatterType *types.Interface // possibly nil
httpResponseType types.Type // possibly nil
httpClientType types.Type // possibly nil
) )
func inittypes() { func inittypes() {
@ -35,12 +33,16 @@ func inittypes() {
if typ := importType("fmt", "Formatter"); typ != nil { if typ := importType("fmt", "Formatter"); typ != nil {
formatterType = typ.Underlying().(*types.Interface) formatterType = typ.Underlying().(*types.Interface)
} }
if typ := importType("net/http", "Response"); typ != nil { }
httpResponseType = typ
} // isNamedType reports whether t is the named type path.name.
if typ := importType("net/http", "Client"); typ != nil { func isNamedType(t types.Type, path, name string) bool {
httpClientType = typ n, ok := t.(*types.Named)
if !ok {
return false
} }
obj := n.Obj()
return obj.Name() == name && obj.Pkg() != nil && obj.Pkg().Path() == path
} }
// importType returns the type denoted by the qualified identifier // importType returns the type denoted by the qualified identifier