@@ -101,6 +101,42 @@ Pair this with the extractor's planned `--stats` coverage (methods analysed vs
101101skipped) and the picture is complete: how much we looked at, and how our verdicts
102102line up with two independent engines.
103103
104+ ## What the first Dapper three-way showed (a worked example)
105+
106+ Running all three on Dapper's ** product code** (commit ` 72a54c4 ` ; ` --exclude-tests `
107+ dropped 165 test/benchmark findings) gave ** Own.NET 0 · Infer# 3 · CodeQL 2**
108+ leak-class, ` agree 0 ` , and ** ` own-only 0 ` ** (no false positives from us). The five
109+ oracle-only findings are both classes we deliberately don't model — and one of them
110+ is arguably a * precision win for us* :
111+
112+ - ** Infer# ×3** — all ` WrappedBasicReader ` / ` DbWrappedReader ` in ` SqlMapper `
113+ (` SqlMapper.cs:1952 ` , ` :3294 ` , ` SqlMapper.IDataReader.cs:118 ` ): "resource
114+ allocated … is not closed." But that wrapper is Dapper's ** caller-owned disposal
115+ handle** — ` ExecuteReader* ` does ` return DbWrappedReader.Create(cmd, reader) `
116+ (ownership handed to the caller), and the ` GetRowParser ` /` GetDbDataReader ` adapter
117+ wraps the * caller's own* reader (disposing it would close the caller's reader).
118+ ` WrappedBasicReader.Dispose() ` simply forwards to ` _reader.Dispose() ` — it exists
119+ precisely so the ** holder** disposes it. So these are Infer# ** over-reports on the
120+ returns-` IDisposable ` pattern** — exactly the escape / ownership-transfer case
121+ Own.NET treats as * not a leak* . Our ` 0 ` is the right verdict here; Infer#'s ` 3 `
122+ look like false positives. (Verified against ` WrappedReader.cs ` and the
123+ ` ExecuteReader ` return; the two ` SqlMapper.cs ` sites match the same direct-return
124+ and adapter shapes.)
125+ - ** CodeQL ×2** — ` cs/dispose-not-called-on-throw ` at ` SqlMapper.cs:1242/1333 ` : a
126+ disposable local may leak * only if* an exception is thrown mid-method. We don't
127+ model exceptional CFG edges, so this is an honest recall gap ** by design** , not a
128+ logic bug.
129+
130+ Caveat: ` own-only 0 ` also reflects ** coverage** — Dapper's core is heavily async and
131+ interprocedural, which we under-analyse, so "found nothing" partly means "didn't
132+ reach it." Telling the two apart needs the extractor's ` --stats ` (analysed vs
133+ skipped) — the missing signal this run made concrete.
134+
135+ Net: the oracle did its job. It turned "are we behind?" into a precise map —
136+ precision held (0 FPs; we look * more* correct than Infer# on ownership transfer),
137+ and the recall gap is two named, roadmapped classes (interprocedural escape
138+ tracking, exception-path disposal).
139+
104140## Honest gaps (v1)
105141
106142- ** No tool versions pinned in the report yet.** ` microsoft/infersharpaction@v1.5 `
0 commit comments