Skip to content

Commit bcfb984

Browse files
fix(enrichment): track multi-line block comments in unsafe-any scan
Track block-comment state across added patch lines and tighten object- literal guards so commented or shorthand values are not reported. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent cc33dae commit bcfb984

2 files changed

Lines changed: 51 additions & 7 deletions

File tree

review-enrichment/src/analyzers/unsafe-any.ts

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const TS_EXTS = new Set(["ts", "tsx", "mts", "cts"]);
1212

1313
const CAST_RE = /\bas\s+any\b/;
1414
const ASSERTION_RE = /<\s*any\s*>/;
15-
const ANNOTATION_RE = /:\s*any\b(?!\s*\})/;
15+
const ANNOTATION_RE = /:\s*any\b(?!\s*[,}])/;
1616

1717
function isTypeScriptPath(path: string): boolean {
1818
const ext = /\.([^.]+)$/.exec(path)?.[1]?.toLowerCase();
@@ -31,6 +31,31 @@ function isFullLineComment(line: string): boolean {
3131
return /^\s*\/\*[\s\S]*\*\/\s*$/.test(line);
3232
}
3333

34+
type BlockCommentState = { open: boolean };
35+
36+
/** Remove block-comment spans from one added line, tracking multi-line comment state. Pure aside from state. */
37+
export function stripBlockCommentsFromAddedLine(line: string, state: BlockCommentState): string | null {
38+
let scanLine = line;
39+
if (state.open) {
40+
const end = scanLine.indexOf("*/");
41+
if (end < 0) return null;
42+
state.open = false;
43+
scanLine = scanLine.slice(end + 2);
44+
}
45+
while (true) {
46+
const start = scanLine.indexOf("/*");
47+
if (start < 0) break;
48+
const end = scanLine.indexOf("*/", start + 2);
49+
if (end < 0) {
50+
scanLine = scanLine.slice(0, start);
51+
state.open = true;
52+
break;
53+
}
54+
scanLine = `${scanLine.slice(0, start)} ${scanLine.slice(end + 2)}`;
55+
}
56+
return scanLine;
57+
}
58+
3459
/** Classify one added TS line for an unsafe-`any` pattern, or null. Pure. */
3560
export function detectUnsafeAny(line: string): UnsafeAnyFinding["kind"] | null {
3661
if (isFullLineComment(line)) return null;
@@ -57,27 +82,35 @@ export function scanPatchForUnsafeAny(
5782
const findings: UnsafeAnyFinding[] = [];
5883
let newLine = 0;
5984
let inHunk = false;
85+
const blockComment: BlockCommentState = { open: false };
6086
for (const line of patch.split("\n")) {
6187
if (limits.signal?.aborted) throw new Error("analyzer_aborted");
6288
const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(line);
6389
if (hunk) {
6490
newLine = Number(hunk[1]);
6591
inHunk = true;
92+
blockComment.open = false;
6693
continue;
6794
}
6895
if (!inHunk) continue;
6996
if (line.startsWith("+")) {
7097
const body = line.slice(1);
7198
if (body.length <= MAX_LINE_CHARS) {
72-
const kind = detectUnsafeAny(body);
73-
if (kind) {
74-
findings.push({ file: path, line: newLine, kind });
75-
if (findings.length >= maxFindings) return findings;
99+
const scanLine = stripBlockCommentsFromAddedLine(body, blockComment);
100+
if (scanLine !== null) {
101+
const kind = detectUnsafeAny(scanLine);
102+
if (kind) {
103+
findings.push({ file: path, line: newLine, kind });
104+
if (findings.length >= maxFindings) return findings;
105+
}
76106
}
77107
}
78108
newLine++;
79-
} else if (!line.startsWith("-") && !line.startsWith("\\")) {
80-
newLine++;
109+
} else {
110+
blockComment.open = false;
111+
if (!line.startsWith("-") && !line.startsWith("\\")) {
112+
newLine++;
113+
}
81114
}
82115
}
83116
return findings;

review-enrichment/test/unsafe-any.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,17 @@ test("detectUnsafeAny: ignores block comments after real code", () => {
5151

5252
test("detectUnsafeAny: does not treat object-literal values as type annotations", () => {
5353
assert.equal(detectUnsafeAny("return { value: any };"), null);
54+
assert.equal(detectUnsafeAny("return { value: any, other: true };"), null);
55+
});
56+
57+
test("scanPatchForUnsafeAny: ignores multi-line block comments spanning added lines", () => {
58+
const patch = [
59+
"@@ -1,0 +1,3 @@",
60+
"+/*",
61+
"+ * const x: any = 1;",
62+
"+ */",
63+
].join("\n");
64+
assert.deepEqual(scanPatchForUnsafeAny("src/a.ts", patch), []);
5465
});
5566

5667
test("scanPatchForUnsafeAny: accepts .mts and .cts extensions", () => {

0 commit comments

Comments
 (0)