From 737d84329f00cc4680c28d77c8f03792d29a6f9a Mon Sep 17 00:00:00 2001 From: Adam Magued Date: Sat, 17 Oct 2026 07:30:30 +0300 Subject: [PATCH] fix(analyzer): ignore response body for http.Head calls --- passes/bodyclose/bodyclose.go | 33 ++++++ passes/bodyclose/testdata/src/a/issue78.go | 122 +++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 passes/bodyclose/testdata/src/a/issue78.go diff --git a/passes/bodyclose/bodyclose.go b/passes/bodyclose/bodyclose.go index 2a2eaa8..70c75dd 100644 --- a/passes/bodyclose/bodyclose.go +++ b/passes/bodyclose/bodyclose.go @@ -177,6 +177,10 @@ func (r *runner) isopen(b *ssa.BasicBlock, i int) bool { return false } + if r.isHeadCall(call) { + return false + } + if r.responseHandledByDirective(call) { return false } @@ -335,9 +339,38 @@ func (r *runner) getReqCall(instr ssa.Instruction) (*ssa.Call, bool) { return nil, false } + if r.isHeadCall(call) { + return nil, false + } + return call, true } +func (r *runner) isHeadCall(call *ssa.Call) bool { + if call == nil { + return false + } + callee := call.Call.StaticCallee() + if callee == nil { + return false + } + if callee.Pkg == nil || callee.Pkg.Pkg.Path() != nethttpPath { + return false + } + if callee.Name() != "Head" { + return false + } + recv := callee.Signature.Recv() + if recv == nil { + return true + } + recvType := recv.Type() + if ptr, ok := recvType.(*types.Pointer); ok { + recvType = ptr.Elem() + } + return isNamedType(recvType, nethttpPath, "Client") +} + func (r *runner) getResVal(instr ssa.Instruction) (ssa.Value, bool) { switch instr := instr.(type) { case *ssa.FieldAddr: diff --git a/passes/bodyclose/testdata/src/a/issue78.go b/passes/bodyclose/testdata/src/a/issue78.go new file mode 100644 index 0000000..ffb747e --- /dev/null +++ b/passes/bodyclose/testdata/src/a/issue78.go @@ -0,0 +1,122 @@ +package a + +import ( + "net/http" +) + +type CustomClient struct { + *http.Client +} + +func issue78HeadPackage() { + resp, err := http.Head("http://example.com/") // OK + if err != nil { + return + } + _ = resp.Header +} + +func issue78HeadPackageUnassigned() { + http.Head("http://example.com/") // OK +} + +func issue78HeadPackageDiscard() { + _, err := http.Head("http://example.com/") // OK + if err != nil { + return + } +} + +func issue78HeadClientPointer() { + client := &http.Client{} + resp, err := client.Head("http://example.com/") // OK + if err != nil { + return + } + _ = resp.Header +} + +func issue78HeadClientValue() { + var client http.Client + resp, err := client.Head("http://example.com/") // OK + if err != nil { + return + } + _ = resp.Header +} + +func issue78HeadDefaultClient() { + resp, err := http.DefaultClient.Head("http://example.com/") // OK + if err != nil { + return + } + _ = resp.Header +} + +func issue78HeadClientUnassigned() { + client := &http.Client{} + client.Head("http://example.com/") // OK +} + +func issue78HeadWithClose() { + resp, err := http.Head("http://example.com/") // OK + if err != nil { + return + } + defer resp.Body.Close() +} + +func issue78HeadCustomClient() { + cc := &CustomClient{Client: &http.Client{}} + resp, err := cc.Head("http://example.com/") // OK + if err != nil { + return + } + _ = resp.Header +} + +func issue78HeadReadBody() { + resp, err := http.Head("http://example.com/") // OK + if err != nil { + return + } + buf := make([]byte, 10) + resp.Body.Read(buf) +} + +func issue78HeadInClosure() { + go func() { + resp, err := http.Head("http://example.com/") // OK + if err != nil { + return + } + _ = resp.StatusCode + }() +} + +func issue78HeadLoop() { + for i := 0; i < 3; i++ { + resp, err := http.Head("http://example.com/") // OK + if err != nil { + continue + } + _ = resp.StatusCode + } +} + +func issue78GetStillReported() { + resp, err := http.Get("http://example.com/") // want "response body must be closed" + if err != nil { + return + } + _ = resp.Header +} + +func issue78ClientGetStillReported() { + client := &http.Client{} + resp, err := client.Get("http://example.com/") // want "response body must be closed" + if err != nil { + return + } + _ = resp.Header +}