diff --git a/review-enrichment/src/analyzers/duplication-scan.ts b/review-enrichment/src/analyzers/duplication-scan.ts index 3549c7fd7c..3440f4f787 100644 --- a/review-enrichment/src/analyzers/duplication-scan.ts +++ b/review-enrichment/src/analyzers/duplication-scan.ts @@ -184,7 +184,10 @@ export function extractAddedBlocks(patch: string | undefined): NormBlock[] { continue; } if (!inHunk) continue; - if (line.startsWith("+++")) continue; // file header inside the patch, not an added line + // NOTE: no `+++` skip here — the `+++ b/file` header only appears in the patch preamble (before the first + // `@@`), which the `!inHunk` guard above already drops. Inside a hunk a line starting with `+++` is an + // added line whose content begins with `++` (e.g. a pre-increment `++x;`); skipping it dropped real code + // and shifted every following added line's number down by one. if (line.startsWith("+")) { const norm = normalizeLine(line.slice(1)); if (norm === null) { diff --git a/review-enrichment/test/duplication-scan.test.ts b/review-enrichment/test/duplication-scan.test.ts index 30e124a60f..9bf374aab2 100644 --- a/review-enrichment/test/duplication-scan.test.ts +++ b/review-enrichment/test/duplication-scan.test.ts @@ -129,6 +129,23 @@ test("extractAddedBlocks: groups consecutive added lines, breaks on context/remo assert.deepEqual(blocks[2].lineNos, [9]); }); +test("extractAddedBlocks: keeps an added line whose content starts with `++` (patch line `+++…`) and keeps line numbers aligned", () => { + // A pre-increment statement `++counterValueForLoop;` becomes the patch line `+++counterValueForLoop;`. The + // `+++` file-header marker only appears in the preamble (before the first `@@`), so inside a hunk this is a + // real added line — it must not be dropped, and it must not shift the following lines' numbers. + const patch = [ + "@@ -1,0 +10,3 @@", + "+++counterValueForLoop;", + "+const secondSignificantLineHere = makeValue(2)", + "+const thirdSignificantLineHere = makeValue(3)", + ].join("\n"); + const blocks = extractAddedBlocks(patch); + assert.equal(blocks.length, 1); + assert.deepEqual(blocks[0].lineNos, [10, 11, 12]); // not [10, 11] with the ++ line dropped + assert.equal(blocks[0].norm.length, 3); + assert.ok(blocks[0].norm[0].includes("counterValueForLoop")); // the ++ line survived +}); + test("decodeBase64Utf8: decodes UTF-8 blob content without globalThis.Buffer (Cloudflare Worker deployment path)", () => { // The production decode must not depend on Node's Buffer. Encode while Buffer exists, then remove it and decode. const text = "const computedScore = weight * factor + base\nconst total = computedScore + bonusAmount + café";