diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3e850245e7f..086c121b173 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1922,7 +1922,6 @@ namespace ts { return createMissingList(); } - // The allowReservedWords parameter controls whether reserved words are permitted after the first dot function parseEntityName(allowReservedWords: boolean, diagnosticMessage?: DiagnosticMessage): EntityName { let entity: EntityName = allowReservedWords ? parseIdentifierName() : parseIdentifier(diagnosticMessage); let dotPos = scanner.getStartPos(); @@ -1932,6 +1931,7 @@ namespace ts { entity.jsdocDotPos = dotPos; break; } + dotPos = scanner.getStartPos(); const node: QualifiedName = createNode(SyntaxKind.QualifiedName, entity.pos); node.left = entity; node.right = parseRightSideOfDot(allowReservedWords); @@ -2140,7 +2140,7 @@ namespace ts { return finishNode(parameter); } - function parseJSDocNodeWithType(kind: SyntaxKind): TypeNode { + function parseJSDocNodeWithType(kind: SyntaxKind.JSDocVariadicType | SyntaxKind.JSDocNonNullableType): TypeNode { const result = createNode(kind) as JSDocVariadicType | JSDocNonNullableType; nextToken(); result.type = parseType(); @@ -2689,27 +2689,30 @@ namespace ts { } function parseJSDocPostfixTypeOrHigher(): TypeNode { - let type = parseArrayTypeOrHigher(); - let postfix: JSDocOptionalType | JSDocNonNullableType | JSDocNullableType; - // only parse postfix = inside jsdoc, because it's ambiguous elsewhere - if (contextFlags & NodeFlags.JSDoc && parseOptional(SyntaxKind.EqualsToken)) { - postfix = createNode(SyntaxKind.JSDocOptionalType, type.pos) as JSDocOptionalType; + const type = parseNonArrayType(); + const kind = getKind(token()); + if (!kind) return type; + nextToken(); + + const postfix = createNode(kind, type.pos) as JSDocOptionalType | JSDocNonNullableType | JSDocNullableType; + postfix.type = type; + return finishNode(postfix); + + function getKind(tokenKind: SyntaxKind): SyntaxKind | undefined { + switch (tokenKind) { + case SyntaxKind.EqualsToken: + // only parse postfix = inside jsdoc, because it's ambiguous elsewhere + return contextFlags & NodeFlags.JSDoc ? SyntaxKind.JSDocOptionalType : undefined; + case SyntaxKind.ExclamationToken: + return SyntaxKind.JSDocNonNullableType; + case SyntaxKind.QuestionToken: + return SyntaxKind.JSDocNullableType; + } } - else if (parseOptional(SyntaxKind.ExclamationToken)) { - postfix = createNode(SyntaxKind.JSDocNonNullableType, type.pos) as JSDocNonNullableType; - } - else if (parseOptional(SyntaxKind.QuestionToken)) { - postfix = createNode(SyntaxKind.JSDocNullableType, type.pos) as JSDocNullableType; - } - if (postfix) { - postfix.type = type; - type = finishNode(postfix); - } - return type; } function parseArrayTypeOrHigher(): TypeNode { - let type = parseNonArrayType(); + let type = parseJSDocPostfixTypeOrHigher(); while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) { if (isStartOfType()) { const node = createNode(SyntaxKind.IndexedAccessType, type.pos); @@ -2741,7 +2744,7 @@ namespace ts { case SyntaxKind.KeyOfKeyword: return parseTypeOperator(SyntaxKind.KeyOfKeyword); } - return parseJSDocPostfixTypeOrHigher(); + return parseArrayTypeOrHigher(); } function parseUnionOrIntersectionType(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, parseConstituentType: () => TypeNode, operator: SyntaxKind.BarToken | SyntaxKind.AmpersandToken): TypeNode { @@ -6576,15 +6579,8 @@ namespace ts { return finishNode(typedefTag); function isObjectTypeReference(node: TypeNode) { - if (node.kind === SyntaxKind.ObjectKeyword) { - return true; - } - if (node.kind === SyntaxKind.TypeReference) { - const jsDocTypeReference = node; - if (jsDocTypeReference.typeName.kind === SyntaxKind.Identifier) { - return (jsDocTypeReference.typeName as Identifier).text === "Object"; - } - } + return node.kind === SyntaxKind.ObjectKeyword || + isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.text === "Object"; } function scanChildTags(): JSDocTypeLiteral { diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 48d280c78dc..0e6221567a2 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -54,7 +54,7 @@ namespace ts { parsesCorrectly("typeReference3", "{a.function}"); parsesCorrectly("arrayType1", "{a[]}"); parsesCorrectly("arrayType2", "{a[][]}"); - parsesCorrectly("arrayType3", "{a[][]=}"); + parsesCorrectly("arrayType3", "{(a[][])=}"); parsesCorrectly("keyword1", "{var}"); parsesCorrectly("keyword2", "{null}"); parsesCorrectly("keyword3", "{undefined}"); diff --git a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.arrayType3.json b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.arrayType3.json index ca2b2e175cd..49ecaf53d4c 100644 --- a/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.arrayType3.json +++ b/tests/baselines/reference/JSDocParsing/TypeExpressions.parsesCorrectly.arrayType3.json @@ -1,29 +1,35 @@ { "kind": "JSDocOptionalType", "pos": 1, - "end": 7, + "end": 9, "flags": "JSDoc", "type": { - "kind": "ArrayType", + "kind": "ParenthesizedType", "pos": 1, - "end": 6, + "end": 8, "flags": "JSDoc", - "elementType": { + "type": { "kind": "ArrayType", - "pos": 1, - "end": 4, + "pos": 2, + "end": 7, "flags": "JSDoc", "elementType": { - "kind": "TypeReference", - "pos": 1, - "end": 2, + "kind": "ArrayType", + "pos": 2, + "end": 5, "flags": "JSDoc", - "typeName": { - "kind": "Identifier", - "pos": 1, - "end": 2, + "elementType": { + "kind": "TypeReference", + "pos": 2, + "end": 3, "flags": "JSDoc", - "text": "a" + "typeName": { + "kind": "Identifier", + "pos": 2, + "end": 3, + "flags": "JSDoc", + "text": "a" + } } } } diff --git a/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols b/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols new file mode 100644 index 00000000000..c5363a2ba3f --- /dev/null +++ b/tests/baselines/reference/jsdocPrefixPostfixParsing.symbols @@ -0,0 +1,25 @@ +=== tests/cases/conformance/jsdoc/prefixPostfix.js === +/** + * @param {number![]} x - number[] + * @param {!number[]} y - number[] + * @param {(number[])!} z - number[] + * @param {number?[]} a - (number | null)[] + * @param {?number[]} b - number[] | null + * @param {(number[])?} c - number[] | null + * @param {?...number} d - number[] | null + * @param {...?number} e - (number | null)[] + * @param {...number?} f - (number | null)[] + */ +function f(x, y, z, a, b, c, d, e, f) { +>f : Symbol(f, Decl(prefixPostfix.js, 0, 0)) +>x : Symbol(x, Decl(prefixPostfix.js, 11, 11)) +>y : Symbol(y, Decl(prefixPostfix.js, 11, 13)) +>z : Symbol(z, Decl(prefixPostfix.js, 11, 16)) +>a : Symbol(a, Decl(prefixPostfix.js, 11, 19)) +>b : Symbol(b, Decl(prefixPostfix.js, 11, 22)) +>c : Symbol(c, Decl(prefixPostfix.js, 11, 25)) +>d : Symbol(d, Decl(prefixPostfix.js, 11, 28)) +>e : Symbol(e, Decl(prefixPostfix.js, 11, 31)) +>f : Symbol(f, Decl(prefixPostfix.js, 11, 34)) +} + diff --git a/tests/baselines/reference/jsdocPrefixPostfixParsing.types b/tests/baselines/reference/jsdocPrefixPostfixParsing.types new file mode 100644 index 00000000000..46b15304bfa --- /dev/null +++ b/tests/baselines/reference/jsdocPrefixPostfixParsing.types @@ -0,0 +1,25 @@ +=== tests/cases/conformance/jsdoc/prefixPostfix.js === +/** + * @param {number![]} x - number[] + * @param {!number[]} y - number[] + * @param {(number[])!} z - number[] + * @param {number?[]} a - (number | null)[] + * @param {?number[]} b - number[] | null + * @param {(number[])?} c - number[] | null + * @param {?...number} d - number[] | null + * @param {...?number} e - (number | null)[] + * @param {...number?} f - (number | null)[] + */ +function f(x, y, z, a, b, c, d, e, f) { +>f : (x: number[], y: number[], z: number[], a: (number | null)[], b: number[] | null, c: number[] | null, d: number[] | null, ...e: (number | null)[], ...f: (number | null)[]) => void +>x : number[] +>y : number[] +>z : number[] +>a : (number | null)[] +>b : number[] | null +>c : number[] | null +>d : number[] | null +>e : (number | null)[] +>f : (number | null)[] +} + diff --git a/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts b/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts new file mode 100644 index 00000000000..707ae5a02a2 --- /dev/null +++ b/tests/cases/conformance/jsdoc/jsdocPrefixPostfixParsing.ts @@ -0,0 +1,21 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @strictNullChecks: true +// @noImplicitAny: true + +// @Filename: prefixPostfix.js + +/** + * @param {number![]} x - number[] + * @param {!number[]} y - number[] + * @param {(number[])!} z - number[] + * @param {number?[]} a - (number | null)[] + * @param {?number[]} b - number[] | null + * @param {(number[])?} c - number[] | null + * @param {?...number} d - number[] | null + * @param {...?number} e - (number | null)[] + * @param {...number?} f - (number | null)[] + */ +function f(x, y, z, a, b, c, d, e, f) { +}