///
///
///
namespace ts {
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
export function createNode(kind: SyntaxKind, pos?: number, end?: number): Node {
if (kind === SyntaxKind.SourceFile) {
return new (SourceFileConstructor || (SourceFileConstructor = objectAllocator.getSourceFileConstructor()))(kind, pos, end);
}
else if (kind === SyntaxKind.Identifier) {
return new (IdentifierConstructor || (IdentifierConstructor = objectAllocator.getIdentifierConstructor()))(kind, pos, end);
}
else if (kind < SyntaxKind.FirstNode) {
return new (TokenConstructor || (TokenConstructor = objectAllocator.getTokenConstructor()))(kind, pos, end);
}
else {
return new (NodeConstructor || (NodeConstructor = objectAllocator.getNodeConstructor()))(kind, pos, end);
}
}
function visitNode(cbNode: (node: Node) => T, node: Node): T {
if (node) {
return cbNode(node);
}
}
function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) {
if (nodes) {
return cbNodes(nodes);
}
}
function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) {
if (nodes) {
for (const node of nodes) {
const result = cbNode(node);
if (result) {
return result;
}
}
}
}
// Invokes a callback for each child of the given node. The 'cbNode' callback is invoked for all child nodes
// stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise,
// embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns
// a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned.
export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T {
if (!node) {
return;
}
// The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray
// callback parameters, but that causes a closure allocation for each invocation with noticeable effects
// on performance.
const visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode;
const cbNodes = cbNodeArray || cbNode;
switch (node.kind) {
case SyntaxKind.QualifiedName:
return visitNode(cbNode, (node).left) ||
visitNode(cbNode, (node).right);
case SyntaxKind.TypeParameter:
return visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).constraint) ||
visitNode(cbNode, (node).default) ||
visitNode(cbNode, (node).expression);
case SyntaxKind.ShorthandPropertyAssignment:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).questionToken) ||
visitNode(cbNode, (node).equalsToken) ||
visitNode(cbNode, (node).objectAssignmentInitializer);
case SyntaxKind.SpreadAssignment:
return visitNode(cbNode, (node).expression);
case SyntaxKind.Parameter:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertySignature:
case SyntaxKind.PropertyAssignment:
case SyntaxKind.VariableDeclaration:
case SyntaxKind.BindingElement:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).propertyName) ||
visitNode(cbNode, (node).dotDotDotToken) ||
visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).questionToken) ||
visitNode(cbNode, (node).type) ||
visitNode(cbNode, (node).initializer);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNodes(cbNodes, (node).typeParameters) ||
visitNodes(cbNodes, (node).parameters) ||
visitNode(cbNode, (node).type);
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
case SyntaxKind.Constructor:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.ArrowFunction:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).asteriskToken) ||
visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).questionToken) ||
visitNodes(cbNodes, (node).typeParameters) ||
visitNodes(cbNodes, (node).parameters) ||
visitNode(cbNode, (node).type) ||
visitNode(cbNode, (node).equalsGreaterThanToken) ||
visitNode(cbNode, (node).body);
case SyntaxKind.TypeReference:
return visitNode(cbNode, (node).typeName) ||
visitNodes(cbNodes, (node).typeArguments);
case SyntaxKind.TypePredicate:
return visitNode(cbNode, (node).parameterName) ||
visitNode(cbNode, (node).type);
case SyntaxKind.TypeQuery:
return visitNode(cbNode, (node).exprName);
case SyntaxKind.TypeLiteral:
return visitNodes(cbNodes, (node).members);
case SyntaxKind.ArrayType:
return visitNode(cbNode, (node).elementType);
case SyntaxKind.TupleType:
return visitNodes(cbNodes, (node).elementTypes);
case SyntaxKind.UnionType:
case SyntaxKind.IntersectionType:
return visitNodes(cbNodes, (node).types);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.TypeOperator:
return visitNode(cbNode, (node).type);
case SyntaxKind.IndexedAccessType:
return visitNode(cbNode, (node).objectType) ||
visitNode(cbNode, (node).indexType);
case SyntaxKind.MappedType:
return visitNode(cbNode, (node).readonlyToken) ||
visitNode(cbNode, (node).typeParameter) ||
visitNode(cbNode, (node).questionToken) ||
visitNode(cbNode, (node).type);
case SyntaxKind.LiteralType:
return visitNode(cbNode, (node).literal);
case SyntaxKind.ObjectBindingPattern:
case SyntaxKind.ArrayBindingPattern:
return visitNodes(cbNodes, (node).elements);
case SyntaxKind.ArrayLiteralExpression:
return visitNodes(cbNodes, (node).elements);
case SyntaxKind.ObjectLiteralExpression:
return visitNodes(cbNodes, (node).properties);
case SyntaxKind.PropertyAccessExpression:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).name);
case SyntaxKind.ElementAccessExpression:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).argumentExpression);
case SyntaxKind.CallExpression:
case SyntaxKind.NewExpression:
return visitNode(cbNode, (node).expression) ||
visitNodes(cbNodes, (node).typeArguments) ||
visitNodes(cbNodes, (node).arguments);
case SyntaxKind.TaggedTemplateExpression:
return visitNode(cbNode, (node).tag) ||
visitNode(cbNode, (node).template);
case SyntaxKind.TypeAssertionExpression:
return visitNode(cbNode, (node).type) ||
visitNode(cbNode, (node).expression);
case SyntaxKind.ParenthesizedExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.DeleteExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.TypeOfExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.VoidExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.PrefixUnaryExpression:
return visitNode(cbNode, (node).operand);
case SyntaxKind.YieldExpression:
return visitNode(cbNode, (node).asteriskToken) ||
visitNode(cbNode, (node).expression);
case SyntaxKind.AwaitExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.PostfixUnaryExpression:
return visitNode(cbNode, (node).operand);
case SyntaxKind.BinaryExpression:
return visitNode(cbNode, (node).left) ||
visitNode(cbNode, (node).operatorToken) ||
visitNode(cbNode, (node).right);
case SyntaxKind.AsExpression:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).type);
case SyntaxKind.NonNullExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.MetaProperty:
return visitNode(cbNode, (node).name);
case SyntaxKind.ConditionalExpression:
return visitNode(cbNode, (node).condition) ||
visitNode(cbNode, (node).questionToken) ||
visitNode(cbNode, (node).whenTrue) ||
visitNode(cbNode, (node).colonToken) ||
visitNode(cbNode, (node).whenFalse);
case SyntaxKind.SpreadElement:
return visitNode(cbNode, (node).expression);
case SyntaxKind.Block:
case SyntaxKind.ModuleBlock:
return visitNodes(cbNodes, (node).statements);
case SyntaxKind.SourceFile:
return visitNodes(cbNodes, (node).statements) ||
visitNode(cbNode, (node).endOfFileToken);
case SyntaxKind.VariableStatement:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).declarationList);
case SyntaxKind.VariableDeclarationList:
return visitNodes(cbNodes, (node).declarations);
case SyntaxKind.ExpressionStatement:
return visitNode(cbNode, (node).expression);
case SyntaxKind.IfStatement:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).thenStatement) ||
visitNode(cbNode, (node).elseStatement);
case SyntaxKind.DoStatement:
return visitNode(cbNode, (node).statement) ||
visitNode(cbNode, (node).expression);
case SyntaxKind.WhileStatement:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).statement);
case SyntaxKind.ForStatement:
return visitNode(cbNode, (node).initializer) ||
visitNode(cbNode, (node).condition) ||
visitNode(cbNode, (node).incrementor) ||
visitNode(cbNode, (node).statement);
case SyntaxKind.ForInStatement:
return visitNode(cbNode, (node).initializer) ||
visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).statement);
case SyntaxKind.ForOfStatement:
return visitNode(cbNode, (node).awaitModifier) ||
visitNode(cbNode, (node).initializer) ||
visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).statement);
case SyntaxKind.ContinueStatement:
case SyntaxKind.BreakStatement:
return visitNode(cbNode, (node).label);
case SyntaxKind.ReturnStatement:
return visitNode(cbNode, (node).expression);
case SyntaxKind.WithStatement:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).statement);
case SyntaxKind.SwitchStatement:
return visitNode(cbNode, (node).expression) ||
visitNode(cbNode, (node).caseBlock);
case SyntaxKind.CaseBlock:
return visitNodes(cbNodes, (node).clauses);
case SyntaxKind.CaseClause:
return visitNode(cbNode, (node).expression) ||
visitNodes(cbNodes, (node).statements);
case SyntaxKind.DefaultClause:
return visitNodes(cbNodes, (node).statements);
case SyntaxKind.LabeledStatement:
return visitNode(cbNode, (node).label) ||
visitNode(cbNode, (node).statement);
case SyntaxKind.ThrowStatement:
return visitNode(cbNode, (node).expression);
case SyntaxKind.TryStatement:
return visitNode(cbNode, (node).tryBlock) ||
visitNode(cbNode, (node).catchClause) ||
visitNode(cbNode, (node).finallyBlock);
case SyntaxKind.CatchClause:
return visitNode(cbNode, (node).variableDeclaration) ||
visitNode(cbNode, (node).block);
case SyntaxKind.Decorator:
return visitNode(cbNode, (node).expression);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNodes(cbNodes, (node).typeParameters) ||
visitNodes(cbNodes, (node).heritageClauses) ||
visitNodes(cbNodes, (node).members);
case SyntaxKind.InterfaceDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNodes(cbNodes, (node).typeParameters) ||
visitNodes(cbNodes, (node).heritageClauses) ||
visitNodes(cbNodes, (node).members);
case SyntaxKind.TypeAliasDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNodes(cbNodes, (node).typeParameters) ||
visitNode(cbNode, (node).type);
case SyntaxKind.EnumDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNodes(cbNodes, (node).members);
case SyntaxKind.EnumMember:
return visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).initializer);
case SyntaxKind.ModuleDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).body);
case SyntaxKind.ImportEqualsDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).moduleReference);
case SyntaxKind.ImportDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).importClause) ||
visitNode(cbNode, (node).moduleSpecifier);
case SyntaxKind.ImportClause:
return visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).namedBindings);
case SyntaxKind.NamespaceExportDeclaration:
return visitNode(cbNode, (node).name);
case SyntaxKind.NamespaceImport:
return visitNode(cbNode, (node).name);
case SyntaxKind.NamedImports:
case SyntaxKind.NamedExports:
return visitNodes(cbNodes, (node).elements);
case SyntaxKind.ExportDeclaration:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).exportClause) ||
visitNode(cbNode, (node).moduleSpecifier);
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return visitNode(cbNode, (node).propertyName) ||
visitNode(cbNode, (node).name);
case SyntaxKind.ExportAssignment:
return visitNodes(cbNodes, node.decorators) ||
visitNodes(cbNodes, node.modifiers) ||
visitNode(cbNode, (node).expression);
case SyntaxKind.TemplateExpression:
return visitNode(cbNode, (node).head) || visitNodes(cbNodes, (node).templateSpans);
case SyntaxKind.TemplateSpan:
return visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).literal);
case SyntaxKind.ComputedPropertyName:
return visitNode(cbNode, (node).expression);
case SyntaxKind.HeritageClause:
return visitNodes(cbNodes, (node).types);
case SyntaxKind.ExpressionWithTypeArguments:
return visitNode(cbNode, (node).expression) ||
visitNodes(cbNodes, (node).typeArguments);
case SyntaxKind.ExternalModuleReference:
return visitNode(cbNode, (node).expression);
case SyntaxKind.MissingDeclaration:
return visitNodes(cbNodes, node.decorators);
case SyntaxKind.JsxElement:
return visitNode(cbNode, (node).openingElement) ||
visitNodes(cbNodes, (node).children) ||
visitNode(cbNode, (node).closingElement);
case SyntaxKind.JsxSelfClosingElement:
case SyntaxKind.JsxOpeningElement:
return visitNode(cbNode, (node).tagName) ||
visitNode(cbNode, (node).attributes);
case SyntaxKind.JsxAttributes:
return visitNodes(cbNodes, (node).properties);
case SyntaxKind.JsxAttribute:
return visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).initializer);
case SyntaxKind.JsxSpreadAttribute:
return visitNode(cbNode, (node).expression);
case SyntaxKind.JsxExpression:
return visitNode(cbNode, (node as JsxExpression).dotDotDotToken) ||
visitNode(cbNode, (node as JsxExpression).expression);
case SyntaxKind.JsxClosingElement:
return visitNode(cbNode, (node).tagName);
case SyntaxKind.JSDocTypeExpression:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocUnionType:
return visitNodes(cbNodes, (node).types);
case SyntaxKind.JSDocTupleType:
return visitNodes(cbNodes, (node).types);
case SyntaxKind.JSDocArrayType:
return visitNode(cbNode, (node).elementType);
case SyntaxKind.JSDocNonNullableType:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocNullableType:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocRecordType:
return visitNode(cbNode, (node).literal);
case SyntaxKind.JSDocTypeReference:
return visitNode(cbNode, (node).name) ||
visitNodes(cbNodes, (node).typeArguments);
case SyntaxKind.JSDocOptionalType:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocFunctionType:
return visitNodes(cbNodes, (node).parameters) ||
visitNode(cbNode, (node).type);
case SyntaxKind.JSDocVariadicType:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocConstructorType:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocThisType:
return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocRecordMember:
return visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).type);
case SyntaxKind.JSDocComment:
return visitNodes(cbNodes, (node).tags);
case SyntaxKind.JSDocParameterTag:
return visitNode(cbNode, (node).preParameterName) ||
visitNode(cbNode, (node).typeExpression) ||
visitNode(cbNode, (node).postParameterName);
case SyntaxKind.JSDocReturnTag:
return visitNode(cbNode, (node).typeExpression);
case SyntaxKind.JSDocTypeTag:
return visitNode(cbNode, (node).typeExpression);
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (node).typeExpression);
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNodes, (node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
return visitNode(cbNode, (node).typeExpression) ||
visitNode(cbNode, (node).fullName) ||
visitNode(cbNode, (node).name) ||
visitNode(cbNode, (node).jsDocTypeLiteral);
case SyntaxKind.JSDocTypeLiteral:
return visitNodes(cbNodes, (node).jsDocPropertyTags);
case SyntaxKind.JSDocPropertyTag:
return visitNode(cbNode, (node).typeExpression) ||
visitNode(cbNode, (node).name);
case SyntaxKind.PartiallyEmittedExpression:
return visitNode(cbNode, (node).expression);
case SyntaxKind.JSDocLiteralType:
return visitNode(cbNode, (node).literal);
}
}
export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile {
performance.mark("beforeParse");
const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind);
performance.mark("afterParse");
performance.measure("Parse", "beforeParse", "afterParse");
return result;
}
export function parseIsolatedEntityName(text: string, languageVersion: ScriptTarget): EntityName {
return Parser.parseIsolatedEntityName(text, languageVersion);
}
export function isExternalModule(file: SourceFile): boolean {
return file.externalModuleIndicator !== undefined;
}
// Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter
// indicates what changed between the 'text' that this SourceFile has and the 'newText'.
// The SourceFile will be created with the compiler attempting to reuse as many nodes from
// this file as possible.
//
// Note: this function mutates nodes from this SourceFile. That means any existing nodes
// from this SourceFile that are being held onto may change as a result (including
// becoming detached from any SourceFile). It is recommended that this SourceFile not
// be used once 'update' is called on it.
export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile {
return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks);
}
/* @internal */
export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
if (result && result.jsDoc) {
// because the jsDocComment was parsed out of the source file, it might
// not be covered by the fixupParentReferences.
Parser.fixupParentReferences(result.jsDoc);
}
return result;
}
/* @internal */
// Exposed only for testing.
export function parseJSDocTypeExpressionForTests(content: string, start?: number, length?: number) {
return Parser.JSDocParser.parseJSDocTypeExpressionForTests(content, start, length);
}
// Implement the parser as a singleton module. We do this for perf reasons because creating
// parser instances can actually be expensive enough to impact us on projects with many source
// files.
namespace Parser {
// Share a single scanner across all calls to parse a source file. This helps speed things
// up by avoiding the cost of creating/compiling scanners over and over again.
const scanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ true);
const disallowInAndDecoratorContext = NodeFlags.DisallowInContext | NodeFlags.DecoratorContext;
// capture constructors in 'initializeState' to avoid null checks
let NodeConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let TokenConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let IdentifierConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let SourceFileConstructor: new (kind: SyntaxKind, pos: number, end: number) => Node;
let sourceFile: SourceFile;
let parseDiagnostics: Diagnostic[];
let syntaxCursor: IncrementalParser.SyntaxCursor;
let currentToken: SyntaxKind;
let sourceText: string;
let nodeCount: number;
let identifiers: Map;
let identifierCount: number;
let parsingContext: ParsingContext;
// 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
// that some tokens that would be considered identifiers may be considered keywords.
//
// When adding more parser context flags, consider which is the more common case that the
// flag will be in. This should be the 'false' state for that flag. The reason for this is
// that we don't store data in our nodes unless the value is in the *non-default* state. So,
// for example, more often than code 'allows-in' (or doesn't 'disallow-in'). We opt for
// 'disallow-in' set to 'false'. Otherwise, if we had 'allowsIn' set to 'true', then almost
// all nodes would need extra state on them to store this info.
//
// Note: 'allowIn' and 'allowYield' track 1:1 with the [in] and [yield] concepts in the ES6
// grammar specification.
//
// An important thing about these context concepts. By default they are effectively inherited
// while parsing through every grammar production. i.e. if you don't change them, then when
// you parse a sub-production, it will have the same context values as the parent production.
// This is great most of the time. After all, consider all the 'expression' grammar productions
// and how nearly all of them pass along the 'in' and 'yield' context values:
//
// EqualityExpression[In, Yield] :
// RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] == RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] != RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] === RelationalExpression[?In, ?Yield]
// EqualityExpression[?In, ?Yield] !== RelationalExpression[?In, ?Yield]
//
// Where you have to be careful is then understanding what the points are in the grammar
// where the values are *not* passed along. For example:
//
// SingleNameBinding[Yield,GeneratorParameter]
// [+GeneratorParameter]BindingIdentifier[Yield] Initializer[In]opt
// [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt
//
// Here this is saying that if the GeneratorParameter context flag is set, that we should
// explicitly set the 'yield' context flag to false before calling into the BindingIdentifier
// and we should explicitly unset the 'yield' context flag before calling into the Initializer.
// production. Conversely, if the GeneratorParameter context flag is not set, then we
// should leave the 'yield' context flag alone.
//
// Getting this all correct is tricky and requires careful reading of the grammar to
// understand when these values should be changed versus when they should be inherited.
//
// Note: it should not be necessary to save/restore these flags during speculative/lookahead
// parsing. These context flags are naturally stored and restored through normal recursive
// descent parsing and unwinding.
let contextFlags: NodeFlags;
// Whether or not we've had a parse error since creating the last AST node. If we have
// encountered an error, it will be stored on the next AST node we create. Parse errors
// can be broken down into three categories:
//
// 1) An error that occurred during scanning. For example, an unterminated literal, or a
// character that was completely not understood.
//
// 2) A token was expected, but was not present. This type of error is commonly produced
// by the 'parseExpected' function.
//
// 3) A token was present that no parsing function was able to consume. This type of error
// only occurs in the 'abortParsingListOrMoveToNextToken' function when the parser
// decides to skip the token.
//
// In all of these cases, we want to mark the next node as having had an error before it.
// With this mark, we can know in incremental settings if this node can be reused, or if
// we have to reparse it. If we don't keep this information around, we may just reuse the
// node. in that event we would then not produce the same errors as we did before, causing
// significant confusion problems.
//
// Note: it is necessary that this value be saved/restored during speculative/lookahead
// parsing. During lookahead parsing, we will often create a node. That node will have
// this value attached, and then this value will be set back to 'false'. If we decide to
// rewind, we must get back to the same value we had prior to the lookahead.
//
// Note: any errors at the end of the file that do not precede a regular node, should get
// attached to the EOF token.
let parseErrorBeforeNextFinishedNode = false;
export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile {
scriptKind = ensureScriptKind(fileName, scriptKind);
initializeState(sourceText, languageVersion, syntaxCursor, scriptKind);
const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind);
clearState();
return result;
}
export function parseIsolatedEntityName(content: string, languageVersion: ScriptTarget): EntityName {
initializeState(content, languageVersion, /*syntaxCursor*/ undefined, ScriptKind.JS);
// Prime the scanner.
nextToken();
const entityName = parseEntityName(/*allowReservedWords*/ true);
const isInvalid = token() === SyntaxKind.EndOfFileToken && !parseDiagnostics.length;
clearState();
return isInvalid ? entityName : undefined;
}
function getLanguageVariant(scriptKind: ScriptKind) {
// .tsx and .jsx files are treated as jsx language variant.
return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS ? LanguageVariant.JSX : LanguageVariant.Standard;
}
function initializeState(_sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) {
NodeConstructor = objectAllocator.getNodeConstructor();
TokenConstructor = objectAllocator.getTokenConstructor();
IdentifierConstructor = objectAllocator.getIdentifierConstructor();
SourceFileConstructor = objectAllocator.getSourceFileConstructor();
sourceText = _sourceText;
syntaxCursor = _syntaxCursor;
parseDiagnostics = [];
parsingContext = 0;
identifiers = createMap();
identifierCount = 0;
nodeCount = 0;
contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX ? NodeFlags.JavaScriptFile : NodeFlags.None;
parseErrorBeforeNextFinishedNode = false;
// Initialize and prime the scanner before parsing the source elements.
scanner.setText(sourceText);
scanner.setOnError(scanError);
scanner.setScriptTarget(languageVersion);
scanner.setLanguageVariant(getLanguageVariant(scriptKind));
}
function clearState() {
// Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily.
scanner.setText("");
scanner.setOnError(undefined);
// Clear any data. We don't want to accidentally hold onto it for too long.
parseDiagnostics = undefined;
sourceFile = undefined;
identifiers = undefined;
syntaxCursor = undefined;
sourceText = undefined;
}
function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile {
sourceFile = createSourceFile(fileName, languageVersion, scriptKind);
sourceFile.flags = contextFlags;
// Prime the scanner.
nextToken();
processReferenceComments(sourceFile);
sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
Debug.assert(token() === SyntaxKind.EndOfFileToken);
sourceFile.endOfFileToken = parseTokenNode();
setExternalModuleIndicator(sourceFile);
sourceFile.nodeCount = nodeCount;
sourceFile.identifierCount = identifierCount;
sourceFile.identifiers = identifiers;
sourceFile.parseDiagnostics = parseDiagnostics;
if (setParentNodes) {
fixupParentReferences(sourceFile);
}
return sourceFile;
}
function addJSDocComment(node: T): T {
const comments = getJSDocCommentRanges(node, sourceFile.text);
if (comments) {
for (const comment of comments) {
const jsDoc = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos);
if (!jsDoc) {
continue;
}
if (!node.jsDoc) {
node.jsDoc = [];
}
node.jsDoc.push(jsDoc);
}
}
return node;
}
export function fixupParentReferences(rootNode: Node) {
// normally parent references are set during binding. However, for clients that only need
// a syntax tree, and no semantic features, then the binding process is an unnecessary
// overhead. This functions allows us to set all the parents, without all the expense of
// binding.
let parent: Node = rootNode;
forEachChild(rootNode, visitNode);
return;
function visitNode(n: Node): void {
// walk down setting parents that differ from the parent we think it should be. This
// allows us to quickly bail out of setting parents for subtrees during incremental
// parsing
if (n.parent !== parent) {
n.parent = parent;
const saveParent = parent;
parent = n;
forEachChild(n, visitNode);
if (n.jsDoc) {
for (const jsDoc of n.jsDoc) {
jsDoc.parent = n;
parent = jsDoc;
forEachChild(jsDoc, visitNode);
}
}
parent = saveParent;
}
}
}
function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind: ScriptKind): SourceFile {
// code from createNode is inlined here so createNode won't have to deal with special case of creating source files
// this is quite rare comparing to other nodes and createNode should be as fast as possible
const sourceFile = new SourceFileConstructor(SyntaxKind.SourceFile, /*pos*/ 0, /* end */ sourceText.length);
nodeCount++;
sourceFile.text = sourceText;
sourceFile.bindDiagnostics = [];
sourceFile.languageVersion = languageVersion;
sourceFile.fileName = normalizePath(fileName);
sourceFile.languageVariant = getLanguageVariant(scriptKind);
sourceFile.isDeclarationFile = fileExtensionIs(sourceFile.fileName, ".d.ts");
sourceFile.scriptKind = scriptKind;
return sourceFile;
}
function setContextFlag(val: boolean, flag: NodeFlags) {
if (val) {
contextFlags |= flag;
}
else {
contextFlags &= ~flag;
}
}
function setDisallowInContext(val: boolean) {
setContextFlag(val, NodeFlags.DisallowInContext);
}
function setYieldContext(val: boolean) {
setContextFlag(val, NodeFlags.YieldContext);
}
function setDecoratorContext(val: boolean) {
setContextFlag(val, NodeFlags.DecoratorContext);
}
function setAwaitContext(val: boolean) {
setContextFlag(val, NodeFlags.AwaitContext);
}
function doOutsideOfContext(context: NodeFlags, func: () => T): T {
// contextFlagsToClear will contain only the context flags that are
// currently set that we need to temporarily clear
// We don't just blindly reset to the previous flags to ensure
// that we do not mutate cached flags for the incremental
// parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and
// HasAggregatedChildData).
const contextFlagsToClear = context & contextFlags;
if (contextFlagsToClear) {
// clear the requested context flags
setContextFlag(/*val*/ false, contextFlagsToClear);
const result = func();
// restore the context flags we just cleared
setContextFlag(/*val*/ true, contextFlagsToClear);
return result;
}
// no need to do anything special as we are not in any of the requested contexts
return func();
}
function doInsideOfContext(context: NodeFlags, func: () => T): T {
// contextFlagsToSet will contain only the context flags that
// are not currently set that we need to temporarily enable.
// We don't just blindly reset to the previous flags to ensure
// that we do not mutate cached flags for the incremental
// parser (ThisNodeHasError, ThisNodeOrAnySubNodesHasError, and
// HasAggregatedChildData).
const contextFlagsToSet = context & ~contextFlags;
if (contextFlagsToSet) {
// set the requested context flags
setContextFlag(/*val*/ true, contextFlagsToSet);
const result = func();
// reset the context flags we just set
setContextFlag(/*val*/ false, contextFlagsToSet);
return result;
}
// no need to do anything special as we are already in all of the requested contexts
return func();
}
function allowInAnd(func: () => T): T {
return doOutsideOfContext(NodeFlags.DisallowInContext, func);
}
function disallowInAnd(func: () => T): T {
return doInsideOfContext(NodeFlags.DisallowInContext, func);
}
function doInYieldContext(func: () => T): T {
return doInsideOfContext(NodeFlags.YieldContext, func);
}
function doInDecoratorContext(func: () => T): T {
return doInsideOfContext(NodeFlags.DecoratorContext, func);
}
function doInAwaitContext(func: () => T): T {
return doInsideOfContext(NodeFlags.AwaitContext, func);
}
function doOutsideOfAwaitContext(func: () => T): T {
return doOutsideOfContext(NodeFlags.AwaitContext, func);
}
function doInYieldAndAwaitContext(func: () => T): T {
return doInsideOfContext(NodeFlags.YieldContext | NodeFlags.AwaitContext, func);
}
function inContext(flags: NodeFlags) {
return (contextFlags & flags) !== 0;
}
function inYieldContext() {
return inContext(NodeFlags.YieldContext);
}
function inDisallowInContext() {
return inContext(NodeFlags.DisallowInContext);
}
function inDecoratorContext() {
return inContext(NodeFlags.DecoratorContext);
}
function inAwaitContext() {
return inContext(NodeFlags.AwaitContext);
}
function parseErrorAtCurrentToken(message: DiagnosticMessage, arg0?: any): void {
const start = scanner.getTokenPos();
const length = scanner.getTextPos() - start;
parseErrorAtPosition(start, length, message, arg0);
}
function parseErrorAtPosition(start: number, length: number, message: DiagnosticMessage, arg0?: any): void {
// Don't report another error if it would just be at the same position as the last error.
const lastError = lastOrUndefined(parseDiagnostics);
if (!lastError || start !== lastError.start) {
parseDiagnostics.push(createFileDiagnostic(sourceFile, start, length, message, arg0));
}
// Mark that we've encountered an error. We'll set an appropriate bit on the next
// node we finish so that it can't be reused incrementally.
parseErrorBeforeNextFinishedNode = true;
}
function scanError(message: DiagnosticMessage, length?: number) {
const pos = scanner.getTextPos();
parseErrorAtPosition(pos, length || 0, message);
}
function getNodePos(): number {
return scanner.getStartPos();
}
function getNodeEnd(): number {
return scanner.getStartPos();
}
// Use this function to access the current token instead of reading the currentToken
// variable. Since function results aren't narrowed in control flow analysis, this ensures
// that the type checker doesn't make wrong assumptions about the type of the current
// token (e.g. a call to nextToken() changes the current token but the checker doesn't
// reason about this side effect). Mainstream VMs inline simple functions like this, so
// there is no performance penalty.
function token(): SyntaxKind {
return currentToken;
}
function nextToken(): SyntaxKind {
return currentToken = scanner.scan();
}
function reScanGreaterToken(): SyntaxKind {
return currentToken = scanner.reScanGreaterToken();
}
function reScanSlashToken(): SyntaxKind {
return currentToken = scanner.reScanSlashToken();
}
function reScanTemplateToken(): SyntaxKind {
return currentToken = scanner.reScanTemplateToken();
}
function scanJsxIdentifier(): SyntaxKind {
return currentToken = scanner.scanJsxIdentifier();
}
function scanJsxText(): SyntaxKind {
return currentToken = scanner.scanJsxToken();
}
function scanJsxAttributeValue(): SyntaxKind {
return currentToken = scanner.scanJsxAttributeValue();
}
function speculationHelper(callback: () => T, isLookAhead: boolean): T {
// Keep track of the state we'll need to rollback to if lookahead fails (or if the
// caller asked us to always reset our state).
const saveToken = currentToken;
const saveParseDiagnosticsLength = parseDiagnostics.length;
const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode;
// Note: it is not actually necessary to save/restore the context flags here. That's
// because the saving/restoring of these flags happens naturally through the recursive
// descent nature of our parser. However, we still store this here just so we can
// assert that invariant holds.
const saveContextFlags = contextFlags;
// If we're only looking ahead, then tell the scanner to only lookahead as well.
// Otherwise, if we're actually speculatively parsing, then tell the scanner to do the
// same.
const result = isLookAhead
? scanner.lookAhead(callback)
: scanner.tryScan(callback);
Debug.assert(saveContextFlags === contextFlags);
// If our callback returned something 'falsy' or we're just looking ahead,
// then unconditionally restore us to where we were.
if (!result || isLookAhead) {
currentToken = saveToken;
parseDiagnostics.length = saveParseDiagnosticsLength;
parseErrorBeforeNextFinishedNode = saveParseErrorBeforeNextFinishedNode;
}
return result;
}
/** Invokes the provided callback then unconditionally restores the parser to the state it
* was in immediately prior to invoking the callback. The result of invoking the callback
* is returned from this function.
*/
function lookAhead(callback: () => T): T {
return speculationHelper(callback, /*isLookAhead*/ true);
}
/** Invokes the provided callback. If the callback returns something falsy, then it restores
* the parser to the state it was in immediately prior to invoking the callback. If the
* callback returns something truthy, then the parser state is not rolled back. The result
* of invoking the callback is returned from this function.
*/
function tryParse(callback: () => T): T {
return speculationHelper(callback, /*isLookAhead*/ false);
}
// Ignore strict mode flag because we will report an error in type checker instead.
function isIdentifier(): boolean {
if (token() === SyntaxKind.Identifier) {
return true;
}
// If we have a 'yield' keyword, and we're in the [yield] context, then 'yield' is
// considered a keyword and is not an identifier.
if (token() === SyntaxKind.YieldKeyword && inYieldContext()) {
return false;
}
// If we have a 'await' keyword, and we're in the [Await] context, then 'await' is
// considered a keyword and is not an identifier.
if (token() === SyntaxKind.AwaitKeyword && inAwaitContext()) {
return false;
}
return token() > SyntaxKind.LastReservedWord;
}
function parseExpected(kind: SyntaxKind, diagnosticMessage?: DiagnosticMessage, shouldAdvance = true): boolean {
if (token() === kind) {
if (shouldAdvance) {
nextToken();
}
return true;
}
// Report specific message if provided with one. Otherwise, report generic fallback message.
if (diagnosticMessage) {
parseErrorAtCurrentToken(diagnosticMessage);
}
else {
parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(kind));
}
return false;
}
function parseOptional(t: SyntaxKind): boolean {
if (token() === t) {
nextToken();
return true;
}
return false;
}
function parseOptionalToken(t: TKind): Token;
function parseOptionalToken(t: SyntaxKind): Node {
if (token() === t) {
return parseTokenNode();
}
return undefined;
}
function parseExpectedToken(t: TKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Token;
function parseExpectedToken(t: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node {
return parseOptionalToken(t) ||
createMissingNode(t, reportAtCurrentPosition, diagnosticMessage, arg0);
}
function parseTokenNode(): T {
const node = createNode(token());
nextToken();
return finishNode(node);
}
function canParseSemicolon() {
// If there's a real semicolon, then we can always parse it out.
if (token() === SyntaxKind.SemicolonToken) {
return true;
}
// We can parse out an optional semicolon in ASI cases in the following cases.
return token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.EndOfFileToken || scanner.hasPrecedingLineBreak();
}
function parseSemicolon(): boolean {
if (canParseSemicolon()) {
if (token() === SyntaxKind.SemicolonToken) {
// consume the semicolon if it was explicitly provided.
nextToken();
}
return true;
}
else {
return parseExpected(SyntaxKind.SemicolonToken);
}
}
// note: this function creates only node
function createNode(kind: TKind, pos?: number): Node | Token | Identifier {
nodeCount++;
if (!(pos >= 0)) {
pos = scanner.getStartPos();
}
return kind >= SyntaxKind.FirstNode ? new NodeConstructor(kind, pos, pos) :
kind === SyntaxKind.Identifier ? new IdentifierConstructor(kind, pos, pos) :
new TokenConstructor(kind, pos, pos);
}
function createNodeArray(elements?: T[], pos?: number): NodeArray {
const array = >(elements || []);
if (!(pos >= 0)) {
pos = getNodePos();
}
array.pos = pos;
array.end = pos;
return array;
}
function finishNode(node: T, end?: number): T {
node.end = end === undefined ? scanner.getStartPos() : end;
if (contextFlags) {
node.flags |= contextFlags;
}
// Keep track on the node if we encountered an error while parsing it. If we did, then
// we cannot reuse the node incrementally. Once we've marked this node, clear out the
// flag so that we don't mark any subsequent nodes.
if (parseErrorBeforeNextFinishedNode) {
parseErrorBeforeNextFinishedNode = false;
node.flags |= NodeFlags.ThisNodeHasError;
}
return node;
}
function createMissingNode(kind: SyntaxKind, reportAtCurrentPosition: boolean, diagnosticMessage: DiagnosticMessage, arg0?: any): Node {
if (reportAtCurrentPosition) {
parseErrorAtPosition(scanner.getStartPos(), 0, diagnosticMessage, arg0);
}
else {
parseErrorAtCurrentToken(diagnosticMessage, arg0);
}
const result = createNode(kind, scanner.getStartPos());
(result).text = "";
return finishNode(result);
}
function internIdentifier(text: string): string {
text = escapeIdentifier(text);
let identifier = identifiers.get(text);
if (identifier === undefined) {
identifiers.set(text, identifier = text);
}
return identifier;
}
// An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues
// with magic property names like '__proto__'. The 'identifiers' object is used to share a single string instance for
// each identifier in order to reduce memory consumption.
function createIdentifier(isIdentifier: boolean, diagnosticMessage?: DiagnosticMessage): Identifier {
identifierCount++;
if (isIdentifier) {
const node = createNode(SyntaxKind.Identifier);
// Store original token kind if it is not just an Identifier so we can report appropriate error later in type checker
if (token() !== SyntaxKind.Identifier) {
node.originalKeywordKind = token();
}
node.text = internIdentifier(scanner.getTokenValue());
nextToken();
return finishNode(node);
}
return createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, diagnosticMessage || Diagnostics.Identifier_expected);
}
function parseIdentifier(diagnosticMessage?: DiagnosticMessage): Identifier {
return createIdentifier(isIdentifier(), diagnosticMessage);
}
function parseIdentifierName(): Identifier {
return createIdentifier(tokenIsIdentifierOrKeyword(token()));
}
function isLiteralPropertyName(): boolean {
return tokenIsIdentifierOrKeyword(token()) ||
token() === SyntaxKind.StringLiteral ||
token() === SyntaxKind.NumericLiteral;
}
function parsePropertyNameWorker(allowComputedPropertyNames: boolean): PropertyName {
if (token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral) {
return parseLiteralNode(/*internName*/ true);
}
if (allowComputedPropertyNames && token() === SyntaxKind.OpenBracketToken) {
return parseComputedPropertyName();
}
return parseIdentifierName();
}
function parsePropertyName(): PropertyName {
return parsePropertyNameWorker(/*allowComputedPropertyNames*/ true);
}
function parseSimplePropertyName(): Identifier | LiteralExpression {
return parsePropertyNameWorker(/*allowComputedPropertyNames*/ false);
}
function isSimplePropertyName() {
return token() === SyntaxKind.StringLiteral || token() === SyntaxKind.NumericLiteral || tokenIsIdentifierOrKeyword(token());
}
function parseComputedPropertyName(): ComputedPropertyName {
// PropertyName [Yield]:
// LiteralPropertyName
// ComputedPropertyName[?Yield]
const node = createNode(SyntaxKind.ComputedPropertyName);
parseExpected(SyntaxKind.OpenBracketToken);
// We parse any expression (including a comma expression). But the grammar
// says that only an assignment expression is allowed, so the grammar checker
// will error if it sees a comma expression.
node.expression = allowInAnd(parseExpression);
parseExpected(SyntaxKind.CloseBracketToken);
return finishNode(node);
}
function parseContextualModifier(t: SyntaxKind): boolean {
return token() === t && tryParse(nextTokenCanFollowModifier);
}
function nextTokenIsOnSameLineAndCanFollowModifier() {
nextToken();
if (scanner.hasPrecedingLineBreak()) {
return false;
}
return canFollowModifier();
}
function nextTokenCanFollowModifier() {
if (token() === SyntaxKind.ConstKeyword) {
// 'const' is only a modifier if followed by 'enum'.
return nextToken() === SyntaxKind.EnumKeyword;
}
if (token() === SyntaxKind.ExportKeyword) {
nextToken();
if (token() === SyntaxKind.DefaultKeyword) {
return lookAhead(nextTokenIsClassOrFunctionOrAsync);
}
return token() !== SyntaxKind.AsteriskToken && token() !== SyntaxKind.AsKeyword && token() !== SyntaxKind.OpenBraceToken && canFollowModifier();
}
if (token() === SyntaxKind.DefaultKeyword) {
return nextTokenIsClassOrFunctionOrAsync();
}
if (token() === SyntaxKind.StaticKeyword) {
nextToken();
return canFollowModifier();
}
return nextTokenIsOnSameLineAndCanFollowModifier();
}
function parseAnyContextualModifier(): boolean {
return isModifierKind(token()) && tryParse(nextTokenCanFollowModifier);
}
function canFollowModifier(): boolean {
return token() === SyntaxKind.OpenBracketToken
|| token() === SyntaxKind.OpenBraceToken
|| token() === SyntaxKind.AsteriskToken
|| token() === SyntaxKind.DotDotDotToken
|| isLiteralPropertyName();
}
function nextTokenIsClassOrFunctionOrAsync(): boolean {
nextToken();
return token() === SyntaxKind.ClassKeyword || token() === SyntaxKind.FunctionKeyword ||
(token() === SyntaxKind.AbstractKeyword && lookAhead(nextTokenIsClassKeywordOnSameLine)) ||
(token() === SyntaxKind.AsyncKeyword && lookAhead(nextTokenIsFunctionKeywordOnSameLine));
}
// True if positioned at the start of a list element
function isListElement(parsingContext: ParsingContext, inErrorRecovery: boolean): boolean {
const node = currentNode(parsingContext);
if (node) {
return true;
}
switch (parsingContext) {
case ParsingContext.SourceElements:
case ParsingContext.BlockStatements:
case ParsingContext.SwitchClauseStatements:
// If we're in error recovery, then we don't want to treat ';' as an empty statement.
// The problem is that ';' can show up in far too many contexts, and if we see one
// and assume it's a statement, then we may bail out inappropriately from whatever
// we're parsing. For example, if we have a semicolon in the middle of a class, then
// we really don't want to assume the class is over and we're on a statement in the
// outer module. We just want to consume and move on.
return !(token() === SyntaxKind.SemicolonToken && inErrorRecovery) && isStartOfStatement();
case ParsingContext.SwitchClauses:
return token() === SyntaxKind.CaseKeyword || token() === SyntaxKind.DefaultKeyword;
case ParsingContext.TypeMembers:
return lookAhead(isTypeMemberStart);
case ParsingContext.ClassMembers:
// We allow semicolons as class elements (as specified by ES6) as long as we're
// not in error recovery. If we're in error recovery, we don't want an errant
// semicolon to be treated as a class member (since they're almost always used
// for statements.
return lookAhead(isClassMemberStart) || (token() === SyntaxKind.SemicolonToken && !inErrorRecovery);
case ParsingContext.EnumMembers:
// Include open bracket computed properties. This technically also lets in indexers,
// which would be a candidate for improved error reporting.
return token() === SyntaxKind.OpenBracketToken || isLiteralPropertyName();
case ParsingContext.ObjectLiteralMembers:
return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.AsteriskToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName();
case ParsingContext.RestProperties:
return isLiteralPropertyName();
case ParsingContext.ObjectBindingElements:
return token() === SyntaxKind.OpenBracketToken || token() === SyntaxKind.DotDotDotToken || isLiteralPropertyName();
case ParsingContext.HeritageClauseElement:
// If we see `{ ... }` then only consume it as an expression if it is followed by `,` or `{`
// That way we won't consume the body of a class in its heritage clause.
if (token() === SyntaxKind.OpenBraceToken) {
return lookAhead(isValidHeritageClauseObjectLiteral);
}
if (!inErrorRecovery) {
return isStartOfLeftHandSideExpression() && !isHeritageClauseExtendsOrImplementsKeyword();
}
else {
// If we're in error recovery we tighten up what we're willing to match.
// That way we don't treat something like "this" as a valid heritage clause
// element during recovery.
return isIdentifier() && !isHeritageClauseExtendsOrImplementsKeyword();
}
case ParsingContext.VariableDeclarations:
return isIdentifierOrPattern();
case ParsingContext.ArrayBindingElements:
return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isIdentifierOrPattern();
case ParsingContext.TypeParameters:
return isIdentifier();
case ParsingContext.ArgumentExpressions:
case ParsingContext.ArrayLiteralMembers:
return token() === SyntaxKind.CommaToken || token() === SyntaxKind.DotDotDotToken || isStartOfExpression();
case ParsingContext.Parameters:
return isStartOfParameter();
case ParsingContext.TypeArguments:
case ParsingContext.TupleElementTypes:
return token() === SyntaxKind.CommaToken || isStartOfType();
case ParsingContext.HeritageClauses:
return isHeritageClause();
case ParsingContext.ImportOrExportSpecifiers:
return tokenIsIdentifierOrKeyword(token());
case ParsingContext.JsxAttributes:
return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken;
case ParsingContext.JsxChildren:
return true;
case ParsingContext.JSDocFunctionParameters:
case ParsingContext.JSDocTypeArguments:
case ParsingContext.JSDocTupleTypes:
return JSDocParser.isJSDocType();
case ParsingContext.JSDocRecordMembers:
return isSimplePropertyName();
}
Debug.fail("Non-exhaustive case in 'isListElement'.");
}
function isValidHeritageClauseObjectLiteral() {
Debug.assert(token() === SyntaxKind.OpenBraceToken);
if (nextToken() === SyntaxKind.CloseBraceToken) {
// if we see "extends {}" then only treat the {} as what we're extending (and not
// the class body) if we have:
//
// extends {} {
// extends {},
// extends {} extends
// extends {} implements
const next = nextToken();
return next === SyntaxKind.CommaToken || next === SyntaxKind.OpenBraceToken || next === SyntaxKind.ExtendsKeyword || next === SyntaxKind.ImplementsKeyword;
}
return true;
}
function nextTokenIsIdentifier() {
nextToken();
return isIdentifier();
}
function nextTokenIsIdentifierOrKeyword() {
nextToken();
return tokenIsIdentifierOrKeyword(token());
}
function isHeritageClauseExtendsOrImplementsKeyword(): boolean {
if (token() === SyntaxKind.ImplementsKeyword ||
token() === SyntaxKind.ExtendsKeyword) {
return lookAhead(nextTokenIsStartOfExpression);
}
return false;
}
function nextTokenIsStartOfExpression() {
nextToken();
return isStartOfExpression();
}
// True if positioned at a list terminator
function isListTerminator(kind: ParsingContext): boolean {
if (token() === SyntaxKind.EndOfFileToken) {
// Being at the end of the file ends all lists.
return true;
}
switch (kind) {
case ParsingContext.BlockStatements:
case ParsingContext.SwitchClauses:
case ParsingContext.TypeMembers:
case ParsingContext.ClassMembers:
case ParsingContext.EnumMembers:
case ParsingContext.ObjectLiteralMembers:
case ParsingContext.ObjectBindingElements:
case ParsingContext.ImportOrExportSpecifiers:
return token() === SyntaxKind.CloseBraceToken;
case ParsingContext.SwitchClauseStatements:
return token() === SyntaxKind.CloseBraceToken || token() === SyntaxKind.CaseKeyword || token() === SyntaxKind.DefaultKeyword;
case ParsingContext.HeritageClauseElement:
return token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword;
case ParsingContext.VariableDeclarations:
return isVariableDeclaratorListTerminator();
case ParsingContext.TypeParameters:
// Tokens other than '>' are here for better error recovery
return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword;
case ParsingContext.ArgumentExpressions:
// Tokens other than ')' are here for better error recovery
return token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.SemicolonToken;
case ParsingContext.ArrayLiteralMembers:
case ParsingContext.TupleElementTypes:
case ParsingContext.ArrayBindingElements:
return token() === SyntaxKind.CloseBracketToken;
case ParsingContext.Parameters:
case ParsingContext.RestProperties:
// Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery
return token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.CloseBracketToken /*|| token === SyntaxKind.OpenBraceToken*/;
case ParsingContext.TypeArguments:
// All other tokens should cause the type-argument to terminate except comma token
return token() !== SyntaxKind.CommaToken;
case ParsingContext.HeritageClauses:
return token() === SyntaxKind.OpenBraceToken || token() === SyntaxKind.CloseBraceToken;
case ParsingContext.JsxAttributes:
return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.SlashToken;
case ParsingContext.JsxChildren:
return token() === SyntaxKind.LessThanToken && lookAhead(nextTokenIsSlash);
case ParsingContext.JSDocFunctionParameters:
return token() === SyntaxKind.CloseParenToken || token() === SyntaxKind.ColonToken || token() === SyntaxKind.CloseBraceToken;
case ParsingContext.JSDocTypeArguments:
return token() === SyntaxKind.GreaterThanToken || token() === SyntaxKind.CloseBraceToken;
case ParsingContext.JSDocTupleTypes:
return token() === SyntaxKind.CloseBracketToken || token() === SyntaxKind.CloseBraceToken;
case ParsingContext.JSDocRecordMembers:
return token() === SyntaxKind.CloseBraceToken;
}
}
function isVariableDeclaratorListTerminator(): boolean {
// If we can consume a semicolon (either explicitly, or with ASI), then consider us done
// with parsing the list of variable declarators.
if (canParseSemicolon()) {
return true;
}
// in the case where we're parsing the variable declarator of a 'for-in' statement, we
// are done if we see an 'in' keyword in front of us. Same with for-of
if (isInOrOfKeyword(token())) {
return true;
}
// ERROR RECOVERY TWEAK:
// For better error recovery, if we see an '=>' then we just stop immediately. We've got an
// arrow function here and it's going to be very unlikely that we'll resynchronize and get
// another variable declaration.
if (token() === SyntaxKind.EqualsGreaterThanToken) {
return true;
}
// Keep trying to parse out variable declarators.
return false;
}
// True if positioned at element or terminator of the current list or any enclosing list
function isInSomeParsingContext(): boolean {
for (let kind = 0; kind < ParsingContext.Count; kind++) {
if (parsingContext & (1 << kind)) {
if (isListElement(kind, /*inErrorRecovery*/ true) || isListTerminator(kind)) {
return true;
}
}
}
return false;
}
// Parses a list of elements
function parseList(kind: ParsingContext, parseElement: () => T): NodeArray {
const saveParsingContext = parsingContext;
parsingContext |= 1 << kind;
const result = createNodeArray();
while (!isListTerminator(kind)) {
if (isListElement(kind, /*inErrorRecovery*/ false)) {
const element = parseListElement(kind, parseElement);
result.push(element);
continue;
}
if (abortParsingListOrMoveToNextToken(kind)) {
break;
}
}
result.end = getNodeEnd();
parsingContext = saveParsingContext;
return result;
}
function parseListElement(parsingContext: ParsingContext, parseElement: () => T): T {
const node = currentNode(parsingContext);
if (node) {
return consumeNode(node);
}
return parseElement();
}
function currentNode(parsingContext: ParsingContext): Node {
// If there is an outstanding parse error that we've encountered, but not attached to
// some node, then we cannot get a node from the old source tree. This is because we
// want to mark the next node we encounter as being unusable.
//
// Note: This may be too conservative. Perhaps we could reuse the node and set the bit
// on it (or its leftmost child) as having the error. For now though, being conservative
// is nice and likely won't ever affect perf.
if (parseErrorBeforeNextFinishedNode) {
return undefined;
}
if (!syntaxCursor) {
// if we don't have a cursor, we could never return a node from the old tree.
return undefined;
}
const node = syntaxCursor.currentNode(scanner.getStartPos());
// Can't reuse a missing node.
if (nodeIsMissing(node)) {
return undefined;
}
// Can't reuse a node that intersected the change range.
if (node.intersectsChange) {
return undefined;
}
// Can't reuse a node that contains a parse error. This is necessary so that we
// produce the same set of errors again.
if (containsParseError(node)) {
return undefined;
}
// We can only reuse a node if it was parsed under the same strict mode that we're
// currently in. i.e. if we originally parsed a node in non-strict mode, but then
// the user added 'using strict' at the top of the file, then we can't use that node
// again as the presence of strict mode may cause us to parse the tokens in the file
// differently.
//
// Note: we *can* reuse tokens when the strict mode changes. That's because tokens
// are unaffected by strict mode. It's just the parser will decide what to do with it
// differently depending on what mode it is in.
//
// This also applies to all our other context flags as well.
const nodeContextFlags = node.flags & NodeFlags.ContextFlags;
if (nodeContextFlags !== contextFlags) {
return undefined;
}
// Ok, we have a node that looks like it could be reused. Now verify that it is valid
// in the current list parsing context that we're currently at.
if (!canReuseNode(node, parsingContext)) {
return undefined;
}
return node;
}
function consumeNode(node: Node) {
// Move the scanner so it is after the node we just consumed.
scanner.setTextPos(node.end);
nextToken();
return node;
}
function canReuseNode(node: Node, parsingContext: ParsingContext): boolean {
switch (parsingContext) {
case ParsingContext.ClassMembers:
return isReusableClassMember(node);
case ParsingContext.SwitchClauses:
return isReusableSwitchClause(node);
case ParsingContext.SourceElements:
case ParsingContext.BlockStatements:
case ParsingContext.SwitchClauseStatements:
return isReusableStatement(node);
case ParsingContext.EnumMembers:
return isReusableEnumMember(node);
case ParsingContext.TypeMembers:
return isReusableTypeMember(node);
case ParsingContext.VariableDeclarations:
return isReusableVariableDeclaration(node);
case ParsingContext.Parameters:
return isReusableParameter(node);
case ParsingContext.RestProperties:
return false;
// Any other lists we do not care about reusing nodes in. But feel free to add if
// you can do so safely. Danger areas involve nodes that may involve speculative
// parsing. If speculative parsing is involved with the node, then the range the
// parser reached while looking ahead might be in the edited range (see the example
// in canReuseVariableDeclaratorNode for a good case of this).
case ParsingContext.HeritageClauses:
// This would probably be safe to reuse. There is no speculative parsing with
// heritage clauses.
case ParsingContext.TypeParameters:
// This would probably be safe to reuse. There is no speculative parsing with
// type parameters. Note that that's because type *parameters* only occur in
// unambiguous *type* contexts. While type *arguments* occur in very ambiguous
// *expression* contexts.
case ParsingContext.TupleElementTypes:
// This would probably be safe to reuse. There is no speculative parsing with
// tuple types.
// Technically, type argument list types are probably safe to reuse. While
// speculative parsing is involved with them (since type argument lists are only
// produced from speculative parsing a < as a type argument list), we only have
// the types because speculative parsing succeeded. Thus, the lookahead never
// went past the end of the list and rewound.
case ParsingContext.TypeArguments:
// Note: these are almost certainly not safe to ever reuse. Expressions commonly
// need a large amount of lookahead, and we should not reuse them as they may
// have actually intersected the edit.
case ParsingContext.ArgumentExpressions:
// This is not safe to reuse for the same reason as the 'AssignmentExpression'
// cases. i.e. a property assignment may end with an expression, and thus might
// have lookahead far beyond it's old node.
case ParsingContext.ObjectLiteralMembers:
// This is probably not safe to reuse. There can be speculative parsing with
// type names in a heritage clause. There can be generic names in the type
// name list, and there can be left hand side expressions (which can have type
// arguments.)
case ParsingContext.HeritageClauseElement:
// Perhaps safe to reuse, but it's unlikely we'd see more than a dozen attributes
// on any given element. Same for children.
case ParsingContext.JsxAttributes:
case ParsingContext.JsxChildren:
}
return false;
}
function isReusableClassMember(node: Node) {
if (node) {
switch (node.kind) {
case SyntaxKind.Constructor:
case SyntaxKind.IndexSignature:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.SemicolonClassElement:
return true;
case SyntaxKind.MethodDeclaration:
// Method declarations are not necessarily reusable. An object-literal
// may have a method calls "constructor(...)" and we must reparse that
// into an actual .ConstructorDeclaration.
const methodDeclaration = node;
const nameIsConstructor = methodDeclaration.name.kind === SyntaxKind.Identifier &&
(methodDeclaration.name).originalKeywordKind === SyntaxKind.ConstructorKeyword;
return !nameIsConstructor;
}
}
return false;
}
function isReusableSwitchClause(node: Node) {
if (node) {
switch (node.kind) {
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
return true;
}
}
return false;
}
function isReusableStatement(node: Node) {
if (node) {
switch (node.kind) {
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.VariableStatement:
case SyntaxKind.Block:
case SyntaxKind.IfStatement:
case SyntaxKind.ExpressionStatement:
case SyntaxKind.ThrowStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.BreakStatement:
case SyntaxKind.ContinueStatement:
case SyntaxKind.ForInStatement:
case SyntaxKind.ForOfStatement:
case SyntaxKind.ForStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.WithStatement:
case SyntaxKind.EmptyStatement:
case SyntaxKind.TryStatement:
case SyntaxKind.LabeledStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.DebuggerStatement:
case SyntaxKind.ImportDeclaration:
case SyntaxKind.ImportEqualsDeclaration:
case SyntaxKind.ExportDeclaration:
case SyntaxKind.ExportAssignment:
case SyntaxKind.ModuleDeclaration:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.EnumDeclaration:
case SyntaxKind.TypeAliasDeclaration:
return true;
}
}
return false;
}
function isReusableEnumMember(node: Node) {
return node.kind === SyntaxKind.EnumMember;
}
function isReusableTypeMember(node: Node) {
if (node) {
switch (node.kind) {
case SyntaxKind.ConstructSignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.PropertySignature:
case SyntaxKind.CallSignature:
return true;
}
}
return false;
}
function isReusableVariableDeclaration(node: Node) {
if (node.kind !== SyntaxKind.VariableDeclaration) {
return false;
}
// Very subtle incremental parsing bug. Consider the following code:
//
// let v = new List < A, B
//
// This is actually legal code. It's a list of variable declarators "v = new List