From 0401ffcdbece600f73aa98b963dc1b9e60cd320f Mon Sep 17 00:00:00 2001 From: James Henry Date: Sun, 22 Oct 2017 17:13:46 +0100 Subject: [PATCH] Fix: Calculate typeArguments loc data correctly if empty (fixes #395) (#396) --- lib/convert.js | 29 +- .../errorRecovery/empty-type-arguments.src.ts | 1 + tests/lib/__snapshots__/typescript.js.snap | 259 ++++++++++++++++++ 3 files changed, 282 insertions(+), 7 deletions(-) create mode 100644 tests/fixtures/typescript/errorRecovery/empty-type-arguments.src.ts diff --git a/lib/convert.js b/lib/convert.js index c6a3deb..61d17b9 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -102,17 +102,32 @@ module.exports = function convert(config) { * @returns {TypeParameterInstantiation} TypeParameterInstantiation node */ function convertTypeArgumentsToTypeParameters(typeArguments) { - const firstTypeArgument = typeArguments[0]; - const lastTypeArgument = typeArguments[typeArguments.length - 1]; - const greaterThanToken = nodeUtils.findNextToken(lastTypeArgument, ast); - + /** + * Even if typeArguments is an empty array, TypeScript sets a `pos` and `end` + * property on the array object so we can safely read the values here + */ + const start = typeArguments.pos - 1; + let end = typeArguments.end + 1; + if (typeArguments && typeArguments.length) { + const firstTypeArgument = typeArguments[0]; + const typeArgumentsParent = firstTypeArgument.parent; + /** + * In the case of the parent being a CallExpression we have to use slightly different + * logic to calculate the correct end position + */ + if (typeArgumentsParent && typeArgumentsParent.kind === SyntaxKind.CallExpression) { + const lastTypeArgument = typeArguments[typeArguments.length - 1]; + const greaterThanToken = nodeUtils.findNextToken(lastTypeArgument, ast); + end = greaterThanToken.end; + } + } return { type: AST_NODE_TYPES.TypeParameterInstantiation, range: [ - firstTypeArgument.pos - 1, - greaterThanToken.end + start, + end ], - loc: nodeUtils.getLocFor(firstTypeArgument.pos - 1, greaterThanToken.end, ast), + loc: nodeUtils.getLocFor(start, end, ast), params: typeArguments.map(typeArgument => { if (nodeUtils.isTypeKeyword(typeArgument.kind)) { return { diff --git a/tests/fixtures/typescript/errorRecovery/empty-type-arguments.src.ts b/tests/fixtures/typescript/errorRecovery/empty-type-arguments.src.ts new file mode 100644 index 0000000..d0a415f --- /dev/null +++ b/tests/fixtures/typescript/errorRecovery/empty-type-arguments.src.ts @@ -0,0 +1 @@ +const foo: Foo<> \ No newline at end of file diff --git a/tests/lib/__snapshots__/typescript.js.snap b/tests/lib/__snapshots__/typescript.js.snap index 1dbca46..81f17a5 100644 --- a/tests/lib/__snapshots__/typescript.js.snap +++ b/tests/lib/__snapshots__/typescript.js.snap @@ -55360,6 +55360,265 @@ Object { } `; +exports[`typescript fixtures/errorRecovery/empty-type-arguments.src 1`] = ` +Object { + "body": Array [ + Object { + "declarations": Array [ + Object { + "id": Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "name": "foo", + "range": Array [ + 6, + 16, + ], + "type": "Identifier", + "typeAnnotation": Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 9, + "line": 1, + }, + }, + "range": Array [ + 9, + 16, + ], + "type": "TypeAnnotation", + "typeAnnotation": Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "range": Array [ + 11, + 16, + ], + "type": "TSTypeReference", + "typeName": Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "name": "Foo", + "range": Array [ + 11, + 14, + ], + "type": "Identifier", + }, + "typeParameters": Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 14, + "line": 1, + }, + }, + "params": Array [], + "range": Array [ + 14, + 16, + ], + "type": "TypeParameterInstantiation", + }, + }, + }, + }, + "init": null, + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 16, + ], + "type": "VariableDeclarator", + }, + ], + "kind": "const", + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 16, + ], + "type": "VariableDeclaration", + }, + ], + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 16, + ], + "sourceType": "script", + "tokens": Array [ + Object { + "loc": Object { + "end": Object { + "column": 5, + "line": 1, + }, + "start": Object { + "column": 0, + "line": 1, + }, + }, + "range": Array [ + 0, + 5, + ], + "type": "Keyword", + "value": "const", + }, + Object { + "loc": Object { + "end": Object { + "column": 9, + "line": 1, + }, + "start": Object { + "column": 6, + "line": 1, + }, + }, + "range": Array [ + 6, + 9, + ], + "type": "Identifier", + "value": "foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 10, + "line": 1, + }, + "start": Object { + "column": 9, + "line": 1, + }, + }, + "range": Array [ + 9, + 10, + ], + "type": "Punctuator", + "value": ":", + }, + Object { + "loc": Object { + "end": Object { + "column": 14, + "line": 1, + }, + "start": Object { + "column": 11, + "line": 1, + }, + }, + "range": Array [ + 11, + 14, + ], + "type": "Identifier", + "value": "Foo", + }, + Object { + "loc": Object { + "end": Object { + "column": 15, + "line": 1, + }, + "start": Object { + "column": 14, + "line": 1, + }, + }, + "range": Array [ + 14, + 15, + ], + "type": "Punctuator", + "value": "<", + }, + Object { + "loc": Object { + "end": Object { + "column": 16, + "line": 1, + }, + "start": Object { + "column": 15, + "line": 1, + }, + }, + "range": Array [ + 15, + 16, + ], + "type": "Punctuator", + "value": ">", + }, + ], + "type": "Program", +} +`; + exports[`typescript fixtures/errorRecovery/enum-with-keywords.src 1`] = ` Object { "body": Array [