From 8c1685e0d1ad20682cf34e490f0cbc55217b9232 Mon Sep 17 00:00:00 2001 From: soh335 Date: Wed, 11 Mar 2026 13:22:40 +0900 Subject: [PATCH] Fix false positive in getReqCall by using types.Identical instead of strings.Contains Replace string-based type matching (strings.Contains on call.Type().String()) with precise type comparison using types.Identical. This eliminates false positives where functions returning types that contain *http.Response as a parameter (e.g., retryablehttp.CheckRetry-style function types) were incorrectly flagged. Changes: - Use types.Identical for single return values and *types.Tuple element checking for multiple return values - Remove strings import (no longer needed) - Add test case for function type return values (function_type.go) Co-Authored-By: Claude Opus 4.6 --- passes/bodyclose/bodyclose.go | 20 +++++++++++----- .../bodyclose/testdata/src/a/function_type.go | 23 +++++++++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 passes/bodyclose/testdata/src/a/function_type.go 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 +}