diff --git a/config/oxlint-anti-slop.json b/config/oxlint-anti-slop.json index d2ac1d35d75..f0c779bd7e2 100644 --- a/config/oxlint-anti-slop.json +++ b/config/oxlint-anti-slop.json @@ -32,7 +32,7 @@ "anti-slop/no-module-mocking": "off", "anti-slop/no-object-parameters": "off", "anti-slop/no-reduce-accumulator-copy": "error", - "anti-slop/no-reflect-apply": "off", + "anti-slop/no-reflect-apply": "error", "anti-slop/no-reflect-get": "off", "anti-slop/no-runtime-typeof": "off", "anti-slop/no-shape-in-symbol-names": "off", diff --git a/config/scripts/main-blocking-probe.mjs b/config/scripts/main-blocking-probe.mjs index 93a5372a765..e8b038abcaa 100644 --- a/config/scripts/main-blocking-probe.mjs +++ b/config/scripts/main-blocking-probe.mjs @@ -12,7 +12,7 @@ export function installMainBlockingProbe() { const epoch = Date.now() let result try { - result = Reflect.apply(original, this, args) + result = original.call(this, ...args) return result } finally { const durationMs = performance.now() - start diff --git a/config/scripts/persistence-call-probe.mjs b/config/scripts/persistence-call-probe.mjs index d62df17cc0b..dc15462d0cf 100644 --- a/config/scripts/persistence-call-probe.mjs +++ b/config/scripts/persistence-call-probe.mjs @@ -21,7 +21,7 @@ export function installPersistenceCallProbe() { const epoch = Date.now() let result try { - result = Reflect.apply(original, this, args) + result = original.call(this, ...args) return result } finally { const durationMs = performance.now() - start diff --git a/config/scripts/terminal-stream-byte-length-benchmark.mjs b/config/scripts/terminal-stream-byte-length-benchmark.mjs index 4ea2548b393..7ad53e74dce 100644 --- a/config/scripts/terminal-stream-byte-length-benchmark.mjs +++ b/config/scripts/terminal-stream-byte-length-benchmark.mjs @@ -88,7 +88,7 @@ function runWithNativeCallCount(fn) { let calls = 0 Buffer.byteLength = (...args) => { calls += 1 - return Reflect.apply(nativeByteLength, Buffer, args) + return nativeByteLength.call(Buffer, ...args) } try { return { output: fn(), calls } diff --git a/src/main/gitlab/client-mr-auth-rate-limit.test.ts b/src/main/gitlab/client-mr-auth-rate-limit.test.ts index abf928a7c9f..6d76bea52a5 100644 --- a/src/main/gitlab/client-mr-auth-rate-limit.test.ts +++ b/src/main/gitlab/client-mr-auth-rate-limit.test.ts @@ -95,7 +95,7 @@ describe('gitlab client — MR operations', () => { if (this[0] === 'gitlab.com' && this.every((value) => typeof value === 'string')) { knownHostCacheScans += 1 } - return Reflect.apply(originalMap, this, [callback, thisArg]) + return originalMap.call(this, callback, thisArg) }) try { diff --git a/src/main/native-chat/agent-session-wire/agent-session-history-byte-accounting.test.ts b/src/main/native-chat/agent-session-wire/agent-session-history-byte-accounting.test.ts index 757e7e44084..17bdf710490 100644 --- a/src/main/native-chat/agent-session-wire/agent-session-history-byte-accounting.test.ts +++ b/src/main/native-chat/agent-session-wire/agent-session-history-byte-accounting.test.ts @@ -69,13 +69,17 @@ it.each([1, 100, 200])('serializes each of %i unchanged forward page items once' await appendItems(count, 'x'.repeat(8_000)) const snapshot = journal.snapshot() const stringify = JSON.stringify + // Method-shaped type: the JSON.stringify overloads split on replacer shape and reject a forwarded one. + const forwardStringify: { + stringify(value: unknown, replacer?: unknown, space?: unknown): string + }['stringify'] = stringify let itemSerializations = 0 - JSON.stringify = ((value: unknown, ...args: unknown[]) => { + JSON.stringify = (value: unknown, replacer?: unknown, space?: unknown): string => { if (value && typeof value === 'object' && 'itemId' in value && 'body' in value) { itemSerializations++ } - return Reflect.apply(stringify, JSON, [value, ...args]) - }) as typeof JSON.stringify + return forwardStringify(value, replacer, space) + } try { const result = readAgentSessionHistory( journal, diff --git a/src/main/runtime/orca-runtime-browser-client-hosted.test.ts b/src/main/runtime/orca-runtime-browser-client-hosted.test.ts index e0a901a0f96..f9f69834463 100644 --- a/src/main/runtime/orca-runtime-browser-client-hosted.test.ts +++ b/src/main/runtime/orca-runtime-browser-client-hosted.test.ts @@ -312,9 +312,7 @@ describe('RuntimeBrowserCommands client-hosted routing', () => { .spyOn(registry, 'publishClientPage') .mockImplementation((input) => { order.push('publish') - return Reflect.apply(RuntimeBrowserPageRegistry.prototype.publishClientPage, registry, [ - input - ]) + return RuntimeBrowserPageRegistry.prototype.publishClientPage.call(registry, input) }) const notifyHeadlessBrowserSessionTabsChanged = vi.fn(() => order.push('notify')) const issueClientPageCommand = vi.fn(() => { diff --git a/src/main/runtime/runtime-linear-command-surface.ts b/src/main/runtime/runtime-linear-command-surface.ts index 63b053f45df..0f234768203 100644 --- a/src/main/runtime/runtime-linear-command-surface.ts +++ b/src/main/runtime/runtime-linear-command-surface.ts @@ -66,7 +66,7 @@ export function installRuntimeLinearCommandSurface(target: object): void { const method = { [name](this: LinearFacadeInstance, ...args: unknown[]): unknown { const commands = this.linearCommands as unknown as LinearMethodBag - return Reflect.apply(commands[name], overrideAwareReceiver(this, commands, names), args) + return commands[name].call(overrideAwareReceiver(this, commands, names), ...args) } }[name] delegators.add(method) diff --git a/src/main/runtime/runtime-search-line-fragments.test.ts b/src/main/runtime/runtime-search-line-fragments.test.ts index 17c873efdd8..701abd1327e 100644 --- a/src/main/runtime/runtime-search-line-fragments.test.ts +++ b/src/main/runtime/runtime-search-line-fragments.test.ts @@ -90,7 +90,9 @@ describe('RuntimeFileCommands', () => { submatches: [{ start: 0, end: 6 }] } }) - const originalSplit = String.prototype.split + // Method-shaped type: a call-signature capture would reject `split`'s splitter-object overload. + const originalSplit: { split(separator: unknown, limit?: number): string[] }['split'] = + String.prototype.split let scanned = 0 const spy = vi.spyOn(String.prototype, 'split').mockImplementation(function ( this: string, @@ -100,7 +102,7 @@ describe('RuntimeFileCommands', () => { if (separator === '\n') { scanned += this.length } - return Reflect.apply(originalSplit, this, [separator, limit]) + return originalSplit.call(this, separator, limit) }) try { for (let offset = 0; offset < line.length; offset += 1024) { diff --git a/src/relay/fs-search-line-fragments.test.ts b/src/relay/fs-search-line-fragments.test.ts index 53b8902ddfd..74d17c428fe 100644 --- a/src/relay/fs-search-line-fragments.test.ts +++ b/src/relay/fs-search-line-fragments.test.ts @@ -83,7 +83,9 @@ describe.each(searchCases)('relay $name line fragments', ({ search, encode }) => for (let offset = 0; offset < wire.length; offset += 4096) { chunks.push(wire.slice(offset, offset + 4096)) } - const originalSplit = String.prototype.split + // Method-shaped type: a call-signature capture would reject `split`'s splitter-object overload. + const originalSplit: { split(separator: unknown, limit?: number): string[] }['split'] = + String.prototype.split let scannedCharacters = 0 const spy = vi.spyOn(String.prototype, 'split').mockImplementation(function ( this: string, @@ -93,7 +95,7 @@ describe.each(searchCases)('relay $name line fragments', ({ search, encode }) => if (separator === '\n') { scannedCharacters += this.length } - return Reflect.apply(originalSplit, this, [separator, limit]) + return originalSplit.call(this, separator, limit) }) let fragmented try { diff --git a/src/renderer/src/components/dashboard-popout/agent-map-worktree-lineage-layout.test.ts b/src/renderer/src/components/dashboard-popout/agent-map-worktree-lineage-layout.test.ts index 8bfb9fc28d4..3025aecad77 100644 --- a/src/renderer/src/components/dashboard-popout/agent-map-worktree-lineage-layout.test.ts +++ b/src/renderer/src/components/dashboard-popout/agent-map-worktree-lineage-layout.test.ts @@ -43,8 +43,8 @@ function layoutWithNumericMapSetCount(worktrees: ReturnType) if (typeof key === 'number') { numericMapSets += 1 } - return Reflect.apply(set, this, [key, value]) - } as typeof Map.prototype.set + return set.call(this, key, value) + } try { return { layout: layoutAgentMapWorktreeLineage(worktrees), numericMapSets } } finally { @@ -64,7 +64,7 @@ function layoutWithWorktreePushCount(count: number) { typeof item.id === 'string' && item.id.startsWith('worktree-') ).length - return Reflect.apply(push, this, items) + return push.call(this, ...items) } try { return { diff --git a/src/renderer/src/components/dashboard-popout/agent-map-worktree-packing.test.ts b/src/renderer/src/components/dashboard-popout/agent-map-worktree-packing.test.ts index a07490dab92..d074c30b2ad 100644 --- a/src/renderer/src/components/dashboard-popout/agent-map-worktree-packing.test.ts +++ b/src/renderer/src/components/dashboard-popout/agent-map-worktree-packing.test.ts @@ -145,8 +145,8 @@ describe('packAgentMapWorktrees', () => { if (typeof key === 'number') { numericMapSets += 1 } - return Reflect.apply(set, this, [key, value]) - } as typeof Map.prototype.set + return set.call(this, key, value) + } try { const packed = packAgentMapWorktrees( Array.from({ length: 5 }, (_, index) => ({ diff --git a/src/renderer/src/components/editor/diff-section-layout.test.ts b/src/renderer/src/components/editor/diff-section-layout.test.ts index c1b100a2f33..16a8783ff49 100644 --- a/src/renderer/src/components/editor/diff-section-layout.test.ts +++ b/src/renderer/src/components/editor/diff-section-layout.test.ts @@ -110,8 +110,10 @@ describe('diff section layout', () => { }) it('estimates line-count height without allocating split arrays', () => { - const originalSplit = String.prototype.split - const patchedSplit = function patchedSplit( + // Method-shaped type: a call-signature capture would reject `split`'s splitter-object overload. + const originalSplit: { split(separator: unknown, limit?: number): string[] }['split'] = + String.prototype.split + const patchedSplit: typeof String.prototype.split = function patchedSplit( this: string, separator?: unknown, limit?: number @@ -119,9 +121,8 @@ describe('diff section layout', () => { if (String(this).startsWith('line 0')) { throw new Error('layout should not split full diff content') } - const args = limit === undefined ? [separator] : [separator, limit] - return Reflect.apply(originalSplit, this, args) as string[] - } as typeof String.prototype.split + return originalSplit.call(this, separator, limit) + } String.prototype.split = patchedSplit try { diff --git a/src/renderer/src/components/editor/tiptap-marked-facade.ts b/src/renderer/src/components/editor/tiptap-marked-facade.ts index e34f6755566..823217be6ef 100644 --- a/src/renderer/src/components/editor/tiptap-marked-facade.ts +++ b/src/renderer/src/components/editor/tiptap-marked-facade.ts @@ -32,7 +32,8 @@ export function createTiptapMarkedFacade(): typeof marked { const lexer = (src: string, options?: MarkedOptions): TokensList => new RegistryLexer(options).lex(src) const facade = new Proxy(marked, { - apply: (_target, _thisArg, args) => Reflect.apply(registry.parse, registry, args), + apply: (_target, _thisArg, args: [src: string, options?: MarkedOptions | null]) => + registry.parse(...args), get: (target, property, receiver) => { switch (property) { case 'defaults': diff --git a/src/renderer/src/components/task-page-mutation-page-allocation.test.ts b/src/renderer/src/components/task-page-mutation-page-allocation.test.ts index 08f18926a42..8e1f272afaf 100644 --- a/src/renderer/src/components/task-page-mutation-page-allocation.test.ts +++ b/src/renderer/src/components/task-page-mutation-page-allocation.test.ts @@ -33,7 +33,12 @@ it('avoids allocating copies of unaffected pages during an item mutation', () => if (inputs.has(this)) { allocations++ } - return Reflect.apply(map, this, [callback, thisArg]) as U[] + // Explicit `call` type arguments: inference through `call` erases `map`'s own `U` to `unknown`. + return map.call U, unknown], U[]>( + this, + callback, + thisArg + ) } Array.prototype.slice = function (this: unknown[], ...args: Parameters) { if (inputs.has(this)) { diff --git a/src/shared/git-history-message-allocation.test.ts b/src/shared/git-history-message-allocation.test.ts index cfd8561b0c5..a3bad7d59b3 100644 --- a/src/shared/git-history-message-allocation.test.ts +++ b/src/shared/git-history-message-allocation.test.ts @@ -14,14 +14,16 @@ it('keeps a multiline commit body intact without materializing every message lin '', message ].join('\n') - const original = String.prototype.split + // Method-shaped type: a call-signature capture would reject `split`'s splitter-object overload. + const original: { split(separator: unknown, limit?: number): string[] }['split'] = + String.prototype.split let allocatedFields = 0 const spy = vi.spyOn(String.prototype, 'split').mockImplementation(function ( this: string, - separator: string | RegExp | { [Symbol.split](value: string, limit?: number): string[] }, + separator: unknown, limit?: number ) { - const result = Reflect.apply(original, this, [separator, limit]) as string[] + const result = original.call(this, separator, limit) if (separator === '\n' && String(this).includes('body line')) { allocatedFields += result.length } diff --git a/src/shared/workspace-space-compaction.test.ts b/src/shared/workspace-space-compaction.test.ts index bf774645fbb..7bae49bd73f 100644 --- a/src/shared/workspace-space-compaction.test.ts +++ b/src/shared/workspace-space-compaction.test.ts @@ -19,7 +19,7 @@ it('sums omitted sizes without constructing a replacement object per omitted ite if (initial && typeof initial === 'object' && 'name' in initial && initial.name === 'Other') { objectAccumulators += this.length } - return Reflect.apply(original, this, [callback, initial]) + return original.call(this, callback, initial) }) let result: ReturnType try {