Skip to content

Commit

Permalink
feat(plugin): declare missing argument members in const arrow functions
Browse files Browse the repository at this point in the history
fix #77
  • Loading branch information
tamj0rd2 committed Feb 18, 2021
1 parent 27349ac commit 378ada1
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 18 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ should be updated/released manually for now.
2. Run `npm i --prefix ./packages/extension/ ts-quickfixes-plugin` (If the plugin
has had a recent release, the package-lock.json for the extension should have
pending changes in git. If not, something went wrong,)
3. Commit the `package-lock.json` as a feat (so that a new extension versions can be released)
3. Commit the `package-lock.json` as a feat (so that a new extension version can be released)
4. Push the changes
5. Run `./publish.sh` to publish a new version of the extension

Expand Down
17 changes: 17 additions & 0 deletions packages/e2e/src/tests/extension.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ describe('Acceptance tests', () => {
`export const newBalance = withdrawMoney({ balance: 200, accountNumber: 'todo', sortCode: 'todo' }, 123)`,
)
})

it('declares missing members for const arrow function arguments', async () => {
const { getCodeActions } = createTestDeps()
const fileUri = vscode.Uri.file(TEST_ENV_DIR + '/testing.ts')
const document = await vscode.workspace.openTextDocument(fileUri)
await vscode.window.showTextDocument(document)

const argumentValue = '{ balance: 400 }'
const codeActions = await getCodeActions(document, argumentValue)
expect(codeActions[0].title).toStrictEqual('Declare missing argument members')
await vscode.workspace.applyEdit(codeActions[0].edit!)

const documentText = getAllDocumentText(document)
expect(documentText).toContain(
`sendMoney({ balance: 400, accountNumber: 'todo', sortCode: 'todo' }, 200)`,
)
})
})
})

Expand Down
13 changes: 7 additions & 6 deletions packages/extension/scripts/shipit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ semantic-release
print 'Semantic release complete'
UPDATED_VERSION_NUMBER="$(node ./scripts/get-marketplace-version.js)"

if [ "$CURRENT_VERSION_NUMBER" == "$UPDATED_VERSION_NUMBER" ]; then
print 'Not publishing the extension because there was no version increment'
else
print "Going to publish the extension under version ${UPDATED_VERSION_NUMBER}"
vsce publish -p $PUBLISHER_TOKEN $UPDATED_VERSION_NUMBER
fi
# === Still haven't figure out how to make this work. Use publish.sh in the repo root.
# if [ "$CURRENT_VERSION_NUMBER" == "$UPDATED_VERSION_NUMBER" ]; then
# print 'Not publishing the extension because there was no version increment'
# else
# print "Going to publish the extension under version ${UPDATED_VERSION_NUMBER}"
# vsce publish -p $PUBLISHER_TOKEN $UPDATED_VERSION_NUMBER
# fi
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,36 @@ describe('Fill Missing Argument Member', () => {
},
])
})

it('fills in undeclared argument members for const functions', () => {
const argumentValue = '{}'
const [filePath, fileContent] = FsMocker.addFile(`
const targetFunction = (someArgument: { min: number; max: number } ) => {
return 123
}
export const functionOutput = targetFunction(${argumentValue})
`)

const initializerLocation = getNodeRange(fileContent, argumentValue)
const fix = new MissingArgumentMembersFix({
filePath,
ts,
program: createTestProgram([filePath], MissingArgumentMembersFix.supportedErrorCodes),
logger: createDummyLogger(),
...initializerLocation,
})

expect(fix.changes).toStrictEqual<ts.FileTextChanges[]>([
{
fileName: filePath,
textChanges: [
{
span: { start: initializerLocation.start, length: argumentValue.length },
newText: `{ min: 0, max: 0 }`,
},
],
isNewFile: false,
},
])
})
})
34 changes: 24 additions & 10 deletions packages/plugin/src/code-fixes/missing-argument-members-fix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,7 @@ export class MissingArgumentMembersFix extends CodeFix {
TODO('Invalid argument index')
}

const { symbol: identifierSymbol } = this.typeChecker.getTypeAtLocation(callExpression.expression)
if (
!identifierSymbol.valueDeclaration ||
!this.ts.isFunctionDeclaration(identifierSymbol.valueDeclaration)
) {
TODO('no value declaration')
}

const functionDeclaration = identifierSymbol.valueDeclaration
const expectedType = functionDeclaration.parameters[argumentIndex]?.type
const expectedType = this.getExpectedTypeForArgumentAtIndex(callExpression, argumentIndex)

if (!expectedType) {
TODO('could not find a matching parameter for that argument')
Expand All @@ -91,6 +82,29 @@ export class MissingArgumentMembersFix extends CodeFix {

throw new Error('found a paramter for that argument but it was an unsupported type')
}

private getExpectedTypeForArgumentAtIndex(
callExpression: ts.CallExpression,
argumentIndex: number,
): ts.TypeNode {
const { symbol: identifierSymbol } = this.typeChecker.getTypeAtLocation(callExpression.expression)
if (!identifierSymbol.valueDeclaration) {
TODO('no value declaration')
}

if (
this.ts.isFunctionDeclaration(identifierSymbol.valueDeclaration) ||
this.ts.isArrowFunction(identifierSymbol.valueDeclaration)
) {
const functionDeclaration = identifierSymbol.valueDeclaration
const expectedType = functionDeclaration.parameters[argumentIndex]?.type
if (expectedType) return expectedType

TODO('could not find a matching parameter for that argument')
}

TODO('not a function or arrow function')
}
}

type NodeRange = Pick<MissingArgumentMembersArgs, 'start' | 'end'>
Expand Down
8 changes: 7 additions & 1 deletion test-environment/testing.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { Person } from './another-file'

interface Employee extends Person {
Expand All @@ -12,9 +13,14 @@ interface Account {
balance: number
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function withdrawMoney(account: Account, amount: number): number {
return 0
}

export const newBalance = withdrawMoney({ balance: 200 }, 123)

export const sendMoney = (account: Account, amount: number): void => {
return
}

sendMoney({ balance: 400 }, 200)

0 comments on commit 378ada1

Please sign in to comment.