From f727f420042a919d3b3735c3c7049a23047c80e7 Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Wed, 1 May 2024 11:13:56 -0700 Subject: [PATCH] [flow][dev-tools] Hermes-parser based `getAST` for error suppression Summary: This diff removes our dependence on `flow ast` command for error suppression. Previously, we rely on it to get the AST of the source code, so we can choose what kind of suppressions to add (e.g. we special case jsx elements since it's not always valid to add line comment above it). `flow ast` doesn't read from flowconfig and the parser configuration can become out of sync with the rest of the setup. In this diff, I switched us to use hermes-parser instead. We can remove all of our dependency on flow-bin as a result. There is an important change: we can just use normal js string indexing to get characters, rather than going through the Buffer, since hermes-parser, like most of the other js tools (including the js_of_ocaml compiled flow.js, but not flow-bin ...), encode the string as utf-16 (the js string encoding), so we don't need to worry about these longer-than-1-byte characters. flow-bin handles it differently, probably as a result of negligence, but js_of_ocaml happens to correct it, because there is no straightforward way to get a byte out of a string in the JS. Changelog: [internal] Reviewed By: pieterv Differential Revision: D56796632 fbshipit-source-id: fbe1f923d3b37614e7fd5274a9401a7c4749cc77 --- .../flow-dev-tools/src/__tests__/all-tests.js | 113 ++++++------------ .../src/comment/add-commentsRunner.js | 12 +- .../src/comment/commentMutator.js | 84 +++++-------- packages/flow-dev-tools/src/comment/getAst.js | 12 +- .../src/comment/remove-commentsRunner.js | 15 +-- .../update-suppressionsRunner.js | 26 ++-- 6 files changed, 85 insertions(+), 177 deletions(-) diff --git a/packages/flow-dev-tools/src/__tests__/all-tests.js b/packages/flow-dev-tools/src/__tests__/all-tests.js index 608797c53d8..19e885aa768 100644 --- a/packages/flow-dev-tools/src/__tests__/all-tests.js +++ b/packages/flow-dev-tools/src/__tests__/all-tests.js @@ -79,7 +79,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText does not remove eslint-disable multiline comment', async () => { @@ -106,7 +106,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('exec', async () => { @@ -131,8 +131,6 @@ test('splitIntoChunks', () => { expect(splitIntoChunks('✓✓✓✓✓', 1)).toEqual(['✓', '✓', '✓', '✓', '✓']); }); -const flowBinPath = path.resolve(process.env.FLOW_BIN); - test('updateSuppressionsInText removes unused comments', async () => { const testInput = ` // $FlowFixMe @@ -152,7 +150,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText removes unused comments with sites', async () => { @@ -174,7 +172,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText can add site', async () => { @@ -191,7 +189,7 @@ const x = 4; `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, [], ['foo']); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText keeps unknown sites', async () => { @@ -214,7 +212,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText keeps unknown sites (2)', async () => { @@ -237,7 +235,7 @@ const x = 4; [], ['foo', 'bar'], ); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText adds site and keeps unknown sites', async () => { @@ -254,7 +252,7 @@ const x = 4; `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, [], ['foo']); - await expectUpdatedComments(testInput, testOutput, errorsByLine, flowBinPath); + await expectUpdatedComments(testInput, testOutput, errorsByLine); }); test('updateSuppressionsInText handles JSX children', async () => { @@ -276,13 +274,7 @@ test('updateSuppressionsInText handles JSX children', async () => { `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, ['code'], []); - await expectUpdatedComments( - testInput, - testOutput, - errorsByLine, - flowBinPath, - [], - ); + await expectUpdatedComments(testInput, testOutput, errorsByLine, []); }); test('updateSuppressionsInText handles JSX children with multiline open tag', async () => { @@ -306,13 +298,7 @@ test('updateSuppressionsInText handles JSX children with multiline open tag', as `.trimLeft(); const errorsByLine = addErrorByLine(new Map(), testInput, loc, ['code'], []); - await expectUpdatedComments( - testInput, - testOutput, - errorsByLine, - flowBinPath, - [], - ); + await expectUpdatedComments(testInput, testOutput, errorsByLine, []); }); function addErrorByLine(errorsByLine, contents, loc, errorCodes, unusedRoots) { @@ -352,9 +338,11 @@ function posToOffset(contents, line, col) { // Using 1-indexed line and column for this let currentLine = 1; let currentCol = 1; - const buf = Buffer.from(contents, 'utf8'); - while (offset < buf.length && !(currentLine === line && currentCol === col)) { - const char = buf.toString('utf8', offset, offset + 1); + while ( + offset < contents.length && + !(currentLine === line && currentCol === col) + ) { + const char = contents[offset]; if (char === '\n') { currentLine++; currentCol = 1; @@ -371,25 +359,20 @@ async function expectUpdatedComments( input, expectedOutput, errorsByLine, - flowBinPath, sites = ['foo', 'bar'], ) { const actualOutput = await updateSuppressionsInText( - Buffer.from(input), + input, new Set(sites), 1, errorsByLine, '', - flowBinPath, ); - expect(actualOutput.toString()).toEqual(expectedOutput); + expect(actualOutput).toEqual(expectedOutput); } test('addCommentsToCode > basic', async () => { - expect(await addCommentsToCode('foobar', null, '', [], flowBinPath)).toEqual([ - '', - 0, - ]); + expect(await addCommentsToCode('foobar', null, '', [])).toEqual(['', 0]); }); test('addCommentsToCode > advanced', async () => { @@ -400,7 +383,6 @@ test('addCommentsToCode > advanced', async () => { testInput, /* Intentionally made these out of order to test that they are still inserted properly */ [1, 6, 5, 3].map(line => makeSuppression(line, testInput)), - flowBinPath, ), ).toEqual([testOutput, 8]); }); @@ -453,7 +435,6 @@ function bar(): b {} error_codes: ['code4'], }, ], - flowBinPath, ), ).toEqual([ `// $FlowFixMe[code1] @@ -471,23 +452,17 @@ function bar(): b {} test('addCommentsToCode > does not touch AST when no errors match the given code', async () => { expect( - await addCommentsToCode( - '', - 'code2', - `function foo() {}`, - [ - { - loc: { - start: {line: 1, column: 13, offset: 13}, - end: {line: 1, column: 14, offset: 14}, - }, - isError: true, - lints: new Set(), - error_codes: ['code1'], + await addCommentsToCode('', 'code2', `function foo() {}`, [ + { + loc: { + start: {line: 1, column: 13, offset: 13}, + end: {line: 1, column: 14, offset: 14}, }, - ], - flowBinPath, - ), + isError: true, + lints: new Set(), + error_codes: ['code1'], + }, + ]), ).toEqual([`function foo() {}`, 0]); }); @@ -519,7 +494,6 @@ function bar(): b {} error_codes: ['code2'], }, ], - flowBinPath, ), ).toEqual([ `function foo() {} @@ -554,7 +528,6 @@ class A { error_codes: ['code1'], }, ], - flowBinPath, ), ).toEqual([ ` @@ -590,7 +563,6 @@ class Foo { error_codes: ['code1'], }, ], - flowBinPath, ), ).toEqual([ ` @@ -667,7 +639,6 @@ test('addCommentsToCode > JSX', async () => { error_codes: ['code7'], }, ], - flowBinPath, ), ).toEqual([ `function A(): React.Node { @@ -769,8 +740,6 @@ function locForLine(line, text) { } test('removeUnusedErrorSuppressionsFromText', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = ` // single line comment with some whitespace const bar = 4; /* multiline @@ -805,12 +774,10 @@ const foo = 4; [7, 4, 8, 20], [], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); test('removeExtraSpaceWhenRemovingUnusedFlowLint', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = `// flowlint foo bar //flowlint foo bar // flowlint foo bar @@ -844,12 +811,10 @@ test('removeExtraSpaceWhenRemovingUnusedFlowLint', async () => { [8, 22, 8, 25], [9, 27, 9, 30], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); test('deleteUnusedFlowLintComments', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = `// flowlint foo bar //flowlint foo bar // flowlint-next-line foo bar @@ -917,12 +882,10 @@ let x = [19, 15, 19, 18], [21, 6, 21, 9], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); test('unicode', async () => { - const flowBinPath = path.resolve(process.env.FLOW_BIN); - const testInput = `const unicode = "\u{714E}\u{8336}"; const smiley = "\uD83D\uDE00"; @@ -992,21 +955,15 @@ let x = [21, 17, 21, 20], [22, 15, 22, 18], ].map(args => makeLoc(testInput, ...args)); - await expectCommentsAreRemoved(testInput, testOutput, errorLocs, flowBinPath); + await expectCommentsAreRemoved(testInput, testOutput, errorLocs); }); -async function expectCommentsAreRemoved( - input, - expectedOutput, - errorLocs, - flowBinPath, -) { +async function expectCommentsAreRemoved(input, expectedOutput, errorLocs) { const actualOutput = await removeUnusedErrorSuppressionsFromText( - Buffer.from(input), + input, errorLocs, - flowBinPath, ); - expect(actualOutput.toString()).toEqual(expectedOutput); + expect(actualOutput).toEqual(expectedOutput); } (async () => { diff --git a/packages/flow-dev-tools/src/comment/add-commentsRunner.js b/packages/flow-dev-tools/src/comment/add-commentsRunner.js index dfa640ae6fa..a976519523c 100644 --- a/packages/flow-dev-tools/src/comment/add-commentsRunner.js +++ b/packages/flow-dev-tools/src/comment/add-commentsRunner.js @@ -147,7 +147,6 @@ async function addCommentsToSource( args.error_code, codeString, locs, - args.bin, ); await writeFile(source, code); return commentCount; @@ -160,13 +159,7 @@ function addCommentsToCodeInternal( path: Array, ) { const [inside, ast] = getContext(loc, path); - return addCommentToText( - Buffer.from(code), - loc, - inside, - comments, - ast, - ).toString(); + return addCommentToText(code, loc, inside, comments, ast).toString(); } async function addCommentsToCode( @@ -174,13 +167,12 @@ async function addCommentsToCode( error_code: ?string, code: string, locs: Array, - flowBinPath: string, ): Promise< [string, number], > /* [resulting code, number of comments inserted] */ { locs.sort((l1, l2) => l2.loc.start.line - l1.loc.start.line); - const ast = await getAst(code, flowBinPath); + const ast = await getAst(code); let commentCount = 0; for (let {loc, isError, lints, error_codes} of locs) { diff --git a/packages/flow-dev-tools/src/comment/commentMutator.js b/packages/flow-dev-tools/src/comment/commentMutator.js index b56595102a3..7569d11e47d 100644 --- a/packages/flow-dev-tools/src/comment/commentMutator.js +++ b/packages/flow-dev-tools/src/comment/commentMutator.js @@ -15,10 +15,6 @@ const {getNodeAtRange} = require('./getPathToLoc'); const {NORMAL, JSX, JSX_FRAGMENT, TEMPLATE} = require('./getContext'); const {format} = require('util'); -function bufferCharAt(buf: Buffer, pos: number): string { - return buf.toString('utf8', pos, pos + 1); -} - const flowlintRegex = /^[ \t\n\r*]*flowlint(-line|-next-line)?\b/; function isLintSuppression(commentAST: {value: string}): boolean { return flowlintRegex.test(commentAST.value); @@ -45,7 +41,7 @@ const edible = /[\t ]/; * in the case where there is nothing before or after it */ function expandComment( - contents: Buffer, + contents: string, startOffset: number, endOffset: number, commentAST: {value: string, range: [number, number]} | void, @@ -89,21 +85,18 @@ function expandComment( // Find the next interesting characters before and after the removed comment let beforeStart = origBeforeStart; let afterEnd = origAfterEnd; - while ( - beforeStart >= 0 && - bufferCharAt(contents, beforeStart).match(edible) - ) { + while (beforeStart >= 0 && contents[beforeStart].match(edible)) { beforeStart--; } - while (afterEnd < length && bufferCharAt(contents, afterEnd).match(edible)) { + while (afterEnd < length && contents[afterEnd].match(edible)) { afterEnd++; } if ( beforeStart >= 0 && afterEnd < length && - bufferCharAt(contents, beforeStart) === '{' && - bufferCharAt(contents, afterEnd) === '}' + contents[beforeStart] === '{' && + contents[afterEnd] === '}' ) { // If this is JSX, then the curly braces start and stop a JSXExpressionContainer const node = getNodeAtRange([beforeStart, afterEnd + 1], ast); @@ -116,24 +109,18 @@ function expandComment( origBeforeStart = beforeStart; origAfterEnd = afterEnd; - while ( - beforeStart >= 0 && - bufferCharAt(contents, beforeStart).match(edible) - ) { + while (beforeStart >= 0 && contents[beforeStart].match(edible)) { beforeStart--; } - while ( - afterEnd < length && - bufferCharAt(contents, afterEnd).match(edible) - ) { + while (afterEnd < length && contents[afterEnd].match(edible)) { afterEnd++; } } } - if (beforeStart < 0 || bufferCharAt(contents, beforeStart) === '\n') { + if (beforeStart < 0 || contents[beforeStart] === '\n') { // There's nothing before the removed comment on this line - if (afterEnd > length || bufferCharAt(contents, afterEnd) === '\n') { + if (afterEnd > length || contents[afterEnd] === '\n') { // The line is completely empty. Let's remove a newline from the start or // end of the line if (afterEnd < length) { @@ -146,7 +133,7 @@ function expandComment( // preceding whitespace thanks to indentation beforeStart = origBeforeStart; } - } else if (afterEnd >= length || bufferCharAt(contents, afterEnd) === '\n') { + } else if (afterEnd >= length || contents[afterEnd] === '\n') { // There's something preceding the comment but nothing afterwards. We can // just remove the rest of the line } else { @@ -158,47 +145,40 @@ function expandComment( return [beforeStart + 1, afterEnd]; } -function findStartOfLine(contents: Buffer, startOffset: number): number { +function findStartOfLine(contents: string, startOffset: number): number { // if startOffset is already a newline, that's not the start of the line, // it's the end of the line. so start from the character before. let start = startOffset - 1; - while (start >= 0 && !bufferCharAt(contents, start).match(newlineRegex)) { + while (start >= 0 && !contents[start].match(newlineRegex)) { start--; } return start + 1; } -function findEndOfLine(contents: Buffer, startOffset: number): number { +function findEndOfLine(contents: string, startOffset: number): number { let start = startOffset; - while ( - start < contents.length && - !bufferCharAt(contents, start).match(newlineRegex) - ) { + while (start < contents.length && !contents[start].match(newlineRegex)) { start++; } return start; } function insertCommentToText( - contents: Buffer, + contents: string, startOffset: number, comment: string, -): Buffer { - return Buffer.concat([ - contents.slice(0, startOffset), - Buffer.from(comment), - contents.slice(startOffset), - ]); +): string { + return contents.slice(0, startOffset) + comment + contents.slice(startOffset); } function addCommentToText( - contents: Buffer, + contents: string, loc: FlowLoc, inside: Context, comments: Array, ast: any, startOfLine?: number, -): Buffer { +): string { let startOffset; let start; if (startOfLine == null) { @@ -210,7 +190,7 @@ function addCommentToText( } const endOfLine = findEndOfLine(contents, startOffset); - let line = contents.toString('utf8', start, endOfLine); + let line = contents.slice(start, endOfLine); const inJSX = inside === JSX_FRAGMENT || inside === JSX; if (inside === NORMAL) { return insertCommentToText( @@ -273,11 +253,11 @@ function addCommentToText( newCodeParts.push(padding + part2); } - return Buffer.concat([ - contents.slice(0, start), - Buffer.from(newCodeParts.join('\n')), - contents.slice(endOfLine), - ]); + return ( + contents.slice(0, start) + + newCodeParts.join('\n') + + contents.slice(endOfLine) + ); } else if (inJSX && ast.type === 'JSXText') { /* Ignore the case where the error's loc starts after the last non-whitespace * character of the line. This can occur when an error's loc spans the @@ -294,13 +274,9 @@ function addCommentToText( let firstNonWhitespaceCharacter = startOffset; let atEndOfLine = true; while (firstNonWhitespaceCharacter < contents.length) { - if ( - bufferCharAt(contents, firstNonWhitespaceCharacter).match(newlineRegex) - ) { + if (contents[firstNonWhitespaceCharacter].match(newlineRegex)) { break; - } else if ( - bufferCharAt(contents, firstNonWhitespaceCharacter).match(edible) - ) { + } else if (contents[firstNonWhitespaceCharacter].match(edible)) { firstNonWhitespaceCharacter++; } else { atEndOfLine = false; @@ -328,12 +304,12 @@ function addCommentToText( } function removeUnusedErrorSuppressionFromText( - contents: Buffer, + contents: string, startOffset: number, endOffset: number, commentAST: Object | void, ast: Object, -): Buffer { +): string { // remove the comment and surrounding whitespace let [start, end] = expandComment( contents, @@ -343,7 +319,7 @@ function removeUnusedErrorSuppressionFromText( ast, ); - return Buffer.concat([contents.slice(0, start), contents.slice(end)]); + return contents.slice(0, start) + contents.slice(end); } /* Take up to `max` characters from str, trying to split at a space or dash or diff --git a/packages/flow-dev-tools/src/comment/getAst.js b/packages/flow-dev-tools/src/comment/getAst.js index 7c30101ea9f..c5417b32cdf 100644 --- a/packages/flow-dev-tools/src/comment/getAst.js +++ b/packages/flow-dev-tools/src/comment/getAst.js @@ -8,18 +8,12 @@ * @format */ +const HermesParser = require('hermes-parser'); const {format} = require('util'); const {exec} = require('../utils/async'); -async function getAst( - code: string, - flowBinPath: string, -): Promise /* AST */ { - const stdout = await exec(format('%s ast', flowBinPath), { - maxBuffer: 16 * 1024 * 1024, - stdin: code, - }); - return JSON.parse(stdout); +function getAst(code: string): Object /* AST */ { + return HermesParser.parse(code, {babel: false}); } module.exports = { diff --git a/packages/flow-dev-tools/src/comment/remove-commentsRunner.js b/packages/flow-dev-tools/src/comment/remove-commentsRunner.js index 0a811eaef11..8da2ce63326 100644 --- a/packages/flow-dev-tools/src/comment/remove-commentsRunner.js +++ b/packages/flow-dev-tools/src/comment/remove-commentsRunner.js @@ -33,29 +33,26 @@ async function getErrors(args: Args): Promise>> { async function removeUnusedErrorSuppressions( filename: string, errors: Array, - flowBinPath: string, ): Promise { const contentsString = await readFile(filename, 'utf8'); const contents = await removeUnusedErrorSuppressionsFromText( - Buffer.from(contentsString, 'utf8'), + contentsString, errors, - flowBinPath, ); - await writeFile(filename, contents.toString('utf8')); + await writeFile(filename, contents); } // Exported for testing async function removeUnusedErrorSuppressionsFromText( - contents: Buffer, + contents: string, errors: Array, - flowBinPath: string, -): Promise { +): Promise { // Sort in reverse order so that we remove comments later in the file first. Otherwise, the // removal of comments earlier in the file would outdate the locations for comments later in the // file. errors.sort((loc1, loc2) => loc2.start.offset - loc1.start.offset); - const ast = await getAst(contents.toString('utf8'), flowBinPath); + const ast = await getAst(contents); for (const error of errors) { const origStart = error.start.offset; @@ -110,7 +107,7 @@ async function runner(args: Args): Promise { }); await Promise.all( errors.map(([filename, errors]) => - removeUnusedErrorSuppressions(filename, errors, args.bin), + removeUnusedErrorSuppressions(filename, errors), ), ); console.log( diff --git a/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js b/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js index 5c2c99c4306..21940d30cf7 100644 --- a/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js +++ b/packages/flow-dev-tools/src/update-suppressions/update-suppressionsRunner.js @@ -201,14 +201,14 @@ function replaceSites(text: string, sites: $ReadOnlyArray): string { * comment if it's unused everywhere. */ function updateErrorSuppression( - contents: Buffer, + contents: string, startOffset: number, endOffset: number, commentAST: Object | void, ast: Object, knownRoots: Set, unusedRoots: Set, -): Buffer { +): string { let commentStartOffset; let commentEndOffset; if (commentAST) { @@ -218,7 +218,7 @@ function updateErrorSuppression( commentEndOffset = endOffset; } let innerOffset = commentStartOffset + 2; // `/*` and `//` are both 2 chars - let text = contents.slice(innerOffset, endOffset).toString('utf8'); + let text = contents.slice(innerOffset, endOffset); // do not remove the comment if it's a eslint suppression // TODO update the logging provided in the end of the command @@ -250,11 +250,7 @@ function updateErrorSuppression( text = replaceSites(text, [...roots]); } - return Buffer.concat([ - contents.slice(0, innerOffset), - Buffer.from(text), - contents.slice(endOffset), - ]); + return contents.slice(0, innerOffset) + text + contents.slice(endOffset); } } @@ -264,36 +260,33 @@ async function updateSuppressions( numBins: number, errors: Map, comment: string, - flowBinPath: string, ): Promise { const contentsString = await readFile(filename, 'utf8'); const contents = await updateSuppressionsInText( - Buffer.from(contentsString, 'utf8'), + contentsString, allRoots, numBins, errors, comment, - flowBinPath, ); - await writeFile(filename, contents.toString('utf8')); + await writeFile(filename, contents); } // Exported for testing async function updateSuppressionsInText( - contents: Buffer, + contents: string, allRoots: Set, numBins: number, errorsByLine: Map, comment: string, - flowBinPath: string, -): Promise { +): Promise { const errors = Array.from(errorsByLine); // Sort in reverse order so that we remove comments later in the file first. Otherwise, the // removal of comments earlier in the file would outdate the locations for comments later in the // file. errors.sort(([line1, _errs1], [line2, _errs2]) => line2 - line1); - const ast = await getAst(contents.toString('utf8'), flowBinPath); + const ast = await getAst(contents); for (const [ _line, @@ -432,7 +425,6 @@ async function runner(args: Args): Promise { args.diffBin ? 2 : 1, errors, args.comment, - args.bin, ); }), );