From ddf0df9cbb92bca14861b683535a51258291fa4c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 12 Nov 2017 09:11:09 -0800 Subject: [PATCH 1/6] Introduce TokenFlags enum --- src/compiler/binder.ts | 4 +- src/compiler/checker.ts | 2 +- src/compiler/parser.ts | 2 +- src/compiler/scanner.ts | 69 ++++++++++++----------------- src/compiler/transformers/es2015.ts | 2 +- src/compiler/types.ts | 19 +++++--- 6 files changed, 46 insertions(+), 52 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a0146740678..eeeeab1d81b 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1835,7 +1835,7 @@ namespace ts { } function checkStrictModeNumericLiteral(node: NumericLiteral) { - if (inStrictMode && node.numericLiteralFlags & NumericLiteralFlags.Octal) { + if (inStrictMode && node.numericLiteralFlags & TokenFlags.Octal) { file.bindDiagnostics.push(createDiagnosticForNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode)); } } @@ -3319,7 +3319,7 @@ namespace ts { break; case SyntaxKind.NumericLiteral: - if ((node).numericLiteralFlags & NumericLiteralFlags.BinaryOrOctalSpecifier) { + if ((node).numericLiteralFlags & TokenFlags.BinaryOrOctalSpecifier) { transformFlags |= TransformFlags.AssertES2015; } break; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 981380c70de..7ed7580b483 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25779,7 +25779,7 @@ namespace ts { function checkGrammarNumericLiteral(node: NumericLiteral): boolean { // Grammar checking - if (node.numericLiteralFlags & NumericLiteralFlags.Octal) { + if (node.numericLiteralFlags & TokenFlags.Octal) { let diagnosticMessage: DiagnosticMessage | undefined; if (languageVersion >= ScriptTarget.ES5) { diagnosticMessage = Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher_Use_the_syntax_0; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 79b5eb30c49..db13e843529 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2111,7 +2111,7 @@ namespace ts { // We also do not need to check for negatives because any prefix operator would be part of a // parent unary expression. if (node.kind === SyntaxKind.NumericLiteral) { - (node).numericLiteralFlags = scanner.getNumericLiteralFlags(); + (node).numericLiteralFlags = scanner.getTokenFlags() & TokenFlags.NumericLiteralFlags; } nextToken(); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 9fddece11d0..ddc89a60e2a 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -27,7 +27,7 @@ namespace ts { isReservedWord(): boolean; isUnterminated(): boolean; /* @internal */ - getNumericLiteralFlags(): NumericLiteralFlags; + getTokenFlags(): TokenFlags; reScanGreaterToken(): SyntaxKind; reScanSlashToken(): SyntaxKind; reScanTemplateToken(): SyntaxKind; @@ -814,10 +814,7 @@ namespace ts { let token: SyntaxKind; let tokenValue: string; - let precedingLineBreak: boolean; - let hasExtendedUnicodeEscape: boolean; - let tokenIsUnterminated: boolean; - let numericLiteralFlags: NumericLiteralFlags; + let tokenFlags: TokenFlags; setText(text, start, length); @@ -828,12 +825,12 @@ namespace ts { getTokenPos: () => tokenPos, getTokenText: () => text.substring(tokenPos, pos), getTokenValue: () => tokenValue, - hasExtendedUnicodeEscape: () => hasExtendedUnicodeEscape, - hasPrecedingLineBreak: () => precedingLineBreak, + hasExtendedUnicodeEscape: () => (tokenFlags & TokenFlags.ExtendedUnicodeEscape) !== 0, + hasPrecedingLineBreak: () => (tokenFlags & TokenFlags.PrecedingLineBreak) !== 0, isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord, isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord, - isUnterminated: () => tokenIsUnterminated, - getNumericLiteralFlags: () => numericLiteralFlags, + isUnterminated: () => (tokenFlags & TokenFlags.Unterminated) !== 0, + getTokenFlags: () => tokenFlags, reScanGreaterToken, reScanSlashToken, reScanTemplateToken, @@ -870,7 +867,7 @@ namespace ts { let end = pos; if (text.charCodeAt(pos) === CharacterCodes.E || text.charCodeAt(pos) === CharacterCodes.e) { pos++; - numericLiteralFlags = NumericLiteralFlags.Scientific; + tokenFlags |= TokenFlags.Scientific; if (text.charCodeAt(pos) === CharacterCodes.plus || text.charCodeAt(pos) === CharacterCodes.minus) pos++; if (isDigit(text.charCodeAt(pos))) { pos++; @@ -942,7 +939,7 @@ namespace ts { while (true) { if (pos >= end) { result += text.substring(start, pos); - tokenIsUnterminated = true; + tokenFlags |= TokenFlags.Unterminated; error(Diagnostics.Unterminated_string_literal); break; } @@ -960,7 +957,7 @@ namespace ts { } if (isLineBreak(ch)) { result += text.substring(start, pos); - tokenIsUnterminated = true; + tokenFlags |= TokenFlags.Unterminated; error(Diagnostics.Unterminated_string_literal); break; } @@ -984,7 +981,7 @@ namespace ts { while (true) { if (pos >= end) { contents += text.substring(start, pos); - tokenIsUnterminated = true; + tokenFlags |= TokenFlags.Unterminated; error(Diagnostics.Unterminated_template_literal); resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail; break; @@ -1070,7 +1067,7 @@ namespace ts { case CharacterCodes.u: // '\u{DDDDDDDD}' if (pos < end && text.charCodeAt(pos) === CharacterCodes.openBrace) { - hasExtendedUnicodeEscape = true; + tokenFlags |= TokenFlags.ExtendedUnicodeEscape; pos++; return scanExtendedUnicodeEscape(); } @@ -1239,10 +1236,7 @@ namespace ts { function scan(): SyntaxKind { startPos = pos; - hasExtendedUnicodeEscape = false; - precedingLineBreak = false; - tokenIsUnterminated = false; - numericLiteralFlags = 0; + tokenFlags = 0; while (true) { tokenPos = pos; if (pos >= end) { @@ -1264,7 +1258,7 @@ namespace ts { switch (ch) { case CharacterCodes.lineFeed: case CharacterCodes.carriageReturn: - precedingLineBreak = true; + tokenFlags |= TokenFlags.PrecedingLineBreak; if (skipTrivia) { pos++; continue; @@ -1407,7 +1401,7 @@ namespace ts { } if (isLineBreak(ch)) { - precedingLineBreak = true; + tokenFlags |= TokenFlags.PrecedingLineBreak; } pos++; } @@ -1420,7 +1414,9 @@ namespace ts { continue; } else { - tokenIsUnterminated = !commentClosed; + if (!commentClosed) { + tokenFlags |= TokenFlags.Unterminated; + } return token = SyntaxKind.MultiLineCommentTrivia; } } @@ -1441,7 +1437,7 @@ namespace ts { value = 0; } tokenValue = "" + value; - numericLiteralFlags = NumericLiteralFlags.HexSpecifier; + tokenFlags |= TokenFlags.HexSpecifier; return token = SyntaxKind.NumericLiteral; } else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.B || text.charCodeAt(pos + 1) === CharacterCodes.b)) { @@ -1452,7 +1448,7 @@ namespace ts { value = 0; } tokenValue = "" + value; - numericLiteralFlags = NumericLiteralFlags.BinarySpecifier; + tokenFlags |= TokenFlags.BinarySpecifier; return token = SyntaxKind.NumericLiteral; } else if (pos + 2 < end && (text.charCodeAt(pos + 1) === CharacterCodes.O || text.charCodeAt(pos + 1) === CharacterCodes.o)) { @@ -1463,13 +1459,13 @@ namespace ts { value = 0; } tokenValue = "" + value; - numericLiteralFlags = NumericLiteralFlags.OctalSpecifier; + tokenFlags |= TokenFlags.OctalSpecifier; return token = SyntaxKind.NumericLiteral; } // Try to parse as an octal if (pos + 1 < end && isOctalDigit(text.charCodeAt(pos + 1))) { tokenValue = "" + scanOctalDigits(); - numericLiteralFlags = NumericLiteralFlags.Octal; + tokenFlags |= TokenFlags.Octal; return token = SyntaxKind.NumericLiteral; } // This fall-through is a deviation from the EcmaScript grammar. The grammar says that a leading zero @@ -1626,7 +1622,7 @@ namespace ts { continue; } else if (isLineBreak(ch)) { - precedingLineBreak = true; + tokenFlags |= TokenFlags.PrecedingLineBreak; pos++; continue; } @@ -1669,14 +1665,14 @@ namespace ts { // If we reach the end of a file, or hit a newline, then this is an unterminated // regex. Report error and return what we have so far. if (p >= end) { - tokenIsUnterminated = true; + tokenFlags |= TokenFlags.Unterminated; error(Diagnostics.Unterminated_regular_expression_literal); break; } const ch = text.charCodeAt(p); if (isLineBreak(ch)) { - tokenIsUnterminated = true; + tokenFlags |= TokenFlags.Unterminated; error(Diagnostics.Unterminated_regular_expression_literal); break; } @@ -1894,7 +1890,7 @@ namespace ts { const saveTokenPos = tokenPos; const saveToken = token; const saveTokenValue = tokenValue; - const savePrecedingLineBreak = precedingLineBreak; + const saveTokenFlags = tokenFlags; const result = callback(); // If our callback returned something 'falsy' or we're just looking ahead, @@ -1905,7 +1901,7 @@ namespace ts { tokenPos = saveTokenPos; token = saveToken; tokenValue = saveTokenValue; - precedingLineBreak = savePrecedingLineBreak; + tokenFlags = saveTokenFlags; } return result; } @@ -1916,10 +1912,8 @@ namespace ts { const saveStartPos = startPos; const saveTokenPos = tokenPos; const saveToken = token; - const savePrecedingLineBreak = precedingLineBreak; const saveTokenValue = tokenValue; - const saveHasExtendedUnicodeEscape = hasExtendedUnicodeEscape; - const saveTokenIsUnterminated = tokenIsUnterminated; + const saveTokenFlags = tokenFlags; setText(text, start, length); const result = callback(); @@ -1929,10 +1923,8 @@ namespace ts { startPos = saveStartPos; tokenPos = saveTokenPos; token = saveToken; - precedingLineBreak = savePrecedingLineBreak; tokenValue = saveTokenValue; - hasExtendedUnicodeEscape = saveHasExtendedUnicodeEscape; - tokenIsUnterminated = saveTokenIsUnterminated; + tokenFlags = saveTokenFlags; return result; } @@ -1973,11 +1965,8 @@ namespace ts { startPos = textPos; tokenPos = textPos; token = SyntaxKind.Unknown; - precedingLineBreak = false; - tokenValue = undefined; - hasExtendedUnicodeEscape = false; - tokenIsUnterminated = false; + tokenFlags = 0; } } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 989b0827570..0c5c3cfbd3a 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3633,7 +3633,7 @@ namespace ts { * @param node A string literal. */ function visitNumericLiteral(node: NumericLiteral) { - if (node.numericLiteralFlags & NumericLiteralFlags.BinaryOrOctalSpecifier) { + if (node.numericLiteralFlags & TokenFlags.BinaryOrOctalSpecifier) { return setTextRange(createNumericLiteral(node.text), node); } return node; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3141e3f22d9..7b0145ea134 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1451,20 +1451,25 @@ namespace ts { } /* @internal */ - export const enum NumericLiteralFlags { + export const enum TokenFlags { None = 0, - Scientific = 1 << 1, // e.g. `10e2` - Octal = 1 << 2, // e.g. `0777` - HexSpecifier = 1 << 3, // e.g. `0x00000000` - BinarySpecifier = 1 << 4, // e.g. `0b0110010000000000` - OctalSpecifier = 1 << 5, // e.g. `0o777` + PrecedingLineBreak = 1 << 0, + PrecedingComment = 1 << 1, + Unterminated = 1 << 2, + ExtendedUnicodeEscape = 1 << 3, + Scientific = 1 << 4, // e.g. `10e2` + Octal = 1 << 5, // e.g. `0777` + HexSpecifier = 1 << 6, // e.g. `0x00000000` + BinarySpecifier = 1 << 7, // e.g. `0b0110010000000000` + OctalSpecifier = 1 << 8, // e.g. `0o777` BinaryOrOctalSpecifier = BinarySpecifier | OctalSpecifier, + NumericLiteralFlags = Scientific | Octal | HexSpecifier | BinarySpecifier | OctalSpecifier } export interface NumericLiteral extends LiteralExpression { kind: SyntaxKind.NumericLiteral; /* @internal */ - numericLiteralFlags?: NumericLiteralFlags; + numericLiteralFlags?: TokenFlags; } export interface TemplateHead extends LiteralLikeNode { From 3411318e6b905aed2969db5568beb670af510bda Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 13 Nov 2017 09:51:56 -0800 Subject: [PATCH 2/6] Use TokenFlags.PrecedingJSDocComment to guide JSDoc comment processing --- src/compiler/parser.ts | 508 +++++++++++++++++----------------------- src/compiler/scanner.ts | 3 + src/compiler/types.ts | 2 +- 3 files changed, 222 insertions(+), 291 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db13e843529..52ed714a533 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -753,20 +753,19 @@ namespace ts { return sourceFile; } - function addJSDocComment(node: T): T { const comments = getJSDocCommentRanges(node, sourceFile.text); if (comments) { for (const comment of comments) { const jsDoc = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos); - if (!jsDoc) { - continue; + if (jsDoc) { + if (!node.jsDoc) { + node.jsDoc = [jsDoc]; + } + else { + node.jsDoc.push(jsDoc); + } } - - if (!node.jsDoc) { - node.jsDoc = []; - } - node.jsDoc.push(jsDoc); } } @@ -1144,16 +1143,20 @@ namespace ts { } } - // note: this function creates only node - function createNode(kind: TKind, pos?: number): Node | Token | Identifier { + function createNode(kind: SyntaxKind, pos?: number): Node { nodeCount++; - if (!(pos >= 0)) { - pos = scanner.getStartPos(); - } + const p = pos >= 0 ? pos : scanner.getStartPos(); + return isNodeKind(kind) || kind === SyntaxKind.Unknown ? new NodeConstructor(kind, p, p) : + kind === SyntaxKind.Identifier ? new IdentifierConstructor(kind, p, p) : + new TokenConstructor(kind, p, p); + } - return isNodeKind(kind) ? new NodeConstructor(kind, pos, pos) : - kind === SyntaxKind.Identifier ? new IdentifierConstructor(kind, pos, pos) : - new TokenConstructor(kind, pos, pos); + function createNodeWithJSDoc(kind: SyntaxKind): Node { + const node = createNode(kind); + if (scanner.getTokenFlags() & TokenFlags.PrecedingJSDocComment) { + addJSDocComment(node); + } + return node; } function createNodeArray(elements: T[], pos: number, end?: number): NodeArray { @@ -1193,7 +1196,7 @@ namespace ts { parseErrorAtCurrentToken(diagnosticMessage, arg0); } - const result = createNode(kind, scanner.getStartPos()); + const result = createNode(kind); if (kind === SyntaxKind.Identifier) { (result as Identifier).escapedText = "" as __String; @@ -2185,10 +2188,10 @@ namespace ts { function parseJSDocFunctionType(): JSDocFunctionType | TypeReferenceNode { if (lookAhead(nextTokenIsOpenParen)) { - const result = createNode(SyntaxKind.JSDocFunctionType); + const result = createNodeWithJSDoc(SyntaxKind.JSDocFunctionType); nextToken(); fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type | SignatureFlags.JSDoc, result); - return addJSDocComment(finishNode(result)); + return finishNode(result); } const node = createNode(SyntaxKind.TypeReference); node.typeName = parseIdentifierName(); @@ -2272,7 +2275,7 @@ namespace ts { } function parseParameter(requireEqualsToken?: boolean): ParameterDeclaration { - const node = createNode(SyntaxKind.Parameter); + const node = createNodeWithJSDoc(SyntaxKind.Parameter); if (token() === SyntaxKind.ThisKeyword) { node.name = createIdentifier(/*isIdentifier*/ true); node.type = parseParameterType(); @@ -2302,7 +2305,11 @@ namespace ts { node.type = parseParameterType(); node.initializer = parseInitializer(/*inParameter*/ true, requireEqualsToken); - return addJSDocComment(finishNode(node)); + return finishNode(node); + } + + function parseParameterWithEqualsToken(): ParameterDeclaration { + return parseParameter(/*requireEqualsToken*/ true); } function fillSignature( @@ -2358,7 +2365,9 @@ namespace ts { setAwaitContext(!!(flags & SignatureFlags.Await)); const result = parseDelimitedList(ParsingContext.Parameters, - flags & SignatureFlags.JSDoc ? parseJSDocParameter : () => parseParameter(!!(flags & SignatureFlags.RequireCompleteParameterList))); + flags & SignatureFlags.JSDoc ? parseJSDocParameter : + flags & SignatureFlags.RequireCompleteParameterList ? parseParameterWithEqualsToken : + parseParameter); setYieldContext(savedYieldContext); setAwaitContext(savedAwaitContext); @@ -2390,21 +2399,17 @@ namespace ts { } function parseSignatureMember(kind: SyntaxKind.CallSignature | SyntaxKind.ConstructSignature): CallSignatureDeclaration | ConstructSignatureDeclaration { - const node = createNode(kind); + const node = createNodeWithJSDoc(kind); if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, node); parseTypeMemberSemicolon(); - return addJSDocComment(finishNode(node)); + return finishNode(node); } function isIndexSignature(): boolean { - if (token() !== SyntaxKind.OpenBracketToken) { - return false; - } - - return lookAhead(isUnambiguouslyIndexSignature); + return token() === SyntaxKind.OpenBracketToken && lookAhead(isUnambiguouslyIndexSignature); } function isUnambiguouslyIndexSignature() { @@ -2462,49 +2467,35 @@ namespace ts { return token() === SyntaxKind.ColonToken || token() === SyntaxKind.CommaToken || token() === SyntaxKind.CloseBracketToken; } - function parseIndexSignatureDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): IndexSignatureDeclaration { - const node = createNode(SyntaxKind.IndexSignature, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseIndexSignatureDeclaration(node: IndexSignatureDeclaration): IndexSignatureDeclaration { + node.kind = SyntaxKind.IndexSignature; node.parameters = parseBracketedList(ParsingContext.Parameters, parseParameter, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); node.type = parseTypeAnnotation(); parseTypeMemberSemicolon(); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parsePropertyOrMethodSignature(fullStart: number, modifiers: NodeArray): PropertySignature | MethodSignature { - const name = parsePropertyName(); - const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); - + function parsePropertyOrMethodSignature(node: PropertySignature | MethodSignature): PropertySignature | MethodSignature { + node.name = parsePropertyName(); + node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken); if (token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { - const method = createNode(SyntaxKind.MethodSignature, fullStart); - method.modifiers = modifiers; - method.name = name; - method.questionToken = questionToken; - + node.kind = SyntaxKind.MethodSignature; // Method signatures don't exist in expression contexts. So they have neither // [Yield] nor [Await] - fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, method); - parseTypeMemberSemicolon(); - return addJSDocComment(finishNode(method)); + fillSignature(SyntaxKind.ColonToken, SignatureFlags.Type, node); } else { - const property = createNode(SyntaxKind.PropertySignature, fullStart); - property.modifiers = modifiers; - property.name = name; - property.questionToken = questionToken; - property.type = parseTypeAnnotation(); - + node.kind = SyntaxKind.PropertySignature; + node.type = parseTypeAnnotation(); if (token() === SyntaxKind.EqualsToken) { // Although type literal properties cannot not have initializers, we attempt // to parse an initializer so we can report in the checker that an interface // property or type literal property cannot have an initializer. - property.initializer = parseNonParameterInitializer(); + (node).initializer = parseNonParameterInitializer(); } - - parseTypeMemberSemicolon(); - return addJSDocComment(finishNode(property)); } + parseTypeMemberSemicolon(); + return finishNode(node); } function isTypeMemberStart(): boolean { @@ -2547,12 +2538,12 @@ namespace ts { if (token() === SyntaxKind.NewKeyword && lookAhead(nextTokenIsOpenParenOrLessThan)) { return parseSignatureMember(SyntaxKind.ConstructSignature); } - const fullStart = getNodePos(); - const modifiers = parseModifiers(); + const node = createNodeWithJSDoc(SyntaxKind.Unknown); + node.modifiers = parseModifiers(); if (isIndexSignature()) { - return parseIndexSignatureDeclaration(fullStart, /*decorators*/ undefined, modifiers); + return parseIndexSignatureDeclaration(node); } - return parsePropertyOrMethodSignature(fullStart, modifiers); + return parsePropertyOrMethodSignature(node); } function nextTokenIsOpenParenOrLessThan() { @@ -2624,12 +2615,12 @@ namespace ts { } function parseFunctionOrConstructorType(kind: SyntaxKind): FunctionOrConstructorTypeNode { - const node = createNode(kind); + const node = createNodeWithJSDoc(kind); if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node); - return addJSDocComment(finishNode(node)); + return finishNode(node); } function parseKeywordAndNoDot(): TypeNode | undefined { @@ -3243,7 +3234,7 @@ namespace ts { ? parseArrowFunctionExpressionBody(isAsync) : parseIdentifier(); - return addJSDocComment(finishNode(arrowFunction)); + return finishNode(arrowFunction); } // True -> We definitely expect a parenthesized arrow function here. @@ -3410,7 +3401,7 @@ namespace ts { } function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { - const node = createNode(SyntaxKind.ArrowFunction); + const node = createNodeWithJSDoc(SyntaxKind.ArrowFunction); node.modifiers = parseModifiersForArrowFunction(); const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; // Arrow functions are never generators. @@ -4042,7 +4033,7 @@ namespace ts { } function parseJsxText(): JsxText { - const node = createNode(SyntaxKind.JsxText, scanner.getStartPos()); + const node = createNode(SyntaxKind.JsxText); node.containsOnlyWhiteSpaces = currentToken === SyntaxKind.JsxTextAllWhiteSpaces; currentToken = scanner.scanJsxToken(); return finishNode(node); @@ -4450,11 +4441,11 @@ namespace ts { } function parseParenthesizedExpression(): ParenthesizedExpression { - const node = createNode(SyntaxKind.ParenthesizedExpression); + const node = createNodeWithJSDoc(SyntaxKind.ParenthesizedExpression); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); - return addJSDocComment(finishNode(node)); + return finishNode(node); } function parseSpreadElement(): Expression { @@ -4485,41 +4476,32 @@ namespace ts { return finishNode(node); } - function tryParseAccessorDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): AccessorDeclaration | undefined { - if (parseContextualModifier(SyntaxKind.GetKeyword)) { - return parseAccessorDeclaration(SyntaxKind.GetAccessor, fullStart, decorators, modifiers); - } - else if (parseContextualModifier(SyntaxKind.SetKeyword)) { - return parseAccessorDeclaration(SyntaxKind.SetAccessor, fullStart, decorators, modifiers); - } - - return undefined; - } - function parseObjectLiteralElement(): ObjectLiteralElementLike { - const fullStart = scanner.getStartPos(); - const dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); - if (dotDotDotToken) { - const spreadElement = createNode(SyntaxKind.SpreadAssignment, fullStart); - spreadElement.expression = parseAssignmentExpressionOrHigher(); - return addJSDocComment(finishNode(spreadElement)); - } - const decorators = parseDecorators(); - const modifiers = parseModifiers(); + const node = createNodeWithJSDoc(SyntaxKind.Unknown); - const accessor = tryParseAccessorDeclaration(fullStart, decorators, modifiers); - if (accessor) { - return accessor; + if (parseOptionalToken(SyntaxKind.DotDotDotToken)) { + node.kind = SyntaxKind.SpreadAssignment; + (node).expression = parseAssignmentExpressionOrHigher(); + return finishNode(node); + } + + node.decorators = parseDecorators(); + node.modifiers = parseModifiers(); + + if (parseContextualModifier(SyntaxKind.GetKeyword)) { + return parseAccessorDeclaration(node, SyntaxKind.GetAccessor); + } + if (parseContextualModifier(SyntaxKind.SetKeyword)) { + return parseAccessorDeclaration(node, SyntaxKind.SetAccessor); } const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); const tokenIsIdentifier = isIdentifier(); - const propertyName = parsePropertyName(); - + node.name = parsePropertyName(); // Disallowing of optional property assignments happens in the grammar checker. - const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); + (node).questionToken = parseOptionalToken(SyntaxKind.QuestionToken); if (asteriskToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { - return parseMethodDeclaration(fullStart, decorators, modifiers, asteriskToken, propertyName, questionToken); + return parseMethodDeclaration(node, asteriskToken); } // check if it is short-hand property assignment or normal property assignment @@ -4529,27 +4511,20 @@ namespace ts { // this is necessary because ObjectLiteral productions are also used to cover grammar for ObjectAssignmentPattern const isShorthandPropertyAssignment = tokenIsIdentifier && (token() === SyntaxKind.CommaToken || token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.EqualsToken); - if (isShorthandPropertyAssignment) { - const shorthandDeclaration = createNode(SyntaxKind.ShorthandPropertyAssignment, fullStart); - shorthandDeclaration.name = propertyName; - shorthandDeclaration.questionToken = questionToken; + node.kind = SyntaxKind.ShorthandPropertyAssignment; const equalsToken = parseOptionalToken(SyntaxKind.EqualsToken); if (equalsToken) { - shorthandDeclaration.equalsToken = equalsToken; - shorthandDeclaration.objectAssignmentInitializer = allowInAnd(parseAssignmentExpressionOrHigher); + (node).equalsToken = equalsToken; + (node).objectAssignmentInitializer = allowInAnd(parseAssignmentExpressionOrHigher); } - return addJSDocComment(finishNode(shorthandDeclaration)); } else { - const propertyAssignment = createNode(SyntaxKind.PropertyAssignment, fullStart); - propertyAssignment.modifiers = modifiers; - propertyAssignment.name = propertyName; - propertyAssignment.questionToken = questionToken; + node.kind = SyntaxKind.PropertyAssignment; parseExpected(SyntaxKind.ColonToken); - propertyAssignment.initializer = allowInAnd(parseAssignmentExpressionOrHigher); - return addJSDocComment(finishNode(propertyAssignment)); + (node).initializer = allowInAnd(parseAssignmentExpressionOrHigher); } + return finishNode(node); } function parseObjectLiteralExpression(): ObjectLiteralExpression { @@ -4575,7 +4550,7 @@ namespace ts { setDecoratorContext(/*val*/ false); } - const node = createNode(SyntaxKind.FunctionExpression); + const node = createNodeWithJSDoc(SyntaxKind.FunctionExpression); node.modifiers = parseModifiers(); parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); @@ -4595,7 +4570,7 @@ namespace ts { setDecoratorContext(/*val*/ true); } - return addJSDocComment(finishNode(node)); + return finishNode(node); } function parseOptionalIdentifier(): Identifier | undefined { @@ -4820,7 +4795,7 @@ namespace ts { parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); - const caseBlock = createNode(SyntaxKind.CaseBlock, scanner.getStartPos()); + const caseBlock = createNode(SyntaxKind.CaseBlock); parseExpected(SyntaxKind.OpenBraceToken); caseBlock.clauses = parseList(ParsingContext.SwitchClauses, parseCaseOrDefaultClause); parseExpected(SyntaxKind.CloseBraceToken); @@ -4890,21 +4865,19 @@ namespace ts { // Avoiding having to do the lookahead for a labeled statement by just trying to parse // out an expression, seeing if it is identifier and then seeing if it is followed by // a colon. - const fullStart = scanner.getStartPos(); + const node = createNodeWithJSDoc(SyntaxKind.Unknown); const expression = allowInAnd(parseExpression); - if (expression.kind === SyntaxKind.Identifier && parseOptional(SyntaxKind.ColonToken)) { - const labeledStatement = createNode(SyntaxKind.LabeledStatement, fullStart); - labeledStatement.label = expression; - labeledStatement.statement = parseStatement(); - return addJSDocComment(finishNode(labeledStatement)); + node.kind = SyntaxKind.LabeledStatement; + (node).label = expression; + (node).statement = parseStatement(); } else { - const expressionStatement = createNode(SyntaxKind.ExpressionStatement, fullStart); - expressionStatement.expression = expression; + node.kind = SyntaxKind.ExpressionStatement; + (node).expression = expression; parseSemicolon(); - return addJSDocComment(finishNode(expressionStatement)); } + return finishNode(node); } function nextTokenIsIdentifierOrKeywordOnSameLine() { @@ -5086,16 +5059,16 @@ namespace ts { case SyntaxKind.OpenBraceToken: return parseBlock(/*ignoreMissingOpenBrace*/ false); case SyntaxKind.VarKeyword: - return parseVariableStatement(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined); + return parseVariableStatement(createNodeWithJSDoc(SyntaxKind.VariableDeclaration)); case SyntaxKind.LetKeyword: if (isLetDeclaration()) { - return parseVariableStatement(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined); + return parseVariableStatement(createNodeWithJSDoc(SyntaxKind.VariableDeclaration)); } break; case SyntaxKind.FunctionKeyword: - return parseFunctionDeclaration(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined); + return parseFunctionDeclaration(createNodeWithJSDoc(SyntaxKind.FunctionDeclaration)); case SyntaxKind.ClassKeyword: - return parseClassDeclaration(scanner.getStartPos(), /*decorators*/ undefined, /*modifiers*/ undefined); + return parseClassDeclaration(createNodeWithJSDoc(SyntaxKind.ClassDeclaration)); case SyntaxKind.IfKeyword: return parseIfStatement(); case SyntaxKind.DoKeyword: @@ -5150,63 +5123,67 @@ namespace ts { return parseExpressionOrLabeledStatement(); } + function isDeclareModifier(modifier: Modifier) { + return modifier.kind === SyntaxKind.DeclareKeyword; + } + function parseDeclaration(): Statement { - const fullStart = getNodePos(); - const decorators = parseDecorators(); - const modifiers = parseModifiers(); - if (some(modifiers, m => m.kind === SyntaxKind.DeclareKeyword)) { - for (const m of modifiers) { + const node = createNodeWithJSDoc(SyntaxKind.Unknown); + node.decorators = parseDecorators(); + node.modifiers = parseModifiers(); + if (some(node.modifiers, isDeclareModifier)) { + for (const m of node.modifiers) { m.flags |= NodeFlags.Ambient; } - return doInsideOfContext(NodeFlags.Ambient, () => parseDeclarationWorker(fullStart, decorators, modifiers)); + return doInsideOfContext(NodeFlags.Ambient, () => parseDeclarationWorker(node)); } else { - return parseDeclarationWorker(fullStart, decorators, modifiers); + return parseDeclarationWorker(node); } } - function parseDeclarationWorker(fullStart: number, decorators: NodeArray | undefined, modifiers: NodeArray | undefined): Statement { + function parseDeclarationWorker(node: Statement): Statement { switch (token()) { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: - return parseVariableStatement(fullStart, decorators, modifiers); + return parseVariableStatement(node); case SyntaxKind.FunctionKeyword: - return parseFunctionDeclaration(fullStart, decorators, modifiers); + return parseFunctionDeclaration(node); case SyntaxKind.ClassKeyword: - return parseClassDeclaration(fullStart, decorators, modifiers); + return parseClassDeclaration(node); case SyntaxKind.InterfaceKeyword: - return parseInterfaceDeclaration(fullStart, decorators, modifiers); + return parseInterfaceDeclaration(node); case SyntaxKind.TypeKeyword: - return parseTypeAliasDeclaration(fullStart, decorators, modifiers); + return parseTypeAliasDeclaration(node); case SyntaxKind.EnumKeyword: - return parseEnumDeclaration(fullStart, decorators, modifiers); + return parseEnumDeclaration(node); case SyntaxKind.GlobalKeyword: case SyntaxKind.ModuleKeyword: case SyntaxKind.NamespaceKeyword: - return parseModuleDeclaration(fullStart, decorators, modifiers); + return parseModuleDeclaration(node); case SyntaxKind.ImportKeyword: - return parseImportDeclarationOrImportEqualsDeclaration(fullStart, decorators, modifiers); + return parseImportDeclarationOrImportEqualsDeclaration(node); case SyntaxKind.ExportKeyword: nextToken(); switch (token()) { case SyntaxKind.DefaultKeyword: case SyntaxKind.EqualsToken: - return parseExportAssignment(fullStart, decorators, modifiers); + return parseExportAssignment(node); case SyntaxKind.AsKeyword: - return parseNamespaceExportDeclaration(fullStart, decorators, modifiers); + return parseNamespaceExportDeclaration(node); default: - return parseExportDeclaration(fullStart, decorators, modifiers); + return parseExportDeclaration(node); } default: - if (decorators || modifiers) { + if (node.decorators || node.modifiers) { // We reached this point because we encountered decorators and/or modifiers and assumed a declaration // would follow. For recovery and error reporting purposes, return an incomplete declaration. - const node = createMissingNode(SyntaxKind.MissingDeclaration, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); - node.pos = fullStart; - node.decorators = decorators; - node.modifiers = modifiers; - return finishNode(node); + const missing = createMissingNode(SyntaxKind.MissingDeclaration, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); + missing.pos = node.pos; + missing.decorators = node.decorators; + missing.modifiers = node.modifiers; + return finishNode(missing); } } } @@ -5341,19 +5318,15 @@ namespace ts { return nextTokenIsIdentifier() && nextToken() === SyntaxKind.CloseParenToken; } - function parseVariableStatement(fullStart: number, decorators: NodeArray, modifiers: NodeArray): VariableStatement { - const node = createNode(SyntaxKind.VariableStatement, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseVariableStatement(node: VariableStatement): VariableStatement { + node.kind = SyntaxKind.VariableStatement; node.declarationList = parseVariableDeclarationList(/*inForStatementInitializer*/ false); parseSemicolon(); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parseFunctionDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): FunctionDeclaration { - const node = createNode(SyntaxKind.FunctionDeclaration, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseFunctionDeclaration(node: FunctionDeclaration): FunctionDeclaration { + node.kind = SyntaxKind.FunctionDeclaration; parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier(); @@ -5361,40 +5334,30 @@ namespace ts { const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node); node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parseConstructorDeclaration(pos: number, decorators: NodeArray, modifiers: NodeArray): ConstructorDeclaration { - const node = createNode(SyntaxKind.Constructor, pos); - node.decorators = decorators; - node.modifiers = modifiers; + function parseConstructorDeclaration(node: ConstructorDeclaration): ConstructorDeclaration { + node.kind = SyntaxKind.Constructor; parseExpected(SyntaxKind.ConstructorKeyword); fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node); node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None, Diagnostics.or_expected); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parseMethodDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, asteriskToken: AsteriskToken, name: PropertyName, questionToken: QuestionToken, diagnosticMessage?: DiagnosticMessage): MethodDeclaration { - const method = createNode(SyntaxKind.MethodDeclaration, fullStart); - method.decorators = decorators; - method.modifiers = modifiers; - method.asteriskToken = asteriskToken; - method.name = name; - method.questionToken = questionToken; + function parseMethodDeclaration(node: MethodDeclaration, asteriskToken: AsteriskToken, diagnosticMessage?: DiagnosticMessage): MethodDeclaration { + node.kind = SyntaxKind.MethodDeclaration; + node.asteriskToken = asteriskToken; const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None; - const isAsync = hasModifier(method, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; - fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, method); - method.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage); - return addJSDocComment(finishNode(method)); + const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None; + fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node); + node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage); + return finishNode(node); } - function parsePropertyDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, name: PropertyName, questionToken: QuestionToken): ClassElement { - const property = createNode(SyntaxKind.PropertyDeclaration, fullStart); - property.decorators = decorators; - property.modifiers = modifiers; - property.name = name; - property.questionToken = questionToken; - property.type = parseTypeAnnotation(); + function parsePropertyDeclaration(node: PropertyDeclaration): PropertyDeclaration { + node.kind = SyntaxKind.PropertyDeclaration; + node.type = parseTypeAnnotation(); // For instance properties specifically, since they are evaluated inside the constructor, // we do *not * want to parse yield expressions, so we specifically turn the yield context @@ -5405,41 +5368,36 @@ namespace ts { // AccessibilityModifier_opt static_opt PropertyName TypeAnnotation_opt Initializer_opt[In, ?Yield]; // // The checker may still error in the static case to explicitly disallow the yield expression. - property.initializer = hasModifier(property, ModifierFlags.Static) + node.initializer = hasModifier(node, ModifierFlags.Static) ? allowInAnd(parseNonParameterInitializer) : doOutsideOfContext(NodeFlags.YieldContext | NodeFlags.DisallowInContext, parseNonParameterInitializer); parseSemicolon(); - return addJSDocComment(finishNode(property)); + return finishNode(node); } - function parsePropertyOrMethodDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ClassElement { + function parsePropertyOrMethodDeclaration(node: PropertyDeclaration | MethodDeclaration): PropertyDeclaration | MethodDeclaration { const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - const name = parsePropertyName(); - + node.name = parsePropertyName(); // Note: this is not legal as per the grammar. But we allow it in the parser and // report an error in the grammar checker. - const questionToken = parseOptionalToken(SyntaxKind.QuestionToken); + node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken); if (asteriskToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { - return parseMethodDeclaration(fullStart, decorators, modifiers, asteriskToken, name, questionToken, Diagnostics.or_expected); - } - else { - return parsePropertyDeclaration(fullStart, decorators, modifiers, name, questionToken); + return parseMethodDeclaration(node, asteriskToken, Diagnostics.or_expected); } + return parsePropertyDeclaration(node); } function parseNonParameterInitializer() { return parseInitializer(/*inParameter*/ false); } - function parseAccessorDeclaration(kind: SyntaxKind, fullStart: number, decorators: NodeArray, modifiers: NodeArray): AccessorDeclaration { - const node = createNode(kind, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseAccessorDeclaration(node: AccessorDeclaration, kind: AccessorDeclaration["kind"]): AccessorDeclaration { + node.kind = kind; node.name = parsePropertyName(); fillSignature(SyntaxKind.ColonToken, SignatureFlags.None, node); node.body = parseFunctionBlockOrSemicolon(SignatureFlags.None); - return addJSDocComment(finishNode(node)); + return finishNode(node); } function isClassMemberModifier(idToken: SyntaxKind) { @@ -5591,21 +5549,24 @@ namespace ts { return finishNode(result); } - const fullStart = getNodePos(); - const decorators = parseDecorators(); - const modifiers = parseModifiers(/*permitInvalidConstAsModifier*/ true); + const node = createNodeWithJSDoc(SyntaxKind.Unknown); + node.decorators = parseDecorators(); + node.modifiers = parseModifiers(/*permitInvalidConstAsModifier*/ true); - const accessor = tryParseAccessorDeclaration(fullStart, decorators, modifiers); - if (accessor) { - return accessor; + if (parseContextualModifier(SyntaxKind.GetKeyword)) { + return parseAccessorDeclaration(node, SyntaxKind.GetAccessor); + } + + if (parseContextualModifier(SyntaxKind.SetKeyword)) { + return parseAccessorDeclaration(node, SyntaxKind.SetAccessor); } if (token() === SyntaxKind.ConstructorKeyword) { - return parseConstructorDeclaration(fullStart, decorators, modifiers); + return parseConstructorDeclaration(node); } if (isIndexSignature()) { - return parseIndexSignatureDeclaration(fullStart, decorators, modifiers); + return parseIndexSignatureDeclaration(node); } // It is very important that we check this *after* checking indexers because @@ -5616,13 +5577,13 @@ namespace ts { token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.OpenBracketToken) { - return parsePropertyOrMethodDeclaration(fullStart, decorators, modifiers); + return parsePropertyOrMethodDeclaration(node); } - if (decorators || modifiers) { + if (node.decorators || node.modifiers) { // treat this as a property declaration with a missing name. - const name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); - return parsePropertyDeclaration(fullStart, decorators, modifiers, name, /*questionToken*/ undefined); + node.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ true, Diagnostics.Declaration_expected); + return parsePropertyDeclaration(node); } // 'isClassMemberStart' should have hinted not to attempt parsing. @@ -5630,21 +5591,15 @@ namespace ts { } function parseClassExpression(): ClassExpression { - return parseClassDeclarationOrExpression( - /*fullStart*/ scanner.getStartPos(), - /*decorators*/ undefined, - /*modifiers*/ undefined, - SyntaxKind.ClassExpression); + return parseClassDeclarationOrExpression(createNodeWithJSDoc(SyntaxKind.Unknown), SyntaxKind.ClassExpression); } - function parseClassDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ClassDeclaration { - return parseClassDeclarationOrExpression(fullStart, decorators, modifiers, SyntaxKind.ClassDeclaration); + function parseClassDeclaration(node: ClassLikeDeclaration): ClassDeclaration { + return parseClassDeclarationOrExpression(node, SyntaxKind.ClassDeclaration); } - function parseClassDeclarationOrExpression(fullStart: number, decorators: NodeArray, modifiers: NodeArray, kind: SyntaxKind): ClassLikeDeclaration { - const node = createNode(kind, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseClassDeclarationOrExpression(node: ClassLikeDeclaration, kind: ClassLikeDeclaration["kind"]): ClassLikeDeclaration { + node.kind = kind; parseExpected(SyntaxKind.ClassKeyword); node.name = parseNameOfClassDeclarationOrExpression(); node.typeParameters = parseTypeParameters(); @@ -5660,7 +5615,7 @@ namespace ts { node.members = createMissingList(); } - return addJSDocComment(finishNode(node)); + return finishNode(node); } function parseNameOfClassDeclarationOrExpression(): Identifier | undefined { @@ -5723,29 +5678,25 @@ namespace ts { return parseList(ParsingContext.ClassMembers, parseClassElement); } - function parseInterfaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): InterfaceDeclaration { - const node = createNode(SyntaxKind.InterfaceDeclaration, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseInterfaceDeclaration(node: InterfaceDeclaration): InterfaceDeclaration { + node.kind = SyntaxKind.InterfaceDeclaration; parseExpected(SyntaxKind.InterfaceKeyword); node.name = parseIdentifier(); node.typeParameters = parseTypeParameters(); node.heritageClauses = parseHeritageClauses(); node.members = parseObjectTypeMembers(); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parseTypeAliasDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): TypeAliasDeclaration { - const node = createNode(SyntaxKind.TypeAliasDeclaration, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseTypeAliasDeclaration(node: TypeAliasDeclaration): TypeAliasDeclaration { + node.kind = SyntaxKind.TypeAliasDeclaration; parseExpected(SyntaxKind.TypeKeyword); node.name = parseIdentifier(); node.typeParameters = parseTypeParameters(); parseExpected(SyntaxKind.EqualsToken); node.type = parseType(); parseSemicolon(); - return addJSDocComment(finishNode(node)); + return finishNode(node); } // In an ambient declaration, the grammar only allows integer literals as initializers. @@ -5753,16 +5704,14 @@ namespace ts { // ConstantEnumMemberSection, which starts at the beginning of an enum declaration // or any time an integer literal initializer is encountered. function parseEnumMember(): EnumMember { - const node = createNode(SyntaxKind.EnumMember, scanner.getStartPos()); + const node = createNodeWithJSDoc(SyntaxKind.EnumMember); node.name = parsePropertyName(); node.initializer = allowInAnd(parseNonParameterInitializer); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parseEnumDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): EnumDeclaration { - const node = createNode(SyntaxKind.EnumDeclaration, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseEnumDeclaration(node: EnumDeclaration): EnumDeclaration { + node.kind = SyntaxKind.EnumDeclaration; parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -5772,11 +5721,11 @@ namespace ts { else { node.members = createMissingList(); } - return addJSDocComment(finishNode(node)); + return finishNode(node); } function parseModuleBlock(): ModuleBlock { - const node = createNode(SyntaxKind.ModuleBlock, scanner.getStartPos()); + const node = createNode(SyntaxKind.ModuleBlock); if (parseExpected(SyntaxKind.OpenBraceToken)) { node.statements = parseList(ParsingContext.BlockStatements, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); @@ -5787,25 +5736,21 @@ namespace ts { return finishNode(node); } - function parseModuleOrNamespaceDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, flags: NodeFlags): ModuleDeclaration { - const node = createNode(SyntaxKind.ModuleDeclaration, fullStart); + function parseModuleOrNamespaceDeclaration(node: ModuleDeclaration, flags: NodeFlags): ModuleDeclaration { + node.kind = SyntaxKind.ModuleDeclaration; // If we are parsing a dotted namespace name, we want to // propagate the 'Namespace' flag across the names if set. const namespaceFlag = flags & NodeFlags.Namespace; - node.decorators = decorators; - node.modifiers = modifiers; node.flags |= flags; node.name = parseIdentifier(); node.body = parseOptional(SyntaxKind.DotToken) - ? parseModuleOrNamespaceDeclaration(getNodePos(), /*decorators*/ undefined, /*modifiers*/ undefined, NodeFlags.NestedNamespace | namespaceFlag) + ? parseModuleOrNamespaceDeclaration(createNode(SyntaxKind.Unknown), NodeFlags.NestedNamespace | namespaceFlag) : parseModuleBlock(); - return addJSDocComment(finishNode(node)); + return finishNode(node); } - function parseAmbientExternalModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ModuleDeclaration { - const node = createNode(SyntaxKind.ModuleDeclaration, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseAmbientExternalModuleDeclaration(node: ModuleDeclaration): ModuleDeclaration { + node.kind = SyntaxKind.ModuleDeclaration; if (token() === SyntaxKind.GlobalKeyword) { // parse 'global' as name of global scope augmentation node.name = parseIdentifier(); @@ -5815,22 +5760,20 @@ namespace ts { node.name = parseLiteralNode(); node.name.text = internIdentifier(node.name.text); } - if (token() === SyntaxKind.OpenBraceToken) { node.body = parseModuleBlock(); } else { parseSemicolon(); } - return finishNode(node); } - function parseModuleDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ModuleDeclaration { + function parseModuleDeclaration(node: ModuleDeclaration): ModuleDeclaration { let flags: NodeFlags = 0; if (token() === SyntaxKind.GlobalKeyword) { // global augmentation - return parseAmbientExternalModuleDeclaration(fullStart, decorators, modifiers); + return parseAmbientExternalModuleDeclaration(node); } else if (parseOptional(SyntaxKind.NamespaceKeyword)) { flags |= NodeFlags.Namespace; @@ -5838,10 +5781,10 @@ namespace ts { else { parseExpected(SyntaxKind.ModuleKeyword); if (token() === SyntaxKind.StringLiteral) { - return parseAmbientExternalModuleDeclaration(fullStart, decorators, modifiers); + return parseAmbientExternalModuleDeclaration(node); } } - return parseModuleOrNamespaceDeclaration(fullStart, decorators, modifiers, flags); + return parseModuleOrNamespaceDeclaration(node, flags); } function isExternalModuleReference() { @@ -5857,21 +5800,16 @@ namespace ts { return nextToken() === SyntaxKind.SlashToken; } - function parseNamespaceExportDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): NamespaceExportDeclaration { - const exportDeclaration = createNode(SyntaxKind.NamespaceExportDeclaration, fullStart); - exportDeclaration.decorators = decorators; - exportDeclaration.modifiers = modifiers; + function parseNamespaceExportDeclaration(node: NamespaceExportDeclaration): NamespaceExportDeclaration { + node.kind = SyntaxKind.NamespaceExportDeclaration; parseExpected(SyntaxKind.AsKeyword); parseExpected(SyntaxKind.NamespaceKeyword); - - exportDeclaration.name = parseIdentifier(); - + node.name = parseIdentifier(); parseSemicolon(); - - return finishNode(exportDeclaration); + return finishNode(node); } - function parseImportDeclarationOrImportEqualsDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ImportEqualsDeclaration | ImportDeclaration { + function parseImportDeclarationOrImportEqualsDeclaration(node: ImportEqualsDeclaration | ImportDeclaration): ImportEqualsDeclaration | ImportDeclaration { parseExpected(SyntaxKind.ImportKeyword); const afterImportPos = scanner.getStartPos(); @@ -5879,39 +5817,34 @@ namespace ts { if (isIdentifier()) { identifier = parseIdentifier(); if (token() !== SyntaxKind.CommaToken && token() !== SyntaxKind.FromKeyword) { - return parseImportEqualsDeclaration(fullStart, decorators, modifiers, identifier); + return parseImportEqualsDeclaration(node, identifier); } } // Import statement - const importDeclaration = createNode(SyntaxKind.ImportDeclaration, fullStart); - importDeclaration.decorators = decorators; - importDeclaration.modifiers = modifiers; - + node.kind = SyntaxKind.ImportDeclaration; // ImportDeclaration: // import ImportClause from ModuleSpecifier ; // import ModuleSpecifier; if (identifier || // import id token() === SyntaxKind.AsteriskToken || // import * token() === SyntaxKind.OpenBraceToken) { // import { - importDeclaration.importClause = parseImportClause(identifier, afterImportPos); + (node).importClause = parseImportClause(identifier, afterImportPos); parseExpected(SyntaxKind.FromKeyword); } - importDeclaration.moduleSpecifier = parseModuleSpecifier(); + (node).moduleSpecifier = parseModuleSpecifier(); parseSemicolon(); - return finishNode(importDeclaration); + return finishNode(node); } - function parseImportEqualsDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray, identifier: ts.Identifier): ImportEqualsDeclaration { - const importEqualsDeclaration = createNode(SyntaxKind.ImportEqualsDeclaration, fullStart); - importEqualsDeclaration.decorators = decorators; - importEqualsDeclaration.modifiers = modifiers; - importEqualsDeclaration.name = identifier; + function parseImportEqualsDeclaration(node: ImportEqualsDeclaration, identifier: ts.Identifier): ImportEqualsDeclaration { + node.kind = SyntaxKind.ImportEqualsDeclaration; + node.name = identifier; parseExpected(SyntaxKind.EqualsToken); - importEqualsDeclaration.moduleReference = parseModuleReference(); + node.moduleReference = parseModuleReference(); parseSemicolon(); - return addJSDocComment(finishNode(importEqualsDeclaration)); + return finishNode(node); } function parseImportClause(identifier: Identifier, fullStart: number) { @@ -6035,17 +5968,14 @@ namespace ts { return finishNode(node); } - function parseExportDeclaration(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ExportDeclaration { - const node = createNode(SyntaxKind.ExportDeclaration, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseExportDeclaration(node: ExportDeclaration): ExportDeclaration { + node.kind = SyntaxKind.ExportDeclaration; if (parseOptional(SyntaxKind.AsteriskToken)) { parseExpected(SyntaxKind.FromKeyword); node.moduleSpecifier = parseModuleSpecifier(); } else { node.exportClause = parseNamedImportsOrExports(SyntaxKind.NamedExports); - // It is not uncommon to accidentally omit the 'from' keyword. Additionally, in editing scenarios, // the 'from' keyword can be parsed as a named export when the export clause is unterminated (i.e. `export { from "moduleName";`) // If we don't have a 'from' keyword, see if we have a string literal such that ASI won't take effect. @@ -6058,10 +5988,8 @@ namespace ts { return finishNode(node); } - function parseExportAssignment(fullStart: number, decorators: NodeArray, modifiers: NodeArray): ExportAssignment { - const node = createNode(SyntaxKind.ExportAssignment, fullStart); - node.decorators = decorators; - node.modifiers = modifiers; + function parseExportAssignment(node: ExportAssignment): ExportAssignment { + node.kind = SyntaxKind.ExportAssignment; if (parseOptional(SyntaxKind.EqualsToken)) { node.isExportEquals = true; } @@ -6254,7 +6182,7 @@ namespace ts { comment.parent = parent; } - if (isInJavaScriptFile(parent)) { + if (contextFlags & NodeFlags.JavaScriptFile) { if (!sourceFile.jsDocDiagnostics) { sourceFile.jsDocDiagnostics = []; } @@ -6857,7 +6785,7 @@ namespace ts { function tryParseChildTag(target: PropertyLikeParse): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false { Debug.assert(token() === SyntaxKind.AtToken); - const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos()); + const atToken = createNode(SyntaxKind.AtToken); atToken.end = scanner.getTextPos(); nextJSDocToken(); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index ddc89a60e2a..a496b499ae0 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1389,6 +1389,9 @@ namespace ts { // Multi-line comment if (text.charCodeAt(pos + 1) === CharacterCodes.asterisk) { pos += 2; + if (text.charCodeAt(pos) === CharacterCodes.asterisk && text.charCodeAt(pos + 1) !== CharacterCodes.slash) { + tokenFlags |= TokenFlags.PrecedingJSDocComment; + } let commentClosed = false; while (pos < end) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7b0145ea134..3c81b355816 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1454,7 +1454,7 @@ namespace ts { export const enum TokenFlags { None = 0, PrecedingLineBreak = 1 << 0, - PrecedingComment = 1 << 1, + PrecedingJSDocComment = 1 << 1, Unterminated = 1 << 2, ExtendedUnicodeEscape = 1 << 3, Scientific = 1 << 4, // e.g. `10e2` From d143eeda1f8ab71f63900f5b434005aa0c1e22ac Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 13 Nov 2017 12:42:39 -0800 Subject: [PATCH 3/6] Accept new baselines --- .../baselines/reference/duplicateIdentifierDifferentSpelling.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/duplicateIdentifierDifferentSpelling.js b/tests/baselines/reference/duplicateIdentifierDifferentSpelling.js index 9f139ff8572..10155af12e2 100644 --- a/tests/baselines/reference/duplicateIdentifierDifferentSpelling.js +++ b/tests/baselines/reference/duplicateIdentifierDifferentSpelling.js @@ -10,7 +10,7 @@ var X = { 0b11: '', 3: '' }; //// [duplicateIdentifierDifferentSpelling.js] var A = /** @class */ (function () { function A() { - this[0b11] = ''; + this[3] = ''; this[3] = ''; } return A; From 969f06462d13105141fcef599cb5139129ef15aa Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 13 Nov 2017 15:12:46 -0800 Subject: [PATCH 4/6] Improve lookahead for arrow functions to reduce speculative parsing --- src/compiler/parser.ts | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 52ed714a533..d523fa5173c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3303,24 +3303,40 @@ namespace ts { return Tristate.True; } + // Check for "(xxx yyy", where xxx is a modifier and yyy is an identifier. This + // isn't actually allowed, but we want to treat it as a lambda so we can provide + // a good error message. + if (isModifierKind(second) && lookAhead(nextTokenIsIdentifier)) { + return Tristate.True; + } + // If we had "(" followed by something that's not an identifier, // then this definitely doesn't look like a lambda. - // Note: we could be a little more lenient and allow - // "(public" or "(private". These would not ever actually be allowed, - // but we could provide a good error message instead of bailing out. if (!isIdentifier()) { return Tristate.False; } - // If we have something like "(a:", then we must have a - // type-annotated parameter in an arrow function expression. - if (nextToken() === SyntaxKind.ColonToken) { - return Tristate.True; + switch (nextToken()) { + case SyntaxKind.ColonToken: + // If we have something like "(a:", then we must have a + // type-annotated parameter in an arrow function expression. + return Tristate.True; + case SyntaxKind.QuestionToken: + nextToken(); + // If we have "(a?:" or "(a?," or "(a?=" or "(a?)" then it is definitely a lamnda. + if (token() === SyntaxKind.ColonToken || token() === SyntaxKind.CommaToken || token() === SyntaxKind.EqualsToken || token() === SyntaxKind.CloseParenToken) { + return Tristate.True; + } + // Otherwise it is definitely not a lambda. + return Tristate.False; + case SyntaxKind.CommaToken: + case SyntaxKind.EqualsToken: + case SyntaxKind.CloseParenToken: + // If we have "(a," or "(a=" or "(a)" this *could* be an arrow function + return Tristate.Unknown; } - - // This *could* be a parenthesized arrow function. - // Return Unknown to let the caller know. - return Tristate.Unknown; + // It is definitely not an arrow function + return Tristate.False; } else { Debug.assert(first === SyntaxKind.LessThanToken); From 787c3239909e5ad3337040cd7d5a3fed0f58b782 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 14 Nov 2017 09:32:28 -0800 Subject: [PATCH 5/6] Accept new baselines --- .../parserArrowFunctionExpression6.errors.txt | 37 ------------------- .../parserArrowFunctionExpression6.js | 3 +- .../parserArrowFunctionExpression6.symbols | 10 ++--- .../parserArrowFunctionExpression6.types | 26 +++++++------ 4 files changed, 21 insertions(+), 55 deletions(-) delete mode 100644 tests/baselines/reference/parserArrowFunctionExpression6.errors.txt diff --git a/tests/baselines/reference/parserArrowFunctionExpression6.errors.txt b/tests/baselines/reference/parserArrowFunctionExpression6.errors.txt deleted file mode 100644 index 03312b63438..00000000000 --- a/tests/baselines/reference/parserArrowFunctionExpression6.errors.txt +++ /dev/null @@ -1,37 +0,0 @@ -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,23): error TS2300: Duplicate identifier '(Missing)'. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,24): error TS1005: ',' expected. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,29): error TS1138: Parameter declaration expected. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,30): error TS2300: Duplicate identifier '(Missing)'. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,31): error TS1003: Identifier expected. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,40): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,41): error TS2371: A parameter initializer is only allowed in a function or constructor implementation. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,55): error TS1138: Parameter declaration expected. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,66): error TS1005: '=>' expected. -tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts(2,69): error TS1005: ':' expected. - - -==== tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts (10 errors) ==== - function foo(q: string, b: number) { - return true ? (q ? true : false) : (b = q.length, function() { }); - -!!! error TS2300: Duplicate identifier '(Missing)'. - ~~~~ -!!! error TS1005: ',' expected. - ~ -!!! error TS1138: Parameter declaration expected. - -!!! error TS2300: Duplicate identifier '(Missing)'. - ~~~~~ -!!! error TS1003: Identifier expected. - ~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. - ~~~~~~~~~~~~ -!!! error TS2371: A parameter initializer is only allowed in a function or constructor implementation. - ~~~~~~~~ -!!! error TS1138: Parameter declaration expected. - ~ -!!! error TS1005: '=>' expected. - ~ -!!! error TS1005: ':' expected. - }; - \ No newline at end of file diff --git a/tests/baselines/reference/parserArrowFunctionExpression6.js b/tests/baselines/reference/parserArrowFunctionExpression6.js index 75824704b16..1de3035cc76 100644 --- a/tests/baselines/reference/parserArrowFunctionExpression6.js +++ b/tests/baselines/reference/parserArrowFunctionExpression6.js @@ -6,7 +6,6 @@ function foo(q: string, b: number) { //// [parserArrowFunctionExpression6.js] function foo(q, b) { - return true ? function (q, , ) { } : ; - ; + return true ? (q ? true : false) : (b = q.length, function () { }); } ; diff --git a/tests/baselines/reference/parserArrowFunctionExpression6.symbols b/tests/baselines/reference/parserArrowFunctionExpression6.symbols index 3cf5f44877f..9b3afdbb950 100644 --- a/tests/baselines/reference/parserArrowFunctionExpression6.symbols +++ b/tests/baselines/reference/parserArrowFunctionExpression6.symbols @@ -5,11 +5,11 @@ function foo(q: string, b: number) { >b : Symbol(b, Decl(parserArrowFunctionExpression6.ts, 0, 23)) return true ? (q ? true : false) : (b = q.length, function() { }); ->q : Symbol(q, Decl(parserArrowFunctionExpression6.ts, 1, 19)) -> : Symbol((Missing), Decl(parserArrowFunctionExpression6.ts, 1, 22)) -> : Symbol((Missing), Decl(parserArrowFunctionExpression6.ts, 1, 29)) ->b : Symbol(b, Decl(parserArrowFunctionExpression6.ts, 1, 40)) ->q : Symbol(q, Decl(parserArrowFunctionExpression6.ts, 1, 19)) +>q : Symbol(q, Decl(parserArrowFunctionExpression6.ts, 0, 13)) +>b : Symbol(b, Decl(parserArrowFunctionExpression6.ts, 0, 23)) +>q.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>q : Symbol(q, Decl(parserArrowFunctionExpression6.ts, 0, 13)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) }; diff --git a/tests/baselines/reference/parserArrowFunctionExpression6.types b/tests/baselines/reference/parserArrowFunctionExpression6.types index 990670eea63..6cc4f16e216 100644 --- a/tests/baselines/reference/parserArrowFunctionExpression6.types +++ b/tests/baselines/reference/parserArrowFunctionExpression6.types @@ -1,21 +1,25 @@ === tests/cases/conformance/parser/ecmascript5/ArrowFunctionExpressions/parserArrowFunctionExpression6.ts === function foo(q: string, b: number) { ->foo : (q: string, b: number) => any +>foo : (q: string, b: number) => boolean | (() => void) >q : string >b : number return true ? (q ? true : false) : (b = q.length, function() { }); ->true ? (q ? true : false) : (b = q.length, function() { } : any +>true ? (q ? true : false) : (b = q.length, function() { }) : boolean | (() => void) >true : true ->(q ? true : false) : (b = q.length, function() { } : (q?: any, : any, : any) => (b?: any) => () => any ->q : any -> : any -> : any ->b : any ->q.length : any ->q : any ->length : any -> : any +>(q ? true : false) : boolean +>q ? true : false : boolean +>q : string +>true : true +>false : false +>(b = q.length, function() { }) : () => void +>b = q.length, function() { } : () => void +>b = q.length : number +>b : number +>q.length : number +>q : string +>length : number +>function() { } : () => void }; From ee851784a63debc1c37ba9c69afda1e5987da2b4 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 14 Nov 2017 14:03:56 -0800 Subject: [PATCH 6/6] Remove dead code that didn't get removed in #19979 --- src/compiler/parser.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e0991612b56..b20fd79757e 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3409,8 +3409,7 @@ namespace ts { // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. // // So we need just a bit of lookahead to ensure that it can only be a signature. - if (!allowAmbiguity && ((token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) || - find(node.parameters, p => p.initializer && ts.isIdentifier(p.initializer) && p.initializer.escapedText === "= not found"))) { + if (!allowAmbiguity && token() !== SyntaxKind.EqualsGreaterThanToken && token() !== SyntaxKind.OpenBraceToken) { // Returning undefined here will cause our caller to rewind to where we started from. return undefined; }