diff --git a/passes/bodyclose/bodyclose.go b/passes/bodyclose/bodyclose.go index 4a86252..fd75532 100644 --- a/passes/bodyclose/bodyclose.go +++ b/passes/bodyclose/bodyclose.go @@ -5,7 +5,6 @@ import ( "go/ast" "go/types" "strconv" - "strings" "github.com/gostaticanalysis/analysisutil" "golang.org/x/tools/go/analysis" @@ -270,13 +269,22 @@ func (r *runner) getReqCall(instr ssa.Instruction) (*ssa.Call, bool) { if !ok { return nil, false } - callType := call.Type().String() - if !strings.Contains(callType, r.resTyp.String()) || - strings.Contains(callType, "net/http.ResponseController") { - return nil, false + + callType := call.Type() + + if types.Identical(callType, r.resTyp) { + return call, true + } + + if tuple, ok := callType.(*types.Tuple); ok { + for i := 0; i < tuple.Len(); i++ { + if types.Identical(tuple.At(i).Type(), r.resTyp) { + return call, true + } + } } - return call, true + return nil, false } func (r *runner) getResVal(instr ssa.Instruction) (ssa.Value, bool) { diff --git a/passes/bodyclose/testdata/src/a/function_type.go b/passes/bodyclose/testdata/src/a/function_type.go new file mode 100644 index 0000000..4d692f3 --- /dev/null +++ b/passes/bodyclose/testdata/src/a/function_type.go @@ -0,0 +1,23 @@ +package a + +import ( + "context" + "net/http" +) + +// Simulates a retryablehttp.CheckRetry policy builder. +// Returns func(context.Context, *http.Response, error) (bool, error). +// call.Type() is *types.Signature — not *http.Response or a tuple containing it. +func NewRetryPolicy(isKnown func(*http.Response, []byte) bool) func(context.Context, *http.Response, error) (bool, error) { + return func(ctx context.Context, resp *http.Response, err error) (bool, error) { + return false, nil + } +} + +func useFunctionTypes() { + _ = NewRetryPolicy(nil) // OK - returns a function type, not *http.Response + + // Real http call should still be detected + resp, _ := http.Get("http://example.com/") // want "response body must be closed" + _ = resp +}