From 0b0b564eee8c556ae197a17de99656d51a186720 Mon Sep 17 00:00:00 2001 From: "Marc J. Schmidt" Date: Mon, 30 May 2022 22:02:14 +0200 Subject: [PATCH] more parser --- src/core.h | 67 ++- src/node_factory.h | 1431 +++++++++++++++++++++++--------------------- src/node_test.h | 33 +- src/parser2.h | 1051 ++++++++++++++++---------------- src/scanner.h | 6 + src/types.h | 98 ++- src/utilities.h | 111 +++- 7 files changed, 1519 insertions(+), 1278 deletions(-) diff --git a/src/core.h b/src/core.h index 9893374..8d7186b 100644 --- a/src/core.h +++ b/src/core.h @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -51,8 +52,8 @@ namespace ts { } /** - * shared_ptr has optional semantic already built-in, so we use it instead of std::optional, - * but instead of using shared_ptr directly, we use opt to make it clear that it can be empty. + * shared_ptr has optional semantic already built-in, so we use it instead of std::optional>, + * but instead of using shared_ptr directly, we use sharedOpt to make it clear that it can be empty. */ template using sharedOpt = shared_ptr; @@ -76,7 +77,7 @@ namespace ts { * Returns the last element of an array if non-empty, `undefined` otherwise. */ template - optional lastOrUndefined(const vector &array) { + optional lastOrUndefined(vector &array) { if (array.empty()) return std::nullopt; return array.back(); } @@ -87,6 +88,14 @@ namespace ts { return def; } + bool isTrue(optional b = {}) { + return b && *b; + } + + bool isFalse(optional b = {}) { + return !b || !*b; + } + vector charToStringVector(vector chars) { vector s; for (auto c: chars) s.push_back(c); @@ -109,8 +118,15 @@ namespace ts { } template - vector slice(const vector &v, int start) { - return vector(v.begin() + start, v.end()); + vector slice(const vector &v, int start = 0, int end = 0) { + if (!end) end = v.size(); + return std::vector(v.begin() + start, v.begin() + end); + } + + template + optional> slice(const optional> &v, int start = 0, int end = 0) { + if (!v) return std::nullopt; + return slice(*v, start, end); } string join(const vector &vec, const char *delim) { @@ -209,24 +225,24 @@ namespace ts { return some(optional(array), optional(predicate)); } - template - class LogicalOrReturnLast { - protected: - T value; - public: - LogicalOrReturnLast(T value): value(value) {} - operator T() { return value; } - LogicalOrReturnLast operator||(LogicalOrReturnLast other) { - if (value) return *this; - - return other; - } - LogicalOrReturnLast operator||(T other) { - if (value) return *this; - - return LogicalOrReturnLast(other); - } - }; +// template +// class LogicalOrReturnLast { +// protected: +// T value; +// public: +// LogicalOrReturnLast(T value): value(value) {} +// operator T() { return value; } +// LogicalOrReturnLast operator||(LogicalOrReturnLast other) { +// if (value) return *this; +// +// return other; +// } +// LogicalOrReturnLast operator||(T other) { +// if (value) return *this; +// +// return LogicalOrReturnLast(other); +// } +// }; //template //inline bool some(ts::NodeArray array, std::function predicate) { @@ -247,6 +263,11 @@ namespace ts { vector.erase(remove(vector.begin(), vector.end(), item), vector.end()); }; + template + static bool has(std::set &s, T item) { + return s.find(item) != s.end(); + }; + template static bool has(vector &vector, T item) { return find(vector.begin(), vector.end(), item) != vector.end(); diff --git a/src/node_factory.h b/src/node_factory.h index a8413d4..3413504 100644 --- a/src/node_factory.h +++ b/src/node_factory.h @@ -12,6 +12,8 @@ namespace ts::factory { NodeArray createNodeArray(optional elements, optional hasTrailingComma = {}); NodeArray createNodeArray(vector> elements, optional hasTrailingComma = {}); shared createParenthesizedType(shared type); + shared updateCallExpression(shared node, shared expression, optional typeArguments, NodeArray argumentsArray); + shared restoreOuterExpressions(sharedOpt outerExpression, shared innerExpression, int kinds = (int) OuterExpressionKinds::All); } namespace ts::parenthesizerRules { @@ -19,7 +21,7 @@ namespace ts::parenthesizerRules { using namespace ts::types; template - NodeArray map(optional array, function(shared, int)> callback) { + NodeArray map(optional array, function(shared, int)> callback) { NodeArray result; if (array) { auto i = 0; @@ -241,27 +243,27 @@ namespace ts::parenthesizerRules { shared parenthesizeExpressionOfComputedPropertyName(shared expression) { return isCommaSequence(expression) ? factory::createParenthesizedExpression(expression) : expression; } -// -// function parenthesizeConditionOfConditionalExpression(condition: Expression): Expression { -// auto conditionalPrecedence = getOperatorPrecedence(SyntaxKind::ConditionalExpression, SyntaxKind::QuestionToken); -// auto emittedCondition = skipPartiallyEmittedExpressions(condition); -// auto conditionPrecedence = getExpressionPrecedence(emittedCondition); -// if (compareValues(conditionPrecedence, conditionalPrecedence) !== Comparison.GreaterThan) { -// return factory::createParenthesizedExpression(condition); -// } -// return condition; -// } -// -// function parenthesizeBranchOfConditionalExpression(branch: Expression): Expression { -// // per ES grammar both 'whenTrue' and 'whenFalse' parts of conditional expression are assignment expressions -// // so in case when comma expression is introduced as a part of previous transformations -// // if should be wrapped in parens since comma operator has the lowest precedence -// auto emittedExpression = skipPartiallyEmittedExpressions(branch); -// return isCommaSequence(emittedExpression) -// ? factory::createParenthesizedExpression(branch) -// : branch; -// } -// + + shared parenthesizeConditionOfConditionalExpression(shared condition) { + auto conditionalPrecedence = getOperatorPrecedence(SyntaxKind::ConditionalExpression, SyntaxKind::QuestionToken); + auto emittedCondition = skipPartiallyEmittedExpressions(condition); + auto conditionPrecedence = getExpressionPrecedence(emittedCondition); + if (compareValues(conditionPrecedence, conditionalPrecedence) != Comparison::GreaterThan) { + return factory::createParenthesizedExpression(condition); + } + return condition; + } + + shared parenthesizeBranchOfConditionalExpression(shared branch) { + // per ES grammar both 'whenTrue' and 'whenFalse' parts of conditional expression are assignment expressions + // so in case when comma expression is introduced as a part of previous transformations + // if should be wrapped in parens since comma operator has the lowest precedence + auto emittedExpression = skipPartiallyEmittedExpressions(branch); + return isCommaSequence(emittedExpression) + ? factory::createParenthesizedExpression(branch) + : branch; + } + // /** // * [Per the spec](https://tc39.github.io/ecma262/#prod-ExportDeclaration), `export default` accepts _AssigmentExpression_ but // * has a lookahead restriction for `function`, `async function`, and `class`. @@ -351,46 +353,48 @@ namespace ts::parenthesizerRules { return setTextRange(factory::createNodeArray(result, elements.hasTrailingComma), elements); } -// function parenthesizeExpressionOfExpressionStatement(shared expression): Expression { -// auto emittedExpression = skipPartiallyEmittedExpressions(expression); -// if (isCallExpression(emittedExpression)) { -// auto callee = emittedExpression.expression; -// auto kind = skipPartiallyEmittedExpressions(callee).kind; -// if (kind === SyntaxKind::FunctionExpression || kind === SyntaxKind::ArrowFunction) { -// // TODO(rbuckton): Verifiy whether `setTextRange` is needed. -// auto updated = factory::updateCallExpression( -// emittedExpression, -// setTextRange(factory::createParenthesizedExpression(callee), callee), -// emittedExpression.typeArguments, -// emittedExpression.arguments -// ); -// return factory::restoreOuterExpressions(expression, updated, OuterExpressionKinds.PartiallyEmittedExpressions); -// } -// } -// -// auto leftmostExpressionKind = getLeftmostExpression(emittedExpression, /*stopAtCallExpressions*/ false).kind; -// if (leftmostExpressionKind === SyntaxKind::ObjectLiteralExpression || leftmostExpressionKind === SyntaxKind::FunctionExpression) { -// // TODO(rbuckton): Verifiy whether `setTextRange` is needed. -// return setTextRange(factory::createParenthesizedExpression(expression), expression); -// } -// -// return expression; -// } -// + shared parenthesizeExpressionOfExpressionStatement(shared expression) { + auto emittedExpression = skipPartiallyEmittedExpressions(expression); + if (auto e = to(emittedExpression)) { + auto callee = e->expression; + auto kind = skipPartiallyEmittedExpressions(callee)->kind; + if (kind == SyntaxKind::FunctionExpression || kind == SyntaxKind::ArrowFunction) { + // TODO(rbuckton): Verifiy whether `setTextRange` is needed. + auto updated = factory::updateCallExpression( + e, + setTextRange(factory::createParenthesizedExpression(callee), callee), + e->typeArguments, + e->arguments + ); + return factory::restoreOuterExpressions(expression, updated, (int) OuterExpressionKinds::PartiallyEmittedExpressions); + } + } + + auto leftmostExpressionKind = getLeftmostExpression(to(emittedExpression), /*stopAtCallExpressions*/ false)->kind; + if (leftmostExpressionKind == SyntaxKind::ObjectLiteralExpression || leftmostExpressionKind == SyntaxKind::FunctionExpression) { + // TODO(rbuckton): Verifiy whether `setTextRange` is needed. + return setTextRange(factory::createParenthesizedExpression(expression), expression); + } + + return expression; + } + // function parenthesizeConciseBodyOfArrowFunction(body: Expression): Expression; // function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody; -// function parenthesizeConciseBodyOfArrowFunction(body: ConciseBody): ConciseBody { -// if (!isBlock(body) && (isCommaSequence(body) || getLeftmostExpression(body, /*stopAtCallExpressions*/ false).kind === SyntaxKind::ObjectLiteralExpression)) { -// // TODO(rbuckton): Verifiy whether `setTextRange` is needed. -// return setTextRange(factory::createParenthesizedExpression(body), body); -// } -// -// return body; -// } -// -// // Type[Extends] : -// // FunctionOrConstructorType -// // ConditionalType[?Extends] + shared parenthesizeConciseBodyOfArrowFunction(shared body) { + if (isBlock(body)) return body; + auto e = dynamic_pointer_cast(body); + if (isCommaSequence(body) || getLeftmostExpression(e, /*stopAtCallExpressions*/ false)->kind == SyntaxKind::ObjectLiteralExpression) { + // TODO(rbuckton): Verifiy whether `setTextRange` is needed. + return setTextRange(factory::createParenthesizedExpression(e), body); + } + + return body; + } + + // Type[Extends] : + // FunctionOrConstructorType + // ConditionalType[?Extends] // ConditionalType[Extends] : // UnionType[?Extends] @@ -408,21 +412,21 @@ namespace ts::parenthesizerRules { } return checkType; } -// -// function parenthesizeExtendsTypeOfConditionalType(extendsType: TypeNode): TypeNode { -// switch (extendsType.kind) { -// case SyntaxKind::ConditionalType: -// return factory::createParenthesizedType(extendsType); -// } -// return extendsType; -// } + + shared parenthesizeExtendsTypeOfConditionalType(shared extendsType) { + switch (extendsType->kind) { + case SyntaxKind::ConditionalType: + return factory::createParenthesizedType(extendsType); + } + return extendsType; + } // UnionType[Extends] : // `|`? IntersectionType[?Extends] // UnionType[?Extends] `|` IntersectionType[?Extends] // // - A union type constituent has the same precedence as the check type of a conditional type - shared parenthesizeConstituentTypeOfUnionType(shared type) { + shared parenthesizeConstituentTypeOfUnionType(shared type, int = 0) { switch (type->kind) { case SyntaxKind::UnionType: // Not strictly necessary, but a union containing a union should have been flattened case SyntaxKind::IntersectionType: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests @@ -431,16 +435,16 @@ namespace ts::parenthesizerRules { return parenthesizeCheckTypeOfConditionalType(type); } -// function parenthesizeConstituentTypesOfUnionType(members: readonly TypeNode[]): NodeArray { -// return factory::createNodeArray(sameMap(members, parenthesizeConstituentTypeOfUnionType)); -// } + NodeArray parenthesizeConstituentTypesOfUnionType(NodeArray members) { + return factory::createNodeArray(map < TypeNode > (members, parenthesizeConstituentTypeOfUnionType)); + } // IntersectionType[Extends] : // `&`? TypeOperator[?Extends] // IntersectionType[?Extends] `&` TypeOperator[?Extends] // // - An intersection type constituent does not allow function, constructor, conditional, or union types (they must be parenthesized) - shared parenthesizeConstituentTypeOfIntersectionType(shared type) { + shared parenthesizeConstituentTypeOfIntersectionType(shared type, int = 0) { switch (type->kind) { case SyntaxKind::UnionType: case SyntaxKind::IntersectionType: // Not strictly necessary, but an intersection containing an intersection should have been flattened @@ -448,10 +452,10 @@ namespace ts::parenthesizerRules { } return parenthesizeConstituentTypeOfUnionType(type); } -// -// function parenthesizeConstituentTypesOfIntersectionType(members: readonly TypeNode[]): NodeArray { -// return factory::createNodeArray(sameMap(members, parenthesizeConstituentTypeOfIntersectionType)); -// } + + NodeArray parenthesizeConstituentTypesOfIntersectionType(NodeArray members) { + return factory::createNodeArray(map < TypeNode > (members, parenthesizeConstituentTypeOfIntersectionType)); + } // TypeOperator[Extends] : // PostfixType @@ -476,69 +480,69 @@ namespace ts::parenthesizerRules { return parenthesizeOperandOfTypeOperator(type); } -// // PostfixType : -// // NonArrayType -// // NonArrayType [no LineTerminator here] `!` // JSDoc -// // NonArrayType [no LineTerminator here] `?` // JSDoc -// // IndexedAccessType -// // ArrayType -// // -// // IndexedAccessType : -// // NonArrayType `[` Type[~Extends] `]` -// // -// // ArrayType : -// // NonArrayType `[` `]` -// // -// function parenthesizeNonArrayTypeOfPostfixType(type: TypeNode) { -// switch (type.kind) { -// case SyntaxKind::InferType: -// case SyntaxKind::TypeOperator: -// case SyntaxKind::TypeQuery: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests -// return factory::createParenthesizedType(type); -// } -// return parenthesizeOperandOfTypeOperator(type); -// } -// -// // TupleType : -// // `[` Elision? `]` -// // `[` NamedTupleElementTypes `]` -// // `[` NamedTupleElementTypes `,` Elision? `]` -// // `[` TupleElementTypes `]` -// // `[` TupleElementTypes `,` Elision? `]` -// // -// // NamedTupleElementTypes : -// // Elision? NamedTupleMember -// // NamedTupleElementTypes `,` Elision? NamedTupleMember -// // -// // NamedTupleMember : -// // Identifier `?`? `:` Type[~Extends] -// // `...` Identifier `:` Type[~Extends] -// // -// // TupleElementTypes : -// // Elision? TupleElementType -// // TupleElementTypes `,` Elision? TupleElementType -// // -// // TupleElementType : -// // Type[~Extends] // NOTE: Needs cover grammar to disallow JSDoc postfix-optional -// // OptionalType -// // RestType -// // -// // OptionalType : -// // Type[~Extends] `?` // NOTE: Needs cover grammar to disallow JSDoc postfix-optional -// // -// // RestType : -// // `...` Type[~Extends] -// // -// function parenthesizeElementTypesOfTupleType(types: readonly (TypeNode | NamedTupleMember)[]): NodeArray { -// return factory::createNodeArray(sameMap(types, parenthesizeElementTypeOfTupleType)); -// } -// -// function parenthesizeElementTypeOfTupleType(type: TypeNode | NamedTupleMember): TypeNode { + // PostfixType : + // NonArrayType + // NonArrayType [no LineTerminator here] `!` // JSDoc + // NonArrayType [no LineTerminator here] `?` // JSDoc + // IndexedAccessType + // ArrayType + // + // IndexedAccessType : + // NonArrayType `[` Type[~Extends] `]` + // + // ArrayType : + // NonArrayType `[` `]` + // + shared parenthesizeNonArrayTypeOfPostfixType(shared type) { + switch (type->kind) { + case SyntaxKind::InferType: + case SyntaxKind::TypeOperator: + case SyntaxKind::TypeQuery: // Not strictly necessary, but makes generated output more readable and avoids breaks in DT tests + return factory::createParenthesizedType(type); + } + return parenthesizeOperandOfTypeOperator(type); + } + + shared parenthesizeElementTypeOfTupleType(shared type, int = 0) { // if (hasJSDocPostfixQuestion(type)) return factory::createParenthesizedType(type); -// return type; -// } -// -// function hasJSDocPostfixQuestion(type: TypeNode | NamedTupleMember): boolean { + return type; + } + + // TupleType : + // `[` Elision? `]` + // `[` NamedTupleElementTypes `]` + // `[` NamedTupleElementTypes `,` Elision? `]` + // `[` TupleElementTypes `]` + // `[` TupleElementTypes `,` Elision? `]` + // + // NamedTupleElementTypes : + // Elision? NamedTupleMember + // NamedTupleElementTypes `,` Elision? NamedTupleMember + // + // NamedTupleMember : + // Identifier `?`? `:` Type[~Extends] + // `...` Identifier `:` Type[~Extends] + // + // TupleElementTypes : + // Elision? TupleElementType + // TupleElementTypes `,` Elision? TupleElementType + // + // TupleElementType : + // Type[~Extends] // NOTE: Needs cover grammar to disallow JSDoc postfix-optional + // OptionalType + // RestType + // + // OptionalType : + // Type[~Extends] `?` // NOTE: Needs cover grammar to disallow JSDoc postfix-optional + // + // RestType : + // `...` Type[~Extends] + // + NodeArray parenthesizeElementTypesOfTupleType(NodeArray types) { + return factory::createNodeArray(map < TypeNode > (types, parenthesizeElementTypeOfTupleType)); + } + +// function hasJSDocPostfixQuestion(shared type | NamedTupleMember): boolean { // if (isJSDocNullableType(type)) return type.postfix; // if (isNamedTupleMember(type)) return hasJSDocPostfixQuestion(type.type); // if (isFunctionTypeNode(type) || isConstructorTypeNode(type) || isTypeOperatorNode(type)) return hasJSDocPostfixQuestion(type.type); @@ -549,7 +553,7 @@ namespace ts::parenthesizerRules { // return false; // } // -// function parenthesizeTypeOfOptionalType(type: TypeNode): TypeNode { +// function parenthesizeTypeOfOptionalType(shared type): TypeNode { // if (hasJSDocPostfixQuestion(type)) return factory::createParenthesizedType(type); // return parenthesizeNonArrayTypeOfPostfixType(type); // } @@ -767,10 +771,10 @@ namespace ts::factory { // function createToken(token: TKind): Token; template shared createToken(SyntaxKind token) { -// Debug.assert(token >= SyntaxKind::FirstToken && token <= SyntaxKind::LastToken, "Invalid token"); -// Debug.assert(token <= SyntaxKind::FirstTemplateToken || token >= SyntaxKind::LastTemplateToken, "Invalid token. Use 'createTemplateLiteralLikeNode' to create template literals."); -// Debug.assert(token <= SyntaxKind::FirstLiteralToken || token >= SyntaxKind::LastLiteralToken, "Invalid token. Use 'createLiteralLikeNode' to create literals."); -// Debug.assert(token != SyntaxKind::Identifier, "Invalid token. Use 'createIdentifier' to create identifiers"); +// Debug::asserts(token >= SyntaxKind::FirstToken && token <= SyntaxKind::LastToken, "Invalid token"); +// Debug::asserts(token <= SyntaxKind::FirstTemplateToken || token >= SyntaxKind::LastTemplateToken, "Invalid token. Use 'createTemplateLiteralLikeNode' to create template literals."); +// Debug::asserts(token <= SyntaxKind::FirstLiteralToken || token >= SyntaxKind::LastLiteralToken, "Invalid token. Use 'createLiteralLikeNode' to create literals."); +// Debug::asserts(token != SyntaxKind::Identifier, "Invalid token. Use 'createIdentifier' to create identifiers"); auto node = createBaseToken(token); int transformFlags = (int) TransformFlags::None; switch (token) { @@ -915,6 +919,19 @@ namespace ts::factory { lName = rName; } + template + shared updateWithoutOriginal(shared updated, shared original) { + if (updated != original) { + setTextRange(updated, original); + } + return updated; + } + + template + shared update(shared updated, shared original) { + return updateWithoutOriginal(updated, original); + } + template shared createBaseNamedDeclaration( SyntaxKind kind, @@ -1014,7 +1031,7 @@ namespace ts::factory { optional typeParameters, optional parameters, sharedOpt type, - shared body + decltype(declval().body) body ) { auto node = createBaseSignatureDeclaration( kind, @@ -1245,15 +1262,15 @@ namespace ts::factory { // /** Create a unique name based on the supplied text. */ // // @api // function createUniqueName(text: string, flags: GeneratedIdentifierFlags = GeneratedIdentifierFlags.None): Identifier { -// Debug.assert(!(flags & GeneratedIdentifierFlags.KindMask), "Argument out of range: flags"); -// Debug.assert((flags & (GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel)) != GeneratedIdentifierFlags.FileLevel, "GeneratedIdentifierFlags.FileLevel cannot be set without also setting GeneratedIdentifierFlags.Optimistic"); +// Debug::asserts(!(flags & GeneratedIdentifierFlags.KindMask), "Argument out of range: flags"); +// Debug::asserts((flags & (GeneratedIdentifierFlags.Optimistic | GeneratedIdentifierFlags.FileLevel)) != GeneratedIdentifierFlags.FileLevel, "GeneratedIdentifierFlags.FileLevel cannot be set without also setting GeneratedIdentifierFlags.Optimistic"); // return createBaseGeneratedIdentifier(text, GeneratedIdentifierFlags.Unique | flags); // } // // /** Create a unique name generated for a node-> */ // // @api // function getGeneratedNameForNode(node: Node | undefined, flags: GeneratedIdentifierFlags = 0): Identifier { -// Debug.assert(!(flags & GeneratedIdentifierFlags.KindMask), "Argument out of range: flags"); +// Debug::asserts(!(flags & GeneratedIdentifierFlags.KindMask), "Argument out of range: flags"); // auto name = createBaseGeneratedIdentifier(node && isIdentifier(node) ? idText(node) : "", GeneratedIdentifierFlags.Node | flags); // name.original = node; // return name; @@ -1306,16 +1323,16 @@ namespace ts::factory { // // Names // // // - // @api - auto createQualifiedName(shared left, NameType right) { - auto node = createBaseNode(SyntaxKind::QualifiedName); - node->left = left; - node->right = dynamic_pointer_cast(asName(right)); - node->transformFlags |= + // @api + auto createQualifiedName(shared left, NameType right) { + auto node = createBaseNode(SyntaxKind::QualifiedName); + node->left = left; + node->right = dynamic_pointer_cast(asName(right)); + node->transformFlags |= propagateChildFlags(node->left) | propagateIdentifierNameFlags(node->right); - return node; - } + return node; + } // // @api // function updateQualifiedName(node: QualifiedName, left: EntityName, right: Identifier) { @@ -1945,7 +1962,7 @@ namespace ts::factory { // optional decorators, // optional modifiers, // NodeArray parameters, -// type: TypeNode +// shared type // ) { // return node->parameters != parameters // || node->type != type @@ -1954,18 +1971,18 @@ namespace ts::factory { // ? updateBaseSignatureDeclaration(createIndexSignature(decorators, modifiers, parameters, type), node) // : node; // } -// -// // @api -// function createTemplateLiteralTypeSpan(type: TypeNode, literal: TemplateMiddle | TemplateTail) { -// auto node = createBaseNode(SyntaxKind::TemplateLiteralTypeSpan); -// node->type = type; -// node->literal = literal; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + // @api + auto createTemplateLiteralTypeSpan(shared type, shared literal) { + auto node = createBaseNode(SyntaxKind::TemplateLiteralTypeSpan); + node->type = type; + node->literal = literal; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api -// function updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, type: TypeNode, literal: TemplateMiddle | TemplateTail) { +// function updateTemplateLiteralTypeSpan(node: TemplateLiteralTypeSpan, shared type, literal: TemplateMiddle | TemplateTail) { // return node->type != type // || node->literal != literal // ? update(createTemplateLiteralTypeSpan(type, literal), node) @@ -2000,14 +2017,14 @@ namespace ts::factory { // : node; // } - // @api - auto createTypeReferenceNode(NameType typeName, optional typeArguments) { - auto node = createBaseNode(SyntaxKind::TypeReference); - node->typeName = asName(typeName); - if (typeArguments) node->typeArguments = parenthesizerRules::parenthesizeTypeArguments(createNodeArray(typeArguments)); - node->transformFlags = (int)TransformFlags::ContainsTypeScript; - return node; - } + // @api + auto createTypeReferenceNode(NameType typeName, optional typeArguments) { + auto node = createBaseNode(SyntaxKind::TypeReference); + node->typeName = asName(typeName); + if (typeArguments) node->typeArguments = parenthesizerRules::parenthesizeTypeArguments(createNodeArray(typeArguments)); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } // // @api // function updateTypeReferenceNode(node: TypeReferenceNode, typeName: EntityName, typeArguments: NodeArray | undefined) { @@ -2117,15 +2134,15 @@ namespace ts::factory { // return updateConstructorTypeNode1(node, node->modifiers, typeParameters, parameters, type); // } // -// // @api -// function createTypeQueryNode(exprName: EntityName, typeArguments?: readonly TypeNode[]) { -// auto node = createBaseNode(SyntaxKind::TypeQuery); -// node->exprName = exprName; -// node->typeArguments = typeArguments && parenthesizerRules::parenthesizeTypeArguments(typeArguments); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + // @api + auto createTypeQueryNode(shared exprName, optional typeArguments) { + auto node = createBaseNode(SyntaxKind::TypeQuery); + node->exprName = exprName; + if (typeArguments) node->typeArguments = parenthesizerRules::parenthesizeTypeArguments(typeArguments); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateTypeQueryNode(node: TypeQueryNode, exprName: EntityName, typeArguments?: readonly TypeNode[]) { // return node->exprName != exprName @@ -2148,30 +2165,30 @@ namespace ts::factory { // ? update(createTypeLiteralNode(members), node) // : node; // } -// -// // @api -// function createArrayTypeNode(elementType: TypeNode) { -// auto node = createBaseNode(SyntaxKind::ArrayType); -// node->elementType = parenthesizerRules::parenthesizeNonArrayTypeOfPostfixType(elementType); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + // @api + auto createArrayTypeNode(shared elementType) { + auto node = createBaseNode(SyntaxKind::ArrayType); + node->elementType = parenthesizerRules::parenthesizeNonArrayTypeOfPostfixType(elementType); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api -// function updateArrayTypeNode(node: ArrayTypeNode, elementType: TypeNode): ArrayTypeNode { +// function updateArrayTypeNode(node: ArrayTypeNode, shared elementType): ArrayTypeNode { // return node->elementType != elementType // ? update(createArrayTypeNode(elementType), node) // : node; // } // -// // @api -// function createTupleTypeNode(elements: readonly (TypeNode | NamedTupleMember)[]) { -// auto node = createBaseNode(SyntaxKind::TupleType); -// node->elements = createNodeArray(parenthesizerRules::parenthesizeElementTypesOfTupleType(elements)); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + // @api + auto createTupleTypeNode(NodeArray elements) { + auto node = createBaseNode(SyntaxKind::TupleType); + node->elements = createNodeArray(parenthesizerRules::parenthesizeElementTypesOfTupleType(elements)); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateTupleTypeNode(node: TupleTypeNode, elements: readonly (TypeNode | NamedTupleMember)[]) { // return node->elements != elements @@ -2179,19 +2196,19 @@ namespace ts::factory { // : node; // } // + // @api + auto createNamedTupleMember(sharedOpt dotDotDotToken, shared name, sharedOpt questionToken, shared type) { + auto node = createBaseNode(SyntaxKind::NamedTupleMember); + node->dotDotDotToken = dotDotDotToken; + node->name = name; + node->questionToken = questionToken; + node->type = type; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api -// function createNamedTupleMember(dotDotDotToken: DotDotDotToken | undefined, name: Identifier, sharedOpt questionToken, type: TypeNode) { -// auto node = createBaseNode(SyntaxKind::NamedTupleMember); -// node->dotDotDotToken = dotDotDotToken; -// node->name = name; -// node->questionToken = questionToken; -// node->type = type; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// -// // @api -// function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: DotDotDotToken | undefined, name: Identifier, sharedOpt questionToken, type: TypeNode) { +// function updateNamedTupleMember(node: NamedTupleMember, dotDotDotToken: DotDotDotToken | undefined, name: Identifier, sharedOpt questionToken, shared type) { // return node->dotDotDotToken != dotDotDotToken // || node->name != name // || node->questionToken != questionToken @@ -2201,7 +2218,7 @@ namespace ts::factory { // } // // // @api -// function createOptionalTypeNode(type: TypeNode) { +// function createOptionalTypeNode(shared type) { // auto node = createBaseNode(SyntaxKind::OptionalType); // node->type = parenthesizerRules::parenthesizeTypeOfOptionalType(type); // node->transformFlags = (int)TransformFlags::ContainsTypeScript; @@ -2209,71 +2226,72 @@ namespace ts::factory { // } // // // @api -// function updateOptionalTypeNode(node: OptionalTypeNode, type: TypeNode): OptionalTypeNode { +// function updateOptionalTypeNode(node: OptionalTypeNode, shared type): OptionalTypeNode { // return node->type != type // ? update(createOptionalTypeNode(type), node) // : node; // } // + // @api + auto createRestTypeNode(shared type) { + auto node = createBaseNode(SyntaxKind::RestType); + node->type = type; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api -// function createRestTypeNode(type: TypeNode) { -// auto node = createBaseNode(SyntaxKind::RestType); -// node->type = type; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// -// // @api -// function updateRestTypeNode(node: RestTypeNode, type: TypeNode): RestTypeNode { +// function updateRestTypeNode(node: RestTypeNode, shared type): RestTypeNode { // return node->type != type // ? update(createRestTypeNode(type), node) // : node; // } -// -// function createUnionOrIntersectionTypeNode(kind: SyntaxKind::UnionType | SyntaxKind::IntersectionType, types: readonly TypeNode[], parenthesize: (nodes: readonly TypeNode[]) => readonly TypeNode[]) { -// auto node = createBaseNode(kind); -// node->types = factory::createNodeArray(parenthesize(types)); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + template + shared createUnionOrIntersectionTypeNode(SyntaxKind kind, NodeArray types, function parenthesize) { + auto node = createBaseNode(kind); + node->types = factory::createNodeArray(parenthesize(types)); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // function updateUnionOrIntersectionTypeNode(node: T, types: NodeArray, parenthesize: (nodes: readonly TypeNode[]) => readonly TypeNode[]): T { // return node->types != types // ? update(createUnionOrIntersectionTypeNode(node->kind, types, parenthesize) as T, node) // : node; // } -// -// // @api -// function createUnionTypeNode(types: readonly TypeNode[]): UnionTypeNode { -// return createUnionOrIntersectionTypeNode(SyntaxKind::UnionType, types, parenthesizerRules::parenthesizeConstituentTypesOfUnionType) as UnionTypeNode; -// } -// + + // @api + auto createUnionTypeNode(NodeArray types) { + return createUnionOrIntersectionTypeNode(SyntaxKind::UnionType, types, parenthesizerRules::parenthesizeConstituentTypesOfUnionType); + } + // // @api // function updateUnionTypeNode(node: UnionTypeNode, types: NodeArray) { // return updateUnionOrIntersectionTypeNode(node, types, parenthesizerRules::parenthesizeConstituentTypesOfUnionType); // } -// -// // @api -// function createIntersectionTypeNode(types: readonly TypeNode[]): IntersectionTypeNode { -// return createUnionOrIntersectionTypeNode(SyntaxKind::IntersectionType, types, parenthesizerRules::parenthesizeConstituentTypesOfIntersectionType) as IntersectionTypeNode; -// } -// + + // @api + auto createIntersectionTypeNode(NodeArray types) { + return createUnionOrIntersectionTypeNode(SyntaxKind::IntersectionType, types, parenthesizerRules::parenthesizeConstituentTypesOfIntersectionType); + } + // // @api // function updateIntersectionTypeNode(node: IntersectionTypeNode, types: NodeArray) { // return updateUnionOrIntersectionTypeNode(node, types, parenthesizerRules::parenthesizeConstituentTypesOfIntersectionType); // } -// -// // @api -// function createConditionalTypeNode(checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) { -// auto node = createBaseNode(SyntaxKind::ConditionalType); -// node->checkType = parenthesizerRules::parenthesizeCheckTypeOfConditionalType(checkType); -// node->extendsType = parenthesizerRules::parenthesizeExtendsTypeOfConditionalType(extendsType); -// node->trueType = trueType; -// node->falseType = falseType; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + // @api + auto createConditionalTypeNode(shared checkType, shared extendsType, shared trueType, shared falseType) { + auto node = createBaseNode(SyntaxKind::ConditionalType); + node->checkType = parenthesizerRules::parenthesizeCheckTypeOfConditionalType(checkType); + node->extendsType = parenthesizerRules::parenthesizeExtendsTypeOfConditionalType(extendsType); + node->trueType = trueType; + node->falseType = falseType; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateConditionalTypeNode(node: ConditionalTypeNode, checkType: TypeNode, extendsType: TypeNode, trueType: TypeNode, falseType: TypeNode) { // return node->checkType != checkType @@ -2284,13 +2302,13 @@ namespace ts::factory { // : node; // } // - // @api - auto createInferTypeNode(shared typeParameter) { - auto node = createBaseNode(SyntaxKind::InferType); - node->typeParameter = typeParameter; - node->transformFlags = (int)TransformFlags::ContainsTypeScript; - return node; - } + // @api + auto createInferTypeNode(shared typeParameter) { + auto node = createBaseNode(SyntaxKind::InferType); + node->typeParameter = typeParameter; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } // // @api // function updateInferTypeNode(node: InferTypeNode, typeParameter: TypeParameterDeclaration) { @@ -2298,16 +2316,16 @@ namespace ts::factory { // ? update(createInferTypeNode(typeParameter), node) // : node; // } -// -// // @api -// function createTemplateLiteralType(head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]) { -// auto node = createBaseNode(SyntaxKind::TemplateLiteralType); -// node->head = head; -// node->templateSpans = createNodeArray(templateSpans); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + // @api + auto createTemplateLiteralType(shared head, NodeArray templateSpans) { + auto node = createBaseNode(SyntaxKind::TemplateLiteralType); + node->head = head; + node->templateSpans = createNodeArray(templateSpans); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateTemplateLiteralType(node: TemplateLiteralTypeNode, head: TemplateHead, templateSpans: readonly TemplateLiteralTypeSpan[]) { // return node->head != head @@ -2319,29 +2337,56 @@ namespace ts::factory { // // @api // function createImportTypeNode(argument: TypeNode, qualifier?: EntityName, typeArguments?: readonly TypeNode[], isTypeOf?: boolean): ImportTypeNode; // function createImportTypeNode(argument: TypeNode, assertions?: ImportTypeAssertionContainer, qualifier?: EntityName, typeArguments?: readonly TypeNode[], isTypeOf?: boolean): ImportTypeNode; -// function createImportTypeNode( -// argument: TypeNode, -// qualifierOrAssertions?: EntityName | ImportTypeAssertionContainer, -// typeArgumentsOrQualifier?: readonly TypeNode[] | EntityName, -// isTypeOfOrTypeArguments?: boolean | readonly TypeNode[], -// isTypeOf?: boolean -// ) { + auto createImportTypeNode( + shared argument, + optional, shared>> qualifierOrAssertions, + optional, NodeArray>> typeArgumentsOrQualifier, + optional> isTypeOfOrTypeArguments, + optional isTypeOf + ) { + sharedOpt assertion; + sharedOpt qualifier; + optional typeArguments; + if (qualifierOrAssertions) { + if (holds_alternative>(*qualifierOrAssertions)) { + assertion = get>(*qualifierOrAssertions); + } else if (holds_alternative>(*qualifierOrAssertions)) { + qualifier = get>(*qualifierOrAssertions); + } + } + + if (typeArgumentsOrQualifier) { + if (holds_alternative(*typeArgumentsOrQualifier)) { + typeArguments = get(*typeArgumentsOrQualifier); + } else if (holds_alternative>(*typeArgumentsOrQualifier)) { + qualifier = get>(*typeArgumentsOrQualifier); + } + } + + if (isTypeOfOrTypeArguments) { + if (holds_alternative(*isTypeOfOrTypeArguments)) { + typeArguments = get(*isTypeOfOrTypeArguments); + } else if (holds_alternative(*isTypeOfOrTypeArguments)) { + isTypeOf = get(*isTypeOfOrTypeArguments); + } + } + // auto assertion = qualifierOrAssertions && qualifierOrAssertions.kind == SyntaxKind::ImportTypeAssertionContainer ? qualifierOrAssertions : undefined; // auto qualifier = qualifierOrAssertions && isEntityName(qualifierOrAssertions) ? qualifierOrAssertions // : typeArgumentsOrQualifier && !isArray(typeArgumentsOrQualifier) ? typeArgumentsOrQualifier : undefined; // auto typeArguments = isArray(typeArgumentsOrQualifier) ? typeArgumentsOrQualifier : isArray(isTypeOfOrTypeArguments) ? isTypeOfOrTypeArguments : undefined; // isTypeOf = typeof isTypeOfOrTypeArguments == "boolean" ? isTypeOfOrTypeArguments : typeof isTypeOf == "boolean" ? isTypeOf : false; -// -// auto node = createBaseNode(SyntaxKind::ImportType); -// node->argument = argument; -// node->assertions = assertion; -// node->qualifier = qualifier; -// node->typeArguments = typeArguments && parenthesizerRules::parenthesizeTypeArguments(typeArguments); -// node->isTypeOf = isTypeOf; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + auto node = createBaseNode(SyntaxKind::ImportType); + node->argument = argument; + node->assertions = assertion; + node->qualifier = qualifier; + if (typeArguments) node->typeArguments = parenthesizerRules::parenthesizeTypeArguments(*typeArguments); + node->isTypeOf = isTrue(isTypeOf); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, qualifier: EntityName | undefined, optional typeArguments, isTypeOf?: boolean | undefined): ImportTypeNode; // function updateImportTypeNode(node: ImportTypeNode, argument: TypeNode, assertions: ImportTypeAssertionContainer | undefined, qualifier: EntityName | undefined, optional typeArguments, isTypeOf?: boolean | undefined): ImportTypeNode; @@ -2367,7 +2412,7 @@ namespace ts::factory { // ? update(createImportTypeNode(argument, assertion, qualifier, typeArguments, isTypeOf), node) // : node; // } -// + // @api shared createParenthesizedType(shared type) { auto node = createBaseNode(SyntaxKind::ParenthesizedType); @@ -2375,48 +2420,48 @@ namespace ts::factory { node->transformFlags = (int) TransformFlags::ContainsTypeScript; return node; } -// + // // @api -// function updateParenthesizedType(node: ParenthesizedTypeNode, type: TypeNode) { +// function updateParenthesizedType(node: ParenthesizedTypeNode, shared type) { // return node->type != type // ? update(createParenthesizedType(type), node) // : node; -// } -// -// // @api -// function createThisTypeNode() { -// auto node = createBaseNode(SyntaxKind::ThisType); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; // } - // @api - auto createTypeOperatorNode(SyntaxKind operatorKind, shared type) { - auto node = createBaseNode(SyntaxKind::TypeOperator); - node->operatorKind = operatorKind; - node->type = operatorKind == SyntaxKind::ReadonlyKeyword ? - parenthesizerRules::parenthesizeOperandOfReadonlyTypeOperator(type) : - parenthesizerRules::parenthesizeOperandOfTypeOperator(type); - node->transformFlags = (int)TransformFlags::ContainsTypeScript; - return node; - } + // @api + auto createThisTypeNode() { + auto node = createBaseNode(SyntaxKind::ThisType); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + + // @api + auto createTypeOperatorNode(SyntaxKind operatorKind, shared type) { + auto node = createBaseNode(SyntaxKind::TypeOperator); + node->operatorKind = operatorKind; + node->type = operatorKind == SyntaxKind::ReadonlyKeyword ? + parenthesizerRules::parenthesizeOperandOfReadonlyTypeOperator(type) : + parenthesizerRules::parenthesizeOperandOfTypeOperator(type); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } // // @api -// function updateTypeOperatorNode(node: TypeOperatorNode, type: TypeNode) { +// function updateTypeOperatorNode(node: TypeOperatorNode, shared type) { // return node->type != type // ? update(createTypeOperatorNode(node->operator, type), node) // : node; // } -// -// // @api -// function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) { -// auto node = createBaseNode(SyntaxKind::IndexedAccessType); -// node->objectType = parenthesizerRules::parenthesizeNonArrayTypeOfPostfixType(objectType); -// node->indexType = indexType; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + // @api + auto createIndexedAccessTypeNode(shared objectType, shared indexType) { + auto node = createBaseNode(SyntaxKind::IndexedAccessType); + node->objectType = parenthesizerRules::parenthesizeNonArrayTypeOfPostfixType(objectType); + node->indexType = indexType; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateIndexedAccessTypeNode(node: IndexedAccessTypeNode, objectType: TypeNode, indexType: TypeNode) { // return node->objectType != objectType @@ -2424,20 +2469,27 @@ namespace ts::factory { // ? update(createIndexedAccessTypeNode(objectType, indexType), node) // : node; // } -// -// // @api -// function createMappedTypeNode(readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, namesharedOpt type, questionToken: QuestionToken | PlusToken | MinusToken | undefined, sharedOpt type, optional members): MappedTypeNode { -// auto node = createBaseNode(SyntaxKind::MappedType); -// node->readonlyToken = readonlyToken; -// node->typeParameter = typeParameter; -// node->nameType = nameType; -// node->questionToken = questionToken; -// node->type = type; -// node->members = members && createNodeArray(members); -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + + // @api + auto createMappedTypeNode( + sharedOpt readonlyToken, //: ReadonlyKeyword | PlusToken | MinusToken | undefined, + shared typeParameter, + sharedOpt nameType, + sharedOpt questionToken, //: QuestionToken | PlusToken | MinusToken | undefined, + sharedOpt type, + optional members + ) { + auto node = createBaseNode(SyntaxKind::MappedType); + node->readonlyToken = readonlyToken; + node->typeParameter = typeParameter; + node->nameType = nameType; + node->questionToken = questionToken; + node->type = type; + if (members) node->members = createNodeArray(members); + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateMappedTypeNode(node: MappedTypeNode, readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined, typeParameter: TypeParameterDeclaration, namesharedOpt type, questionToken: QuestionToken | PlusToken | MinusToken | undefined, sharedOpt type, optional members): MappedTypeNode { // return node->readonlyToken != readonlyToken @@ -2450,14 +2502,14 @@ namespace ts::factory { // : node; // } // -// // @api -// function createLiteralTypeNode(literal: LiteralTypeNode["literal"]) { -// auto node = createBaseNode(SyntaxKind::LiteralType); -// node->literal = literal; -// node->transformFlags = (int)TransformFlags::ContainsTypeScript; -// return node; -// } -// + // @api + auto createLiteralTypeNode(shared literal) { + auto node = createBaseNode(SyntaxKind::LiteralType); + node->literal = literal; + node->transformFlags = (int) TransformFlags::ContainsTypeScript; + return node; + } + // // @api // function updateLiteralTypeNode(node: LiteralTypeNode, literal: LiteralTypeNode["literal"]) { // return node->literal != literal @@ -2645,7 +2697,7 @@ namespace ts::factory { // // // @api // function updatePropertyAccessChain(node: PropertyAccessChain, shared expression, sharedOpt questionDotToken, name: Identifier | PrivateIdentifier) { -// Debug.assert(!!(node->flags & NodeFlags::OptionalChain), "Cannot update a PropertyAccessExpression using updatePropertyAccessChain. Use updatePropertyAccess instead."); +// Debug::asserts(!!(node->flags & NodeFlags::OptionalChain), "Cannot update a PropertyAccessExpression using updatePropertyAccessChain. Use updatePropertyAccess instead."); // // Because we are updating an existing PropertyAccessChain we want to inherit its emitFlags // // instead of using the default from createPropertyAccess // return node->expression != expression @@ -2701,7 +2753,7 @@ namespace ts::factory { // // @api // function updateElementAccessChain(node: ElementAccessChain, shared expression, sharedOpt questionDotToken, argumentExpression: Expression) { -// Debug.assert(!!(node->flags & NodeFlags::OptionalChain), "Cannot update a ElementAccessExpression using updateElementAccessChain. Use updateElementAccess instead."); +// Debug::asserts(!!(node->flags & NodeFlags::OptionalChain), "Cannot update a ElementAccessExpression using updateElementAccessChain. Use updateElementAccess instead."); // // Because we are updating an existing ElementAccessChain we want to inherit its emitFlags // // instead of using the default from createElementAccess // return node->expression != expression @@ -2732,18 +2784,6 @@ namespace ts::factory { return node; } -// // @api -// function updateCallExpression(node: CallExpression, shared expression, optional typeArguments, argumentsArray: readonly Expression[]) { -// if (isCallChain(node)) { -// return updateCallChain(node, expression, node->questionDotToken, typeArguments, argumentsArray); -// } -// return node->expression != expression -// || node->typeArguments != typeArguments -// || node->arguments != argumentsArray -// ? update(createCallExpression(expression, typeArguments, argumentsArray), node) -// : node; -// } -// // @api auto createCallChain(shared expression, sharedOpt questionDotToken, optional typeArguments, optional argumentsArray) { auto node = createBaseExpression(SyntaxKind::CallExpression); @@ -2767,16 +2807,28 @@ namespace ts::factory { return node; } -// // @api -// function updateCallChain(node: CallChain, shared expression, sharedOpt questionDotToken, optional typeArguments, argumentsArray: readonly Expression[]) { -// Debug.assert(!!(node->flags & NodeFlags::OptionalChain), "Cannot update a CallExpression using updateCallChain. Use updateCall instead."); -// return node->expression != expression -// || node->questionDotToken != questionDotToken -// || node->typeArguments != typeArguments -// || node->arguments != argumentsArray -// ? update(createCallChain(expression, questionDotToken, typeArguments, argumentsArray), node) -// : node; -// } + // @api + auto updateCallChain(shared node, shared expression, sharedOpt questionDotToken, optional typeArguments, NodeArray argumentsArray) { + Debug::asserts(!!(node->flags & (int) NodeFlags::OptionalChain), "Cannot update a CallExpression using updateCallChain. Use updateCall instead."); + return node->expression != expression + || node->questionDotToken != questionDotToken + || node->typeArguments != typeArguments + || node->arguments != argumentsArray + ? update(createCallChain(expression, questionDotToken, typeArguments, argumentsArray), node) + : node; + } + + // @api + shared updateCallExpression(shared node, shared expression, optional typeArguments, NodeArray argumentsArray) { + if (isCallChain(node)) { + return updateCallChain(node, expression, node->questionDotToken, typeArguments, argumentsArray); + } + return node->expression != expression + || node->typeArguments != typeArguments + || node->arguments != argumentsArray + ? update(createCallExpression(expression, typeArguments, argumentsArray), node) + : node; + } // @api auto createNewExpression(shared expression, optional typeArguments, optional argumentsArray) { @@ -2845,14 +2897,14 @@ namespace ts::factory { return node; } -// // @api -// function updateTypeAssertion(node: TypeAssertion, type: TypeNode, shared expression) { -// return node->type != type -// || node->expression != expression -// ? update(createTypeAssertion(type, expression), node) -// : node; -// } -// + // @api + auto updateTypeAssertion(shared node, shared type, shared expression) { + return node->type != type + || node->expression != expression + ? update(createTypeAssertion(type, expression), node) + : node; + } + // @api shared createParenthesizedExpression(shared expression) { auto node = createBaseExpression(SyntaxKind::ParenthesizedExpression); @@ -2861,13 +2913,13 @@ namespace ts::factory { return node; } -// // @api -// function updateParenthesizedExpression(node: ParenthesizedExpression, shared expression) { -// return node->expression != expression -// ? update(createParenthesizedExpression(expression), node) -// : node; -// } -// + // @api + auto updateParenthesizedExpression(shared node, shared expression) { + return node->expression != expression + ? update(createParenthesizedExpression(expression), node) + : node; + } + // @api auto createFunctionExpression( optional modifiers, @@ -2921,41 +2973,40 @@ namespace ts::factory { // || node->asteriskToken != asteriskToken // || node->typeParameters != typeParameters // || node->parameters != parameters -// || node->type != type -// || node->body != body -// ? updateBaseFunctionLikeDeclaration(createFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, type, body), node) -// : node; -// } -// -// // @api -// function createArrowFunction( -// optional modifiers, -// optional typeParameters, -// NodeArray parameters, -// sharedOpt type, -// equalsGreaterThanToken: EqualsGreaterThanToken | undefined, -// body: ConciseBody -// ) { -// auto node = createBaseFunctionLikeDeclaration( -// SyntaxKind::ArrowFunction, -// /*decorators*/ {}, -// modifiers, -// /*name*/ {}, -// typeParameters, -// parameters, -// type, -// parenthesizerRules::parenthesizeConciseBodyOfArrowFunction(body) -// ); -// node->equalsGreaterThanToken = equalsGreaterThanToken ?? createToken(SyntaxKind::EqualsGreaterThanToken); -// node->transformFlags |= -// propagateChildFlags(node->equalsGreaterThanToken) | -// TransformFlags::ContainsES2015; -// if (modifiersToFlags(node->modifiers) & ModifierFlags::Async) { -// node->transformFlags |= (int)TransformFlags::ContainsES2017 | TransformFlags::ContainsLexicalThis; -// } -// return node; +// || node->type != type +// || node->body != body +// ? updateBaseFunctionLikeDeclaration(createFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, type, body), node) +// : node; // } -// + // @api + auto createArrowFunction( + optional modifiers, + optional typeParameters, + NodeArray parameters, + sharedOpt type, + sharedOpt equalsGreaterThanToken, + shared body + ) { + auto node = createBaseFunctionLikeDeclaration( + SyntaxKind::ArrowFunction, + /*decorators*/ {}, + modifiers, + /*name*/ {}, + typeParameters, + parameters, + type, + parenthesizerRules::parenthesizeConciseBodyOfArrowFunction(body) + ); + node->equalsGreaterThanToken = equalsGreaterThanToken ? equalsGreaterThanToken : createToken(SyntaxKind::EqualsGreaterThanToken); + node->transformFlags |= + propagateChildFlags(node->equalsGreaterThanToken) | + (int) TransformFlags::ContainsES2015; + if (modifiersToFlags(node->modifiers) & (int) ModifierFlags::Async) { + node->transformFlags |= (int) TransformFlags::ContainsES2017 | (int) TransformFlags::ContainsLexicalThis; + } + return node; + } + // // @api // function updateArrowFunction( // node: ArrowFunction, @@ -3105,15 +3156,15 @@ namespace ts::factory { // else if (operatorKind == SyntaxKind::EqualsToken) { // if (isObjectLiteralExpression(node->left)) { // node->transformFlags |= -// TransformFlags::ContainsES2015 | -// TransformFlags::ContainsES2018 | -// TransformFlags::ContainsDestructuringAssignment | +// (int)TransformFlags::ContainsES2015 | +// (int)TransformFlags::ContainsES2018 | +// (int)TransformFlags::ContainsDestructuringAssignment | // propagateAssignmentPatternFlags(node->left); // } // else if (isArrayLiteralExpression(node->left)) { // node->transformFlags |= -// TransformFlags::ContainsES2015 | -// TransformFlags::ContainsDestructuringAssignment | +// (int)TransformFlags::ContainsES2015 | +// (int)TransformFlags::ContainsDestructuringAssignment | // propagateAssignmentPatternFlags(node->left); // } // } @@ -3155,24 +3206,24 @@ namespace ts::factory { // ? update(createBinaryExpression(left, operator, right), node) // : node; // } -// -// // @api -// function createConditionalExpression(condition: Expression, sharedOpt questionToken, whenTrue: Expression, colonToken: ColonToken | undefined, whenFalse: Expression) { -// auto node = createBaseExpression(SyntaxKind::ConditionalExpression); -// node->condition = parenthesizerRules::parenthesizeConditionOfConditionalExpression(condition); -// node->questionToken = questionToken ?? createToken(SyntaxKind::QuestionToken); -// node->whenTrue = parenthesizerRules::parenthesizeBranchOfConditionalExpression(whenTrue); -// node->colonToken = colonToken ?? createToken(SyntaxKind::ColonToken); -// node->whenFalse = parenthesizerRules::parenthesizeBranchOfConditionalExpression(whenFalse); -// node->transformFlags |= -// propagateChildFlags(node->condition) | -// propagateChildFlags(node->questionToken) | -// propagateChildFlags(node->whenTrue) | -// propagateChildFlags(node->colonToken) | -// propagateChildFlags(node->whenFalse); -// return node; -// } -// + + // @api + auto createConditionalExpression(shared condition, sharedOpt questionToken, shared whenTrue, sharedOpt colonToken, shared whenFalse) { + auto node = createBaseExpression(SyntaxKind::ConditionalExpression); + node->condition = parenthesizerRules::parenthesizeConditionOfConditionalExpression(condition); + node->questionToken = questionToken ? questionToken: createToken(SyntaxKind::QuestionToken); + node->whenTrue = parenthesizerRules::parenthesizeBranchOfConditionalExpression(whenTrue); + node->colonToken = colonToken ? colonToken : createToken(SyntaxKind::ColonToken); + node->whenFalse = parenthesizerRules::parenthesizeBranchOfConditionalExpression(whenFalse); + node->transformFlags |= + propagateChildFlags(node->condition) | + propagateChildFlags(node->questionToken) | + propagateChildFlags(node->whenTrue) | + propagateChildFlags(node->colonToken) | + propagateChildFlags(node->whenFalse); + return node; + } + // // @api // function updateConditionalExpression( // node: ConditionalExpression, @@ -3212,7 +3263,7 @@ namespace ts::factory { // } // // function createTemplateLiteralLikeNodeChecked(kind: TemplateLiteralToken["kind"], text: string | undefined, rawText: string | undefined, templateFlags = TokenFlags::None) { -// Debug.assert(!(templateFlags & ~TokenFlags::TemplateLiteralLikeFlags), "Unsupported template flags."); +// Debug::asserts(!(templateFlags & ~TokenFlags::TemplateLiteralLikeFlags), "Unsupported template flags."); // // NOTE: without the assignment to `undefined`, we don't narrow the initial type of `cooked`. // // eslint-disable-next-line no-undef-init // let cooked: string | object | undefined = undefined; @@ -3229,7 +3280,7 @@ namespace ts::factory { // text = cooked; // } // else if (cooked != undefined) { -// Debug.assert(text == cooked, "Expected argument 'text' to be the normalized (i.e. 'cooked') version of argument 'rawText'."); +// Debug::asserts(text == cooked, "Expected argument 'text' to be the normalized (i.e. 'cooked') version of argument 'rawText'."); // } // return createTemplateLiteralLikeNode(kind, text, rawText, templateFlags); // } @@ -3253,22 +3304,22 @@ namespace ts::factory { // function createNoSubstitutionTemplateLiteral(text: string | undefined, rawText?: string, templateFlags?: TokenFlags) { // return createTemplateLiteralLikeNodeChecked(SyntaxKind::NoSubstitutionTemplateLiteral, text, rawText, templateFlags) as NoSubstitutionTemplateLiteral; // } -// -// // @api -// function createYieldExpression(sharedOpt asteriskToken, sharedOpt expression): YieldExpression { -// Debug.assert(!asteriskToken || !!expression, "A `YieldExpression` with an asteriskToken must have an expression."); -// auto node = createBaseExpression(SyntaxKind::YieldExpression); -// node->expression = expression && parenthesizerRules::parenthesizeExpressionForDisallowedComma(expression); -// node->asteriskToken = asteriskToken; -// node->transformFlags |= -// propagateChildFlags(node->expression) | -// propagateChildFlags(node->asteriskToken) | -// TransformFlags::ContainsES2015 | -// TransformFlags::ContainsES2018 | -// TransformFlags::ContainsYield; -// return node; -// } -// + + // @api + shared createYieldExpression(sharedOpt asteriskToken, sharedOpt expression) { + Debug::asserts(!asteriskToken || !!expression, "A `YieldExpression` with an asteriskToken must have an expression."); + auto node = createBaseExpression(SyntaxKind::YieldExpression); + if (expression) node->expression = parenthesizerRules::parenthesizeExpressionForDisallowedComma(expression); + node->asteriskToken = asteriskToken; + node->transformFlags |= + propagateChildFlags(node->expression) | + propagateChildFlags(node->asteriskToken) | + (int) TransformFlags::ContainsES2015 | + (int) TransformFlags::ContainsES2018 | + (int) TransformFlags::ContainsYield; + return node; + } + // // @api // function updateYieldExpression(node: YieldExpression, sharedOpt asteriskToken, shared expression) { // return node->expression != expression @@ -3374,14 +3425,14 @@ namespace ts::factory { return node; } -// // @api -// function updateAsExpression(node: AsExpression, shared expression, type: TypeNode) { -// return node->expression != expression -// || node->type != type -// ? update(createAsExpression(expression, type), node) -// : node; -// } -// + // @api + auto updateAsExpression(shared node, shared expression, shared type) { + return node->expression != expression + || node->type != type + ? update(createAsExpression(expression, type), node) + : node; + } + // @api shared createNonNullExpression(shared expression) { auto node = createBaseExpression(SyntaxKind::NonNullExpression); @@ -3392,35 +3443,35 @@ namespace ts::factory { return node; } -// // @api -// function updateNonNullExpression(node: NonNullExpression, shared expression) { -// if (isNonNullChain(node)) { -// return updateNonNullChain(node, expression); -// } -// return node->expression != expression -// ? update(createNonNullExpression(expression), node) -// : node; -// } -// -// // @api -// function createNonNullChain(shared expression) { -// auto node = createBaseExpression(SyntaxKind::NonNullExpression); -// node->flags |= NodeFlags::OptionalChain; -// node->expression = parenthesizerRules::parenthesizeLeftSideOfAccess(expression); -// node->transformFlags |= -// propagateChildFlags(node->expression) | -// TransformFlags::ContainsTypeScript; -// return node; -// } -// -// // @api -// function updateNonNullChain(node: NonNullChain, shared expression) { -// Debug.assert(!!(node->flags & NodeFlags::OptionalChain), "Cannot update a NonNullExpression using updateNonNullChain. Use updateNonNullExpression instead."); -// return node->expression != expression -// ? update(createNonNullChain(expression), node) -// : node; -// } -// + // @api + auto createNonNullChain(shared expression) { + auto node = createBaseExpression(SyntaxKind::NonNullExpression); + node->flags |= (int) NodeFlags::OptionalChain; + node->expression = parenthesizerRules::parenthesizeLeftSideOfAccess(expression); + node->transformFlags |= + propagateChildFlags(node->expression) | + (int) TransformFlags::ContainsTypeScript; + return node; + } + + // @api + auto updateNonNullChain(shared node, shared expression) { + Debug::asserts(!!(node->flags & (int) NodeFlags::OptionalChain), "Cannot update a NonNullExpression using updateNonNullChain. Use updateNonNullExpression instead."); + return node->expression != expression + ? update(createNonNullChain(expression), node) + : node; + } + + // @api + auto updateNonNullExpression(shared node, shared expression) { + if (isNonNullChain(node)) { + return updateNonNullChain(node, expression); + } + return node->expression != expression + ? update(createNonNullExpression(expression), node) + : node; + } + // @api auto createMetaProperty(SyntaxKind keywordToken, shared name) { auto node = createBaseExpression(SyntaxKind::MetaProperty); @@ -3517,20 +3568,54 @@ namespace ts::factory { // ? update(createVariableStatement(modifiers, declarationList), node) // : node; // } -// -// // @api -// function createEmptyStatement() { -// return createBaseNode(SyntaxKind::EmptyStatement); -// } -// -// // @api -// function createExpressionStatement(shared expression): ExpressionStatement { -// auto node = createBaseNode(SyntaxKind::ExpressionStatement); -// node->expression = parenthesizerRules::parenthesizeExpressionOfExpressionStatement(expression); -// node->transformFlags |= propagateChildFlags(node->expression); -// return node; + + // @api + auto createEmptyStatement() { + return createBaseNode(SyntaxKind::EmptyStatement); + } + + shared mergeEmitNode(shared sourceEmitNode, sharedOpt destEmitNode) { + if (!destEmitNode) destEmitNode = make_shared(); + // We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later. + if (sourceEmitNode->leadingComments) destEmitNode->leadingComments = addRange(slice(sourceEmitNode->leadingComments), destEmitNode->leadingComments); + if (sourceEmitNode->trailingComments) destEmitNode->trailingComments = addRange(slice(sourceEmitNode->trailingComments), destEmitNode->trailingComments); + if (sourceEmitNode->flags) destEmitNode->flags = sourceEmitNode->flags & ~(int)EmitFlags::Immutable; + if (sourceEmitNode->commentRange) destEmitNode->commentRange = sourceEmitNode->commentRange; + if (sourceEmitNode->sourceMapRange) destEmitNode->sourceMapRange = sourceEmitNode->sourceMapRange; +// if (sourceEmitNode->tokenSourceMapRanges) destEmitNode->tokenSourceMapRanges = mergeTokenSourceMapRanges(sourceEmitNode->tokenSourceMapRanges, destEmitNode->tokenSourceMapRanges!); +// if (sourceEmitNode->constantValue != false) destEmitNode->constantValue = constantValue; +// if (sourceEmitNode->helpers) { +// for (auto helper of helpers) { +// destEmitNode.helpers = appendIfUnique(destEmitNode.helpers, helper); +// } // } -// +// if (startsOnNewLine != undefined) destEmitNode.startsOnNewLine = startsOnNewLine; + return destEmitNode; + } + + template + T setOriginalNode(T node, sharedOpt original) { + node->original = original; + if (original) { + auto emitNode = original->emitNode; + if (emitNode) node->emitNode = mergeEmitNode(emitNode, node->emitNode); + } + return node; + } + + template + sharedOpt asEmbeddedStatement(sharedOpt statement) { + return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement; + } + + // @api + auto createExpressionStatement(shared expression) { + auto node = createBaseNode(SyntaxKind::ExpressionStatement); + node->expression = parenthesizerRules::parenthesizeExpressionOfExpressionStatement(expression); + node->transformFlags |= propagateChildFlags(node->expression); + return node; + } + // // @api // function updateExpressionStatement(node: ExpressionStatement, shared expression) { // return node->expression != expression @@ -3657,7 +3742,7 @@ namespace ts::factory { // propagateChildFlags(node->initializer) | // propagateChildFlags(node->expression) | // propagateChildFlags(node->statement) | -// TransformFlags::ContainsES2015; +// (int)TransformFlags::ContainsES2015; // if (awaitModifier) node->transformFlags |= (int)TransformFlags::ContainsES2018; // return node; // } @@ -3678,7 +3763,7 @@ namespace ts::factory { // node->label = asName(label); // node->transformFlags |= // propagateChildFlags(node->label) | -// TransformFlags::ContainsHoistedDeclarationOrCompletion; +// (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; // return node; // } // @@ -3695,7 +3780,7 @@ namespace ts::factory { // node->label = asName(label); // node->transformFlags |= // propagateChildFlags(node->label) | -// TransformFlags::ContainsHoistedDeclarationOrCompletion; +// (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; // return node; // } // @@ -3713,8 +3798,8 @@ namespace ts::factory { // // return in an ES2018 async generator must be awaited // node->transformFlags |= // propagateChildFlags(node->expression) | -// TransformFlags::ContainsES2018 | -// TransformFlags::ContainsHoistedDeclarationOrCompletion; +// (int)TransformFlags::ContainsES2018 | +// (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; // return node; // } // @@ -3763,17 +3848,17 @@ namespace ts::factory { // : node; // } // -// // @api -// function createLabeledStatement(label: string | Identifier, statement: Statement) { -// auto node = createBaseNode(SyntaxKind::LabeledStatement); -// node->label = asName(label); -// node->statement = asEmbeddedStatement(statement); -// node->transformFlags |= -// propagateChildFlags(node->label) | -// propagateChildFlags(node->statement); -// return node; -// } -// + // @api + auto createLabeledStatement(NameType label, shared statement) { + auto node = createBaseNode(SyntaxKind::LabeledStatement); + node->label = to(asName(label)); + node->statement = asEmbeddedStatement(statement); + node->transformFlags |= + propagateChildFlags(node->label) | + propagateChildFlags(node->statement); + return node; + } + // // @api // function updateLabeledStatement(node: LabeledStatement, label: Identifier, statement: Statement) { // return node->label != label @@ -3859,11 +3944,11 @@ namespace ts::factory { // node->declarations = createNodeArray(declarations); // node->transformFlags |= // propagateChildrenFlags(node->declarations) | -// TransformFlags::ContainsHoistedDeclarationOrCompletion; +// (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; // if (flags & NodeFlags::BlockScoped) { // node->transformFlags |= -// TransformFlags::ContainsES2015 | -// TransformFlags::ContainsBlockScopedBinding; +// (int)TransformFlags::ContainsES2015 | +// (int)TransformFlags::ContainsBlockScopedBinding; // } // return node; // } @@ -3903,7 +3988,7 @@ namespace ts::factory { // else { // node->transformFlags |= // propagateChildFlags(node->asteriskToken) | -// TransformFlags::ContainsHoistedDeclarationOrCompletion; +// (int)TransformFlags::ContainsHoistedDeclarationOrCompletion; // if (modifiersToFlags(node->modifiers) & ModifierFlags::Async) { // if (node->asteriskToken) { // node->transformFlags |= (int)TransformFlags::ContainsES2018; @@ -4040,7 +4125,7 @@ namespace ts::factory { // optional modifiers, // name: string | Identifier, // optional typeParameters, -// type: TypeNode +// shared type // ) { // auto node = createBaseGenericNamedDeclaration( // SyntaxKind::TypeAliasDeclaration, @@ -4061,7 +4146,7 @@ namespace ts::factory { // optional modifiers, // name: Identifier, // optional typeParameters, -// type: TypeNode +// shared type // ) { // return node->decorators != decorators // || node->modifiers != modifiers @@ -4088,7 +4173,7 @@ namespace ts::factory { // node->members = createNodeArray(members); // node->transformFlags |= // propagateChildrenFlags(node->members) | -// TransformFlags::ContainsTypeScript; +// (int)TransformFlags::ContainsTypeScript; // node->transformFlags &= ~TransformFlags::ContainsPossibleTopLevelAwait; // Enum declarations cannot contain `await` // return node; // } @@ -4131,7 +4216,7 @@ namespace ts::factory { // node->transformFlags |= // propagateChildFlags(node->name) | // propagateChildFlags(node->body) | -// TransformFlags::ContainsTypeScript; +// (int)TransformFlags::ContainsTypeScript; // } // node->transformFlags &= ~TransformFlags::ContainsPossibleTopLevelAwait; // Module declarations cannot contain `await`. // return node; @@ -4308,15 +4393,15 @@ namespace ts::factory { // : node; // } // -// // @api -// function createAssertClause(elements: readonly AssertEntry[], multiLine?: boolean): AssertClause { -// auto node = createBaseNode(SyntaxKind::AssertClause); -// node->elements = createNodeArray(elements); -// node->multiLine = multiLine; -// node->transformFlags |= (int)TransformFlags::ContainsESNext; -// return node; -// } -// + // @api + auto createAssertClause(NodeArray elements, bool multiLine) { + auto node = createBaseNode(SyntaxKind::AssertClause); + node->elements = createNodeArray(elements); + node->multiLine = multiLine; + node->transformFlags |= (int) TransformFlags::ContainsESNext; + return node; + } + // // @api // function updateAssertClause(node: AssertClause, elements: readonly AssertEntry[], multiLine?: boolean): AssertClause { // return node->elements != elements @@ -4325,15 +4410,15 @@ namespace ts::factory { // : node; // } // -// // @api -// function createAssertEntry(name: AssertionKey, value: Expression): AssertEntry { -// auto node = createBaseNode(SyntaxKind::AssertEntry); -// node->name = name; -// node->value = value; -// node->transformFlags |= (int)TransformFlags::ContainsESNext; -// return node; -// } -// + // @api + auto createAssertEntry(shared name, shared value) { + auto node = createBaseNode(SyntaxKind::AssertEntry); + node->name = name; + node->value = value; + node->transformFlags |= (int) TransformFlags::ContainsESNext; + return node; + } + // // @api // function updateAssertEntry(node: AssertEntry, name: AssertionKey, value: Expression): AssertEntry { // return node->name != name @@ -4341,15 +4426,15 @@ namespace ts::factory { // ? update(createAssertEntry(name, value), node) // : node; // } -// -// // @api -// function createImportTypeAssertionContainer(clause: AssertClause, multiLine?: boolean): ImportTypeAssertionContainer { -// auto node = createBaseNode(SyntaxKind::ImportTypeAssertionContainer); -// node->assertClause = clause; -// node->multiLine = multiLine; -// return node; -// } -// + + // @api + auto createImportTypeAssertionContainer(shared clause, bool multiLine) { + auto node = createBaseNode(SyntaxKind::ImportTypeAssertionContainer); + node->assertClause = clause; + node->multiLine = multiLine; + return node; + } + // // @api // function updateImportTypeAssertionContainer(node: ImportTypeAssertionContainer, clause: AssertClause, multiLine?: boolean): ImportTypeAssertionContainer { // return node->assertClause != clause @@ -4380,7 +4465,7 @@ namespace ts::factory { // node->name = name; // node->transformFlags |= // propagateChildFlags(node->name) | -// TransformFlags::ContainsESNext; +// (int)TransformFlags::ContainsESNext; // node->transformFlags &= ~TransformFlags::ContainsPossibleTopLevelAwait; // always parsed in an Await context // return node; // } @@ -4669,14 +4754,14 @@ namespace ts::factory { // } // // // @api -// function createJSDocTypeExpression(type: TypeNode): JSDocTypeExpression { +// function createJSDocTypeExpression(shared type): JSDocTypeExpression { // auto node = createBaseNode(SyntaxKind::JSDocTypeExpression); // node->type = type; // return node; // } // // // @api -// function updateJSDocTypeExpression(node: JSDocTypeExpression, type: TypeNode): JSDocTypeExpression { +// function updateJSDocTypeExpression(node: JSDocTypeExpression, shared type): JSDocTypeExpression { // return node->type != type // ? update(createJSDocTypeExpression(type), node) // : node; @@ -5443,7 +5528,7 @@ namespace ts::factory { // node->transformFlags |= // propagateChildFlags(node->name) | // propagateChildFlags(node->initializer) | -// TransformFlags::ContainsTypeScript; +// (int)TransformFlags::ContainsTypeScript; // return node; // } // @@ -5637,33 +5722,33 @@ namespace ts::factory { // setTextRange(node, original); // return node; // } -// -// /** -// * Creates a synthetic expression to act as a placeholder for a not-emitted expression in -// * order to preserve comments or sourcemap positions. -// * -// * @param expression The inner expression to emit. -// * @param original The original outer expression. -// */ -// // @api -// function createPartiallyEmittedExpression(shared expression, original?: Node) { -// auto node = createBaseNode(SyntaxKind::PartiallyEmittedExpression); -// node->expression = expression; -// node->original = original; -// node->transformFlags |= -// propagateChildFlags(node->expression) | -// TransformFlags::ContainsTypeScript; -// setTextRange(node, original); -// return node; -// } -// -// // @api -// function updatePartiallyEmittedExpression(node: PartiallyEmittedExpression, shared expression) { -// return node->expression != expression -// ? update(createPartiallyEmittedExpression(expression, node->original), node) -// : node; -// } -// + + /** + * Creates a synthetic expression to act as a placeholder for a not-emitted expression in + * order to preserve comments or sourcemap positions. + * + * @param expression The inner expression to emit. + * @param original The original outer expression. + */ + // @api + auto createPartiallyEmittedExpression(shared expression, sharedOpt original) { + auto node = createBaseNode(SyntaxKind::PartiallyEmittedExpression); + node->expression = expression; + node->original = original; + node->transformFlags |= + propagateChildFlags(node->expression) | + (int) TransformFlags::ContainsTypeScript; + setTextRange(node, original); + return node; + } + + // @api + auto updatePartiallyEmittedExpression(shared node, shared expression) { + return node->expression != expression + ? update(createPartiallyEmittedExpression(expression, node->original), node) + : node; + } + // function flattenCommaElements(node: Expression): Expression | readonly Expression[] { // if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node->original && !node->emitNode && !node->id) { // if (isCommaListExpression(node)) { @@ -5907,53 +5992,59 @@ namespace ts::factory { // let isAccessor = tryAddPropertyAssignment(properties, "get", attributes.get); // isAccessor = tryAddPropertyAssignment(properties, "set", attributes.set) || isAccessor; // -// Debug.assert(!(isData && isAccessor), "A PropertyDescriptor may not be both an accessor descriptor and a data descriptor."); +// Debug::asserts(!(isData && isAccessor), "A PropertyDescriptor may not be both an accessor descriptor and a data descriptor."); // return createObjectLiteralExpression(properties, !singleLine); // } -// -// function updateOuterExpression(shared outerExpression, shared expression) { -// switch (outerExpression.kind) { -// case SyntaxKind::ParenthesizedExpression: return updateParenthesizedExpression(outerExpression, expression); -// case SyntaxKind::TypeAssertionExpression: return updateTypeAssertion(outerExpression, outerExpression.type, expression); -// case SyntaxKind::AsExpression: return updateAsExpression(outerExpression, expression, outerExpression.type); -// case SyntaxKind::NonNullExpression: return updateNonNullExpression(outerExpression, expression); -// case SyntaxKind::PartiallyEmittedExpression: return updatePartiallyEmittedExpression(outerExpression, expression); -// } -// } -// -// /** -// * Determines whether a node is a parenthesized expression that can be ignored when recreating outer expressions. -// * -// * A parenthesized expression can be ignored when all of the following are true: -// * -// * - It's `pos` and `end` are not -1 -// * - It does not have a custom source map range -// * - It does not have a custom comment range -// * - It does not have synthetic leading or trailing comments -// * -// * If an outermost parenthesized expression is ignored, but the containing expression requires a parentheses around -// * the expression to maintain precedence, a new parenthesized expression should be created automatically when -// * the containing expression is created/updated. -// */ -// function isIgnorableParen(node: Expression) { -// return isParenthesizedExpression(node) -// && nodeIsSynthesized(node) -// && nodeIsSynthesized(getSourceMapRange(node)) -// && nodeIsSynthesized(getCommentRange(node)) -// && !some(getSyntheticLeadingComments(node)) -// && !some(getSyntheticTrailingComments(node)); -// } -// -// function restoreOuterExpressions(outerExpression: Expression | undefined, innerExpression: Expression, kinds = OuterExpressionKinds.All): Expression { -// if (outerExpression && isOuterExpression(outerExpression, kinds) && !isIgnorableParen(outerExpression)) { -// return updateOuterExpression( -// outerExpression, -// restoreOuterExpressions(outerExpression.expression, innerExpression) -// ); -// } -// return innerExpression; -// } -// + + shared updateOuterExpression(shared outerExpression, shared expression) { + switch (outerExpression->kind) { + case SyntaxKind::ParenthesizedExpression: + return updateParenthesizedExpression(to(outerExpression), expression); + case SyntaxKind::TypeAssertionExpression: + return updateTypeAssertion(to(outerExpression), to(outerExpression)->type, expression); + case SyntaxKind::AsExpression: + return updateAsExpression(to(outerExpression), expression, to(outerExpression)->type); + case SyntaxKind::NonNullExpression: + return updateNonNullExpression(to(outerExpression), expression); + case SyntaxKind::PartiallyEmittedExpression: + return updatePartiallyEmittedExpression(to(outerExpression), expression); + } + throw runtime_error("Invalid OuterExpression passed"); + } + + /** + * Determines whether a node is a parenthesized expression that can be ignored when recreating outer expressions. + * + * A parenthesized expression can be ignored when all of the following are true: + * + * - It's `pos` and `end` are not -1 + * - It does not have a custom source map range + * - It does not have a custom comment range + * - It does not have synthetic leading or trailing comments + * + * If an outermost parenthesized expression is ignored, but the containing expression requires a parentheses around + * the expression to maintain precedence, a new parenthesized expression should be created automatically when + * the containing expression is created/updated. + */ + bool isIgnorableParen(shared node) { + return isParenthesizedExpression(node) + && nodeIsSynthesized(node) + && nodeIsSynthesized(getSourceMapRange(node)) + && nodeIsSynthesized(getCommentRange(node)) + && !some(getSyntheticLeadingComments(node)) + && !some(getSyntheticTrailingComments(node)); + } + + shared restoreOuterExpressions(sharedOpt outerExpression, shared innerExpression, int kinds) { + if (isOuterExpression(outerExpression, kinds) && !isIgnorableParen(outerExpression)) { + return updateOuterExpression( + outerExpression, + restoreOuterExpressions(getExpression(outerExpression), innerExpression) + ); + } + return innerExpression; + } + // function restoreEnclosingLabel(node: Statement, outermostLabeledStatement: LabeledStatement | undefined, afterRestoreLabelCallback?: (node: LabeledStatement) => void): Statement { // if (!outermostLabeledStatement) { // return node; @@ -5995,7 +6086,7 @@ namespace ts::factory { // } // // function createCallBinding(shared expression, recordTempVariable: (temp: Identifier) => void, languageVersion?: ScriptTarget, cacheIdentifiers = false): CallBinding { -// auto callee = skipOuterExpressions(expression, OuterExpressionKinds.All); +// auto callee = skipOuterExpressions(expression, OuterExpressionKinds::All); // let thisArg: Expression; // let target: LeftHandSideExpression; // if (isSuperProperty(callee)) { @@ -6232,7 +6323,7 @@ namespace ts::factory { // * @returns Count of how many directive statements were copied. // */ // function copyStandardPrologue(source: readonly Statement[], target: Push, statementOffset = 0, ensureUseStrict?: boolean): number { -// Debug.assert(target.length == 0, "Prologue directives should be at the first statement in the target statements array"); +// Debug::asserts(target.length == 0, "Prologue directives should be at the first statement in the target statements array"); // let foundUseStrict = false; // auto numStatements = source.length; // while (statementOffset < numStatements) { @@ -6299,7 +6390,7 @@ namespace ts::factory { // * @param nodes The NodeArray. // */ // function liftToBlock(nodes: readonly Node[]): Statement { -// Debug.assert(every(nodes, isStatementOrBlock), "Cannot lift nodes to a Block."); +// Debug::asserts(every(nodes, isStatementOrBlock), "Cannot lift nodes to a Block."); // return singleOrUndefined(nodes) as Statement || createBlock(nodes as readonly Statement[]); // } // @@ -6357,7 +6448,7 @@ namespace ts::factory { // auto rightHoistedFunctionsEnd = findSpanEnd(declarations, isHoistedFunction, rightStandardPrologueEnd); // auto rightHoistedVariablesEnd = findSpanEnd(declarations, isHoistedVariableStatement, rightHoistedFunctionsEnd); // auto rightCustomPrologueEnd = findSpanEnd(declarations, isCustomPrologue, rightHoistedVariablesEnd); -// Debug.assert(rightCustomPrologueEnd == declarations.length, "Expected declarations to be valid standard or custom prologues"); +// Debug::asserts(rightCustomPrologueEnd == declarations.length, "Expected declarations to be valid standard or custom prologues"); // // // splice prologues from the right into the left. We do this in reverse order // // so that we don't need to recompute the index on the left when we insert items. @@ -6452,18 +6543,9 @@ namespace ts::factory { // // function asEmbeddedStatement(statement: T): T | EmptyStatement; // function asEmbeddedStatement(statement: T | undefined): T | EmptyStatement | undefined; -// function asEmbeddedStatement(statement: T | undefined): T | EmptyStatement | undefined { -// return statement && isNotEmittedStatement(statement) ? setTextRange(setOriginalNode(createEmptyStatement(), statement), statement) : statement; -// } -// } -// -// function updateWithoutOriginal(updated: T, original: T): T { -// if (updated != original) { -// setTextRange(updated, original); -// } -// return updated; + // } -// + // function updateWithOriginal(updated: T, original: T): T { // if (updated != original) { // setOriginalNode(updated, original); @@ -6658,14 +6740,14 @@ namespace ts::factory { // let oldFileOfCurrentEmit: boolean | undefined; // // if (!isString(textOrInputFiles)) { -// Debug.assert(mapPathOrType == "js" || mapPathOrType == "dts"); +// Debug::asserts(mapPathOrType == "js" || mapPathOrType == "dts"); // fileName = (mapPathOrType == "js" ? textOrInputFiles.javascriptPath : textOrInputFiles.declarationPath) || ""; // sourceMapPath = mapPathOrType == "js" ? textOrInputFiles.javascriptMapPath : textOrInputFiles.declarationMapPath; // getText = () => mapPathOrType == "js" ? textOrInputFiles.javascriptText : textOrInputFiles.declarationText; // getSourceMapText = () => mapPathOrType == "js" ? textOrInputFiles.javascriptMapText : textOrInputFiles.declarationMapText; // length = () => getText!().length; // if (textOrInputFiles.buildInfo && textOrInputFiles.buildInfo.bundle) { -// Debug.assert(mapTextOrStripInternal == undefined || typeof mapTextOrStripInternal == "boolean"); +// Debug::asserts(mapTextOrStripInternal == undefined || typeof mapTextOrStripInternal == "boolean"); // stripInternal = mapTextOrStripInternal; // bundleFileInfo = mapPathOrType == "js" ? textOrInputFiles.buildInfo.bundle.js : textOrInputFiles.buildInfo.bundle.dts; // oldFileOfCurrentEmit = textOrInputFiles.oldFileOfCurrentEmit; @@ -6689,7 +6771,7 @@ namespace ts::factory { // Object.defineProperty(node, "sourceMapText", { get: getSourceMapText }); // } // else { -// Debug.assert(!oldFileOfCurrentEmit); +// Debug::asserts(!oldFileOfCurrentEmit); // node->text = text ?? ""; // node->sourceMapText = sourceMapText; // } @@ -6926,45 +7008,6 @@ namespace ts::factory { // // // Utilities // -// export function setOriginalNode(node: T, original: Node | undefined): T { -// node->original = original; -// if (original) { -// auto emitNode = original.emitNode; -// if (emitNode) node->emitNode = mergeEmitNode(emitNode, node->emitNode); -// } -// return node; -// } -// -// function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode | undefined) { -// auto { -// flags, -// leadingComments, -// trailingComments, -// commentRange, -// sourceMapRange, -// tokenSourceMapRanges, -// constantValue, -// helpers, -// startsOnNewLine, -// } = sourceEmitNode; -// if (!destEmitNode) destEmitNode = {} as EmitNode; -// // We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later. -// if (leadingComments) destEmitNode.leadingComments = addRange(leadingComments.slice(), destEmitNode.leadingComments); -// if (trailingComments) destEmitNode.trailingComments = addRange(trailingComments.slice(), destEmitNode.trailingComments); -// if (flags) destEmitNode.flags = flags & ~EmitFlags.Immutable; -// if (commentRange) destEmitNode.commentRange = commentRange; -// if (sourceMapRange) destEmitNode.sourceMapRange = sourceMapRange; -// if (tokenSourceMapRanges) destEmitNode.tokenSourceMapRanges = mergeTokenSourceMapRanges(tokenSourceMapRanges, destEmitNode.tokenSourceMapRanges!); -// if (constantValue != undefined) destEmitNode.constantValue = constantValue; -// if (helpers) { -// for (auto helper of helpers) { -// destEmitNode.helpers = appendIfUnique(destEmitNode.helpers, helper); -// } -// } -// if (startsOnNewLine != undefined) destEmitNode.startsOnNewLine = startsOnNewLine; -// return destEmitNode; -// } -// // function mergeTokenSourceMapRanges(sourceRanges: (TextRange | undefined)[], destRanges: (TextRange | undefined)[]) { // if (!destRanges) destRanges = []; // for (auto key in sourceRanges) { diff --git a/src/node_test.h b/src/node_test.h index 8ab4c39..65b2c96 100644 --- a/src/node_test.h +++ b/src/node_test.h @@ -16,11 +16,19 @@ namespace ts { return node->emitNode ? node->emitNode->flags : 0; } + bool isCommaSequence(shared node) { + return node->kind == SyntaxKind::BinaryExpression && node->to().operatorToken->kind == SyntaxKind::CommaToken || node->kind == SyntaxKind::CommaListExpression; + } + + bool isAssignmentOperator(SyntaxKind token) { + return token >= SyntaxKind::FirstAssignment && token <= SyntaxKind::LastAssignment; + } + /** * Gets whether an identifier should only be referred to by its local name. */ bool isLocalName(const shared &node) { - return (getEmitFlags(node) & (int)EmitFlags::LocalName) != 0; + return (getEmitFlags(node) & (int) EmitFlags::LocalName) != 0; } ModifierFlags modifierToFlag(SyntaxKind token) { @@ -74,19 +82,19 @@ namespace ts { */ int getSyntacticModifierFlagsNoCache(shared node) { auto flags = modifiersToFlags(node->modifiers); - if (node->flags & (int)NodeFlags::NestedNamespace || (node->kind == SyntaxKind::Identifier && node->to().isInJSDocNamespace)) { - flags |= (int)ModifierFlags::Export; + if (node->flags & (int) NodeFlags::NestedNamespace || (node->kind == SyntaxKind::Identifier && node->to().isInJSDocNamespace)) { + flags |= (int) ModifierFlags::Export; } return flags; } int getModifierFlagsWorker(shared node, bool includeJSDoc, optional alwaysIncludeJSDoc = {}) { if (node->kind >= SyntaxKind::FirstToken && node->kind <= SyntaxKind::LastToken) { - return (int)ModifierFlags::None; + return (int) ModifierFlags::None; } - if (!(node->modifierFlagsCache & (int)ModifierFlags::HasComputedFlags)) { - node->modifierFlagsCache = getSyntacticModifierFlagsNoCache(node) | (int)ModifierFlags::HasComputedFlags; + if (!(node->modifierFlagsCache & (int) ModifierFlags::HasComputedFlags)) { + node->modifierFlagsCache = getSyntacticModifierFlagsNoCache(node) | (int) ModifierFlags::HasComputedFlags; } //we don't support JSDoc @@ -94,7 +102,7 @@ namespace ts { // node->modifierFlagsCache |= getJSDocModifierFlagsNoCache(node) | (int)ModifierFlags::HasComputedJSDocModifiers; // } - return node->modifierFlagsCache & ~((int)ModifierFlags::HasComputedFlags | (int)ModifierFlags::HasComputedJSDocModifiers); + return node->modifierFlagsCache & ~((int) ModifierFlags::HasComputedFlags | (int) ModifierFlags::HasComputedJSDocModifiers); } int getSyntacticModifierFlags(shared node) { @@ -110,7 +118,7 @@ namespace ts { } bool hasStaticModifier(shared node) { - return hasSyntacticModifier(node, (int)ModifierFlags::Static); + return hasSyntacticModifier(node, (int) ModifierFlags::Static); } shared resolveNameToNode(const shared &node) { @@ -406,7 +414,6 @@ namespace ts { return node->kind == SyntaxKind::Identifier && identifierIsThisKeyword(node->to()); } - /** * Determines whether a node is a property or element access expression for `super`. */ @@ -668,6 +675,10 @@ namespace ts { return node->kind == SyntaxKind::CallExpression; } + bool isCallChain(shared node) { + return isCallExpression(node) && !!(node->flags & (int)NodeFlags::OptionalChain); + } + bool isNewExpression(const shared &node) { return node->kind == SyntaxKind::NewExpression; } @@ -756,6 +767,10 @@ namespace ts { return node->kind == SyntaxKind::NonNullExpression; } + bool isNonNullChain(shared node) { + return isNonNullExpression(node) && !!(node->flags & (int)NodeFlags::OptionalChain); + } + bool isMetaProperty(const shared &node) { return node->kind == SyntaxKind::MetaProperty; } diff --git a/src/parser2.h b/src/parser2.h index 2ba4aeb..0f5ebd8 100644 --- a/src/parser2.h +++ b/src/parser2.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "types.h" @@ -922,7 +923,9 @@ namespace ts { /*ParsingContext*/ int parsingContext = 0; + // auto notParenthesizedArrow: Set | undefined; + std::set notParenthesizedArrow; // Flags that dictate what parsing context we're in. For example: // Whether or not we are in strict parsing mode. All that changes in strict parsing mode is @@ -1123,6 +1126,7 @@ namespace ts { parsingContext = 0; // identifiers = undefined!; // notParenthesizedArrow = undefined; + notParenthesizedArrow.clear(); topLevel = true; } @@ -1137,8 +1141,12 @@ namespace ts { // return hasJSDoc ? addJSDocComment(node) : node; } -// auto hasDeprecatedTag = false; -// function addJSDocComment(node: T): T { + auto hasDeprecatedTag = false; + + template + shared addJSDocComment(shared node) { + //no JSDoc support + return node; // Debug::asserts(!node.jsDoc); // Should only be called once per node // auto jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos)); // if (jsDoc.length) node.jsDoc = jsDoc; @@ -1147,8 +1155,8 @@ namespace ts { // reinterpret_cast&>(node).flags |= NodeFlags::Deprecated; // } // return node; -// } -// + } + // function reparseTopLevelAwait(sourceFile: SourceFile) { // auto savedSyntaxCursor = syntaxCursor; // auto baseSyntaxCursor = IncrementalParser.createSyntaxCursor(sourceFile); @@ -1419,7 +1427,7 @@ namespace ts { // If our callback returned something 'falsy' or we're just looking ahead, // then unconditionally restore us to where we were. - if (!result || (speculationKind != SpeculationKind::TryParse)) { + if (!(bool)result || (speculationKind != SpeculationKind::TryParse)) { currentToken = saveToken; if (speculationKind != SpeculationKind::Reparse) { parseDiagnostics.resize(saveParseDiagnosticsLength); @@ -3262,42 +3270,6 @@ namespace ts { return createNodeArray(list, pos); } -// -// function parseTemplateType(): TemplateLiteralTypeNode { -// auto pos = getNodePos(); -// return finishNode( -// factory::createTemplateLiteralType( -// parseTemplateHead(/*isTaggedTemplate*/ false), -// parseTemplateTypeSpans() -// ), -// pos -// ); -// } -// -// function parseTemplateTypeSpans() { -// auto pos = getNodePos(); -// auto list = []; -// auto node: TemplateLiteralTypeSpan; -// do { -// node = parseTemplateTypeSpan(); -// list.push(node); -// } -// while (node.literal.kind == SyntaxKind::TemplateMiddle); -// return createNodeArray(list, pos); -// } -// -// function parseTemplateTypeSpan(): TemplateLiteralTypeSpan { -// auto pos = getNodePos(); -// return finishNode( -// factory::createTemplateLiteralTypeSpan( -// parseType(), -// parseLiteralOfTemplateSpan(/*isTaggedTemplate*/ false) -// ), -// pos -// ); -// } -// - shared parseTemplateHead(bool isTaggedTemplate) { if (isTaggedTemplate) { reScanTemplateHeadOrNoSubstitutionTemplate(); @@ -3370,38 +3342,32 @@ namespace ts { ); } -// // If true, we should abort parsing an error function. -// function typeHasArrowFunctionBlockingParseError(node: TypeNode): boolean { -// switch (node.kind) { -// case SyntaxKind::TypeReference: -// return nodeIsMissing(reinterpret_cast(node).typeName); -// case SyntaxKind::FunctionType: -// case SyntaxKind::ConstructorType: { -// auto { parameters, type } = node as FunctionOrConstructorTypeNode; -// return isMissingList(parameters) || typeHasArrowFunctionBlockingParseError(type); -// } -// case SyntaxKind::ParenthesizedType: -// return typeHasArrowFunctionBlockingParseError(reinterpret_cast(node).type); -// default: -// return false; -// } -// } -// -// function parseThisTypePredicate(lhs: ThisTypeNode): TypePredicateNode { -// nextToken(); -// return finishNode(factory::createTypePredicateNode(/*assertsModifier*/ undefined, lhs, parseType()), lhs.pos); -// } -// -// function parseThisTypeNode(): ThisTypeNode { -// auto pos = getNodePos(); -// nextToken(); -// return finishNode(factory::createThisTypeNode(), pos); -// } + // If true, we should abort parsing an error function. + bool typeHasArrowFunctionBlockingParseError(shared node) { + switch (node->kind) { + case SyntaxKind::TypeReference: + return nodeIsMissing(node->to().typeName); + case SyntaxKind::FunctionType: + case SyntaxKind::ConstructorType: { + auto f = dynamic_pointer_cast(node); + return isMissingList(f->parameters) || typeHasArrowFunctionBlockingParseError(f->type); + } + case SyntaxKind::ParenthesizedType: + return typeHasArrowFunctionBlockingParseError(reinterpret_cast(node).type); + default: + return false; + } + } - shared parseJSDocAllType() { + shared parseThisTypePredicate(shared lhs) { + nextToken(); + return finishNode(factory::createTypePredicateNode(/*assertsModifier*/ {}, lhs, parseType()), lhs->pos); + } + + shared parseThisTypeNode() { auto pos = getNodePos(); nextToken(); - return finishNode(factory::createJSDocAllType(), pos); + return finishNode(factory::createThisTypeNode(), pos); } // function parseJSDocNonNullableType(): TypeNode { @@ -3506,16 +3472,18 @@ namespace ts { // } // return type; // } -// -// function parseTypeQuery(): TypeQueryNode { -// auto pos = getNodePos(); -// parseExpected(SyntaxKind::TypeOfKeyword); -// auto entityName = parseEntityName(/*allowReservedWords*/ true); -// // Make sure we perform ASI to prevent parsing the next line's type arguments as part of an instantiation expression. -// auto typeArguments = !scanner.hasPrecedingLineBreak() ? tryParseTypeArguments() : undefined; -// return finishNode(factory::createTypeQueryNode(entityName, typeArguments), pos); -// } -// + + optional tryParseTypeArguments(); + + shared parseTypeQuery() { + auto pos = getNodePos(); + parseExpected(SyntaxKind::TypeOfKeyword); + auto entityName = parseEntityName(/*allowReservedWords*/ true); + // Make sure we perform ASI to prevent parsing the next line's type arguments as part of an instantiation expression. + optional typeArguments = !scanner.hasPrecedingLineBreak() ? tryParseTypeArguments() : nullopt; + return finishNode(factory::createTypeQueryNode(entityName, typeArguments), pos); + } + shared parseLeftHandSideExpressionOrHigher(); bool isTemplateStartOfTaggedTemplate() { @@ -5867,6 +5835,254 @@ namespace ts { return token() == SyntaxKind::DotToken ? nullptr : node; } + + shared parseJSDocAllType() { + throw runtime_error("JSDoc not supported"); +// auto pos = getNodePos(); +// nextToken(); +// return finishNode(factory::createJSDocAllType(), pos); + } + + shared parseLiteralTypeNode(optional negative = {}) { + auto pos = getNodePos(); + if (negative && *negative) { + nextToken(); + } + shared expression = + token() == SyntaxKind::TrueKeyword || token() == SyntaxKind::FalseKeyword || token() == SyntaxKind::NullKeyword ? + parseTokenNode() : + parseLiteralLikeNode(token()); + if (negative) { + expression = finishNode(factory::createPrefixUnaryExpression(SyntaxKind::MinusToken, expression), pos); + } + return finishNode(factory::createLiteralTypeNode(expression), pos); + } + + bool isStartOfTypeOfImportType() { + nextToken(); + return token() == SyntaxKind::ImportKeyword; + } + + shared parseAssertEntry() { + auto pos = getNodePos(); + shared name = tokenIsIdentifierOrKeyword(token()) ? (shared)parseIdentifierName() : (shared)parseLiteralLikeNode(SyntaxKind::StringLiteral); + parseExpected(SyntaxKind::ColonToken); + auto value = parseAssignmentExpressionOrHigher(); + return finishNode(factory::createAssertEntry(name, value), pos); + } + + shared parseAssertClause(optional skipAssertKeyword = {}) { + auto pos = getNodePos(); + if (!isTrue(skipAssertKeyword)) { + parseExpected(SyntaxKind::AssertKeyword); + } + auto openBracePosition = scanner.getTokenPos(); + if (parseExpected(SyntaxKind::OpenBraceToken)) { + auto multiLine = scanner.hasPrecedingLineBreak(); + auto elements = parseDelimitedList(ParsingContext::AssertEntries, parseAssertEntry, /*considerSemicolonAsDelimiter*/ true); + if (!parseExpected(SyntaxKind::CloseBraceToken)) { + auto lastError = lastOrUndefined(parseDiagnostics); + if (lastError && lastError->code == Diagnostics::_0_expected.code) { + addRelatedInfo( + *lastError, + {createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics::The_parser_expected_to_find_a_1_to_match_the_0_token_here, {"{", "}"})} + ); + } + } + return finishNode(factory::createAssertClause(*elements, multiLine), pos); + } + else { + auto elements = createNodeArray({}, getNodePos(), /*end*/ {}, /*hasTrailingComma*/ false); + return finishNode(factory::createAssertClause(elements, /*multiLine*/ false), pos); + } + } + + shared parseImportTypeAssertions() { + auto pos = getNodePos(); + auto openBracePosition = scanner.getTokenPos(); + parseExpected(SyntaxKind::OpenBraceToken); + auto multiLine = scanner.hasPrecedingLineBreak(); + parseExpected(SyntaxKind::AssertKeyword); + parseExpected(SyntaxKind::ColonToken); + auto clause = parseAssertClause(/*skipAssertKeyword*/ true); + if (!parseExpected(SyntaxKind::CloseBraceToken)) { + auto lastError = lastOrUndefined(parseDiagnostics); + if (lastError && lastError->code == Diagnostics::_0_expected.code) { + addRelatedInfo( + *lastError, + {createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics::The_parser_expected_to_find_a_1_to_match_the_0_token_here, {"{", "}"})} + ); + } + } + return finishNode(factory::createImportTypeAssertionContainer(clause, multiLine), pos); + } + + shared parseImportType() { + sourceFlags |= (int)NodeFlags::PossiblyContainsDynamicImport; + auto pos = getNodePos(); + auto isTypeOf = parseOptional(SyntaxKind::TypeOfKeyword); + parseExpected(SyntaxKind::ImportKeyword); + parseExpected(SyntaxKind::OpenParenToken); + auto type = parseType(); + sharedOpt assertions; + if (parseOptional(SyntaxKind::CommaToken)) { + assertions = parseImportTypeAssertions(); + } + parseExpected(SyntaxKind::CloseParenToken); + sharedOpt qualifier = parseOptional(SyntaxKind::DotToken) ? parseEntityNameOfTypeReference() : nullptr; + auto typeArguments = parseTypeArgumentsOfTypeReference(); + return finishNode(factory::createImportTypeNode(type, assertions, qualifier, typeArguments, isTypeOf), pos); + } + + bool isStartOfMappedType() { + nextToken(); + if (token() == SyntaxKind::PlusToken || token() == SyntaxKind::MinusToken) { + return nextToken() == SyntaxKind::ReadonlyKeyword; + } + if (token() == SyntaxKind::ReadonlyKeyword) { + nextToken(); + } + return token() == SyntaxKind::OpenBracketToken && nextTokenIsIdentifier() && nextToken() == SyntaxKind::InKeyword; + } + + shared parseMappedTypeParameter() { + auto pos = getNodePos(); + auto name = parseIdentifierName(); + parseExpected(SyntaxKind::InKeyword); + auto type = parseType(); + return finishNode(factory::createTypeParameterDeclaration(/*modifiers*/ {}, name, type, /*defaultType*/ {}), pos); + } + + shared parseMappedType() { + auto pos = getNodePos(); + parseExpected(SyntaxKind::OpenBraceToken); + sharedOpt readonlyToken; //: ReadonlyKeyword | PlusToken | MinusToken | undefined; + if (token() == SyntaxKind::ReadonlyKeyword || token() == SyntaxKind::PlusToken || token() == SyntaxKind::MinusToken) { + readonlyToken = parseTokenNode(); + if (readonlyToken->kind != SyntaxKind::ReadonlyKeyword) { + parseExpected(SyntaxKind::ReadonlyKeyword); + } + } + parseExpected(SyntaxKind::OpenBracketToken); + auto typeParameter = parseMappedTypeParameter(); + sharedOpt nameType = parseOptional(SyntaxKind::AsKeyword) ? parseType() : nullptr; + parseExpected(SyntaxKind::CloseBracketToken); + sharedOpt questionToken; //: QuestionToken | PlusToken | MinusToken | undefined; + if (token() == SyntaxKind::QuestionToken || token() == SyntaxKind::PlusToken || token() == SyntaxKind::MinusToken) { + questionToken = parseTokenNode(); + if (questionToken->kind != SyntaxKind::QuestionToken) { + parseExpected(SyntaxKind::QuestionToken); + } + } + auto type = parseTypeAnnotation(); + parseSemicolon(); + auto members = parseList(ParsingContext::TypeMembers, parseTypeMember); + parseExpected(SyntaxKind::CloseBraceToken); + return finishNode(factory::createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members), pos); + } + + bool isNextTokenColonOrQuestionColon() { + return nextToken() == SyntaxKind::ColonToken || (token() == SyntaxKind::QuestionToken && nextToken() == SyntaxKind::ColonToken); + } + + bool isTupleElementName() { + if (token() == SyntaxKind::DotDotDotToken) { + return tokenIsIdentifierOrKeyword(nextToken()) && isNextTokenColonOrQuestionColon(); + } + return tokenIsIdentifierOrKeyword(token()) && isNextTokenColonOrQuestionColon(); + } + + shared parseTupleElementType() { + auto pos = getNodePos(); + if (parseOptional(SyntaxKind::DotDotDotToken)) { + return finishNode(factory::createRestTypeNode(parseType()), pos); + } + auto type = parseType(); + //no JSDoc support +// if (isJSDocNullableType(type) && type->pos == type->type->pos) { +// auto node = factory::createOptionalTypeNode(type->type); +// setTextRange(node, type); +// Node->flags = type->flags; +// return node; +// } + return type; + } + + shared parseTupleElementNameOrTupleElementType() { + if (lookAhead(isTupleElementName)) { + auto pos = getNodePos(); + auto hasJSDoc = hasPrecedingJSDocComment(); + auto dotDotDotToken = parseOptionalToken(SyntaxKind::DotDotDotToken); + auto name = parseIdentifierName(); + auto questionToken = parseOptionalToken(SyntaxKind::QuestionToken); + parseExpected(SyntaxKind::ColonToken); + auto type = parseTupleElementType(); + auto node = factory::createNamedTupleMember(dotDotDotToken, name, questionToken, type); + return withJSDoc(finishNode(node, pos), hasJSDoc); + } + return parseTupleElementType(); + } + + shared parseTupleType() { + auto pos = getNodePos(); + return finishNode( + factory::createTupleTypeNode( + parseBracketedList(ParsingContext::TupleElementTypes, parseTupleElementNameOrTupleElementType, SyntaxKind::OpenBracketToken, SyntaxKind::CloseBracketToken) + ), + pos + ); + } + + shared parseParenthesizedType() { + auto pos = getNodePos(); + parseExpected(SyntaxKind::OpenParenToken); + auto type = parseType(); + parseExpected(SyntaxKind::CloseParenToken); + return finishNode(factory::createParenthesizedType(type), pos); + } + + shared parseAssertsTypePredicate() { + auto pos = getNodePos(); + auto assertsModifier = parseExpectedToken(SyntaxKind::AssertsKeyword); + shared parameterName = token() == SyntaxKind::ThisKeyword ? (shared)parseThisTypeNode() : (shared)parseIdentifier(); + sharedOpt type = parseOptional(SyntaxKind::IsKeyword) ? parseType() : nullptr; + return finishNode(factory::createTypePredicateNode(assertsModifier, parameterName, type), pos); + } + + shared parseTemplateTypeSpan() { + auto pos = getNodePos(); + return finishNode( + factory::createTemplateLiteralTypeSpan( + parseType(), + parseLiteralOfTemplateSpan(/*isTaggedTemplate*/ false) + ), + pos + ); + } + + NodeArray parseTemplateTypeSpans() { + auto pos = getNodePos(); + vector> list; + shared node; + do { + node = parseTemplateTypeSpan(); + list.push_back(node); + } + while (node->literal->kind == SyntaxKind::TemplateMiddle); + return createNodeArray(list, pos); + } + + shared parseTemplateType() { + auto pos = getNodePos(); + return finishNode( + factory::createTemplateLiteralType( + parseTemplateHead(/*isTaggedTemplate*/ false), + parseTemplateTypeSpans() + ), + pos + ); + } + shared parseNonArrayType() { switch (token()) { case SyntaxKind::AnyKeyword: @@ -5878,9 +6094,11 @@ namespace ts { case SyntaxKind::BooleanKeyword: case SyntaxKind::UndefinedKeyword: case SyntaxKind::NeverKeyword: - case SyntaxKind::ObjectKeyword: + case SyntaxKind::ObjectKeyword: { // If these are followed by a dot, then parse these out as a dotted type reference instead. - return tryParse>(parseKeywordAndNoDot) || parseTypeReference(); + if (auto a = tryParse>(parseKeywordAndNoDot)) return a; + return parseTypeReference(); + } case SyntaxKind::AsteriskEqualsToken: // If there is '*=', treat it as * followed by postfix = scanner.reScanAsteriskEqualsToken(); @@ -5892,11 +6110,14 @@ namespace ts { scanner.reScanQuestionToken(); // falls through case SyntaxKind::QuestionToken: - return parseJSDocUnknownOrNullableType(); + throw runtime_error("JSDoc not supported"); +// return parseJSDocUnknownOrNullableType(); case SyntaxKind::FunctionKeyword: - return parseJSDocFunctionType(); + throw runtime_error("JSDoc not supported"); +// return parseJSDocFunctionType(); case SyntaxKind::ExclamationToken: - return parseJSDocNonNullableType(); + throw runtime_error("JSDoc not supported"); +// return parseJSDocNonNullableType(); case SyntaxKind::NoSubstitutionTemplateLiteral: case SyntaxKind::StringLiteral: case SyntaxKind::NumericLiteral: @@ -5906,7 +6127,8 @@ namespace ts { case SyntaxKind::NullKeyword: return parseLiteralTypeNode(); case SyntaxKind::MinusToken: - return lookAhead(nextTokenIsNumericOrBigIntLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference(); + if (lookAhead(nextTokenIsNumericOrBigIntLiteral)) return parseLiteralTypeNode(/*negative*/ true); + return parseTypeReference(); case SyntaxKind::VoidKeyword: return parseTokenNode(); case SyntaxKind::ThisKeyword: { @@ -5919,9 +6141,11 @@ namespace ts { } } case SyntaxKind::TypeOfKeyword: - return lookAhead(isStartOfTypeOfImportType) ? parseImportType() : parseTypeQuery(); + if (lookAhead(isStartOfTypeOfImportType)) return parseImportType(); + return parseTypeQuery(); case SyntaxKind::OpenBraceToken: - return lookAhead(isStartOfMappedType) ? parseMappedType() : parseTypeLiteral(); + if (lookAhead(isStartOfMappedType)) return parseMappedType(); + return parseTypeLiteral(); case SyntaxKind::OpenBracketToken: return parseTupleType(); case SyntaxKind::OpenParenToken: @@ -5929,7 +6153,8 @@ namespace ts { case SyntaxKind::ImportKeyword: return parseImportType(); case SyntaxKind::AssertsKeyword: - return lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine) ? parseAssertsTypePredicate() : parseTypeReference(); + if (lookAhead(nextTokenIsIdentifierOrKeywordOnSameLine)) return parseAssertsTypePredicate(); + return parseTypeReference(); case SyntaxKind::TemplateHead: return parseTemplateType(); default: @@ -5937,23 +6162,24 @@ namespace ts { } } - shared parsePostfixTypeOrHigher() { auto pos = getNodePos(); auto type = parseNonArrayType(); while (!scanner.hasPrecedingLineBreak()) { switch (token()) { case SyntaxKind::ExclamationToken: - nextToken(); - type = finishNode(factory::createJSDocNonNullableType(type, /*postfix*/ true), pos); + throw runtime_error("No JSDoc support"); +// nextToken(); +// type = finishNode(factory::createJSDocNonNullableType(type, /*postfix*/ true), pos); break; case SyntaxKind::QuestionToken: // If next token is start of a type we have a conditional type - if (lookAhead(nextTokenIsStartOfType)) { + if (lookAhead(nextTokenIsStartOfType)) { return type; } - nextToken(); - type = finishNode(factory::createJSDocNullableType(type, /*postfix*/ true), pos); + throw runtime_error("No JSDoc support"); +// nextToken(); +// type = finishNode(factory::createJSDocNullableType(type, /*postfix*/ true), pos); break; case SyntaxKind::OpenBracketToken: parseExpected(SyntaxKind::OpenBracketToken); @@ -5984,7 +6210,54 @@ namespace ts { case SyntaxKind::InferKeyword: return parseInferType(); } - return allowConditionalTypesAnd(parsePostfixTypeOrHigher); + return allowConditionalTypesAnd>(parsePostfixTypeOrHigher); + } + + sharedOpt parseFunctionOrConstructorTypeToError(bool isInUnionType) { + // the function type and constructor type shorthand notation + // are not allowed directly in unions and intersections, but we'll + // try to parse them gracefully and issue a helpful message. + if (isStartOfFunctionTypeOrConstructorType()) { + auto type = parseFunctionOrConstructorType(); + DiagnosticMessage diagnostic; + if (isFunctionTypeNode(type)) { + diagnostic = isInUnionType + ? Diagnostics::Function_type_notation_must_be_parenthesized_when_used_in_a_union_type + : Diagnostics::Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; + } + else { + diagnostic = isInUnionType + ? Diagnostics::Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type + : Diagnostics::Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; + + } + parseErrorAtRange(type, diagnostic); + return type; + } + return nullptr; + } + + shared parseUnionOrIntersectionType( + SyntaxKind operatorKind, + function()> parseConstituentType, + function(NodeArray)> createTypeNode //: (types: NodeArray) => UnionOrIntersectionTypeNode + ) { + auto pos = getNodePos(); + auto isUnionType = operatorKind == SyntaxKind::BarToken; + auto hasLeadingOperator = parseOptional(operatorKind); + sharedOpt type = hasLeadingOperator ? parseFunctionOrConstructorTypeToError(isUnionType) : parseConstituentType(); + if (token() == operatorKind || hasLeadingOperator) { + vector> types = {type}; + while (parseOptional(operatorKind)) { + if (auto a = parseFunctionOrConstructorTypeToError(isUnionType)) { + types.push_back(a); + } else { + types.push_back(parseConstituentType()); + } + } + type = finishNode(createTypeNode(createNodeArray(types, pos)), pos); + } + return type; } shared parseIntersectionTypeOrHigher() { @@ -6021,169 +6294,6 @@ namespace ts { return parseOptional(SyntaxKind::ColonToken) ? parseType() : nullptr; } -// function isStartOfMappedType() { -// nextToken(); -// if (token() == SyntaxKind::PlusToken || token() == SyntaxKind::MinusToken) { -// return nextToken() == SyntaxKind::ReadonlyKeyword; -// } -// if (token() == SyntaxKind::ReadonlyKeyword) { -// nextToken(); -// } -// return token() == SyntaxKind::OpenBracketToken && nextTokenIsIdentifier() && nextToken() == SyntaxKind::InKeyword; -// } -// -// function parseMappedTypeParameter() { -// auto pos = getNodePos(); -// auto name = parseIdentifierName(); -// parseExpected(SyntaxKind::InKeyword); -// auto type = parseType(); -// return finishNode(factory::createTypeParameterDeclaration(/*modifiers*/ undefined, name, type, /*defaultType*/ undefined), pos); -// } -// -// function parseMappedType() { -// auto pos = getNodePos(); -// parseExpected(SyntaxKind::OpenBraceToken); -// auto readonlyToken: ReadonlyKeyword | PlusToken | MinusToken | undefined; -// if (token() == SyntaxKind::ReadonlyKeyword || token() == SyntaxKind::PlusToken || token() == SyntaxKind::MinusToken) { -// readonlyToken = parseTokenNode(); -// if (readonlyToken.kind != SyntaxKind::ReadonlyKeyword) { -// parseExpected(SyntaxKind::ReadonlyKeyword); -// } -// } -// parseExpected(SyntaxKind::OpenBracketToken); -// auto typeParameter = parseMappedTypeParameter(); -// auto nameType = parseOptional(SyntaxKind::AsKeyword) ? parseType() : undefined; -// parseExpected(SyntaxKind::CloseBracketToken); -// auto questionToken: QuestionToken | PlusToken | MinusToken | undefined; -// if (token() == SyntaxKind::QuestionToken || token() == SyntaxKind::PlusToken || token() == SyntaxKind::MinusToken) { -// questionToken = parseTokenNode(); -// if (questionToken.kind != SyntaxKind::QuestionToken) { -// parseExpected(SyntaxKind::QuestionToken); -// } -// } -// auto type = parseTypeAnnotation(); -// parseSemicolon(); -// auto members = parseList(ParsingContext::TypeMembers, parseTypeMember); -// parseExpected(SyntaxKind::CloseBraceToken); -// return finishNode(factory::createMappedTypeNode(readonlyToken, typeParameter, nameType, questionToken, type, members), pos); -// } -// -// function parseTupleElementType() { -// auto pos = getNodePos(); -// if (parseOptional(SyntaxKind::DotDotDotToken)) { -// return finishNode(factory::createRestTypeNode(parseType()), pos); -// } -// auto type = parseType(); -// if (isJSDocNullableType(type) && type.pos == type.type.pos) { -// auto node = factory::createOptionalTypeNode(type.type); -// setTextRange(node, type); -// reinterpret_cast&>(node).flags = type.flags; -// return node; -// } -// return type; -// } -// -// function isNextTokenColonOrQuestionColon() { -// return nextToken() == SyntaxKind::ColonToken || (token() == SyntaxKind::QuestionToken && nextToken() == SyntaxKind::ColonToken); -// } -// -// function isTupleElementName() { -// if (token() == SyntaxKind::DotDotDotToken) { -// return tokenIsIdentifierOrKeyword(nextToken()) && isNextTokenColonOrQuestionColon(); -// } -// return tokenIsIdentifierOrKeyword(token()) && isNextTokenColonOrQuestionColon(); -// } -// -// function parseTupleElementNameOrTupleElementType() { -// if (lookAhead(isTupleElementName)) { -// auto pos = getNodePos(); -// auto hasJSDoc = hasPrecedingJSDocComment(); -// auto dotDotDotToken = parseOptionalToken(SyntaxKind::DotDotDotToken); -// auto name = parseIdentifierName(); -// auto questionToken = parseOptionalToken(SyntaxKind::QuestionToken); -// parseExpected(SyntaxKind::ColonToken); -// auto type = parseTupleElementType(); -// auto node = factory::createNamedTupleMember(dotDotDotToken, name, questionToken, type); -// return withJSDoc(finishNode(node, pos), hasJSDoc); -// } -// return parseTupleElementType(); -// } -// -// function parseTupleType(): TupleTypeNode { -// auto pos = getNodePos(); -// return finishNode( -// factory::createTupleTypeNode( -// parseBracketedList(ParsingContext::TupleElementTypes, parseTupleElementNameOrTupleElementType, SyntaxKind::OpenBracketToken, SyntaxKind::CloseBracketToken) -// ), -// pos -// ); -// } -// -// function parseParenthesizedType(): TypeNode { -// auto pos = getNodePos(); -// parseExpected(SyntaxKind::OpenParenToken); -// auto type = parseType(); -// parseExpected(SyntaxKind::CloseParenToken); -// return finishNode(factory::createParenthesizedType(type), pos); -// } -// -// function parseLiteralTypeNode(negative?: boolean): LiteralTypeNode { -// auto pos = getNodePos(); -// if (negative) { -// nextToken(); -// } -// auto expression: BooleanLiteral | NullLiteral | LiteralExpression | PrefixUnaryExpression = -// token() == SyntaxKind::TrueKeyword || token() == SyntaxKind::FalseKeyword || token() == SyntaxKind::NullKeyword ? -// parseTokenNode() : -// parseLiteralLikeNode(token()) as LiteralExpression; -// if (negative) { -// expression = finishNode(factory::createPrefixUnaryExpression(SyntaxKind::MinusToken, expression), pos); -// } -// return finishNode(factory::createLiteralTypeNode(expression), pos); -// } -// -// function isStartOfTypeOfImportType() { -// nextToken(); -// return token() == SyntaxKind::ImportKeyword; -// } -// -// function parseImportTypeAssertions(): ImportTypeAssertionContainer { -// auto pos = getNodePos(); -// auto openBracePosition = scanner.getTokenPos(); -// parseExpected(SyntaxKind::OpenBraceToken); -// auto multiLine = scanner.hasPrecedingLineBreak(); -// parseExpected(SyntaxKind::AssertKeyword); -// parseExpected(SyntaxKind::ColonToken); -// auto clause = parseAssertClause(/*skipAssertKeyword*/ true); -// if (!parseExpected(SyntaxKind::CloseBraceToken)) { -// auto lastError = lastOrUndefined(parseDiagnostics); -// if (lastError && lastError.code == Diagnostics::_0_expected.code) { -// addRelatedInfo( -// lastError, -// createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics::The_parser_expected_to_find_a_1_to_match_the_0_token_here, "{", "}") -// ); -// } -// } -// return finishNode(factory::createImportTypeAssertionContainer(clause, multiLine), pos); -// } -// -// function parseImportType(): ImportTypeNode { -// sourceFlags |= NodeFlags::PossiblyContainsDynamicImport; -// auto pos = getNodePos(); -// auto isTypeOf = parseOptional(SyntaxKind::TypeOfKeyword); -// parseExpected(SyntaxKind::ImportKeyword); -// parseExpected(SyntaxKind::OpenParenToken); -// auto type = parseType(); -// auto assertions: ImportTypeAssertionContainer | undefined; -// if (parseOptional(SyntaxKind::CommaToken)) { -// assertions = parseImportTypeAssertions(); -// } -// parseExpected(SyntaxKind::CloseParenToken); -// auto qualifier = parseOptional(SyntaxKind::DotToken) ? parseEntityNameOfTypeReference() : undefined; -// auto typeArguments = parseTypeArgumentsOfTypeReference(); -// return finishNode(factory::createImportTypeNode(type, assertions, qualifier, typeArguments, isTypeOf), pos); -// } -// // function isStartOfType(inStartOfParameter?: boolean): boolean { // switch (token()) { // case SyntaxKind::AnyKeyword: @@ -6235,52 +6345,6 @@ namespace ts { // return isIdentifier(); // } // } -// -// function parseFunctionOrConstructorTypeToError( -// isInUnionType: boolean -// ): TypeNode | undefined { -// // the function type and constructor type shorthand notation -// // are not allowed directly in unions and intersections, but we'll -// // try to parse them gracefully and issue a helpful message. -// if (isStartOfFunctionTypeOrConstructorType()) { -// auto type = parseFunctionOrConstructorType(); -// auto diagnostic: DiagnosticMessage; -// if (isFunctionTypeNode(type)) { -// diagnostic = isInUnionType -// ? Diagnostics::Function_type_notation_must_be_parenthesized_when_used_in_a_union_type -// : Diagnostics::Function_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; -// } -// else { -// diagnostic = isInUnionType -// ? Diagnostics::Constructor_type_notation_must_be_parenthesized_when_used_in_a_union_type -// : Diagnostics::Constructor_type_notation_must_be_parenthesized_when_used_in_an_intersection_type; -// -// } -// parseErrorAtRange(type, diagnostic); -// return type; -// } -// return undefined; -// } -// -// function parseUnionOrIntersectionType( -// operator: SyntaxKind::BarToken | SyntaxKind::AmpersandToken, -// parseConstituentType: () => TypeNode, -// createTypeNode: (types: NodeArray) => UnionOrIntersectionTypeNode -// ): TypeNode { -// auto pos = getNodePos(); -// auto isUnionType = operator == SyntaxKind::BarToken; -// auto hasLeadingOperator = parseOptional(operator); -// auto type = hasLeadingOperator && parseFunctionOrConstructorTypeToError(isUnionType) -// || parseConstituentType(); -// if (token() == operator || hasLeadingOperator) { -// auto types = [type]; -// while (parseOptional(operator)) { -// types.push(parseFunctionOrConstructorTypeToError(isUnionType) || parseConstituentType()); -// } -// type = finishNode(createTypeNode(createNodeArray(types, pos)), pos); -// } -// return type; -// } // sharedOpt parseTypePredicatePrefix() { auto id = parseIdentifier(); @@ -6302,15 +6366,6 @@ namespace ts { return type; } } -// -// function parseAssertsTypePredicate(): TypeNode { -// auto pos = getNodePos(); -// auto assertsModifier = parseExpectedToken(SyntaxKind::AssertsKeyword); -// auto parameterName = token() == SyntaxKind::ThisKeyword ? parseThisTypeNode() : parseIdentifier(); -// auto type = parseOptional(SyntaxKind::IsKeyword) ? parseType() : undefined; -// return finishNode(factory::createTypePredicateNode(assertsModifier, parameterName, type), pos); -// } - shared parseAssignmentExpressionOrHigher(); shared makeBinaryExpression(shared left, shared operatorNode, shared right, int pos) { @@ -6332,7 +6387,7 @@ namespace ts { auto pos = getNodePos(); auto expr = parseAssignmentExpressionOrHigher(); sharedOpt operatorToken; - while ((operatorToken = parseOptionalToken(SyntaxKind::CommaToken))) { + while ((operatorToken = parseOptionalToken(SyntaxKind::CommaToken))) { expr = makeBinaryExpression(expr, operatorToken, parseAssignmentExpressionOrHigher(), pos); } @@ -6388,7 +6443,7 @@ namespace ts { (token() == SyntaxKind::AsteriskToken || isStartOfExpression())) { return finishNode( factory::createYieldExpression( - parseOptionalToken(SyntaxKind::AsteriskToken), + parseOptionalToken(SyntaxKind::AsteriskToken), parseAssignmentExpressionOrHigher() ), pos @@ -6532,7 +6587,7 @@ namespace ts { // Speculatively look ahead to be sure, and rollback if not. Tristate isParenthesizedArrowFunctionExpression() { if (token() == SyntaxKind::OpenParenToken || token() == SyntaxKind::LessThanToken || token() == SyntaxKind::AsyncKeyword) { - return lookAhead(isParenthesizedArrowFunctionExpressionWorker); + return lookAhead(isParenthesizedArrowFunctionExpressionWorker); } if (token() == SyntaxKind::EqualsGreaterThanToken) { @@ -6555,9 +6610,45 @@ namespace ts { return nullopt; } + shared parseArrowFunctionExpressionBody(bool isAsync) { + if (token() == SyntaxKind::OpenBraceToken) { + return parseFunctionBlock(isAsync ? (int)SignatureFlags::Await : (int)SignatureFlags::None); + } + + if (token() != SyntaxKind::SemicolonToken && + token() != SyntaxKind::FunctionKeyword && + token() != SyntaxKind::ClassKeyword && + isStartOfStatement() && + !isStartOfExpressionStatement()) { + // Check if we got a plain statement (i.e. no expression-statements, no function/class expressions/declarations) + // + // Here we try to recover from a potential error situation in the case where the + // user meant to supply a block. For example, if the user wrote: + // + // a => + // auto v = 0; + // } + // + // they may be missing an open brace. Check to see if that's the case so we can + // try to recover better. If we don't do this, then the next close curly we see may end + // up preemptively closing the containing construct. + // + // Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error. + return parseFunctionBlock((int)SignatureFlags::IgnoreMissingOpenBrace | (isAsync ? (int)SignatureFlags::Await : (int)SignatureFlags::None)); + } + + auto savedTopLevel = topLevel; + topLevel = false; + auto node = isAsync + ? doInAwaitContext>(parseAssignmentExpressionOrHigher) + : doOutsideOfAwaitContext>(parseAssignmentExpressionOrHigher); + topLevel = savedTopLevel; + return node; + } + sharedOpt parseParenthesizedArrowFunctionExpression(bool allowAmbiguity) { auto pos = getNodePos(); -// auto hasJSDoc = hasPrecedingJSDocComment(); + auto hasJSDoc = hasPrecedingJSDocComment(); auto modifiers = parseModifiersForArrowFunction(); auto isAsync = some(modifiers, isAsyncModifier) ? SignatureFlags::Await : SignatureFlags::None; // Arrow functions are never generators. @@ -6572,27 +6663,27 @@ namespace ts { NodeArray parameters; if (!parseExpected(SyntaxKind::OpenParenToken)) { if (!allowAmbiguity) { - return undefined; + return nullptr; } - parameters = createMissingList < ParameterDeclaration > (); + parameters = createMissingList(); } else { if (!allowAmbiguity) { - auto maybeParameters = parseParametersWorker(isAsync, allowAmbiguity); + auto maybeParameters = parseParametersWorker((int)isAsync, allowAmbiguity); if (!maybeParameters) { - return undefined; + return nullptr; } - parameters = maybeParameters; + parameters = *maybeParameters; } else { - parameters = parseParametersWorker(isAsync, allowAmbiguity); + parameters = *parseParametersWorker((int)isAsync, allowAmbiguity); } if (!parseExpected(SyntaxKind::CloseParenToken) && !allowAmbiguity) { - return undefined; + return nullptr; } } auto type = parseReturnType(SyntaxKind::ColonToken, /*isType*/ false); if (type && !allowAmbiguity && typeHasArrowFunctionBlockingParseError(type)) { - return undefined; + return nullptr; } // Parsing a signature isn't enough. @@ -6607,22 +6698,20 @@ namespace ts { // So we need just a bit of lookahead to ensure that it can only be a signature. auto unwrappedType = type; - while (unwrappedType ?.kind == SyntaxKind::ParenthesizedType) { - unwrappedType = (unwrappedType - as - ParenthesizedTypeNode).type; // Skip parens if need be + while (unwrappedType && unwrappedType->kind == SyntaxKind::ParenthesizedType) { + unwrappedType = unwrappedType->to().type; // Skip parens if need be } auto hasJSDocFunctionType = unwrappedType && isJSDocFunctionType(unwrappedType); if (!allowAmbiguity && token() != SyntaxKind::EqualsGreaterThanToken && (hasJSDocFunctionType || token() != SyntaxKind::OpenBraceToken)) { // Returning undefined here will cause our caller to rewind to where we started from. - return undefined; + return nullptr; } // If we have an arrow, then try to parse the body. Even if not, try to parse if we // have an opening brace, just in case we're in an error state. auto lastToken = token(); - auto equalsGreaterThanToken = parseExpectedToken(SyntaxKind::EqualsGreaterThanToken); + auto equalsGreaterThanToken = parseExpectedToken(SyntaxKind::EqualsGreaterThanToken); auto body = (lastToken == SyntaxKind::EqualsGreaterThanToken || lastToken == SyntaxKind::OpenBraceToken) ? parseArrowFunctionExpressionBody(some(modifiers, isAsyncModifier)) : parseIdentifier(); @@ -6631,6 +6720,20 @@ namespace ts { return withJSDoc(finishNode(node, pos), hasJSDoc); } + sharedOpt parsePossibleParenthesizedArrowFunctionExpression() { + auto tokenPos = scanner.getTokenPos(); + if (has(notParenthesizedArrow, tokenPos)) { + return nullptr; + } + + auto result = parseParenthesizedArrowFunctionExpression(/*allowAmbiguity*/ false); + if (!result) { + notParenthesizedArrow.insert(tokenPos); + } + + return result; + } + sharedOpt tryParseParenthesizedArrowFunctionExpression() { auto triState = isParenthesizedArrowFunctionExpression(); if (triState == Tristate::False) { @@ -6644,7 +6747,86 @@ namespace ts { // expression instead. return triState == Tristate::True ? parseParenthesizedArrowFunctionExpression(/*allowAmbiguity*/ true) : - tryParse(parsePossibleParenthesizedArrowFunctionExpression); + tryParse>(parsePossibleParenthesizedArrowFunctionExpression); + } + + Tristate isUnParenthesizedAsyncArrowFunctionWorker() { + // AsyncArrowFunctionExpression: + // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] + // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] + if (token() == SyntaxKind::AsyncKeyword) { + nextToken(); + // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function + // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" + if (scanner.hasPrecedingLineBreak() || token() == SyntaxKind::EqualsGreaterThanToken) { + return Tristate::False; + } + // Check for un-parenthesized AsyncArrowFunction + auto expr = parseBinaryExpressionOrHigher((int)OperatorPrecedence::Lowest); + if (!scanner.hasPrecedingLineBreak() && expr->kind == SyntaxKind::Identifier && token() == SyntaxKind::EqualsGreaterThanToken) { + return Tristate::True; + } + } + + return Tristate::False; + } + + shared parseSimpleArrowFunctionExpression(int pos, shared identifier, optional asyncModifier) { + Debug::asserts(token() == SyntaxKind::EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); + auto parameter = factory::createParameterDeclaration( + /*decorators*/ {}, + /*modifiers*/ {}, + /*dotDotDotToken*/ {}, + identifier, + /*questionToken*/ {}, + /*type*/ {}, + /*initializer*/ {} + ); + finishNode(parameter, identifier->pos); + + auto parameters = createNodeArray({parameter}, parameter->pos, parameter->end); + + auto equalsGreaterThanToken = parseExpectedToken(SyntaxKind::EqualsGreaterThanToken); + auto body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier); + auto node = factory::createArrowFunction(asyncModifier, /*typeParameters*/ {}, parameters, /*type*/ {}, equalsGreaterThanToken, body); + return addJSDocComment(finishNode(node, pos)); + } + + sharedOpt tryParseAsyncSimpleArrowFunctionExpression() { + // We do a check here so that we won't be doing unnecessarily call to "lookAhead" + if (token() == SyntaxKind::AsyncKeyword) { + if (lookAhead(isUnParenthesizedAsyncArrowFunctionWorker) == Tristate::True) { + auto pos = getNodePos(); + auto asyncModifier = parseModifiersForArrowFunction(); + auto expr = parseBinaryExpressionOrHigher((int)OperatorPrecedence::Lowest); + return parseSimpleArrowFunctionExpression(pos, to(expr), asyncModifier); + } + } + return nullptr; + } + + shared parseConditionalExpressionRest(shared leftOperand, int pos) { + // Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher. + auto questionToken = parseOptionalToken(SyntaxKind::QuestionToken); + if (!questionToken) { + return leftOperand; + } + + // Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and + // we do not that for the 'whenFalse' part. + sharedOpt colonToken; + return finishNode( + factory::createConditionalExpression( + leftOperand, + questionToken, + doOutsideOfContext>(disallowInAndDecoratorContext, parseAssignmentExpressionOrHigher), + colonToken = parseExpectedToken(SyntaxKind::ColonToken), + nodeIsPresent(colonToken) + ? parseAssignmentExpressionOrHigher() + : createMissingNode(SyntaxKind::Identifier, /*reportAtCurrentPosition*/ false, Diagnostics::_0_expected, tokenToString(SyntaxKind::ColonToken)) + ), + pos + ); } shared parseAssignmentExpressionOrHigher() { @@ -6675,10 +6857,8 @@ namespace ts { // If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done // with AssignmentExpression if we see one. - auto arrowExpression = tryParseParenthesizedArrowFunctionExpression() || tryParseAsyncSimpleArrowFunctionExpression(); - if (arrowExpression) { - return arrowExpression; - } + if (auto a = tryParseParenthesizedArrowFunctionExpression()) return a; + if (auto a = tryParseAsyncSimpleArrowFunctionExpression()) return a; // Now try to see if we're in production '1', '2' or '3'. A conditional expression can // start with a LogicalOrExpression, while the assignment productions can only start with @@ -6690,15 +6870,13 @@ namespace ts { // binary expression here, so we pass in the 'lowest' precedence here so that it matches // and consumes anything. auto pos = getNodePos(); - auto expr = parseBinaryExpressionOrHigher(OperatorPrecedence::Lowest); + auto expr = parseBinaryExpressionOrHigher((int)OperatorPrecedence::Lowest); // To avoid a look-ahead, we did not handle the case of an arrow function with a single un-parenthesized // parameter ('x => ...') above. We handle it here by checking if the parsed expression was a single // identifier and the current token is an arrow. - if (expr.kind == SyntaxKind::Identifier && token() == SyntaxKind::EqualsGreaterThanToken) { - return parseSimpleArrowFunctionExpression(pos, expr - as - Identifier, /*asyncModifier*/ {}); + if (expr->kind == SyntaxKind::Identifier && token() == SyntaxKind::EqualsGreaterThanToken) { + return parseSimpleArrowFunctionExpression(pos, to(expr), /*asyncModifier*/ {}); } // Now see if we might be in cases '2' or '3'. @@ -6708,142 +6886,13 @@ namespace ts { // Note: we call reScanGreaterToken so that we get an appropriately merged token // for cases like `> > =` becoming `>>=` if (isLeftHandSideExpression(expr) && isAssignmentOperator(reScanGreaterToken())) { - return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher(), pos); + return makeBinaryExpression(expr, parseTokenNode(), parseAssignmentExpressionOrHigher(), pos); } // It wasn't an assignment or a lambda. This is a conditional expression: return parseConditionalExpressionRest(expr, pos); } -// function parseSimpleArrowFunctionExpression(int pos, identifier: Identifier, asyncModifier?: NodeArray | undefined): ArrowFunction { -// Debug::asserts(token() == SyntaxKind::EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); -// auto parameter = factory::createParameterDeclaration( -// /*decorators*/ undefined, -// /*modifiers*/ undefined, -// /*dotDotDotToken*/ undefined, -// identifier, -// /*questionToken*/ undefined, -// /*type*/ undefined, -// /*initializer*/ undefined -// ); -// finishNode(parameter, identifier.pos); -// -// auto parameters = createNodeArray([parameter], parameter.pos, parameter.end); -// auto equalsGreaterThanToken = parseExpectedToken(SyntaxKind::EqualsGreaterThanToken); -// auto body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier); -// auto node = factory::createArrowFunction(asyncModifier, /*typeParameters*/ undefined, parameters, /*type*/ undefined, equalsGreaterThanToken, body); -// return addJSDocComment(finishNode(node, pos)); -// } -// -// -// function parsePossibleParenthesizedArrowFunctionExpression(): ArrowFunction | undefined { -// auto tokenPos = scanner.getTokenPos(); -// if (notParenthesizedArrow?.has(tokenPos)) { -// return undefined; -// } -// -// auto result = parseParenthesizedArrowFunctionExpression(/*allowAmbiguity*/ false); -// if (!result) { -// (notParenthesizedArrow || (notParenthesizedArrow = new Set())).add(tokenPos); -// } -// -// return result; -// } -// -// function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction | undefined { -// // We do a check here so that we won't be doing unnecessarily call to "lookAhead" -// if (token() == SyntaxKind::AsyncKeyword) { -// if (lookAhead(isUnParenthesizedAsyncArrowFunctionWorker) == Tristate::True) { -// auto pos = getNodePos(); -// auto asyncModifier = parseModifiersForArrowFunction(); -// auto expr = parseBinaryExpressionOrHigher(OperatorPrecedence::Lowest); -// return parseSimpleArrowFunctionExpression(pos, expr as Identifier, asyncModifier); -// } -// } -// return undefined; -// } -// -// function isUnParenthesizedAsyncArrowFunctionWorker(): Tristate { -// // AsyncArrowFunctionExpression: -// // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] -// // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] -// if (token() == SyntaxKind::AsyncKeyword) { -// nextToken(); -// // If the "async" is followed by "=>" token then it is not a beginning of an async arrow-function -// // but instead a simple arrow-function which will be parsed inside "parseAssignmentExpressionOrHigher" -// if (scanner.hasPrecedingLineBreak() || token() == SyntaxKind::EqualsGreaterThanToken) { -// return Tristate::False; -// } -// // Check for un-parenthesized AsyncArrowFunction -// auto expr = parseBinaryExpressionOrHigher(OperatorPrecedence::Lowest); -// if (!scanner.hasPrecedingLineBreak() && expr.kind == SyntaxKind::Identifier && token() == SyntaxKind::EqualsGreaterThanToken) { -// return Tristate::True; -// } -// } -// -// return Tristate::False; -// } -// -// function parseArrowFunctionExpressionBody(isAsync: boolean): Block | Expression { -// if (token() == SyntaxKind::OpenBraceToken) { -// return parseFunctionBlock(isAsync ? SignatureFlags::Await : SignatureFlags::None); -// } -// -// if (token() != SyntaxKind::SemicolonToken && -// token() != SyntaxKind::FunctionKeyword && -// token() != SyntaxKind::ClassKeyword && -// isStartOfStatement() && -// !isStartOfExpressionStatement()) { -// // Check if we got a plain statement (i.e. no expression-statements, no function/class expressions/declarations) -// // -// // Here we try to recover from a potential error situation in the case where the -// // user meant to supply a block. For example, if the user wrote: -// // -// // a => -// // auto v = 0; -// // } -// // -// // they may be missing an open brace. Check to see if that's the case so we can -// // try to recover better. If we don't do this, then the next close curly we see may end -// // up preemptively closing the containing construct. -// // -// // Note: even when 'IgnoreMissingOpenBrace' is passed, parseBody will still error. -// return parseFunctionBlock(SignatureFlags::IgnoreMissingOpenBrace | (isAsync ? SignatureFlags::Await : SignatureFlags::None)); -// } -// -// auto savedTopLevel = topLevel; -// topLevel = false; -// auto node = isAsync -// ? doInAwaitContext(parseAssignmentExpressionOrHigher) -// : doOutsideOfAwaitContext(parseAssignmentExpressionOrHigher); -// topLevel = savedTopLevel; -// return node; -// } -// -// function parseConditionalExpressionRest(leftOperand: Expression, int pos): Expression { -// // Note: we are passed in an expression which was produced from parseBinaryExpressionOrHigher. -// auto questionToken = parseOptionalToken(SyntaxKind::QuestionToken); -// if (!questionToken) { -// return leftOperand; -// } -// -// // Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and -// // we do not that for the 'whenFalse' part. -// auto colonToken; -// return finishNode( -// factory::createConditionalExpression( -// leftOperand, -// questionToken, -// doOutsideOfContext(disallowInAndDecoratorContext, parseAssignmentExpressionOrHigher), -// colonToken = parseExpectedToken(SyntaxKind::ColonToken), -// nodeIsPresent(colonToken) -// ? parseAssignmentExpressionOrHigher() -// : createMissingNode(SyntaxKind::Identifier, /*reportAtCurrentPosition*/ false, Diagnostics::_0_expected, tokenToString(SyntaxKind::ColonToken)) -// ), -// pos -// ); -// } -// shared parseEmptyStatement() { auto pos = getNodePos(); auto hasJSDoc = hasPrecedingJSDocComment(); @@ -6899,7 +6948,7 @@ namespace ts { // auto pos = getNodePos(); // auto hasJSDoc = hasPrecedingJSDocComment(); // parseExpected(SyntaxKind::ForKeyword); -// auto awaitToken = parseOptionalToken(SyntaxKind::AwaitKeyword); +// auto awaitToken = parseOptionalToken(SyntaxKind::AwaitKeyword); // parseExpected(SyntaxKind::OpenParenToken); // // auto initializer!: VariableDeclarationList | Expression; @@ -7090,16 +7139,14 @@ namespace ts { // out an expression, seeing if it is identifier and then seeing if it is followed by // a colon. auto pos = getNodePos(); -// auto hasJSDoc = hasPrecedingJSDocComment(); + auto hasJSDoc = hasPrecedingJSDocComment(); shared node; auto hasParen = token() == SyntaxKind::OpenParenToken; auto expression = allowInAnd>(parseExpression); if (isIdentifier(expression) && parseOptional(SyntaxKind::ColonToken)) { - parseStatement(); - node = make_shared({ - .label - }); -// node = factory::createLabeledStatement(expression, parseStatement()); +// parseStatement(); +// node = make_shared({.label}); + node = factory::createLabeledStatement(expression, parseStatement()); } else { if (!tryParseSemicolon()) { parseErrorForMissingSemicolonAfter(expression); @@ -7194,7 +7241,7 @@ namespace ts { // } // break; } - return parseExpressionOrLabeledStatement(); + return to(parseExpressionOrLabeledStatement()); } shared parseSourceFileWorker(ScriptTarget languageVersion, bool setParentNodes, ScriptKind scriptKind, function)> setExternalModuleIndicator) { @@ -7409,7 +7456,7 @@ namespace ts { // // auto modifierFlags = modifiersToFlags(modifiers); // parseExpected(SyntaxKind::FunctionKeyword); -// auto asteriskToken = parseOptionalToken(SyntaxKind::AsteriskToken); +// auto asteriskToken = parseOptionalToken(SyntaxKind::AsteriskToken); // // We don't parse the name here in await context, instead we will report a grammar error in the checker. // auto name = modifierFlags & ModifierFlags.Default ? parseOptionalBindingIdentifier() : parseBindingIdentifier(); // auto isGenerator = asteriskToken ? SignatureFlags::Yield : SignatureFlags::None; @@ -7601,40 +7648,6 @@ namespace ts { // return withJSDoc(finishNode(node, pos), hasJSDoc); // } // -// function parseAssertEntry() { -// auto pos = getNodePos(); -// auto name = tokenIsIdentifierOrKeyword(token()) ? parseIdentifierName() : parseLiteralLikeNode(SyntaxKind::StringLiteral) as StringLiteral; -// parseExpected(SyntaxKind::ColonToken); -// auto value = parseAssignmentExpressionOrHigher(); -// return finishNode(factory::createAssertEntry(name, value), pos); -// } -// -// function parseAssertClause(skipAssertKeyword?: true) { -// auto pos = getNodePos(); -// if (!skipAssertKeyword) { -// parseExpected(SyntaxKind::AssertKeyword); -// } -// auto openBracePosition = scanner.getTokenPos(); -// if (parseExpected(SyntaxKind::OpenBraceToken)) { -// auto multiLine = scanner.hasPrecedingLineBreak(); -// auto elements = parseDelimitedList(ParsingContext::AssertEntries, parseAssertEntry, /*considerSemicolonAsDelimiter*/ true); -// if (!parseExpected(SyntaxKind::CloseBraceToken)) { -// auto lastError = lastOrUndefined(parseDiagnostics); -// if (lastError && lastError.code == Diagnostics::_0_expected.code) { -// addRelatedInfo( -// lastError, -// createDetachedDiagnostic(fileName, openBracePosition, 1, Diagnostics::The_parser_expected_to_find_a_1_to_match_the_0_token_here, "{", "}") -// ); -// } -// } -// return finishNode(factory::createAssertClause(elements, multiLine), pos); -// } -// else { -// auto elements = createNodeArray([], getNodePos(), /*end*/ undefined, /*hasTrailingComma*/ false); -// return finishNode(factory::createAssertClause(elements, /*multiLine*/ false), pos); -// } -// } -// // function tokenAfterImportDefinitelyProducesImportDeclaration() { // return token() == SyntaxKind::AsteriskToken || token() == SyntaxKind::OpenBraceToken; // } @@ -7877,12 +7890,6 @@ namespace ts { // auto node = factory::createExportAssignment(decorators, modifiers, isExportEquals, expression); // return withJSDoc(finishNode(node, pos), hasJSDoc); // } - - enum class Tristate { - False, - True, - Unknown - }; // // export namespace JSDocParser { // export function parseJSDocTypeExpressionForTests(content: string, start: number | undefined, length: number | undefined): { jsDocTypeExpression: JSDocTypeExpression, diagnostics: Diagnostic[] } | undefined { @@ -8455,7 +8462,7 @@ namespace ts { // if (isBracketed) { // skipWhitespace(); // // May have an optional default, e.g. '[foo = 42]' -// if (parseOptionalToken(SyntaxKind::EqualsToken)) { +// if (parseOptionalToken(SyntaxKind::EqualsToken)) { // parseExpression(); // } // diff --git a/src/scanner.h b/src/scanner.h index d9eb43c..abd49f9 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -596,6 +596,12 @@ namespace ts { return token = scanTemplateAndSetTokenValue(isTaggedTemplate); } + SyntaxKind reScanQuestionToken() { + Debug::asserts(token == SyntaxKind::QuestionQuestionToken, "'reScanQuestionToken' should only be called on a '??'"); + pos = tokenPos + 1; + return token = SyntaxKind::QuestionToken; + } + SyntaxKind reScanGreaterToken(); SyntaxKind reScanSlashToken(); diff --git a/src/types.h b/src/types.h index 790cb92..636884e 100644 --- a/src/types.h +++ b/src/types.h @@ -30,6 +30,13 @@ namespace ts::types { */ using __String = std::string; + /* @internal */ + enum class Comparison { + LessThan = -1, + EqualTo = 0, + GreaterThan = 1 + }; + enum class ScriptTarget { ES3 = 0, ES5 = 1, @@ -67,8 +74,8 @@ namespace ts::types { }; struct TextRange { - int pos; - int end; + int pos = -1; + int end = -1; }; struct CommentDirective { @@ -1071,10 +1078,6 @@ namespace ts { return kinds; } - struct ReadonlyTextRange { - int pos = - 1; - int end = - 1; - }; struct Decorator; struct Modifier; @@ -1186,9 +1189,22 @@ namespace ts { return list.empty(); } + void push(shared node) { + list.push_back(node); + } + + bool operator==(NodeArray &other) { + if (pos != other.pos || end != other.end || hasTrailingComma != other.hasTrailingComma || isMissingList != other.isMissingList) return false; + if (transformFlags != other.transformFlags) return false; + if (list.size() != other.list.size()) return false; + for (int i = 0; i < list.size(); i++) { + if (list[i] != other.list[i]) return false; + } + return true; + } + vector> slice(int start, int end = 0) { - if (!end) end = list.size(); - return std::vector>(list.begin() + start, list.begin() + end); + return slice>(list, start, end); } }; @@ -1219,13 +1235,35 @@ namespace ts { // bool empty(); // }; + struct SourceMapSource { + string fileName; + string text; + /* @internal */ vector lineMap; + optional> skipTrivia; + }; + + struct SourceMapRange: types::TextRange { + optional source; + }; + + struct CommentRange: types::TextRange { + bool hasTrailingNewLine = false; + SyntaxKind kind; //SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia + }; + + struct SynthesizedComment: CommentRange { + string text; + bool hasLeadingNewline = false; + }; + struct EmitNode { // optional>> annotatedNodes; // Tracks Parse-tree nodes with EmitNodes for eventual cleanup. /*EmitFlags*/ int flags = 0; // Flags that customize emit -// optional> leadingComments; // Synthesized leading comments -// trailingComments?: SynthesizedComment[]; // Synthesized trailing comments -// commentRange?: TextRange; // The text range to use when emitting leading or trailing comments -// sourceMapRange?: SourceMapRange; // The text range to use when emitting leading or trailing source mappings + optional> leadingComments; // Synthesized leading comments + optional> trailingComments; // Synthesized trailing comments + sharedOpt commentRange; // The text range to use when emitting leading or trailing comments + sharedOpt sourceMapRange; // The text range to use when emitting leading or trailing source mappings + //todo: if we enable that, we have to adjust mergeEmitNode() // tokenSourceMapRanges?: (SourceMapRange | undefined)[]; // The text range to use when emitting source mappings for tokens // constantValue?: string | number; // The constant value of an expression // externalHelpersModuleName?: Identifier; // The local name for an imported helpers module @@ -1241,24 +1279,25 @@ namespace ts { * * There are a big variety of sub types: All have in common that they are the owner of their data (except *parent). */ - class Node: public ReadonlyTextRange { + class Node: public SourceMapRange { protected: Node &parent = *this; // Parent BaseNode (initialized by binding) public: SyntaxKind kind = SyntaxKind::Unknown; + constexpr static auto KIND = SyntaxKind::Unknown; /* types::NodeFlags */ int flags; /* @internal */ /* types::ModifierFlags */ int modifierFlagsCache; optional modifiers; // Array of modifiers optional decorators; // Array of decorators (in document order) /* @internal */ /* types::TransformFlags */ int transformFlags; // Flags for transforms // /* @internal */ id?: NodeId; // Unique id (used to look up NodeLinks) -// /* @internal */ original?: Node; // The original BaseNode if this is an updated node. + /* @internal */ sharedOpt original; // The original BaseNode if this is an updated node. // /* @internal */ symbol: Symbol; // Symbol declared by BaseNode (initialized by binding) // /* @internal */ locals?: SymbolTable; // Locals associated with BaseNode (initialized by binding) // /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) // /* @internal */ localSymbol?: Symbol; // Local symbol declared by BaseNode (initialized by binding only for exported nodes) // /* @internal */ flowNode?: FlowNode; // Associated FlowNode (initialized by binding) - /* @internal */ optional emitNode; //?: ; // Associated EmitNode (initialized by transforms) + /* @internal */ sharedOpt emitNode; //?: ; // Associated EmitNode (initialized by transforms) // /* @internal */ contextualType?: Type; // Used to temporarily assign a contextual type during overload resolution // /* @internal */ inferenceContext?: InferenceContext; // Inference context for contextual type @@ -1299,11 +1338,24 @@ namespace ts { } }; - sharedOpt operator||(sharedOpt a, sharedOpt b) { + template + sharedOpt to(sharedOpt p) { + if (!p) return nullptr; + if (T::KIND != SyntaxKind::Unknown && p->kind != T::KIND) return nullptr; + return reinterpret_pointer_cast(p); + } + + inline sharedOpt operator||(sharedOpt a, sharedOpt b) { if (a) return a; return b; }; + inline bool operator==(optional a, optional b) { + if (!a && !b) return true; + if (!a || !b) return false; + return *a == *b; + }; + template struct BaseNodeUnion: Node { }; @@ -1359,6 +1411,7 @@ namespace ts { struct DotToken: Token {}; struct DotDotDotToken: Token {}; struct QuestionToken: Token {}; + struct CommaToken: Token {}; struct ExclamationToken: Token {}; struct ColonToken: Token {}; struct EqualsToken: Token {}; @@ -1995,7 +2048,7 @@ namespace ts { OptionalProperty(initializer, Expression); }; - struct TypeReferenceNode: BrandKind { + struct TypeReferenceNode: BrandKind { UnionProperty(typeName, EntityName); }; @@ -2176,7 +2229,7 @@ namespace ts { bool multiLine = false; }; - struct ImportTypeNode: BrandKind { + struct ImportTypeNode: BrandKind { bool isTypeOf; Property(argument, TypeNode); OptionalProperty(assertions, ImportTypeAssertionContainer); @@ -2356,7 +2409,7 @@ namespace ts { // // export type BindingOrAssignmentPattern = ObjectBindingOrAssignmentPattern | ArrayBindingOrAssignmentPattern; - struct ConditionalExpression: Expression, BrandKind { + struct ConditionalExpression: BrandKind { Property(condition, Expression); Property(questionToken, QuestionToken); Property(whenTrue, Expression); @@ -2396,7 +2449,8 @@ namespace ts { NodeTypeArray(Expression) arguments; }; - struct CallChain: CallExpression {}; + using CallChain = CallExpression; + using OuterExpression = Expression; // ParenthesizedExpression | TypeAssertion | AsExpression | NonNullExpression | PartiallyEmittedExpression; /* @internal */ struct CallChainRoot: CallChain {}; @@ -2475,6 +2529,8 @@ namespace ts { Property(expression, Expression); }; + using NonNullChain = NonNullExpression; + struct ParenthesizedExpression: BrandKind { Property(expression, Expression); }; @@ -2484,7 +2540,7 @@ namespace ts { Property(expression, Expression); }; - struct TypeQueryNode: BrandKind { + struct TypeQueryNode: BrandKind { UnionProperty(exprName, EntityName); }; diff --git a/src/utilities.h b/src/utilities.h index 2fb6708..f788545 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -45,12 +45,48 @@ namespace ts { return nullptr; } - NodeArray& setTextRangePosEnd(NodeArray &range, int pos, int end) { + NodeArray &setTextRangePosEnd(NodeArray &range, int pos, int end) { range.pos = pos; range.end = end; return range; } + /** + * Gets a custom text range to use when emitting source maps. + */ + shared getSourceMapRange(shared node) { + if (!node->emitNode) return node; + return node->emitNode->sourceMapRange; + } + /** + * Gets a custom text range to use when emitting comments. + */ + shared getCommentRange(shared node) { + if (!node->emitNode) return node; + if (!node->emitNode->commentRange) return node; + return node->emitNode->commentRange; + } + + optional> getSyntheticLeadingComments(shared node) { + if (!node->emitNode) return nullopt; + return node->emitNode->leadingComments; + } + + optional> getSyntheticTrailingComments(shared node) { + if (!node->emitNode) return nullopt; + return node->emitNode->trailingComments; + } + + bool positionIsSynthesized(int pos) { + // This is a fast way of testing the following conditions: + // pos === undefined || pos === null || isNaN(pos) || pos < 0; + return !(pos >= 0); + } + + bool nodeIsSynthesized(shared range) { + return positionIsSynthesized(range->pos) || positionIsSynthesized(range->end); + } + template T setTextRange(T range, sharedOpt location) { return location ? setTextRangePosEnd(range, location->pos, location->end) : range; @@ -566,7 +602,8 @@ namespace ts { } } - bool isOuterExpression(shared node, int kinds = (int) OuterExpressionKinds::All) { + bool isOuterExpression(sharedOpt node, int kinds = (int) OuterExpressionKinds::All) { + if (!node) return false; switch (node->kind) { case SyntaxKind::ParenthesizedExpression: // if (kinds & (int)OuterExpressionKinds::ExcludeJSDocTypeAssertion && isJSDocTypeAssertion(node)) { @@ -584,7 +621,8 @@ namespace ts { return false; } - shared getExpression(shared node) { + sharedOpt getExpression(sharedOpt node) { + if (!node) return nullptr; switch (node->kind) { case SyntaxKind::ParenthesizedExpression: return node->to().expression; @@ -801,12 +839,6 @@ namespace ts { return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node)->kind); } - bool positionIsSynthesized(int pos) { - // This is a fast way of testing the following conditions: - // pos === undefined || pos === null || isNaN(pos) || pos < 0; - return !(pos >= 0); - } - // Returns true if this node is missing from the actual source code. A 'missing' node is different // from 'undefined/defined'. When a node is undefined (which can happen for optional nodes // in the tree), it is definitely missing. However, a node may be defined, but still be @@ -827,6 +859,10 @@ namespace ts { return node->pos == node->end && node->pos >= 0 && node->kind != SyntaxKind::EndOfFileToken; } + bool nodeIsPresent(sharedOpt node) { + return !nodeIsMissing(node); + } + string getTextOfNodeFromSourceText(string &sourceText, shared node, bool includeTrivia = false) { if (nodeIsMissing(node)) { return ""; @@ -899,5 +935,62 @@ namespace ts { } } + Comparison compareComparableValues(optional a, optional b) { + return a == b ? Comparison::EqualTo : + a == false ? Comparison::LessThan : + b == false ? Comparison::GreaterThan : + a < b ? Comparison::LessThan : + Comparison::GreaterThan; + } + + Comparison compareComparableValues(optional a, optional b) { + if (!a) return Comparison::LessThan; + if (!b) return Comparison::GreaterThan; + + return compareComparableValues(std::stod(*a), std::stod(*b)); + } + + /** + * Compare two numeric values for their order relative to each other. + * To compare strings, use any of the `compareStrings` functions. + */ + Comparison compareValues(optional a, optional b) { + return compareComparableValues(a, b); + } + + /** + * Gets the actual offset into an array for a relative offset. Negative offsets indicate a + * position offset from the end of the array. + */ + template + int toOffset(vector array, int offset) { + return offset < 0 ? array.size() + offset : offset; + } + + /** + * Appends a range of value to an array, returning the array. + * + * @param to The array to which `value` is to be appended. If `to` is `undefined`, a new array + * is created if `value` was appended. + * @param from The values to append to the array. If `from` is `undefined`, nothing is + * appended. If an element of `from` is `undefined`, that element is not appended. + * @param start The offset in `from` at which to start copying values. + * @param end The offset in `from` at which to stop copying values (non-inclusive). + */ +// export function addRange(to: T[], from: readonly T[] | undefined, start?: number, end?: number): T[]; +// export function addRange(to: T[] | undefined, from: readonly T[] | undefined, start?: number, end?: number): T[] | undefined; + template + optional> addRange(optional> to, optional> from, int start = 0, int end = 0) { + if (!from || from->empty()) return to; + if (!to) return slice(from, start, end); + start = start == 0 ? 0 : toOffset(*from, start); + end = end == 0 ? from->size() : toOffset(*from, end); + for (int i = start; i < end && i < from->size(); i++) { +// if (!(bool) (*from)[i]) { + to->push_back((*from)[i]); +// } + } + return to; + } }