diff --git a/lint/lostresource/README.md b/lint/lostresource/README.md index 0a09af0..dddd5da 100644 --- a/lint/lostresource/README.md +++ b/lint/lostresource/README.md @@ -114,8 +114,9 @@ returning path transfers its cleanup obligation. Bound release methods capture their original receiver even when its variable is subsequently overwritten. An unused cleanup closure does not count as cleanup. Deferred closures read their captured variables at return; receiver defers capture the handle immediately. -Diagnostics point to the acquisition and carry related information identifying -the return or overwrite that loses it. +Diagnostics point to the acquisition and include the line of the return or +overwrite that loses it. Keeping both in one diagnostic lets a deliberate +`nolint:lostresource` exception suppress the entire finding. Returning a handle, storing it in a field or aggregate, sending it on a channel, or passing it to a configured consumer transfers the obligation. The receiving diff --git a/lint/lostresource/flow.go b/lint/lostresource/flow.go index 8de0e07..f4cfb6d 100644 --- a/lint/lostresource/flow.go +++ b/lint/lostresource/flow.go @@ -120,10 +120,12 @@ func (f *Flow) Check(def ast.Node, call *ast.CallExpr, v *types.Var) { continue } if loss := f.search(block, i+1, state, make(map[string]bool)); loss != nil { + line := strconv.Itoa(f.pass.Fset.Position(loss.Pos()).Line) f.pass.Report(analysis.Diagnostic{ Pos: call.Pos(), End: call.End(), - Message: v.Name() + " from " + functionName(f.pass.TypesInfo, call) + " is not released or transferred on all paths; use " + f.resource.cleanup(), - Related: []analysis.RelatedInformation{{Pos: loss.Pos(), Message: "this path loses " + v.Name() + " without releasing or transferring it"}}, + // Keep the witness in the same diagnostic so driver-level nolint + // filtering cannot leave a separately reported witness behind. + Message: v.Name() + " from " + functionName(f.pass.TypesInfo, call) + " is not released or transferred on all paths; lost at line " + line + "; use " + f.resource.cleanup(), }) } return