/// /// /// module ts { var nodeConstructors = new Array Node>(SyntaxKind.Count); export function getNodeConstructor(kind: SyntaxKind): new () => Node { return nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)); } function createRootNode(kind: SyntaxKind, pos: number, end: number, flags: NodeFlags): Node { var node = new (getNodeConstructor(kind))(); node.pos = pos; node.end = end; node.flags = flags; return node; } interface ReferenceComments { referencedFiles: FileReference[]; amdDependencies: string[]; amdModuleName: string; } export function getSourceFileOfNode(node: Node): SourceFile { while (node && node.kind !== SyntaxKind.SourceFile) node = node.parent; return node; } // This is a useful function for debugging purposes. export function nodePosToString(node: Node): string { var file = getSourceFileOfNode(node); var loc = file.getLineAndCharacterFromPosition(node.pos); return file.filename + "(" + loc.line + "," + loc.character + ")"; } export function getStartPosOfNode(node: Node): number { return node.pos; } export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile): number { // With nodes that have no width (i.e. 'Missing' nodes), we actually *don't* // want to skip trivia because this will launch us forward to the next token. if (node.pos === node.end) { return node.pos; } return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos); } export function getSourceTextOfNodeFromSourceFile(sourceFile: SourceFile, node: Node): string { var text = sourceFile.text; return text.substring(skipTrivia(text, node.pos), node.end); } export function getTextOfNodeFromSourceText(sourceText: string, node: Node): string { return sourceText.substring(skipTrivia(sourceText, node.pos), node.end); } export function getTextOfNode(node: Node): string { return getSourceTextOfNodeFromSourceFile(getSourceFileOfNode(node), node); } // Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' export function escapeIdentifier(identifier: string): string { return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier; } // Remove extra underscore from escaped identifier export function unescapeIdentifier(identifier: string): string { return identifier.length >= 3 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ && identifier.charCodeAt(2) === CharacterCodes._ ? identifier.substr(1) : identifier; } // TODO(jfreeman): Implement declarationNameToString for computed properties // Return display name of an identifier export function declarationNameToString(name: DeclarationName) { return name.kind === SyntaxKind.Missing ? "(Missing)" : getTextOfNode(name); } export function createDiagnosticForNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): Diagnostic { node = getErrorSpanForNode(node); var file = getSourceFileOfNode(node); var start = node.kind === SyntaxKind.Missing ? node.pos : skipTrivia(file.text, node.pos); var length = node.end - start; return createFileDiagnostic(file, start, length, message, arg0, arg1, arg2); } export function createDiagnosticForNodeFromMessageChain(node: Node, messageChain: DiagnosticMessageChain, newLine: string): Diagnostic { node = getErrorSpanForNode(node); var file = getSourceFileOfNode(node); var start = skipTrivia(file.text, node.pos); var length = node.end - start; return flattenDiagnosticChain(file, start, length, messageChain, newLine); } export function getErrorSpanForNode(node: Node): Node { var errorSpan: Node; switch (node.kind) { // This list is a work in progress. Add missing node kinds to improve their error // spans. case SyntaxKind.VariableDeclaration: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.EnumMember: errorSpan = (node).name; break; } // We now have the ideal error span, but it may be a node that is optional and absent // (e.g. the name of a function expression), in which case errorSpan will be undefined. // Alternatively, it might be required and missing (e.g. the name of a module), in which // case its pos will equal its end (length 0). In either of these cases, we should fall // back to the original node that the error was issued on. return errorSpan && errorSpan.pos < errorSpan.end ? errorSpan : node; } export function isExternalModule(file: SourceFile): boolean { return file.externalModuleIndicator !== undefined; } export function isDeclarationFile(file: SourceFile): boolean { return (file.flags & NodeFlags.DeclarationFile) !== 0; } export function isConstEnumDeclaration(node: Declaration): boolean { return node.kind === SyntaxKind.EnumDeclaration && isConst(node); } export function isConst(node: Declaration): boolean { return !!(node.flags & NodeFlags.Const); } export function isLet(node: Declaration): boolean { return !!(node.flags & NodeFlags.Let); } export function isPrologueDirective(node: Node): boolean { return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral; } function isEvalOrArgumentsIdentifier(node: Node): boolean { return node.kind === SyntaxKind.Identifier && (node).text && ((node).text === "eval" || (node).text === "arguments"); } /// Should be called only on prologue directives (isPrologueDirective(node) should be true) function isUseStrictPrologueDirective(node: Node): boolean { Debug.assert(isPrologueDirective(node)); return ((node).expression).text === "use strict"; } export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode?: SourceFile) { sourceFileOfNode = sourceFileOfNode || getSourceFileOfNode(node); // If parameter/type parameter, the prev token trailing comments are part of this node too if (node.kind === SyntaxKind.Parameter || node.kind === SyntaxKind.TypeParameter) { // e.g. (/** blah */ a, /** blah */ b); return concatenate(getTrailingCommentRanges(sourceFileOfNode.text, node.pos), // e.g.: ( // /** blah */ a, // /** blah */ b); getLeadingCommentRanges(sourceFileOfNode.text, node.pos)); } else { return getLeadingCommentRanges(sourceFileOfNode.text, node.pos); } } export function getJsDocComments(node: Declaration, sourceFileOfNode: SourceFile) { return filter(getLeadingCommentRangesOfNode(node, sourceFileOfNode), comment => isJsDocComment(comment)); function isJsDocComment(comment: CommentRange) { // True if the comment starts with '/**' but not if it is '/**/' return sourceFileOfNode.text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk && sourceFileOfNode.text.charCodeAt(comment.pos + 2) === CharacterCodes.asterisk && sourceFileOfNode.text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash; } } export var fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*/ // 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, cbNodes?: (nodes: Node[]) => T): T { function child(node: Node): T { if (node) return cbNode(node); } function children(nodes: Node[]) { if (nodes) { if (cbNodes) return cbNodes(nodes); var result: T; for (var i = 0, len = nodes.length; i < len; i++) { if (result = cbNode(nodes[i])) break; } return result; } } if (!node) return; switch (node.kind) { case SyntaxKind.QualifiedName: return child((node).left) || child((node).right); case SyntaxKind.TypeParameter: return child((node).name) || child((node).constraint); case SyntaxKind.Parameter: return child((node).name) || child((node).type) || child((node).initializer); case SyntaxKind.Property: case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: return child((node).name) || child((node).type) || child((node).initializer); case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: return children((node).typeParameters) || children((node).parameters) || child((node).type); case SyntaxKind.Method: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: return child((node).name) || children((node).typeParameters) || children((node).parameters) || child((node).type) || child((node).body); case SyntaxKind.TypeReference: return child((node).typeName) || children((node).typeArguments); case SyntaxKind.TypeQuery: return child((node).exprName); case SyntaxKind.TypeLiteral: return children((node).members); case SyntaxKind.ArrayType: return child((node).elementType); case SyntaxKind.TupleType: return children((node).elementTypes); case SyntaxKind.UnionType: return children((node).types); case SyntaxKind.ParenType: return child((node).type); case SyntaxKind.ArrayLiteral: return children((node).elements); case SyntaxKind.ObjectLiteral: return children((node).properties); case SyntaxKind.PropertyAccess: return child((node).left) || child((node).right); case SyntaxKind.IndexedAccess: return child((node).object) || child((node).index); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return child((node).func) || children((node).typeArguments) || children((node).arguments); case SyntaxKind.TaggedTemplateExpression: return child((node).tag) || child((node).template); case SyntaxKind.TypeAssertion: return child((node).type) || child((node).operand); case SyntaxKind.ParenExpression: return child((node).expression); case SyntaxKind.PrefixOperator: case SyntaxKind.PostfixOperator: return child((node).operand); case SyntaxKind.BinaryExpression: return child((node).left) || child((node).right); case SyntaxKind.ConditionalExpression: return child((node).condition) || child((node).whenTrue) || child((node).whenFalse); case SyntaxKind.Block: case SyntaxKind.TryBlock: case SyntaxKind.FinallyBlock: case SyntaxKind.FunctionBlock: case SyntaxKind.ModuleBlock: case SyntaxKind.SourceFile: return children((node).statements); case SyntaxKind.VariableStatement: return children((node).declarations); case SyntaxKind.ExpressionStatement: return child((node).expression); case SyntaxKind.IfStatement: return child((node).expression) || child((node).thenStatement) || child((node).elseStatement); case SyntaxKind.DoStatement: return child((node).statement) || child((node).expression); case SyntaxKind.WhileStatement: return child((node).expression) || child((node).statement); case SyntaxKind.ForStatement: return children((node).declarations) || child((node).initializer) || child((node).condition) || child((node).iterator) || child((node).statement); case SyntaxKind.ForInStatement: return children((node).declarations) || child((node).variable) || child((node).expression) || child((node).statement); case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: return child((node).label); case SyntaxKind.ReturnStatement: return child((node).expression); case SyntaxKind.WithStatement: return child((node).expression) || child((node).statement); case SyntaxKind.SwitchStatement: return child((node).expression) || children((node).clauses); case SyntaxKind.CaseClause: case SyntaxKind.DefaultClause: return child((node).expression) || children((node).statements); case SyntaxKind.LabeledStatement: return child((node).label) || child((node).statement); case SyntaxKind.ThrowStatement: return child((node).expression); case SyntaxKind.TryStatement: return child((node).tryBlock) || child((node).catchBlock) || child((node).finallyBlock); case SyntaxKind.CatchBlock: return child((node).variable) || children((node).statements); case SyntaxKind.VariableDeclaration: return child((node).name) || child((node).type) || child((node).initializer); case SyntaxKind.ClassDeclaration: return child((node).name) || children((node).typeParameters) || child((node).baseType) || children((node).implementedTypes) || children((node).members); case SyntaxKind.InterfaceDeclaration: return child((node).name) || children((node).typeParameters) || children((node).baseTypes) || children((node).members); case SyntaxKind.TypeAliasDeclaration: return child((node).name) || child((node).type); case SyntaxKind.EnumDeclaration: return child((node).name) || children((node).members); case SyntaxKind.EnumMember: return child((node).name) || child((node).initializer); case SyntaxKind.ModuleDeclaration: return child((node).name) || child((node).body); case SyntaxKind.ImportDeclaration: return child((node).name) || child((node).entityName) || child((node).externalModuleName); case SyntaxKind.ExportAssignment: return child((node).exportName); case SyntaxKind.TemplateExpression: return child((node).head) || children((node).templateSpans); case SyntaxKind.TemplateSpan: return child((node).expression) || child((node).literal); } } // Warning: This has the same semantics as the forEach family of functions, // in that traversal terminates in the event that 'visitor' supplies a truthy value. export function forEachReturnStatement(body: Block, visitor: (stmt: ReturnStatement) => T): T { return traverse(body); function traverse(node: Node): T { switch (node.kind) { case SyntaxKind.ReturnStatement: return visitor(node); case SyntaxKind.Block: case SyntaxKind.FunctionBlock: case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.WithStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.CaseClause: case SyntaxKind.DefaultClause: case SyntaxKind.LabeledStatement: case SyntaxKind.TryStatement: case SyntaxKind.TryBlock: case SyntaxKind.CatchBlock: case SyntaxKind.FinallyBlock: return forEachChild(node, traverse); } } } export function isAnyFunction(node: Node): boolean { if (node) { switch (node.kind) { case SyntaxKind.FunctionExpression: case SyntaxKind.FunctionDeclaration: case SyntaxKind.ArrowFunction: case SyntaxKind.Method: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.Constructor: return true; } } return false; } export function getContainingFunction(node: Node): SignatureDeclaration { while (true) { node = node.parent; if (!node || isAnyFunction(node)) { return node; } } } export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node { while (true) { node = node.parent; if (!node) { return undefined; } switch (node.kind) { case SyntaxKind.ArrowFunction: if (!includeArrowFunctions) { continue; } // Fall through case SyntaxKind.FunctionDeclaration: case SyntaxKind.FunctionExpression: case SyntaxKind.ModuleDeclaration: case SyntaxKind.Property: case SyntaxKind.Method: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.EnumDeclaration: case SyntaxKind.SourceFile: return node; } } } export function getSuperContainer(node: Node): Node { while (true) { node = node.parent; if (!node) { return undefined; } switch (node.kind) { case SyntaxKind.Property: case SyntaxKind.Method: case SyntaxKind.Constructor: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: return node; } } } export function isExpression(node: Node): boolean { switch (node.kind) { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.ArrayLiteral: case SyntaxKind.ObjectLiteral: case SyntaxKind.PropertyAccess: case SyntaxKind.IndexedAccess: case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.TypeAssertion: case SyntaxKind.ParenExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: case SyntaxKind.PrefixOperator: case SyntaxKind.PostfixOperator: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: case SyntaxKind.TemplateExpression: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.OmittedExpression: return true; case SyntaxKind.QualifiedName: while (node.parent.kind === SyntaxKind.QualifiedName) node = node.parent; return node.parent.kind === SyntaxKind.TypeQuery; case SyntaxKind.Identifier: if (node.parent.kind === SyntaxKind.TypeQuery) { return true; } // fall through case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: var parent = node.parent; switch (parent.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.Parameter: case SyntaxKind.Property: case SyntaxKind.EnumMember: case SyntaxKind.PropertyAssignment: return (parent).initializer === node; case SyntaxKind.ExpressionStatement: case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.ReturnStatement: case SyntaxKind.WithStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.CaseClause: case SyntaxKind.ThrowStatement: case SyntaxKind.SwitchStatement: return (parent).expression === node; case SyntaxKind.ForStatement: return (parent).initializer === node || (parent).condition === node || (parent).iterator === node; case SyntaxKind.ForInStatement: return (parent).variable === node || (parent).expression === node; case SyntaxKind.TypeAssertion: return node === (parent).operand; case SyntaxKind.TemplateSpan: return node === (parent).expression; default: if (isExpression(parent)) { return true; } } } return false; } export function hasRestParameters(s: SignatureDeclaration): boolean { return s.parameters.length > 0 && (s.parameters[s.parameters.length - 1].flags & NodeFlags.Rest) !== 0; } export function isLiteralKind(kind: SyntaxKind): boolean { return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; } export function isTextualLiteralKind(kind: SyntaxKind): boolean { return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; } export function isTemplateLiteralKind(kind: SyntaxKind): boolean { return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken; } export function isInAmbientContext(node: Node): boolean { while (node) { if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) return true; node = node.parent; } return false; } export function isDeclaration(node: Node): boolean { switch (node.kind) { case SyntaxKind.TypeParameter: case SyntaxKind.Parameter: case SyntaxKind.VariableDeclaration: case SyntaxKind.Property: case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.EnumMember: case SyntaxKind.Method: case SyntaxKind.FunctionDeclaration: case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.ImportDeclaration: return true; } return false; } export function isStatement(n: Node): boolean { switch(n.kind) { case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: case SyntaxKind.DebuggerStatement: case SyntaxKind.DoStatement: case SyntaxKind.ExpressionStatement: case SyntaxKind.EmptyStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ForStatement: case SyntaxKind.IfStatement: case SyntaxKind.LabeledStatement: case SyntaxKind.ReturnStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.ThrowKeyword: case SyntaxKind.TryStatement: case SyntaxKind.VariableStatement: case SyntaxKind.WhileStatement: case SyntaxKind.WithStatement: case SyntaxKind.ExportAssignment: return true; default: return false; } } // True if the given identifier, string literal, or number literal is the name of a declaration node export function isDeclarationOrFunctionExpressionOrCatchVariableName(name: Node): boolean { if (name.kind !== SyntaxKind.Identifier && name.kind !== SyntaxKind.StringLiteral && name.kind !== SyntaxKind.NumericLiteral) { return false; } var parent = name.parent; if (isDeclaration(parent) || parent.kind === SyntaxKind.FunctionExpression) { return (parent).name === name; } if (parent.kind === SyntaxKind.CatchBlock) { return (parent).variable === name; } return false; } export function tryResolveScriptReference(program: Program, sourceFile: SourceFile, reference: FileReference) { if (!program.getCompilerOptions().noResolve) { var referenceFileName = isRootedDiskPath(reference.filename) ? reference.filename : combinePaths(getDirectoryPath(sourceFile.filename), reference.filename); referenceFileName = getNormalizedAbsolutePath(referenceFileName, program.getCompilerHost().getCurrentDirectory()); return program.getSourceFile(referenceFileName); } } export function getAncestor(node: Node, kind: SyntaxKind): Node { switch (kind) { // special-cases that can be come first case SyntaxKind.ClassDeclaration: while (node) { switch (node.kind) { case SyntaxKind.ClassDeclaration: return node; case SyntaxKind.EnumDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.ImportDeclaration: // early exit cases - declarations cannot be nested in classes return undefined; default: node = node.parent; continue; } } break; default: while (node) { if (node.kind === kind) { return node; } node = node.parent; } break; } return undefined; } const enum ParsingContext { SourceElements, // Elements in source file ModuleElements, // Elements in module declaration BlockStatements, // Statements in block SwitchClauses, // Clauses in switch statement SwitchClauseStatements, // Statements in switch clause TypeMembers, // Members in interface or type literal ClassMembers, // Members in class declaration EnumMembers, // Members in enum declaration BaseTypeReferences, // Type references in extends or implements clause VariableDeclarations, // Variable declarations in variable statement ArgumentExpressions, // Expressions in argument list ObjectLiteralMembers, // Members in object literal ArrayLiteralMembers, // Members in array literal Parameters, // Parameters in parameter list TypeParameters, // Type parameters in type parameter list TypeArguments, // Type arguments in type argument list TupleElementTypes, // Element types in tuple element type list Count // Number of parsing contexts } const enum Tristate { False, True, Unknown } function parsingContextErrors(context: ParsingContext): DiagnosticMessage { switch (context) { case ParsingContext.SourceElements: return Diagnostics.Declaration_or_statement_expected; case ParsingContext.ModuleElements: return Diagnostics.Declaration_or_statement_expected; case ParsingContext.BlockStatements: return Diagnostics.Statement_expected; case ParsingContext.SwitchClauses: return Diagnostics.case_or_default_expected; case ParsingContext.SwitchClauseStatements: return Diagnostics.Statement_expected; case ParsingContext.TypeMembers: return Diagnostics.Property_or_signature_expected; case ParsingContext.ClassMembers: return Diagnostics.Unexpected_token_A_constructor_method_accessor_or_property_was_expected; case ParsingContext.EnumMembers: return Diagnostics.Enum_member_expected; case ParsingContext.BaseTypeReferences: return Diagnostics.Type_reference_expected; case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_expected; case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected; case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected; case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected; case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected; case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected; case ParsingContext.TupleElementTypes: return Diagnostics.Type_expected; } }; const enum LookAheadMode { NotLookingAhead, NoErrorYet, Error } export interface ReferencePathMatchResult { fileReference?: FileReference diagnostic?: DiagnosticMessage isNoDefaultLib?: boolean } export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult { var simpleReferenceRegEx = /^\/\/\/\s*/gim; if (simpleReferenceRegEx.exec(comment)) { if (isNoDefaultLibRegEx.exec(comment)) { return { isNoDefaultLib: true } } else { var matchResult = fullTripleSlashReferencePathRegEx.exec(comment); if (matchResult) { var start = commentRange.pos; var end = commentRange.end; return { fileReference: { pos: start, end: end, filename: matchResult[3] }, isNoDefaultLib: false }; } else { return { diagnostic: Diagnostics.Invalid_reference_directive_syntax, isNoDefaultLib: false }; } } } return undefined; } export function isKeyword(token: SyntaxKind): boolean { return SyntaxKind.FirstKeyword <= token && token <= SyntaxKind.LastKeyword; } export function isTrivia(token: SyntaxKind) { return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken; } export function isUnterminatedTemplateEnd(node: LiteralExpression) { Debug.assert(node.kind === SyntaxKind.NoSubstitutionTemplateLiteral || node.kind === SyntaxKind.TemplateTail); var sourceText = getSourceFileOfNode(node).text; // If we're not at the EOF, we know we must be terminated. if (node.end !== sourceText.length) { return false; } // If we didn't end in a backtick, we must still be in the middle of a template. // If we did, make sure that it's not the *initial* backtick. return sourceText.charCodeAt(node.end - 1) !== CharacterCodes.backtick || node.text.length === 0; } export function isModifier(token: SyntaxKind): boolean { switch (token) { case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.StaticKeyword: case SyntaxKind.ExportKeyword: case SyntaxKind.DeclareKeyword: return true; } return false; } function modifierToFlag(token: SyntaxKind): NodeFlags { switch (token) { case SyntaxKind.StaticKeyword: return NodeFlags.Static; case SyntaxKind.PublicKeyword: return NodeFlags.Public; case SyntaxKind.ProtectedKeyword: return NodeFlags.Protected; case SyntaxKind.PrivateKeyword: return NodeFlags.Private; case SyntaxKind.ExportKeyword: return NodeFlags.Export; case SyntaxKind.DeclareKeyword: return NodeFlags.Ambient; } return 0; } export function createSourceFile(filename: string, sourceText: string, languageVersion: ScriptTarget, version: string, isOpen: boolean = false): SourceFile { var file: SourceFile; var scanner: Scanner; var token: SyntaxKind; var parsingContext: ParsingContext; var commentRanges: TextRange[]; var identifiers: Map = {}; var identifierCount = 0; var nodeCount = 0; var lineStarts: number[]; var lookAheadMode = LookAheadMode.NotLookingAhead; // 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 // rewinding, we need to store and restore this as the mode may have changed. // // When adding more parser context flags, consider which is the more common case that the // flag will be in. This should be hte '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 hte 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. var strictModeContext = false; var disallowInContext = false; function allowInAnd(func: () => T): T { if (disallowInContext) { setDisallowInContext(false); var result = func(); setDisallowInContext(true); return result; } // no need to do anything special if 'in' is already allowed. return func(); } function disallowInAnd(func: () => T): T { if (disallowInContext) { // no need to do anything special if 'in' is already disallowed. return func(); } setDisallowInContext(true); var result = func(); setDisallowInContext(false); return result; } function setDisallowInContext(val: boolean) { disallowInContext = val; } function getLineStarts(): number[] { return lineStarts || (lineStarts = computeLineStarts(sourceText)); } function getLineAndCharacterFromSourcePosition(position: number) { return getLineAndCharacterOfPosition(getLineStarts(), position); } function getPositionFromSourceLineAndCharacter(line: number, character: number): number { return getPositionFromLineAndCharacter(getLineStarts(), line, character); } function error(message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { var start = scanner.getTokenPos(); var length = scanner.getTextPos() - start; errorAtPos(start, length, message, arg0, arg1, arg2); } function errorAtPos(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): void { var lastErrorPos = file.parseDiagnostics.length ? file.parseDiagnostics[file.parseDiagnostics.length - 1].start : -1; if (start !== lastErrorPos) { var diagnostic = createFileDiagnostic(file, start, length, message, arg0, arg1, arg2); diagnostic.isParseError = true; file.parseDiagnostics.push(diagnostic); } if (lookAheadMode === LookAheadMode.NoErrorYet) { lookAheadMode = LookAheadMode.Error; } } function scanError(message: DiagnosticMessage) { var pos = scanner.getTextPos(); errorAtPos(pos, 0, message); } function onComment(pos: number, end: number) { if (commentRanges) commentRanges.push({ pos: pos, end: end }); } function getNodePos(): number { return scanner.getStartPos(); } function getNodeEnd(): number { return scanner.getStartPos(); } function nextToken(): SyntaxKind { return token = scanner.scan(); } function getTokenPos(pos: number): number { return skipTrivia(sourceText, pos); } function reScanGreaterToken(): SyntaxKind { return token = scanner.reScanGreaterToken(); } function reScanSlashToken(): SyntaxKind { return token = scanner.reScanSlashToken(); } function reScanTemplateToken(): SyntaxKind { return token = scanner.reScanTemplateToken(); } function lookAheadHelper(callback: () => T, alwaysResetState: 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). var saveToken = token; var saveSyntacticErrorsLength = file.parseDiagnostics.length; // Keep track of the current look ahead mode (this matters if we have nested // speculative parsing). var saveLookAheadMode = lookAheadMode; // Mark that we're in speculative parsing and then try to parse out whatever code // the callback wants. lookAheadMode = LookAheadMode.NoErrorYet; var result = callback(); // If we switched from 1 to -1 then a parse error occurred during the callback. // If that's the case, then we want to act as if we never got any result at all. Debug.assert(lookAheadMode === LookAheadMode.Error || lookAheadMode === LookAheadMode.NoErrorYet); if (lookAheadMode === LookAheadMode.Error) { result = undefined; } // Now restore as appropriate. lookAheadMode = saveLookAheadMode; if (!result || alwaysResetState) { token = saveToken; file.parseDiagnostics.length = saveSyntacticErrorsLength; } return result; } function lookAhead(callback: () => T): T { var result: T; scanner.tryScan(() => { result = lookAheadHelper(callback, /*alwaysResetState:*/ true); // Returning false here indicates to the scanner that it should always jump // back to where it started. This makes sense as 'lookahead' acts as if // neither the parser nor scanner was affected by the operation. // // Note: the rewinding of the parser state is already handled in lookAheadHelper // (because we passed 'true' for alwaysResetState). return false; }); return result; } function tryParse(callback: () => T): T { return scanner.tryScan(() => lookAheadHelper(callback, /*alwaysResetState:*/ false)); } function isIdentifier(): boolean { return token === SyntaxKind.Identifier || (strictModeContext ? token > SyntaxKind.LastFutureReservedWord : token > SyntaxKind.LastReservedWord); } function parseExpected(t: SyntaxKind): boolean { if (token === t) { nextToken(); return true; } error(Diagnostics._0_expected, tokenToString(t)); return false; } function parseOptional(t: SyntaxKind): boolean { if (token === t) { nextToken(); return true; } return false; } 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(): void { if (canParseSemicolon()) { if (token === SyntaxKind.SemicolonToken) { // consume the semicolon if it was explicitly provided. nextToken(); } } else { error(Diagnostics._0_expected, ";"); } } function createNode(kind: SyntaxKind, pos?: number): Node { nodeCount++; var node = new (nodeConstructors[kind] || (nodeConstructors[kind] = objectAllocator.getNodeConstructor(kind)))(); if (!(pos >= 0)) { pos = scanner.getStartPos(); } node.pos = pos; node.end = pos; return node; } function finishNode(node: T): T { node.end = scanner.getStartPos(); if (strictModeContext) { node.flags |= NodeFlags.ParsedInStrictModeContext; } if (disallowInContext) { node.flags |= NodeFlags.ParsedInDisallowInContext } return node; } function createMissingNode(pos?: number): Node { return createNode(SyntaxKind.Missing, pos); } function internIdentifier(text: string): string { text = escapeIdentifier(text); return hasProperty(identifiers, text) ? identifiers[text] : (identifiers[text] = text); } // 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): Identifier { identifierCount++; if (isIdentifier) { var node = createNode(SyntaxKind.Identifier); node.text = internIdentifier(scanner.getTokenValue()); nextToken(); return finishNode(node); } error(Diagnostics.Identifier_expected); var node = createMissingNode(); node.text = ""; return node; } function parseIdentifier(): Identifier { return createIdentifier(isIdentifier()); } function parseIdentifierName(): Identifier { return createIdentifier(token >= SyntaxKind.Identifier); } function isPropertyName(): boolean { return token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral; } function parsePropertyName(): Identifier { if (token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral) { return parseLiteralNode(/*internName:*/ true); } return parseIdentifierName(); } function parseContextualModifier(t: SyntaxKind): boolean { return token === t && tryParse(() => { nextToken(); return token === SyntaxKind.OpenBracketToken || isPropertyName(); }); } function parseAnyContextualModifier(): boolean { return isModifier(token) && tryParse(() => { nextToken(); return token === SyntaxKind.OpenBracketToken || isPropertyName(); }); } // True if positioned at the start of a list element function isListElement(kind: ParsingContext, inErrorRecovery: boolean): boolean { switch (kind) { case ParsingContext.SourceElements: case ParsingContext.ModuleElements: return isSourceElement(inErrorRecovery); case ParsingContext.BlockStatements: case ParsingContext.SwitchClauseStatements: return isStatement(inErrorRecovery); case ParsingContext.SwitchClauses: return token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; case ParsingContext.TypeMembers: return isStartOfTypeMember(); case ParsingContext.ClassMembers: return lookAhead(isClassMemberStart); case ParsingContext.EnumMembers: case ParsingContext.ObjectLiteralMembers: return isPropertyName(); case ParsingContext.BaseTypeReferences: return isIdentifier() && ((token !== SyntaxKind.ExtendsKeyword && token !== SyntaxKind.ImplementsKeyword) || !lookAhead(() => (nextToken(), isIdentifier()))); case ParsingContext.VariableDeclarations: case ParsingContext.TypeParameters: return isIdentifier(); case ParsingContext.ArgumentExpressions: return token === SyntaxKind.CommaToken || isStartOfExpression(); case ParsingContext.ArrayLiteralMembers: return token === SyntaxKind.CommaToken || isStartOfExpression(); case ParsingContext.Parameters: return isStartOfParameter(); case ParsingContext.TypeArguments: case ParsingContext.TupleElementTypes: return token === SyntaxKind.CommaToken || isStartOfType(); } Debug.fail("Non-exhaustive case in 'isListElement'."); } // 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.ModuleElements: case ParsingContext.BlockStatements: case ParsingContext.SwitchClauses: case ParsingContext.TypeMembers: case ParsingContext.ClassMembers: case ParsingContext.EnumMembers: case ParsingContext.ObjectLiteralMembers: return token === SyntaxKind.CloseBraceToken; case ParsingContext.SwitchClauseStatements: return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; case ParsingContext.BaseTypeReferences: 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: return token === SyntaxKind.CloseBracketToken; case ParsingContext.Parameters: // 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: // Tokens other than '>' are here for better error recovery return token === SyntaxKind.GreaterThanToken || token === SyntaxKind.OpenParenToken; } } 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. if (token === SyntaxKind.InKeyword) { 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 (var 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, checkForStrictMode: boolean, parseElement: () => T): NodeArray { var saveParsingContext = parsingContext; parsingContext |= 1 << kind; var result = >[]; result.pos = getNodePos(); var savedStrictModeContext = strictModeContext; while (!isListTerminator(kind)) { if (isListElement(kind, /* inErrorRecovery */ false)) { var element = parseElement(); result.push(element); // test elements only if we are not already in strict mode if (!strictModeContext && checkForStrictMode) { if (isPrologueDirective(element)) { if (isUseStrictPrologueDirective(element)) { strictModeContext = true; checkForStrictMode = false; } } else { checkForStrictMode = false; } } } else { error(parsingContextErrors(kind)); if (isInSomeParsingContext()) { break; } nextToken(); } } strictModeContext = savedStrictModeContext; result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; } // Parses a comma-delimited list of elements function parseDelimitedList(kind: ParsingContext, parseElement: () => T): NodeArray { var saveParsingContext = parsingContext; parsingContext |= 1 << kind; var result = >[]; result.pos = getNodePos(); var commaStart = -1; // Meaning the previous token was not a comma while (true) { if (isListElement(kind, /* inErrorRecovery */ false)) { result.push(parseElement()); commaStart = scanner.getTokenPos(); if (parseOptional(SyntaxKind.CommaToken)) { continue; } commaStart = -1; // Back to the state where the last token was not a comma if (isListTerminator(kind)) { break; } error(Diagnostics._0_expected, ","); } else if (isListTerminator(kind)) { break; } else { error(parsingContextErrors(kind)); if (isInSomeParsingContext()) { break; } nextToken(); } } // Recording the trailing comma is deliberately done after the previous // loop, and not just if we see a list terminator. This is because the list // may have ended incorrectly, but it is still important to know if there // was a trailing comma. // Check if the last token was a comma. if (commaStart >= 0) { // Always preserve a trailing comma by marking it on the NodeArray result.hasTrailingComma = true; } result.end = getNodeEnd(); parsingContext = saveParsingContext; return result; } function createMissingList(): NodeArray { var pos = getNodePos(); var result = >[]; result.pos = pos; result.end = pos; return result; } function parseBracketedList(kind: ParsingContext, parseElement: () => T, startToken: SyntaxKind, endToken: SyntaxKind): NodeArray { if (parseExpected(startToken)) { var result = parseDelimitedList(kind, parseElement); parseExpected(endToken); return result; } return createMissingList(); } // The allowReservedWords parameter controls whether reserved words are permitted after the first dot function parseEntityName(allowReservedWords: boolean): EntityName { var entity: EntityName = parseIdentifier(); while (parseOptional(SyntaxKind.DotToken)) { var node = createNode(SyntaxKind.QualifiedName, entity.pos); node.left = entity; node.right = allowReservedWords ? parseIdentifierName() : parseIdentifier(); entity = finishNode(node); } return entity; } function parseTokenNode(): Node { var node = createNode(token); nextToken(); return finishNode(node); } function parseTemplateExpression() { var template = createNode(SyntaxKind.TemplateExpression); template.head = parseLiteralNode(); Debug.assert(template.head.kind === SyntaxKind.TemplateHead, "Template head has wrong token kind"); var templateSpans = >[]; templateSpans.pos = getNodePos(); do { templateSpans.push(parseTemplateSpan()); } while (templateSpans[templateSpans.length - 1].literal.kind === SyntaxKind.TemplateMiddle) templateSpans.end = getNodeEnd(); template.templateSpans = templateSpans; return finishNode(template); } function parseTemplateSpan(): TemplateSpan { var span = createNode(SyntaxKind.TemplateSpan); span.expression = allowInAnd(parseExpression); var literal: LiteralExpression; if (token === SyntaxKind.CloseBraceToken) { reScanTemplateToken() literal = parseLiteralNode(); } else { error(Diagnostics.Invalid_template_literal_expected); literal = createMissingNode(); literal.text = ""; } span.literal = literal; return finishNode(span); } function parseLiteralNode(internName?: boolean): LiteralExpression { var node = createNode(token); var text = scanner.getTokenValue(); node.text = internName ? internIdentifier(text) : text; var tokenPos = scanner.getTokenPos(); nextToken(); finishNode(node); // Octal literals are not allowed in strict mode or ES5 // Note that theoretically the following condition would hold true literals like 009, // which is not octal.But because of how the scanner separates the tokens, we would // never get a token like this. Instead, we would get 00 and 9 as two separate tokens. // We also do not need to check for negatives because any prefix operator would be part of a // parent unary expression. if (node.kind === SyntaxKind.NumericLiteral && sourceText.charCodeAt(tokenPos) === CharacterCodes._0 && isOctalDigit(sourceText.charCodeAt(tokenPos + 1))) { node.flags |= NodeFlags.OctalLiteral; } return node; } function parseStringLiteral(): LiteralExpression { if (token === SyntaxKind.StringLiteral) { return parseLiteralNode(/*internName:*/ true); } error(Diagnostics.String_literal_expected); return createMissingNode(); } // TYPES function parseTypeReference(): TypeReferenceNode { var node = createNode(SyntaxKind.TypeReference); node.typeName = parseEntityName(/*allowReservedWords*/ false); if (!scanner.hasPrecedingLineBreak() && token === SyntaxKind.LessThanToken) { node.typeArguments = parseTypeArguments(); } return finishNode(node); } function parseTypeQuery(): TypeQueryNode { var node = createNode(SyntaxKind.TypeQuery); parseExpected(SyntaxKind.TypeOfKeyword); node.exprName = parseEntityName(/*allowReservedWords*/ true); return finishNode(node); } function parseTypeParameter(): TypeParameterDeclaration { var node = createNode(SyntaxKind.TypeParameter); node.name = parseIdentifier(); if (parseOptional(SyntaxKind.ExtendsKeyword)) { // It's not uncommon for people to write improper constraints to a generic. If the // user writes a constraint that is an expression and not an actual type, then parse // it out as an expression (so we can recover well), but report that a type is needed // instead. if (isStartOfType() || !isStartOfExpression()) { node.constraint = parseType(); } else { // It was not a type, and it looked like an expression. Parse out an expression // here so we recover well. Note: it is important that we call parseUnaryExpression // and not parseExpression here. If the user has: // // // // We do *not* want to consume the > as we're consuming the expression for "". node.expression = parseUnaryExpression(); } } return finishNode(node); } function parseTypeParameters(): NodeArray { if (token === SyntaxKind.LessThanToken) { return parseBracketedList(ParsingContext.TypeParameters, parseTypeParameter, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } } function parseParameterType(): TypeNode { return parseOptional(SyntaxKind.ColonToken) ? token === SyntaxKind.StringLiteral ? parseStringLiteral() : parseType() : undefined; } function isStartOfParameter(): boolean { return token === SyntaxKind.DotDotDotToken || isIdentifier() || isModifier(token); } function setModifiers(node: Node, modifiers: ModifiersArray) { if (modifiers) { node.flags |= modifiers.flags; node.modifiers = modifiers; } } function parseParameter(): ParameterDeclaration { var node = createNode(SyntaxKind.Parameter); var modifiers = parseModifiers(); setModifiers(node, modifiers); if (parseOptional(SyntaxKind.DotDotDotToken)) { node.flags |= NodeFlags.Rest; } node.name = parseIdentifier(); if (node.name.kind === SyntaxKind.Missing && node.flags === 0 && isModifier(token)) { // in cases like // 'use strict' // function foo(static) // isParameter('static') === true, because of isModifier('static') // however 'static' is not a legal identifier in a strict mode. // so result of this function will be ParameterDeclaration (flags = 0, name = missing, type = undefined, initializer = undefined) // and current token will not change => parsing of the enclosing parameter list will last till the end of time (or OOM) // to avoid this we'll advance cursor to the next token. nextToken(); } if (parseOptional(SyntaxKind.QuestionToken)) { node.flags |= NodeFlags.QuestionMark; } node.type = parseParameterType(); node.initializer = parseInitializer(/*inParameter*/ true); // Do not check for initializers in an ambient context for parameters. This is not // a grammar error because the grammar allows arbitrary call signatures in // an ambient context. // It is actually not necessary for this to be an error at all. The reason is that // function/constructor implementations are syntactically disallowed in ambient // contexts. In addition, parameter initializers are semantically disallowed in // overload signatures. So parameter initializers are transitively disallowed in // ambient contexts. return finishNode(node); } function parseSignature(kind: SyntaxKind, returnToken: SyntaxKind, returnTokenRequired: boolean): ParsedSignature { var signature = {}; fillSignature(kind, returnToken, returnTokenRequired, signature); return signature; } function fillSignature(kind: SyntaxKind, returnToken: SyntaxKind, returnTokenRequired: boolean, signature: ParsedSignature): void { if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } signature.typeParameters = parseTypeParameters(); signature.parameters = parseParameterList(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken); if (returnTokenRequired) { parseExpected(returnToken); signature.type = parseType(); } else if (parseOptional(returnToken)) { signature.type = parseType(); } } // Because we use this for index signatures as well, we sometimes use // parentheses, and sometimes use brackets. function parseParameterList(startDelimiter: SyntaxKind, endDelimiter: SyntaxKind) { return parseBracketedList(ParsingContext.Parameters, parseParameter, startDelimiter, endDelimiter); } function parseSignatureMember(kind: SyntaxKind, returnToken: SyntaxKind): SignatureDeclaration { var node = createNode(kind); fillSignature(kind, returnToken, /* returnTokenRequired */ false, node); parseSemicolon(); return finishNode(node); } function parseIndexSignatureMember(fullStart: number, modifiers: ModifiersArray): SignatureDeclaration { var node = createNode(SyntaxKind.IndexSignature, fullStart); setModifiers(node, modifiers); node.parameters = parseParameterList(SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); node.type = parseTypeAnnotation(); parseSemicolon(); return finishNode(node) } function parsePropertyOrMethod(): Declaration { var fullStart = scanner.getStartPos(); var name = parsePropertyName(); var flags = 0; if (parseOptional(SyntaxKind.QuestionToken)) { flags = NodeFlags.QuestionMark; } if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { var method = createNode(SyntaxKind.Method, fullStart); method.name = name; method.flags = flags; fillSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false, method); parseSemicolon(); return finishNode(method); } else { var property = createNode(SyntaxKind.Property, fullStart); property.name = name; property.flags = flags; property.type = parseTypeAnnotation(); parseSemicolon(); return finishNode(property); } } function isStartOfTypeMember(): boolean { switch (token) { case SyntaxKind.OpenParenToken: case SyntaxKind.LessThanToken: case SyntaxKind.OpenBracketToken: return true; default: return isPropertyName() && lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken || token === SyntaxKind.QuestionToken || token === SyntaxKind.ColonToken || canParseSemicolon()); } } function parseTypeMember(): Declaration { switch (token) { case SyntaxKind.OpenParenToken: case SyntaxKind.LessThanToken: return parseSignatureMember(SyntaxKind.CallSignature, SyntaxKind.ColonToken); case SyntaxKind.OpenBracketToken: return parseIndexSignatureMember(scanner.getStartPos(), /*modifiers:*/ undefined); case SyntaxKind.NewKeyword: if (lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken)) { return parseSignatureMember(SyntaxKind.ConstructSignature, SyntaxKind.ColonToken); } case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: return parsePropertyOrMethod(); default: if (token >= SyntaxKind.Identifier) { return parsePropertyOrMethod(); } } } function parseTypeLiteral(): TypeLiteralNode { var node = createNode(SyntaxKind.TypeLiteral); node.members = parseObjectType(); return finishNode(node); } function parseObjectType(): NodeArray { var members: NodeArray; if (parseExpected(SyntaxKind.OpenBraceToken)) { members = parseList(ParsingContext.TypeMembers, /*checkForStrictMode*/ false, parseTypeMember); parseExpected(SyntaxKind.CloseBraceToken); } else { members = createMissingList(); } return members; } function parseTupleType(): TupleTypeNode { var node = createNode(SyntaxKind.TupleType); node.elementTypes = parseBracketedList(ParsingContext.TupleElementTypes, parseType, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken); return finishNode(node); } function parseParenType(): ParenTypeNode { var node = createNode(SyntaxKind.ParenType); parseExpected(SyntaxKind.OpenParenToken); node.type = parseType(); parseExpected(SyntaxKind.CloseParenToken); return finishNode(node); } function parseFunctionType(typeKind: SyntaxKind): SignatureDeclaration { var node = createNode(typeKind); fillSignature(typeKind === SyntaxKind.FunctionType ? SyntaxKind.CallSignature : SyntaxKind.ConstructSignature, SyntaxKind.EqualsGreaterThanToken, /* returnTokenRequired */ true, node); return finishNode(node); } function parseKeywordAndNoDot(): Node { var node = parseTokenNode(); return token === SyntaxKind.DotToken ? undefined : node; } function parseNonArrayType(): TypeNode { switch (token) { case SyntaxKind.AnyKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.VoidKeyword: var node = tryParse(parseKeywordAndNoDot); return node || parseTypeReference(); case SyntaxKind.TypeOfKeyword: return parseTypeQuery(); case SyntaxKind.OpenBraceToken: return parseTypeLiteral(); case SyntaxKind.OpenBracketToken: return parseTupleType(); case SyntaxKind.OpenParenToken: return parseParenType(); default: if (isIdentifier()) { return parseTypeReference(); } } error(Diagnostics.Type_expected); return createMissingNode(); } function isStartOfType(): boolean { switch (token) { case SyntaxKind.AnyKeyword: case SyntaxKind.StringKeyword: case SyntaxKind.NumberKeyword: case SyntaxKind.BooleanKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.OpenBraceToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.LessThanToken: case SyntaxKind.NewKeyword: return true; case SyntaxKind.OpenParenToken: // Only consider '(' the start of a type if followed by ')', '...', an identifier, a modifier, // or something that starts a type. We don't want to consider things like '(1)' a type. return lookAhead(() => { nextToken(); return token === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType(); }); default: return isIdentifier(); } } function parsePrimaryType(): TypeNode { var type = parseNonArrayType(); while (!scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.OpenBracketToken)) { parseExpected(SyntaxKind.CloseBracketToken); var node = createNode(SyntaxKind.ArrayType, type.pos); node.elementType = type; type = finishNode(node); } return type; } function parseUnionType(): TypeNode { var type = parsePrimaryType(); if (token === SyntaxKind.BarToken) { var types = >[type]; types.pos = type.pos; while (parseOptional(SyntaxKind.BarToken)) { types.push(parsePrimaryType()); } types.end = getNodeEnd(); var node = createNode(SyntaxKind.UnionType, type.pos); node.types = types; type = finishNode(node); } return type; } function isStartOfFunctionType(): boolean { return token === SyntaxKind.LessThanToken || token === SyntaxKind.OpenParenToken && lookAhead(() => { nextToken(); if (token === SyntaxKind.CloseParenToken || token === SyntaxKind.DotDotDotToken) { // ( ) // ( ... return true; } if (isIdentifier() || isModifier(token)) { nextToken(); if (token === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken || token === SyntaxKind.QuestionToken || token === SyntaxKind.EqualsToken || isIdentifier() || isModifier(token)) { // ( id : // ( id , // ( id ? // ( id = // ( modifier id return true; } if (token === SyntaxKind.CloseParenToken) { nextToken(); if (token === SyntaxKind.EqualsGreaterThanToken) { // ( id ) => return true; } } } return false; }); } function parseType(): TypeNode { if (isStartOfFunctionType()) { return parseFunctionType(SyntaxKind.FunctionType); } if (token === SyntaxKind.NewKeyword) { return parseFunctionType(SyntaxKind.ConstructorType); } return parseUnionType(); } function parseTypeAnnotation(): TypeNode { return parseOptional(SyntaxKind.ColonToken) ? parseType() : undefined; } // EXPRESSIONS function isStartOfExpression(): boolean { switch (token) { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: case SyntaxKind.OpenParenToken: case SyntaxKind.OpenBracketToken: case SyntaxKind.OpenBraceToken: case SyntaxKind.FunctionKeyword: case SyntaxKind.NewKeyword: case SyntaxKind.SlashToken: case SyntaxKind.SlashEqualsToken: case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: case SyntaxKind.ExclamationToken: case SyntaxKind.DeleteKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: case SyntaxKind.LessThanToken: case SyntaxKind.Identifier: return true; default: return isIdentifier(); } } function isStartOfExpressionStatement(): boolean { // As per the grammar, neither '{' nor 'function' can start an expression statement. return token !== SyntaxKind.OpenBraceToken && token !== SyntaxKind.FunctionKeyword && isStartOfExpression(); } function parseExpression(): Expression { // Expression[in]: // AssignmentExpression[in] // Expression[in] , AssignmentExpression[in] var expr = parseAssignmentExpression(); while (parseOptional(SyntaxKind.CommaToken)) { expr = makeBinaryExpression(expr, SyntaxKind.CommaToken, parseAssignmentExpression()); } return expr; } function parseInitializer(inParameter: boolean): Expression { if (token !== SyntaxKind.EqualsToken) { // It's not uncommon during typing for the user to miss writing the '=' token. Check if // there is no newline after the last token and if we're on an expression. If so, parse // this as an equals-value clause with a missing equals. // NOTE: There are two places where we allow equals-value clauses. The first is in a // variable declarator. The second is with a parameter. For variable declarators // it's more likely that a { would be a allowed (as an object literal). While this // is also allowed for parameters, the risk is that we consume the { as an object // literal when it really will be for the block following the parameter. if (scanner.hasPrecedingLineBreak() || (inParameter && token === SyntaxKind.OpenBraceToken) || !isStartOfExpression()) { // preceding line break, open brace in a parameter (likely a function body) or current token is not an expression - // do not try to parse initializer return undefined; } } // Initializer[In, Yield] : // = AssignmentExpression[?In, ?Yield] parseExpected(SyntaxKind.EqualsToken); return parseAssignmentExpression(); } function parseAssignmentExpression(): Expression { // Augmented by TypeScript: // // AssignmentExpression[in]: // 1) ConditionalExpression[in] // 2) LeftHandSideExpression = AssignmentExpression[in] // 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[in] // 4) ArrowFunctionExpression <-- added by TypeScript // // Note: for ease of implementation we treat productions '2' and '3' as the same thing. // (i.e. they're both BinaryExpressions with an assignment operator in it). // First, check if we have an arrow function (production '4') that starts with a parenthesized // parameter list. If we do, 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. var arrowExpression = tryParseParenthesizedArrowFunctionExpression(); if (arrowExpression) { return arrowExpression; } // Now try to handle the rest of the cases. First, see if we can parse out up to and // including a conditional expression. var expr = parseConditionalExpression(); // 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(expr); } // Now see if we might be in cases '2' or '3'. // If the expression was a LHS expression, and we have an assignment operator, then // we're in '2' or '3'. Consume the assignment and return. if (isLeftHandSideExpression(expr) && isAssignmentOperator(token)) { var operator = token; nextToken(); return makeBinaryExpression(expr, operator, parseAssignmentExpression()); } // otherwise this was production '1'. Return whatever we parsed so far. return expr; } function parseSimpleArrowFunctionExpression(identifier: Identifier): Expression { Debug.assert(token === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); parseExpected(SyntaxKind.EqualsGreaterThanToken); var parameter = createNode(SyntaxKind.Parameter, identifier.pos); parameter.name = identifier; finishNode(parameter); var parameters = >[]; parameters.push(parameter); parameters.pos = parameter.pos; parameters.end = parameter.end; var signature = { parameters: parameters }; return parseArrowExpressionTail(identifier.pos, signature); } function tryParseParenthesizedArrowFunctionExpression(): Expression { // Indicates whether we are certain that we should parse an arrow expression. var triState = isParenthesizedArrowFunctionExpression(); if (triState === Tristate.False) { return undefined; } var pos = getNodePos(); if (triState === Tristate.True) { var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false); // 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. if (parseExpected(SyntaxKind.EqualsGreaterThanToken) || token === SyntaxKind.OpenBraceToken) { return parseArrowExpressionTail(pos, sig); } else { // If not, we're probably better off bailing out and returning a bogus function expression. return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /* name */ undefined, sig, createMissingNode()); } } // *Maybe* we had an arrow function and we need to try to parse it out, // rolling back and trying other parses if we fail. var sig = tryParseSignatureIfArrowOrBraceFollows(); if (sig) { parseExpected(SyntaxKind.EqualsGreaterThanToken); return parseArrowExpressionTail(pos, sig); } else { return undefined; } } // True -> We definitely expect a parenthesized arrow function here. // False -> There *cannot* be a parenthesized arrow function here. // Unknown -> There *might* be a parenthesized arrow function here. // Speculatively look ahead to be sure, and rollback if not. function isParenthesizedArrowFunctionExpression(): Tristate { if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { return lookAhead(() => { var first = token; var second = nextToken(); if (first === SyntaxKind.OpenParenToken) { if (second === SyntaxKind.CloseParenToken) { // Simple cases: "() =>", "(): ", and "() {". // This is an arrow function with no parameters. // The last one is not actually an arrow function, // but this is probably what the user intended. var third = nextToken(); switch (third) { case SyntaxKind.EqualsGreaterThanToken: case SyntaxKind.ColonToken: case SyntaxKind.OpenBraceToken: return Tristate.True; default: return Tristate.False; } } // Simple case: "(..." // This is an arrow function with a rest parameter. if (second === SyntaxKind.DotDotDotToken) { return Tristate.True; } // If we had "(" followed by something that's not an identifier, // then this definitely doesn't look like a lambda. // Note: we could be a little more lenient and allow // "(public" or "(private". These would not ever actually be allowed, // but we could provide a good error message instead of bailing out. if (!isIdentifier()) { return Tristate.False; } // If we have something like "(a:", then we must have a // type-annotated parameter in an arrow function expression. if (nextToken() === SyntaxKind.ColonToken) { return Tristate.True; } // This *could* be a parenthesized arrow function. // Return Unknown to let the caller know. return Tristate.Unknown; } else { Debug.assert(first === SyntaxKind.LessThanToken); // If we have "<" not followed by an identifier, // then this definitely is not an arrow function. if (!isIdentifier()) { return Tristate.False; } // This *could* be a parenthesized arrow function. return Tristate.Unknown; } }); } if (token === SyntaxKind.EqualsGreaterThanToken) { // ERROR RECOVERY TWEAK: // If we see a standalone => try to parse it as an arrow function expression as that's // likely what the user intended to write. return Tristate.True; } // Definitely not a parenthesized arrow function. return Tristate.False; } function tryParseSignatureIfArrowOrBraceFollows(): ParsedSignature { return tryParse(() => { var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false); // Parsing a signature isn't enough. // Parenthesized arrow signatures often look like other valid expressions. // For instance: // - "(x = 10)" is an assignment expression parsed as a signature with a default parameter value. // - "(x,y)" is a comma expression parsed as a signature with two parameters. // - "a ? (b): c" will have "(b):" parsed as a signature with a return type annotation. // // So we need just a bit of lookahead to ensure that it can only be a signature. if (token === SyntaxKind.EqualsGreaterThanToken || token === SyntaxKind.OpenBraceToken) { return sig; } return undefined; }); } function parseArrowExpressionTail(pos: number, sig: ParsedSignature): FunctionExpression { var body: Node; if (token === SyntaxKind.OpenBraceToken) { body = parseFunctionBlock(/* ignoreMissingOpenBrace */ false); } else if (isStatement(/* inErrorRecovery */ true) && !isStartOfExpressionStatement() && token !== SyntaxKind.FunctionKeyword) { // Check if we got a plain statement (i.e. no expression-statements, no functions 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 => // var 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 as true, parseBody will still error. body = parseFunctionBlock(/* ignoreMissingOpenBrace */ true); } else { body = parseAssignmentExpression(); } return makeFunctionExpression(SyntaxKind.ArrowFunction, pos, /* name */ undefined, sig, body); } function parseConditionalExpression(): Expression { // Note: we explicitly 'allowIn' in the whenTrue part of the condition expression, and // we do not that for the 'whenFalse' part. var expr = parseBinaryOperators(parseUnaryExpression(), /*minPrecedence:*/ 0); while (parseOptional(SyntaxKind.QuestionToken)) { var node = createNode(SyntaxKind.ConditionalExpression, expr.pos); node.condition = expr; node.whenTrue = allowInAnd(parseAssignmentExpression); parseExpected(SyntaxKind.ColonToken); node.whenFalse = parseAssignmentExpression(); expr = finishNode(node); } return expr; } function parseBinaryOperators(expr: Expression, minPrecedence: number): Expression { while (true) { reScanGreaterToken(); var precedence = getOperatorPrecedence(); if (precedence && precedence > minPrecedence && (!disallowInContext || token !== SyntaxKind.InKeyword)) { var operator = token; nextToken(); expr = makeBinaryExpression(expr, operator, parseBinaryOperators(parseUnaryExpression(), precedence)); continue; } return expr; } } function getOperatorPrecedence(): number { switch (token) { case SyntaxKind.BarBarToken: return 1; case SyntaxKind.AmpersandAmpersandToken: return 2; case SyntaxKind.BarToken: return 3; case SyntaxKind.CaretToken: return 4; case SyntaxKind.AmpersandToken: return 5; case SyntaxKind.EqualsEqualsToken: case SyntaxKind.ExclamationEqualsToken: case SyntaxKind.EqualsEqualsEqualsToken: case SyntaxKind.ExclamationEqualsEqualsToken: return 6; case SyntaxKind.LessThanToken: case SyntaxKind.GreaterThanToken: case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanEqualsToken: case SyntaxKind.InstanceOfKeyword: case SyntaxKind.InKeyword: return 7; case SyntaxKind.LessThanLessThanToken: case SyntaxKind.GreaterThanGreaterThanToken: case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return 8; case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: return 9; case SyntaxKind.AsteriskToken: case SyntaxKind.SlashToken: case SyntaxKind.PercentToken: return 10; } return undefined; } function makeBinaryExpression(left: Expression, operator: SyntaxKind, right: Expression): BinaryExpression { var node = createNode(SyntaxKind.BinaryExpression, left.pos); node.left = left; node.operator = operator; node.right = right; return finishNode(node); } function parseUnaryExpression(): Expression { var pos = getNodePos(); switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: case SyntaxKind.TildeToken: case SyntaxKind.ExclamationToken: case SyntaxKind.DeleteKeyword: case SyntaxKind.TypeOfKeyword: case SyntaxKind.VoidKeyword: case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: var operator = token; nextToken(); return makeUnaryExpression(SyntaxKind.PrefixOperator, pos, operator, parseUnaryExpression()); case SyntaxKind.LessThanToken: return parseTypeAssertion(); } var primaryExpression = parsePrimaryExpression(); // TS 1.0 spec (2014): 4.8 // CallExpression: ( Modified ) // super ( ArgumentListopt ) // super . IdentifierName var illegalUsageOfSuperKeyword = primaryExpression.kind === SyntaxKind.SuperKeyword && token !== SyntaxKind.OpenParenToken && token !== SyntaxKind.DotToken; if (illegalUsageOfSuperKeyword) { error(Diagnostics.super_must_be_followed_by_an_argument_list_or_member_access); } var expr = parseCallAndAccess(primaryExpression, /* inNewExpression */ false); Debug.assert(isLeftHandSideExpression(expr)); if ((token === SyntaxKind.PlusPlusToken || token === SyntaxKind.MinusMinusToken) && !scanner.hasPrecedingLineBreak()) { var operator = token; nextToken(); expr = makeUnaryExpression(SyntaxKind.PostfixOperator, expr.pos, operator, expr); } return expr; } function parseTypeAssertion(): TypeAssertion { var node = createNode(SyntaxKind.TypeAssertion); parseExpected(SyntaxKind.LessThanToken); node.type = parseType(); parseExpected(SyntaxKind.GreaterThanToken); node.operand = parseUnaryExpression(); return finishNode(node); } function makeUnaryExpression(kind: SyntaxKind, pos: number, operator: SyntaxKind, operand: Expression): UnaryExpression { var node = createNode(kind, pos); node.operator = operator; node.operand = operand; return finishNode(node); } function parseCallAndAccess(expr: Expression, inNewExpression: boolean): Expression { while (true) { var dotOrBracketStart = scanner.getTokenPos(); if (parseOptional(SyntaxKind.DotToken)) { var propertyAccess = createNode(SyntaxKind.PropertyAccess, expr.pos); // Technically a keyword is valid here as all keywords are identifier names. // However, often we'll encounter this in error situations when the keyword // is actually starting another valid construct. // // So, we check for the following specific case: // // name. // keyword identifierNameOrKeyword // // Note: the newlines are important here. For example, if that above code // were rewritten into: // // name.keyword // identifierNameOrKeyword // // Then we would consider it valid. That's because ASI would take effect and // the code would be implicitly: "name.keyword; identifierNameOrKeyword". // In the first case though, ASI will not take effect because there is not a // line terminator after the keyword. var id: Identifier; if (scanner.hasPrecedingLineBreak() && scanner.isReservedWord()) { var matchesPattern = lookAhead(() => { nextToken(); return !scanner.hasPrecedingLineBreak() && (scanner.isIdentifier() || scanner.isReservedWord); }); if (matchesPattern) { errorAtPos(dotOrBracketStart + 1, 0, Diagnostics.Identifier_expected); id = createMissingNode(); } } propertyAccess.left = expr; propertyAccess.right = id || parseIdentifierName(); expr = finishNode(propertyAccess); continue; } if (parseOptional(SyntaxKind.OpenBracketToken)) { var indexedAccess = createNode(SyntaxKind.IndexedAccess, expr.pos); indexedAccess.object = expr; // It's not uncommon for a user to write: "new Type[]". // Check for that common pattern and report a better error message. if (inNewExpression && parseOptional(SyntaxKind.CloseBracketToken)) { indexedAccess.index = createMissingNode(); } else { indexedAccess.index = allowInAnd(parseExpression); if (indexedAccess.index.kind === SyntaxKind.StringLiteral || indexedAccess.index.kind === SyntaxKind.NumericLiteral) { var literal = indexedAccess.index; literal.text = internIdentifier(literal.text); } parseExpected(SyntaxKind.CloseBracketToken); } expr = finishNode(indexedAccess); continue; } // Try to parse a Call Expression unless we are in a New Expression. // If we are parsing a New Expression, then parentheses are optional, // and is taken care of by the 'parseNewExpression' caller. if ((token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) && !inNewExpression) { var callExpr = createNode(SyntaxKind.CallExpression, expr.pos); callExpr.func = expr; if (token === SyntaxKind.LessThanToken) { if (!(callExpr.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) return expr; } else { parseExpected(SyntaxKind.OpenParenToken); } callExpr.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); parseExpected(SyntaxKind.CloseParenToken); expr = finishNode(callExpr); continue; } if (token === SyntaxKind.NoSubstitutionTemplateLiteral || token === SyntaxKind.TemplateHead) { var tagExpression = createNode(SyntaxKind.TaggedTemplateExpression, expr.pos); tagExpression.tag = expr; tagExpression.template = token === SyntaxKind.NoSubstitutionTemplateLiteral ? parseLiteralNode() : parseTemplateExpression(); expr = finishNode(tagExpression); continue; } return expr; } } function parseTypeArgumentsAndOpenParen(): NodeArray { var result = parseTypeArguments(); parseExpected(SyntaxKind.OpenParenToken); return result; } function parseTypeArguments(): NodeArray { // We pass parseSingleTypeArgument instead of parseType as the element parser // because parseSingleTypeArgument knows how to parse a missing type argument. // This is useful for signature help. parseType has the disadvantage that when // it sees a missing type, it changes the LookAheadMode to Error, and the result // is a broken binary expression, which breaks signature help. return parseBracketedList(ParsingContext.TypeArguments, parseSingleTypeArgument, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); } function parseSingleTypeArgument(): TypeNode { // Be resilient to something like: Foo<,,>(); // We want to parse this out as a type argument list (esp. for signature help), and we // don't want to rollback just because we were missing a type arg. The grammar checker // will report the actual error later on. if (token === SyntaxKind.CommaToken) { return createNode(SyntaxKind.Missing); } return parseType(); } function parsePrimaryExpression(): Expression { switch (token) { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: return parseTokenNode(); case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: return parseLiteralNode(); case SyntaxKind.OpenParenToken: return parseParenExpression(); case SyntaxKind.OpenBracketToken: return parseArrayLiteral(); case SyntaxKind.OpenBraceToken: return parseObjectLiteral(); case SyntaxKind.FunctionKeyword: return parseFunctionExpression(); case SyntaxKind.NewKeyword: return parseNewExpression(); case SyntaxKind.SlashToken: case SyntaxKind.SlashEqualsToken: if (reScanSlashToken() === SyntaxKind.RegularExpressionLiteral) { return parseLiteralNode(); } break; case SyntaxKind.TemplateHead: return parseTemplateExpression(); default: if (isIdentifier()) { return parseIdentifier(); } } error(Diagnostics.Expression_expected); return createMissingNode(); } function parseParenExpression(): ParenExpression { var node = createNode(SyntaxKind.ParenExpression); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); return finishNode(node); } function parseAssignmentExpressionOrOmittedExpression(): Expression { return token === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : parseAssignmentExpression(); } function parseArrayLiteralElement(): Expression { return parseAssignmentExpressionOrOmittedExpression(); } function parseArgumentExpression(): Expression { return allowInAnd(parseAssignmentExpressionOrOmittedExpression); } function parseArrayLiteral(): ArrayLiteral { var node = createNode(SyntaxKind.ArrayLiteral); parseExpected(SyntaxKind.OpenBracketToken); if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine; node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } function parsePropertyAssignment(): Declaration { var nodePos = scanner.getStartPos(); var tokenIsIdentifier = isIdentifier(); var nameToken = token; var propertyName = parsePropertyName(); var node: Declaration; if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { node = createNode(SyntaxKind.PropertyAssignment, nodePos); node.name = propertyName; var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false); var body = parseFunctionBlock(/* ignoreMissingOpenBrace */ false); // do not propagate property name as name for function expression // for scenarios like // var x = 1; // var y = { x() { } } // otherwise this will bring y.x into the scope of x which is incorrect (node).initializer = makeFunctionExpression(SyntaxKind.FunctionExpression, node.pos, undefined, sig, body); return finishNode(node); } var flags: NodeFlags = 0; // Disallowing of optional property assignments happens in the grammar checker. if (token === SyntaxKind.QuestionToken) { flags |= NodeFlags.QuestionMark; nextToken(); } // Parse to check if it is short-hand property assignment or normal property assignment if ((token === SyntaxKind.CommaToken || token === SyntaxKind.CloseBraceToken) && tokenIsIdentifier) { node = createNode(SyntaxKind.ShorthandPropertyAssignment, nodePos); node.name = propertyName; } else { node = createNode(SyntaxKind.PropertyAssignment, nodePos); node.name = propertyName; parseExpected(SyntaxKind.ColonToken); (node).initializer = allowInAnd(parseAssignmentExpression); } node.flags = flags; return finishNode(node); } function parseObjectLiteralMember(): Node { var initialPos = getNodePos(); var initialToken = token; if (parseContextualModifier(SyntaxKind.GetKeyword) || parseContextualModifier(SyntaxKind.SetKeyword)) { var kind = initialToken === SyntaxKind.GetKeyword ? SyntaxKind.GetAccessor : SyntaxKind.SetAccessor; return parseMemberAccessorDeclaration(kind, initialPos, /*modifiers*/ undefined); } return parsePropertyAssignment(); } function parseObjectLiteral(): ObjectLiteral { var node = createNode(SyntaxKind.ObjectLiteral); parseExpected(SyntaxKind.OpenBraceToken); if (scanner.hasPrecedingLineBreak()) { node.flags |= NodeFlags.MultiLine; } node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralMember); parseExpected(SyntaxKind.CloseBraceToken); return finishNode(node); } function parseFunctionExpression(): FunctionExpression { var pos = getNodePos(); parseExpected(SyntaxKind.FunctionKeyword); var name = isIdentifier() ? parseIdentifier() : undefined; var sig = parseSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false); var body = parseFunctionBlock(/* ignoreMissingOpenBrace */ false); return makeFunctionExpression(SyntaxKind.FunctionExpression, pos, name, sig, body); } function makeFunctionExpression(kind: SyntaxKind, pos: number, name: Identifier, sig: ParsedSignature, body: Node): FunctionExpression { var node = createNode(kind, pos); node.name = name; node.typeParameters = sig.typeParameters; node.parameters = sig.parameters; node.type = sig.type; node.body = body; return finishNode(node); } function parseNewExpression(): NewExpression { var node = createNode(SyntaxKind.NewExpression); parseExpected(SyntaxKind.NewKeyword); node.func = parseCallAndAccess(parsePrimaryExpression(), /* inNewExpression */ true); if (parseOptional(SyntaxKind.OpenParenToken) || token === SyntaxKind.LessThanToken && (node.typeArguments = tryParse(parseTypeArgumentsAndOpenParen))) { node.arguments = parseDelimitedList(ParsingContext.ArgumentExpressions, parseArgumentExpression); parseExpected(SyntaxKind.CloseParenToken); } return finishNode(node); } // STATEMENTS function parseBlock(ignoreMissingOpenBrace: boolean, checkForStrictMode: boolean): Block { var node = createNode(SyntaxKind.Block); if (parseExpected(SyntaxKind.OpenBraceToken) || ignoreMissingOpenBrace) { node.statements = parseList(ParsingContext.BlockStatements, checkForStrictMode, parseStatement); parseExpected(SyntaxKind.CloseBraceToken); } else { node.statements = createMissingList(); } return finishNode(node); } function parseFunctionBlock(ignoreMissingOpenBrace: boolean): Block { var block = parseBlock(ignoreMissingOpenBrace, /*checkForStrictMode*/ true); block.kind = SyntaxKind.FunctionBlock; return block; } function parseEmptyStatement(): Statement { var node = createNode(SyntaxKind.EmptyStatement); parseExpected(SyntaxKind.SemicolonToken); return finishNode(node); } function parseIfStatement(): IfStatement { var node = createNode(SyntaxKind.IfStatement); parseExpected(SyntaxKind.IfKeyword); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); node.thenStatement = parseStatement(); node.elseStatement = parseOptional(SyntaxKind.ElseKeyword) ? parseStatement() : undefined; return finishNode(node); } function parseDoStatement(): DoStatement { var node = createNode(SyntaxKind.DoStatement); parseExpected(SyntaxKind.DoKeyword); node.statement = parseStatement(); parseExpected(SyntaxKind.WhileKeyword); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); // From: https://mail.mozilla.org/pipermail/es-discuss/2011-August/016188.html // 157 min --- All allen at wirfs-brock.com CONF --- "do{;}while(false)false" prohibited in // spec but allowed in consensus reality. Approved -- this is the de-facto standard whereby // do;while(0)x will have a semicolon inserted before x. parseOptional(SyntaxKind.SemicolonToken); return finishNode(node); } function parseWhileStatement(): WhileStatement { var node = createNode(SyntaxKind.WhileStatement); parseExpected(SyntaxKind.WhileKeyword); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); node.statement = parseStatement(); return finishNode(node); } function parseForOrForInStatement(): Statement { var pos = getNodePos(); parseExpected(SyntaxKind.ForKeyword); parseExpected(SyntaxKind.OpenParenToken); if (token !== SyntaxKind.SemicolonToken) { if (parseOptional(SyntaxKind.VarKeyword)) { var declarations = disallowInAnd(parseVariableDeclarationList); } else if (parseOptional(SyntaxKind.LetKeyword)) { var declarations = setFlag(disallowInAnd(parseVariableDeclarationList), NodeFlags.Let); } else if (parseOptional(SyntaxKind.ConstKeyword)) { var declarations = setFlag(disallowInAnd(parseVariableDeclarationList), NodeFlags.Const); } else { var varOrInit = disallowInAnd(parseExpression); } } var forOrForInStatement: IterationStatement; if (parseOptional(SyntaxKind.InKeyword)) { var forInStatement = createNode(SyntaxKind.ForInStatement, pos); if (declarations) { forInStatement.declarations = declarations; } else { forInStatement.variable = varOrInit; } forInStatement.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); forOrForInStatement = forInStatement; } else { var forStatement = createNode(SyntaxKind.ForStatement, pos); if (declarations) { forStatement.declarations = declarations; } if (varOrInit) { forStatement.initializer = varOrInit; } parseExpected(SyntaxKind.SemicolonToken); if (token !== SyntaxKind.SemicolonToken && token !== SyntaxKind.CloseParenToken) { forStatement.condition = allowInAnd(parseExpression); } parseExpected(SyntaxKind.SemicolonToken); if (token !== SyntaxKind.CloseParenToken) { forStatement.iterator = allowInAnd(parseExpression); } parseExpected(SyntaxKind.CloseParenToken); forOrForInStatement = forStatement; } forOrForInStatement.statement = parseStatement(); return finishNode(forOrForInStatement); } function parseBreakOrContinueStatement(kind: SyntaxKind): BreakOrContinueStatement { var node = createNode(kind); parseExpected(kind === SyntaxKind.BreakStatement ? SyntaxKind.BreakKeyword : SyntaxKind.ContinueKeyword); if (!canParseSemicolon()) { node.label = parseIdentifier(); } parseSemicolon(); return finishNode(node); } function parseReturnStatement(): ReturnStatement { var node = createNode(SyntaxKind.ReturnStatement); parseExpected(SyntaxKind.ReturnKeyword); if (!canParseSemicolon()) { node.expression = allowInAnd(parseExpression); } parseSemicolon(); return finishNode(node); } function parseWithStatement(): WithStatement { var node = createNode(SyntaxKind.WithStatement); parseExpected(SyntaxKind.WithKeyword); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); node.statement = parseStatement(); return finishNode(node); } function parseCaseClause(): CaseOrDefaultClause { var node = createNode(SyntaxKind.CaseClause); parseExpected(SyntaxKind.CaseKeyword); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.ColonToken); node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement); return finishNode(node); } function parseDefaultClause(): CaseOrDefaultClause { var node = createNode(SyntaxKind.DefaultClause); parseExpected(SyntaxKind.DefaultKeyword); parseExpected(SyntaxKind.ColonToken); node.statements = parseList(ParsingContext.SwitchClauseStatements, /*checkForStrictMode*/ false, parseStatement); return finishNode(node); } function parseCaseOrDefaultClause(): CaseOrDefaultClause { return token === SyntaxKind.CaseKeyword ? parseCaseClause() : parseDefaultClause(); } function parseSwitchStatement(): SwitchStatement { var node = createNode(SyntaxKind.SwitchStatement); parseExpected(SyntaxKind.SwitchKeyword); parseExpected(SyntaxKind.OpenParenToken); node.expression = allowInAnd(parseExpression); parseExpected(SyntaxKind.CloseParenToken); parseExpected(SyntaxKind.OpenBraceToken); node.clauses = parseList(ParsingContext.SwitchClauses, /*checkForStrictMode*/ false, parseCaseOrDefaultClause); parseExpected(SyntaxKind.CloseBraceToken); return finishNode(node); } function parseThrowStatement(): ThrowStatement { var node = createNode(SyntaxKind.ThrowStatement); parseExpected(SyntaxKind.ThrowKeyword); if (scanner.hasPrecedingLineBreak()) { error(Diagnostics.Line_break_not_permitted_here); } node.expression = allowInAnd(parseExpression); parseSemicolon(); return finishNode(node); } // TODO: Review for error recovery function parseTryStatement(): TryStatement { var node = createNode(SyntaxKind.TryStatement); node.tryBlock = parseTokenAndBlock(SyntaxKind.TryKeyword, SyntaxKind.TryBlock); if (token === SyntaxKind.CatchKeyword) { node.catchBlock = parseCatchBlock(); } if (token === SyntaxKind.FinallyKeyword) { node.finallyBlock = parseTokenAndBlock(SyntaxKind.FinallyKeyword, SyntaxKind.FinallyBlock); } if (!(node.catchBlock || node.finallyBlock)) { error(Diagnostics.catch_or_finally_expected); } return finishNode(node); } function parseTokenAndBlock(token: SyntaxKind, kind: SyntaxKind): Block { var pos = getNodePos(); parseExpected(token); var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); result.kind = kind; result.pos = pos; return result; } function parseCatchBlock(): CatchBlock { var pos = getNodePos(); parseExpected(SyntaxKind.CatchKeyword); parseExpected(SyntaxKind.OpenParenToken); var variable = parseIdentifier(); var typeAnnotation = parseTypeAnnotation(); parseExpected(SyntaxKind.CloseParenToken); var result = parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); result.kind = SyntaxKind.CatchBlock; result.pos = pos; result.variable = variable; result.type = typeAnnotation; return result; } function parseDebuggerStatement(): Statement { var node = createNode(SyntaxKind.DebuggerStatement); parseExpected(SyntaxKind.DebuggerKeyword); parseSemicolon(); return finishNode(node); } function isLabel(): boolean { return isIdentifier() && lookAhead(() => nextToken() === SyntaxKind.ColonToken); } function parseLabeledStatement(): LabeledStatement { var node = createNode(SyntaxKind.LabeledStatement); node.label = parseIdentifier(); parseExpected(SyntaxKind.ColonToken); node.statement = parseStatement(); return finishNode(node); } function parseExpressionStatement(): ExpressionStatement { var node = createNode(SyntaxKind.ExpressionStatement); node.expression = allowInAnd(parseExpression); parseSemicolon(); return finishNode(node); } function isStatement(inErrorRecovery: boolean): boolean { switch (token) { case SyntaxKind.SemicolonToken: // 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 !inErrorRecovery; case SyntaxKind.OpenBraceToken: case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.FunctionKeyword: case SyntaxKind.IfKeyword: case SyntaxKind.DoKeyword: case SyntaxKind.WhileKeyword: case SyntaxKind.ForKeyword: case SyntaxKind.ContinueKeyword: case SyntaxKind.BreakKeyword: case SyntaxKind.ReturnKeyword: case SyntaxKind.WithKeyword: case SyntaxKind.SwitchKeyword: case SyntaxKind.ThrowKeyword: case SyntaxKind.TryKeyword: case SyntaxKind.DebuggerKeyword: // 'catch' and 'finally' do not actually indicate that the code is part of a statement, // however, we say they are here so that we may gracefully parse them and error later. case SyntaxKind.CatchKeyword: case SyntaxKind.FinallyKeyword: return true; case SyntaxKind.ConstKeyword: // const keyword can precede enum keyword when defining constant enums // 'const enum' do not start statement. // In ES 6 'enum' is a future reserved keyword, so it should not be used as identifier var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); return !isConstEnum; case SyntaxKind.InterfaceKeyword: case SyntaxKind.ClassKeyword: case SyntaxKind.ModuleKeyword: case SyntaxKind.EnumKeyword: case SyntaxKind.TypeKeyword: // When followed by an identifier, these do not start a statement but might // instead be following declarations if (isDeclarationStart()) { return false; } case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.StaticKeyword: // When followed by an identifier or keyword, these do not start a statement but // might instead be following type members if (lookAhead(() => nextToken() >= SyntaxKind.Identifier)) { return false; } default: return isStartOfExpression(); } } function parseStatement(): Statement { switch (token) { case SyntaxKind.OpenBraceToken: return parseBlock(/* ignoreMissingOpenBrace */ false, /*checkForStrictMode*/ false); case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: // const here should always be parsed as const declaration because of check in 'isStatement' return parseVariableStatement(scanner.getStartPos(), /*modifiers:*/ undefined); case SyntaxKind.FunctionKeyword: return parseFunctionDeclaration(scanner.getStartPos(), /*modifiers:*/ undefined); case SyntaxKind.SemicolonToken: return parseEmptyStatement(); case SyntaxKind.IfKeyword: return parseIfStatement(); case SyntaxKind.DoKeyword: return parseDoStatement(); case SyntaxKind.WhileKeyword: return parseWhileStatement(); case SyntaxKind.ForKeyword: return parseForOrForInStatement(); case SyntaxKind.ContinueKeyword: return parseBreakOrContinueStatement(SyntaxKind.ContinueStatement); case SyntaxKind.BreakKeyword: return parseBreakOrContinueStatement(SyntaxKind.BreakStatement); case SyntaxKind.ReturnKeyword: return parseReturnStatement(); case SyntaxKind.WithKeyword: return parseWithStatement(); case SyntaxKind.SwitchKeyword: return parseSwitchStatement(); case SyntaxKind.ThrowKeyword: return parseThrowStatement(); case SyntaxKind.TryKeyword: // Include the next two for error recovery. case SyntaxKind.CatchKeyword: case SyntaxKind.FinallyKeyword: return parseTryStatement(); case SyntaxKind.DebuggerKeyword: return parseDebuggerStatement(); default: return isLabel() ? parseLabeledStatement() : parseExpressionStatement(); } } function parseFunctionBlockOrSemicolon(): Block { if (token === SyntaxKind.OpenBraceToken) { return parseFunctionBlock(/* ignoreMissingOpenBrace */ false); } if (canParseSemicolon()) { parseSemicolon(); return undefined; } error(Diagnostics.Block_or_expected); // block or ';' expected } // DECLARATIONS function parseVariableDeclaration(): VariableDeclaration { var node = createNode(SyntaxKind.VariableDeclaration); node.name = parseIdentifier(); node.type = parseTypeAnnotation(); node.initializer = parseInitializer(/*inParameter*/ false); return finishNode(node); } function setFlag(array: NodeArray, flag: NodeFlags): NodeArray { for (var i = 0, n = array.length; i < n; i++) { array[i].flags |= flag; } return array; } function parseVariableDeclarationList(): NodeArray { return parseDelimitedList(ParsingContext.VariableDeclarations, parseVariableDeclaration); } function parseVariableStatement(fullStart: number, modifiers: ModifiersArray): VariableStatement { var node = createNode(SyntaxKind.VariableStatement, fullStart); setModifiers(node, modifiers); if (token === SyntaxKind.LetKeyword) { node.flags |= NodeFlags.Let; } else if (token === SyntaxKind.ConstKeyword) { node.flags |= NodeFlags.Const; } else { Debug.assert(token === SyntaxKind.VarKeyword); } nextToken(); node.declarations = allowInAnd(parseVariableDeclarationList); setFlag(node.declarations, node.flags); parseSemicolon(); return finishNode(node); } function parseFunctionDeclaration(fullStart: number, modifiers: ModifiersArray): FunctionLikeDeclaration { var node = createNode(SyntaxKind.FunctionDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.FunctionKeyword); node.name = parseIdentifier(); fillSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false, node); node.body = parseFunctionBlockOrSemicolon(); return finishNode(node); } function parseConstructorDeclaration(pos: number, modifiers: ModifiersArray): ConstructorDeclaration { var node = createNode(SyntaxKind.Constructor, pos); setModifiers(node, modifiers); parseExpected(SyntaxKind.ConstructorKeyword); fillSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false, node); node.body = parseFunctionBlockOrSemicolon(); return finishNode(node); } function parsePropertyMemberDeclaration(fullStart: number, modifiers: ModifiersArray): Declaration { var name = parsePropertyName(); var flags = modifiers ? modifiers.flags : 0; if (parseOptional(SyntaxKind.QuestionToken)) { // Note: this is not legal as per the grammar. But we allow it in the parser and // report an error in the grammar checker. flags |= NodeFlags.QuestionMark; } if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { var method = createNode(SyntaxKind.Method, fullStart); setModifiers(method, modifiers); if (flags) { method.flags = flags; } method.name = name; fillSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false, method); method.body = parseFunctionBlockOrSemicolon(); return finishNode(method); } else { var property = createNode(SyntaxKind.Property, fullStart); setModifiers(property, modifiers); if (flags) { property.flags = flags; } property.name = name; property.type = parseTypeAnnotation(); property.initializer = allowInAnd(() => parseInitializer(/*inParameter*/ false)); parseSemicolon(); return finishNode(property); } } function parseMemberAccessorDeclaration(kind: SyntaxKind, fullStart: number, modifiers: ModifiersArray): MethodDeclaration { var node = createNode(kind, fullStart); setModifiers(node, modifiers); node.name = parsePropertyName(); fillSignature(SyntaxKind.CallSignature, SyntaxKind.ColonToken, /* returnTokenRequired */ false, node); node.body = parseFunctionBlockOrSemicolon(); return finishNode(node); } function isClassMemberStart(): boolean { var idToken: SyntaxKind; // Eat up all modifiers, but hold on to the last one in case it is actually an identifier. while (isModifier(token)) { idToken = token; nextToken(); } // Try to get the first property-like token following all modifiers. // This can either be an identifier or the 'get' or 'set' keywords. if (isPropertyName()) { idToken = token; nextToken(); } // Index signatures are class members; we can parse. if (token === SyntaxKind.OpenBracketToken) { return true; } // If we were able to get any potential identifier... if (idToken !== undefined) { // If we have a non-keyword identifier, or if we have an accessor, then it's safe to parse. if (!isKeyword(idToken) || idToken === SyntaxKind.SetKeyword || idToken === SyntaxKind.GetKeyword) { return true; } // If it *is* a keyword, but not an accessor, check a little farther along // to see if it should actually be parsed as a class member. switch (token) { case SyntaxKind.OpenParenToken: // Method declaration case SyntaxKind.LessThanToken: // Generic Method declaration case SyntaxKind.ColonToken: // Type Annotation for declaration case SyntaxKind.EqualsToken: // Initializer for declaration case SyntaxKind.QuestionToken: // Not valid, but permitted so that it gets caught later on. return true; default: // Covers // - Semicolons (declaration termination) // - Closing braces (end-of-class, must be declaration) // - End-of-files (not valid, but permitted so that it gets caught later on) // - Line-breaks (enabling *automatic semicolon insertion*) return canParseSemicolon(); } } return false; } function parseModifiers(): ModifiersArray { var flags = 0; var modifiers: ModifiersArray; while (true) { var modifierStart = scanner.getTokenPos(); var modifierKind = token; if (!parseAnyContextualModifier()) { break; } if (!modifiers) { modifiers = []; } flags |= modifierToFlag(modifierKind); modifiers.push(finishNode(createNode(modifierKind, modifierStart))); } if (modifiers) { modifiers.flags = flags; } return modifiers; } function parseClassMemberDeclaration(): Declaration { var fullStart = getNodePos(); var modifiers = parseModifiers(); if (parseContextualModifier(SyntaxKind.GetKeyword)) { return parseMemberAccessorDeclaration(SyntaxKind.GetAccessor, fullStart, modifiers); } if (parseContextualModifier(SyntaxKind.SetKeyword)) { return parseMemberAccessorDeclaration(SyntaxKind.SetAccessor, fullStart, modifiers); } if (token === SyntaxKind.ConstructorKeyword) { return parseConstructorDeclaration(fullStart, modifiers); } if (token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral) { return parsePropertyMemberDeclaration(fullStart, modifiers); } if (token === SyntaxKind.OpenBracketToken) { return parseIndexSignatureMember(fullStart, modifiers); } // 'isClassMemberStart' should have hinted not to attempt parsing. Debug.fail("Should not have attempted to parse class member declaration."); } function parseClassDeclaration(fullStart: number, modifiers: ModifiersArray): ClassDeclaration { var node = createNode(SyntaxKind.ClassDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.ClassKeyword); node.name = parseIdentifier(); node.typeParameters = parseTypeParameters(); // TODO(jfreeman): Parse arbitrary sequence of heritage clauses and error for order and duplicates node.baseType = parseOptional(SyntaxKind.ExtendsKeyword) ? parseTypeReference() : undefined; if (parseOptional(SyntaxKind.ImplementsKeyword)) { node.implementedTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference); } if (parseExpected(SyntaxKind.OpenBraceToken)) { node.members = parseList(ParsingContext.ClassMembers, /*checkForStrictMode*/ false, parseClassMemberDeclaration); parseExpected(SyntaxKind.CloseBraceToken); } else { node.members = createMissingList(); } return finishNode(node); } function parseInterfaceDeclaration(fullStart: number, modifiers: ModifiersArray): InterfaceDeclaration { var node = createNode(SyntaxKind.InterfaceDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.InterfaceKeyword); node.name = parseIdentifier(); node.typeParameters = parseTypeParameters(); // TODO(jfreeman): Parse arbitrary sequence of heritage clauses and error for order and duplicates if (parseOptional(SyntaxKind.ExtendsKeyword)) { node.baseTypes = parseDelimitedList(ParsingContext.BaseTypeReferences, parseTypeReference); } node.members = parseObjectType(); return finishNode(node); } function parseTypeAliasDeclaration(fullStart: number, modifiers: ModifiersArray): TypeAliasDeclaration { var node = createNode(SyntaxKind.TypeAliasDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.TypeKeyword); node.name = parseIdentifier(); parseExpected(SyntaxKind.EqualsToken); node.type = parseType(); parseSemicolon(); return finishNode(node); } // In an ambient declaration, the grammar only allows integer literals as initializers. // In a non-ambient declaration, the grammar allows uninitialized members only in a // ConstantEnumMemberSection, which starts at the beginning of an enum declaration // or any time an integer literal initializer is encountered. function parseEnumMember(): EnumMember { var node = createNode(SyntaxKind.EnumMember, scanner.getStartPos()); node.name = parsePropertyName(); node.initializer = allowInAnd(() => parseInitializer(/*inParameter*/ false)); return finishNode(node); } function parseAndCheckEnumDeclaration(fullStart: number, flags: NodeFlags): EnumDeclaration { var node = createNode(SyntaxKind.EnumDeclaration, fullStart); node.flags = flags; if (flags & NodeFlags.Const) { parseExpected(SyntaxKind.ConstKeyword); } parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { node.members = parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember); parseExpected(SyntaxKind.CloseBraceToken); } else { node.members = createMissingList(); } return finishNode(node); } function parseModuleBody(): Block { var node = createNode(SyntaxKind.ModuleBlock, scanner.getStartPos()); if (parseExpected(SyntaxKind.OpenBraceToken)) { node.statements = parseList(ParsingContext.ModuleElements, /*checkForStrictMode*/ false, parseModuleElement); parseExpected(SyntaxKind.CloseBraceToken); } else { node.statements = createMissingList(); } return finishNode(node); } function parseInternalModuleTail(fullStart: number, flags: NodeFlags): ModuleDeclaration { var node = createNode(SyntaxKind.ModuleDeclaration, fullStart); node.flags = flags; node.name = parseIdentifier(); node.body = parseOptional(SyntaxKind.DotToken) ? parseInternalModuleTail(getNodePos(), NodeFlags.Export) : parseModuleBody(); return finishNode(node); } function parseAmbientExternalModuleDeclaration(fullStart: number, flags: NodeFlags): ModuleDeclaration { var node = createNode(SyntaxKind.ModuleDeclaration, fullStart); node.flags = flags; node.name = parseStringLiteral(); node.body = parseModuleBody(); return finishNode(node); } function parseModuleDeclaration(fullStart: number, flags: NodeFlags): ModuleDeclaration { parseExpected(SyntaxKind.ModuleKeyword); return token === SyntaxKind.StringLiteral ? parseAmbientExternalModuleDeclaration(fullStart, flags) : parseInternalModuleTail(fullStart, flags); } function parseImportDeclaration(fullStart: number, modifiers: ModifiersArray): ImportDeclaration { var node = createNode(SyntaxKind.ImportDeclaration, fullStart); setModifiers(node, modifiers); parseExpected(SyntaxKind.ImportKeyword); node.name = parseIdentifier(); parseExpected(SyntaxKind.EqualsToken); var entityName = parseEntityName(/*allowReservedWords*/ false); if (entityName.kind === SyntaxKind.Identifier && (entityName).text === "require" && parseOptional(SyntaxKind.OpenParenToken)) { node.externalModuleName = parseStringLiteral(); parseExpected(SyntaxKind.CloseParenToken); } else { node.entityName = entityName; } parseSemicolon(); return finishNode(node); } function parseExportAssignmentTail(fullStart: number, modifiers: ModifiersArray): ExportAssignment { var node = createNode(SyntaxKind.ExportAssignment, fullStart); setModifiers(node, modifiers); node.exportName = parseIdentifier(); parseSemicolon(); return finishNode(node); } function isDeclarationStart(): boolean { switch (token) { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: case SyntaxKind.FunctionKeyword: return true; case SyntaxKind.ClassKeyword: case SyntaxKind.InterfaceKeyword: case SyntaxKind.EnumKeyword: case SyntaxKind.ImportKeyword: case SyntaxKind.TypeKeyword: // Not true keywords so ensure an identifier follows return lookAhead(() => nextToken() >= SyntaxKind.Identifier); case SyntaxKind.ModuleKeyword: // Not a true keyword so ensure an identifier or string literal follows return lookAhead(() => nextToken() >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral); case SyntaxKind.ExportKeyword: // Check for export assignment or modifier on source element return lookAhead(() => nextToken() === SyntaxKind.EqualsToken || isDeclarationStart()); case SyntaxKind.DeclareKeyword: case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.StaticKeyword: // Check for modifier on source element return lookAhead(() => { nextToken(); return isDeclarationStart(); }); } } function parseDeclaration(): Statement { var fullStart = getNodePos(); var modifiers = parseModifiers(); if (token === SyntaxKind.ExportKeyword) { nextToken(); if (parseOptional(SyntaxKind.EqualsToken)) { return parseExportAssignmentTail(fullStart, modifiers); } } var flags = modifiers ? modifiers.flags : 0; var result: Declaration; switch (token) { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: result = parseVariableStatement(fullStart, modifiers); break; case SyntaxKind.ConstKeyword: var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); if (isConstEnum) { result = parseAndCheckEnumDeclaration(fullStart, flags | NodeFlags.Const); } else { result = parseVariableStatement(fullStart, modifiers); } break; case SyntaxKind.FunctionKeyword: result = parseFunctionDeclaration(fullStart, modifiers); break; case SyntaxKind.ClassKeyword: result = parseClassDeclaration(fullStart, modifiers); break; case SyntaxKind.InterfaceKeyword: result = parseInterfaceDeclaration(fullStart, modifiers); break; case SyntaxKind.TypeKeyword: result = parseTypeAliasDeclaration(fullStart, modifiers); break; case SyntaxKind.EnumKeyword: result = parseAndCheckEnumDeclaration(fullStart, flags); break; case SyntaxKind.ModuleKeyword: result = parseModuleDeclaration(fullStart, flags); break; case SyntaxKind.ImportKeyword: result = parseImportDeclaration(fullStart, modifiers); break; default: error(Diagnostics.Declaration_expected); } if (modifiers) { result.modifiers = modifiers; } return result; } function isSourceElement(inErrorRecovery: boolean): boolean { return isDeclarationStart() || isStatement(inErrorRecovery); } function parseSourceElement() { return parseSourceElementOrModuleElement(); } function parseModuleElement() { return parseSourceElementOrModuleElement(); } function parseSourceElementOrModuleElement(): Statement { return isDeclarationStart() ? parseDeclaration() : parseStatement(); } function processReferenceComments(): ReferenceComments { var referencedFiles: FileReference[] = []; var amdDependencies: string[] = []; var amdModuleName: string; commentRanges = []; token = scanner.scan(); for (var i = 0; i < commentRanges.length; i++) { var range = commentRanges[i]; var comment = sourceText.substring(range.pos, range.end); var referencePathMatchResult = getFileReferenceFromReferencePath(comment, range); if (referencePathMatchResult) { var fileReference = referencePathMatchResult.fileReference; file.hasNoDefaultLib = referencePathMatchResult.isNoDefaultLib; var diagnostic = referencePathMatchResult.diagnostic; if (fileReference) { referencedFiles.push(fileReference); } if (diagnostic) { errorAtPos(range.pos, range.end - range.pos, diagnostic); } } else { var amdModuleNameRegEx = /^\/\/\/\s* node.flags & NodeFlags.Export || node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName || node.kind === SyntaxKind.ExportAssignment ? node : undefined); } var syntacticDiagnostics: Diagnostic[]; function getSyntacticDiagnostics() { if (syntacticDiagnostics === undefined) { if (file.parseDiagnostics.length > 0) { // Don't bother doing any grammar checks if there are already parser errors. // Otherwise we may end up with too many cascading errors. syntacticDiagnostics = file.parseDiagnostics; } else { // No parser errors were reported. Perform our stricter grammar checks. syntacticDiagnostics = file.grammarDiagnostics; checkGrammar(sourceText, languageVersion, file); } } Debug.assert(syntacticDiagnostics !== undefined); return syntacticDiagnostics; } scanner = createScanner(languageVersion, /*skipTrivia*/ true, sourceText, scanError, onComment); var rootNodeFlags: NodeFlags = 0; if (fileExtensionIs(filename, ".d.ts")) { rootNodeFlags = NodeFlags.DeclarationFile; } file = createRootNode(SyntaxKind.SourceFile, 0, sourceText.length, rootNodeFlags); file.filename = normalizePath(filename); file.text = sourceText; file.getLineAndCharacterFromPosition = getLineAndCharacterFromSourcePosition; file.getPositionFromLineAndCharacter = getPositionFromSourceLineAndCharacter; file.getLineStarts = getLineStarts; file.getSyntacticDiagnostics = getSyntacticDiagnostics; file.parseDiagnostics = []; file.grammarDiagnostics = []; file.semanticDiagnostics = []; var referenceComments = processReferenceComments(); file.referencedFiles = referenceComments.referencedFiles; file.amdDependencies = referenceComments.amdDependencies; file.amdModuleName = referenceComments.amdModuleName; file.statements = parseList(ParsingContext.SourceElements, /*checkForStrictMode*/ true, parseSourceElement); file.externalModuleIndicator = getExternalModuleIndicator(); file.nodeCount = nodeCount; file.identifierCount = identifierCount; file.version = version; file.isOpen = isOpen; file.languageVersion = languageVersion; file.identifiers = identifiers; return file; } function isLeftHandSideExpression(expr: Expression): boolean { if (expr) { switch (expr.kind) { case SyntaxKind.PropertyAccess: case SyntaxKind.IndexedAccess: case SyntaxKind.NewExpression: case SyntaxKind.CallExpression: case SyntaxKind.TaggedTemplateExpression: case SyntaxKind.ArrayLiteral: case SyntaxKind.ParenExpression: case SyntaxKind.ObjectLiteral: case SyntaxKind.FunctionExpression: case SyntaxKind.Identifier: case SyntaxKind.Missing: case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateExpression: case SyntaxKind.FalseKeyword: case SyntaxKind.NullKeyword: case SyntaxKind.ThisKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.SuperKeyword: return true; } } return false; } function isAssignmentOperator(token: SyntaxKind): boolean { return token >= SyntaxKind.FirstAssignment && token <= SyntaxKind.LastAssignment; } function checkGrammar(sourceText: string, languageVersion: ScriptTarget, file: SourceFile) { var grammarDiagnostics = file.grammarDiagnostics; // Create a scanner so we can find the start of tokens to report errors on. var scanner = createScanner(languageVersion, /*skipTrivia*/ true, sourceText); // We're automatically in an ambient context if this is a .d.ts file. var inAmbientContext = fileExtensionIs(file.filename, ".d.ts"); var inFunctionBlock = false; var parent: Node; visitNode(file); function visitNode(node: Node): void { // Store and restore our recursive state here. var savedParent = parent; node.parent = parent; parent = node; if (!checkModifiers(node)) { var savedInFunctionBlock = inFunctionBlock; if (node.kind === SyntaxKind.FunctionBlock) { inFunctionBlock = true; } var savedInAmbientContext = inAmbientContext if (node.flags & NodeFlags.Ambient) { inAmbientContext = true; } checkNodeAndChildren(node); inAmbientContext = savedInAmbientContext; inFunctionBlock = savedInFunctionBlock; } parent = savedParent; } function checkNodeAndChildren(node: Node) { var nodeKind = node.kind; // First, check if you have a statement in a place where it is not allowed. We want // to do this before recursing, because we'd prefer to report these errors at the top // level instead of at some nested level. if (inAmbientContext && checkForStatementInAmbientContext(node, nodeKind)) { return; } // if we got any errors, just stop performing any more checks on this node or higher. if (checkNode(node, nodeKind)) { return; } // Otherwise, recurse and see if we have any errors below us. forEachChild(node, visitNode); } function checkNode(node: Node, nodeKind: SyntaxKind): boolean { // Now do node specific checks. switch (nodeKind) { case SyntaxKind.ArrowFunction: case SyntaxKind.CallSignature: case SyntaxKind.ConstructorType: case SyntaxKind.ConstructSignature: case SyntaxKind.FunctionType: return checkAnyParsedSignature(node); case SyntaxKind.BreakStatement: case SyntaxKind.ContinueStatement: return checkBreakOrContinueStatement(node); case SyntaxKind.CallExpression: case SyntaxKind.NewExpression: return checkCallOrNewExpression(node); case SyntaxKind.EnumDeclaration: return checkEnumDeclaration(node); case SyntaxKind.Parameter: return checkParameter(node); case SyntaxKind.BinaryExpression: return checkBinaryExpression(node); case SyntaxKind.CatchBlock: return checkCatchBlock(node); case SyntaxKind.ClassDeclaration: return checkClassDeclaration(node); case SyntaxKind.Constructor: return checkConstructor(node); case SyntaxKind.ExportAssignment: return checkExportAssignment(node); case SyntaxKind.ForInStatement: return checkForInStatement(node); case SyntaxKind.ForStatement: return checkForStatement(node); case SyntaxKind.FunctionDeclaration: return checkFunctionDeclaration(node); case SyntaxKind.FunctionExpression: return checkFunctionExpression(node); case SyntaxKind.GetAccessor: return checkGetAccessor(node); case SyntaxKind.IndexedAccess: return checkIndexedAccess(node); case SyntaxKind.IndexSignature: return checkIndexSignature(node); case SyntaxKind.InterfaceDeclaration: return checkInterfaceDeclaration(node); case SyntaxKind.LabeledStatement: return checkLabeledStatement(node); case SyntaxKind.Method: return checkMethod(node); case SyntaxKind.ModuleDeclaration: return checkModuleDeclaration(node); case SyntaxKind.ObjectLiteral: return checkObjectLiteral(node); case SyntaxKind.NumericLiteral: return checkNumericLiteral(node); case SyntaxKind.PostfixOperator: return checkPostfixOperator(node); case SyntaxKind.PrefixOperator: return checkPrefixOperator(node); case SyntaxKind.Property: return checkProperty(node); case SyntaxKind.PropertyAssignment: return checkPropertyAssignment(node); case SyntaxKind.ReturnStatement: return checkReturnStatement(node); case SyntaxKind.SetAccessor: return checkSetAccessor(node); case SyntaxKind.SourceFile: return checkSourceFile(node); case SyntaxKind.ShorthandPropertyAssignment: return checkShorthandPropertyAssignment(node); case SyntaxKind.SwitchStatement: return checkSwitchStatement(node); case SyntaxKind.TaggedTemplateExpression: return checkTaggedTemplateExpression(node); case SyntaxKind.TupleType: return checkTupleType(node); case SyntaxKind.TypeParameter: return checkTypeParameter(node); case SyntaxKind.TypeReference: return checkTypeReference(node); case SyntaxKind.VariableDeclaration: return checkVariableDeclaration(node); case SyntaxKind.VariableStatement: return checkVariableStatement(node); case SyntaxKind.WithStatement: return checkWithStatement(node); } } function grammarErrorOnFirstToken(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { var start = skipTrivia(sourceText, node.pos); scanner.setTextPos(start); scanner.scan(); var end = scanner.getTextPos(); grammarDiagnostics.push(createFileDiagnostic(file, start, end - start, message, arg0, arg1, arg2)); return true; } function grammarErrorOnNode(node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { var span = getErrorSpanForNode(node); var start = span.end > span.pos ? skipTrivia(file.text, span.pos) : span.pos; var length = span.end - start; grammarDiagnostics.push(createFileDiagnostic(file, start, length, message, arg0, arg1, arg2)); return true; } function grammarErrorAtPos(start: number, length: number, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any): boolean { grammarDiagnostics.push(createFileDiagnostic(file, start, length, message, arg0, arg1, arg2)); return true; } function reportInvalidUseInStrictMode(node: Identifier): boolean { // declarationNameToString cannot be used here since it uses a backreference to 'parent' that is not yet set var name = sourceText.substring(skipTrivia(sourceText, node.pos), node.end); return grammarErrorOnNode(node, Diagnostics.Invalid_use_of_0_in_strict_mode, name); } function checkForStatementInAmbientContext(node: Node, kind: SyntaxKind): boolean { switch (kind) { case SyntaxKind.Block: case SyntaxKind.EmptyStatement: case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.ContinueStatement: case SyntaxKind.BreakStatement: case SyntaxKind.ReturnStatement: case SyntaxKind.WithStatement: case SyntaxKind.SwitchStatement: case SyntaxKind.ThrowStatement: case SyntaxKind.TryStatement: case SyntaxKind.DebuggerStatement: case SyntaxKind.LabeledStatement: case SyntaxKind.ExpressionStatement: return grammarErrorOnFirstToken(node, Diagnostics.Statements_are_not_allowed_in_ambient_contexts); } } function checkAnyParsedSignature(node: ParsedSignature): boolean { return checkTypeParameterList(node.typeParameters) || checkParameterList(node.parameters); } function checkBinaryExpression(node: BinaryExpression) { if (node.flags & NodeFlags.ParsedInStrictModeContext) { if (isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operator)) { if (isEvalOrArgumentsIdentifier(node.left)) { // ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an // Assignment operator(11.13) or of a PostfixExpression(11.3) return reportInvalidUseInStrictMode(node.left); } } } } function isIterationStatement(node: Node, lookInLabeledStatements: boolean): boolean { switch (node.kind) { case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: return true; case SyntaxKind.LabeledStatement: return lookInLabeledStatements && isIterationStatement((node).statement, lookInLabeledStatements); } return false; } function checkLabeledStatement(node: LabeledStatement): boolean { // ensure that label is unique var current = node.parent; while (current) { if (isAnyFunction(current)) { break; } if (current.kind === SyntaxKind.LabeledStatement && (current).label.text === node.label.text) { return grammarErrorOnNode(node.label, Diagnostics.Duplicate_label_0, getTextOfNodeFromSourceText(sourceText, node.label)); } current = current.parent; } } function checkBreakOrContinueStatement(node: BreakOrContinueStatement): boolean { var current = node; while (current) { if (isAnyFunction(current)) { return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary); } switch (current.kind) { case SyntaxKind.LabeledStatement: if (node.label && (current).label.text === node.label.text) { // found matching label - verify that label usage is correct // continue can only target labels that are on iteration statements var isMisplacedContinueLabel = node.kind === SyntaxKind.ContinueStatement && !isIterationStatement((current).statement, /*lookInLabeledStatement*/ true); if (isMisplacedContinueLabel) { return grammarErrorOnNode(node, Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement); } return false; } break; case SyntaxKind.SwitchStatement: if (node.kind === SyntaxKind.BreakStatement && !node.label) { // unlabeled break within switch statement - ok return false; } break; default: if (isIterationStatement(current, /*lookInLabeledStatement*/ false) && !node.label) { // unlabeled break or continue within iteration statement - ok return false; } break; } current = current.parent; } if (node.label) { var message = node.kind === SyntaxKind.BreakStatement ? Diagnostics.A_break_statement_can_only_jump_to_a_label_of_an_enclosing_statement : Diagnostics.A_continue_statement_can_only_jump_to_a_label_of_an_enclosing_iteration_statement; return grammarErrorOnNode(node, message) } else { var message = node.kind === SyntaxKind.BreakStatement ? Diagnostics.A_break_statement_can_only_be_used_within_an_enclosing_iteration_or_switch_statement : Diagnostics.A_continue_statement_can_only_be_used_within_an_enclosing_iteration_statement; return grammarErrorOnNode(node, message) } } function checkCallOrNewExpression(node: CallExpression) { return checkTypeArguments(node.typeArguments) || checkArguments(node.arguments); } function checkArguments(arguments: NodeArray) { return checkForDisallowedTrailingComma(arguments) || checkForOmittedArgument(arguments); } function checkTypeArguments(typeArguments: NodeArray) { return checkForDisallowedTrailingComma(typeArguments) || checkForAtLeastOneTypeArgument(typeArguments) || checkForMissingTypeArgument(typeArguments); } function checkForOmittedArgument(arguments: NodeArray) { if (arguments) { for (var i = 0, n = arguments.length; i < n; i++) { var arg = arguments[i]; if (arg.kind === SyntaxKind.OmittedExpression) { return grammarErrorAtPos(arg.pos, 0, Diagnostics.Argument_expression_expected); } } } } function checkForMissingTypeArgument(typeArguments: NodeArray) { if (typeArguments) { for (var i = 0, n = typeArguments.length; i < n; i++) { var arg = typeArguments[i]; if (arg.kind === SyntaxKind.Missing) { return grammarErrorAtPos(arg.pos, 0, Diagnostics.Type_expected); } } } } function checkForAtLeastOneTypeArgument(typeArguments: NodeArray) { if (typeArguments && typeArguments.length === 0) { var start = typeArguments.pos - "<".length; var end = skipTrivia(sourceText, typeArguments.end) + ">".length; return grammarErrorAtPos(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty); } } function checkForDisallowedTrailingComma(list: NodeArray): boolean { if (list && list.hasTrailingComma) { var start = list.end - ",".length; var end = list.end; return grammarErrorAtPos(start, end - start, Diagnostics.Trailing_comma_not_allowed); } } function checkCatchBlock(node: CatchBlock) { if (node.type) { var colonStart = skipTrivia(sourceText, node.variable.end); return grammarErrorAtPos(colonStart, ":".length, Diagnostics.Catch_clause_parameter_cannot_have_a_type_annotation); } if (node.flags & NodeFlags.ParsedInStrictModeContext && isEvalOrArgumentsIdentifier(node.variable)) { // It is a SyntaxError if a TryStatement with a Catch occurs within strict code and the Identifier of the // Catch production is eval or arguments return reportInvalidUseInStrictMode(node.variable); } } function checkClassDeclaration(node: ClassDeclaration) { return checkForDisallowedTrailingComma(node.implementedTypes) || checkForAtLeastOneHeritageClause(node.implementedTypes, "implements"); } function checkForAtLeastOneHeritageClause(types: NodeArray, listType: string): boolean { if (types && types.length === 0) { return grammarErrorAtPos(types.pos, 0, Diagnostics._0_list_cannot_be_empty, listType) } } function checkConstructor(node: ConstructorDeclaration) { return checkAnyParsedSignature(node) || checkConstructorTypeParameters(node) || checkConstructorTypeAnnotation(node) || checkForBodyInAmbientContext(node.body, /*isConstructor:*/ true); } function checkConstructorTypeParameters(node: ConstructorDeclaration) { if (node.typeParameters) { return grammarErrorAtPos(node.typeParameters.pos, node.typeParameters.end - node.typeParameters.pos, Diagnostics.Type_parameters_cannot_appear_on_a_constructor_declaration); } } function checkConstructorTypeAnnotation(node: ConstructorDeclaration) { if (node.type) { return grammarErrorOnNode(node.type, Diagnostics.Type_annotation_cannot_appear_on_a_constructor_declaration); } } function checkEnumDeclaration(enumDecl: EnumDeclaration): boolean { var enumIsConst = (enumDecl.flags & NodeFlags.Const) !== 0; var hasError = false; // skip checks below for const enums - they allow arbitrary initializers as long as they can be evaluated to constant expressions. // since all values are known in compile time - it is not necessary to check that constant enum section precedes computed enum members. if (!enumIsConst) { var inConstantEnumMemberSection = true; for (var i = 0, n = enumDecl.members.length; i < n; i++) { var node = enumDecl.members[i]; if (inAmbientContext) { if (node.initializer && !isIntegerLiteral(node.initializer)) { hasError = grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers) || hasError; } } else if (node.initializer) { inConstantEnumMemberSection = isIntegerLiteral(node.initializer); } else if (!inConstantEnumMemberSection) { hasError = grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer) || hasError; } } } return hasError; } function isIntegerLiteral(expression: Expression): boolean { function isInteger(literalExpression: LiteralExpression): boolean { // Allows for scientific notation since literalExpression.text was formed by // coercing a number to a string. Sometimes this coercion can yield a string // in scientific notation. // We also don't need special logic for hex because a hex integer is converted // to decimal when it is coerced. return /^[0-9]+([eE]\+?[0-9]+)?$/.test(literalExpression.text); } if (expression.kind === SyntaxKind.PrefixOperator) { var unaryExpression = expression; if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) { expression = unaryExpression.operand; } } if (expression.kind === SyntaxKind.NumericLiteral) { return isInteger(expression); } return false; } function checkExportAssignment(node: ExportAssignment) { if (node.flags & NodeFlags.Modifier) { return grammarErrorOnFirstToken(node, Diagnostics.An_export_assignment_cannot_have_modifiers); } } function checkForInStatement(node: ForInStatement) { return checkVariableDeclarations(node.declarations) || checkForMoreThanOneDeclaration(node.declarations); } function checkForStatement(node: ForStatement) { return checkVariableDeclarations(node.declarations); } function checkForMoreThanOneDeclaration(declarations: NodeArray) { if (declarations && declarations.length > 1) { return grammarErrorOnFirstToken(declarations[1], Diagnostics.Only_a_single_variable_declaration_is_allowed_in_a_for_in_statement); } } function checkFunctionDeclaration(node: FunctionLikeDeclaration) { return checkAnyParsedSignature(node) || checkFunctionName(node.name) || checkForBodyInAmbientContext(node.body, /*isConstructor:*/ false); } function checkFunctionExpression(node: FunctionExpression) { return checkAnyParsedSignature(node) || checkFunctionName(node.name); } function checkFunctionName(name: Node) { if (name && name.flags & NodeFlags.ParsedInStrictModeContext && isEvalOrArgumentsIdentifier(name)) { // It is a SyntaxError to use within strict mode code the identifiers eval or arguments as the // Identifier of a FunctionLikeDeclaration or FunctionExpression or as a formal parameter name(13.1) return reportInvalidUseInStrictMode(name); } } function checkGetAccessor(node: MethodDeclaration) { return checkAnyParsedSignature(node) || checkAccessor(node); } function checkIndexedAccess(node: IndexedAccess) { if (node.index.kind === SyntaxKind.Missing && node.parent.kind === SyntaxKind.NewExpression && (node.parent).func === node) { var start = skipTrivia(sourceText, node.parent.pos); var end = node.end; return grammarErrorAtPos(start, end - start, Diagnostics.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead); } } function checkIndexSignature(node: SignatureDeclaration): boolean { return checkIndexSignatureParameters(node) || checkForIndexSignatureModifiers(node); } function checkForIndexSignatureModifiers(node: SignatureDeclaration): boolean { if (node.flags & NodeFlags.Modifier) { return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_not_permitted_on_index_signature_members); } } function checkIndexSignatureParameters(node: SignatureDeclaration): boolean { var parameter = node.parameters[0]; if (node.parameters.length !== 1) { if (parameter) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_must_have_exactly_one_parameter); } else { return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_exactly_one_parameter); } } else if (parameter.flags & NodeFlags.Rest) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_cannot_have_a_rest_parameter); } else if (parameter.flags & NodeFlags.Modifier) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_accessibility_modifier); } else if (parameter.flags & NodeFlags.QuestionMark) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_a_question_mark); } else if (parameter.initializer) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_cannot_have_an_initializer); } else if (!parameter.type) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_must_have_a_type_annotation); } else if (parameter.type.kind !== SyntaxKind.StringKeyword && parameter.type.kind !== SyntaxKind.NumberKeyword) { return grammarErrorOnNode(parameter.name, Diagnostics.An_index_signature_parameter_type_must_be_string_or_number); } else if (!node.type) { return grammarErrorOnNode(node, Diagnostics.An_index_signature_must_have_a_type_annotation); } } function checkInterfaceDeclaration(node: InterfaceDeclaration) { return checkForDisallowedTrailingComma(node.baseTypes) || checkForAtLeastOneHeritageClause(node.baseTypes, "extends"); } function checkMethod(node: MethodDeclaration) { return checkAnyParsedSignature(node) || checkForBodyInAmbientContext(node.body, /*isConstructor:*/ false) || (node.parent.kind === SyntaxKind.ClassDeclaration && checkForInvalidQuestionMark(node, Diagnostics.A_class_member_cannot_be_declared_optional)); } function checkForBodyInAmbientContext(body: Block | Expression, isConstructor: boolean): boolean { if (inAmbientContext && body && body.kind === SyntaxKind.FunctionBlock) { var diagnostic = isConstructor ? Diagnostics.A_constructor_implementation_cannot_be_declared_in_an_ambient_context : Diagnostics.A_function_implementation_cannot_be_declared_in_an_ambient_context; return grammarErrorOnFirstToken(body, diagnostic); } } function checkModuleDeclaration(node: ModuleDeclaration): boolean { return checkModuleDeclarationName(node) || checkModuleDeclarationStatements(node); } function checkModuleDeclarationName(node: ModuleDeclaration) { if (!inAmbientContext && node.name.kind === SyntaxKind.StringLiteral) { return grammarErrorOnNode(node.name, Diagnostics.Only_ambient_modules_can_use_quoted_names); } } function checkModuleDeclarationStatements(node: ModuleDeclaration): boolean { if (node.name.kind === SyntaxKind.Identifier && node.body.kind === SyntaxKind.ModuleBlock) { var statements = (node.body).statements; for (var i = 0, n = statements.length; i < n; i++) { var statement = statements[i]; if (statement.kind === SyntaxKind.ExportAssignment) { // Export assignments are not allowed in an internal module return grammarErrorOnNode(statement, Diagnostics.An_export_assignment_cannot_be_used_in_an_internal_module); } else if (statement.kind === SyntaxKind.ImportDeclaration && (statement).externalModuleName) { return grammarErrorOnNode((statement).externalModuleName, Diagnostics.Import_declarations_in_an_internal_module_cannot_reference_an_external_module); } } } } function checkObjectLiteral(node: ObjectLiteral): boolean { var seen: Map = {}; var Property = 1; var GetAccessor = 2; var SetAccesor = 4; var GetOrSetAccessor = GetAccessor | SetAccesor; var inStrictMode = (node.flags & NodeFlags.ParsedInStrictModeContext) !== 0; for (var i = 0, n = node.properties.length; i < n; i++) { var prop = node.properties[i]; // TODO(jfreeman): continue if we have a computed property if (prop.kind === SyntaxKind.OmittedExpression) { continue; } var p = prop; var name = p.name; // ECMA-262 11.1.5 Object Initialiser // If previous is not undefined then throw a SyntaxError exception if any of the following conditions are true // a.This production is contained in strict code and IsDataDescriptor(previous) is true and // IsDataDescriptor(propId.descriptor) is true. // b.IsDataDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true. // c.IsAccessorDescriptor(previous) is true and IsDataDescriptor(propId.descriptor) is true. // d.IsAccessorDescriptor(previous) is true and IsAccessorDescriptor(propId.descriptor) is true // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields var currentKind: number; if (p.kind === SyntaxKind.PropertyAssignment) { currentKind = Property; } else if (p.kind === SyntaxKind.ShorthandPropertyAssignment) { currentKind = Property; } else if (p.kind === SyntaxKind.GetAccessor) { currentKind = GetAccessor; } else if (p.kind === SyntaxKind.SetAccessor) { currentKind = SetAccesor; } else { Debug.fail("Unexpected syntax kind:" + p.kind); } if (!hasProperty(seen, name.text)) { seen[name.text] = currentKind; } else { var existingKind = seen[name.text]; if (currentKind === Property && existingKind === Property) { if (inStrictMode) { grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_properties_with_the_same_name_in_strict_mode); } } else if ((currentKind & GetOrSetAccessor) && (existingKind & GetOrSetAccessor)) { if (existingKind !== GetOrSetAccessor && currentKind !== existingKind) { seen[name.text] = currentKind | existingKind; } else { return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_multiple_get_Slashset_accessors_with_the_same_name); } } else { return grammarErrorOnNode(name, Diagnostics.An_object_literal_cannot_have_property_and_accessor_with_the_same_name); } } } } function checkNumericLiteral(node: LiteralExpression): boolean { if (node.flags & NodeFlags.OctalLiteral) { if (node.flags & NodeFlags.ParsedInStrictModeContext) { return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_allowed_in_strict_mode); } else if (languageVersion >= ScriptTarget.ES5) { return grammarErrorOnNode(node, Diagnostics.Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher); } } } function checkModifiers(node: Node): boolean { switch (node.kind) { case SyntaxKind.GetAccessor: case SyntaxKind.SetAccessor: case SyntaxKind.Constructor: case SyntaxKind.Property: case SyntaxKind.Method: case SyntaxKind.IndexSignature: case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.ModuleDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.ExportAssignment: case SyntaxKind.VariableStatement: case SyntaxKind.FunctionDeclaration: case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.ImportDeclaration: case SyntaxKind.Parameter: break; default: return false; } if (!node.modifiers) { return; } var lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node; var flags = 0; for (var i = 0, n = node.modifiers.length; i < n; i++) { var modifier = node.modifiers[i]; switch (modifier.kind) { case SyntaxKind.PublicKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.PrivateKeyword: var text: string; if (modifier.kind === SyntaxKind.PublicKeyword) { text = "public"; } else if (modifier.kind === SyntaxKind.ProtectedKeyword) { text = "protected"; lastProtected = modifier; } else { text = "private"; lastPrivate = modifier; } if (flags & NodeFlags.AccessibilityModifier) { return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen); } else if (flags & NodeFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static"); } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, text); } flags |= modifierToFlag(modifier.kind); break; case SyntaxKind.StaticKeyword: if (flags & NodeFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static"); } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "static"); } else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "static"); } flags |= NodeFlags.Static; lastStatic = modifier; break; case SyntaxKind.ExportKeyword: if (flags & NodeFlags.Export) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "export"); } else if (flags & NodeFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare"); } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export"); } else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "export"); } flags |= NodeFlags.Export; break; case SyntaxKind.DeclareKeyword: if (flags & NodeFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare"); } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare"); } else if (node.kind === SyntaxKind.Parameter) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "declare"); } else if (inAmbientContext && node.parent.kind === SyntaxKind.ModuleBlock) { return grammarErrorOnNode(modifier, Diagnostics.A_declare_modifier_cannot_be_used_in_an_already_ambient_context); } flags |= NodeFlags.Ambient; lastDeclare = modifier; break; } } if (node.kind === SyntaxKind.Constructor) { if (flags & NodeFlags.Static) { return grammarErrorOnNode(lastStatic, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "static"); } else if (flags & NodeFlags.Protected) { return grammarErrorOnNode(lastProtected, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "protected"); } else if (flags & NodeFlags.Private) { return grammarErrorOnNode(lastPrivate, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private"); } } else if (node.kind === SyntaxKind.ImportDeclaration && flags & NodeFlags.Ambient) { return grammarErrorOnNode(lastDeclare, Diagnostics.A_declare_modifier_cannot_be_used_with_an_import_declaration, "declare"); } else if (node.kind === SyntaxKind.InterfaceDeclaration && flags & NodeFlags.Ambient) { return grammarErrorOnNode(lastDeclare, Diagnostics.A_declare_modifier_cannot_be_used_with_an_interface_declaration, "declare"); } } function checkParameter(node: ParameterDeclaration): boolean { // It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the // Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code // or if its FunctionBody is strict code(11.1.5). // It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a // strict mode FunctionLikeDeclaration or FunctionExpression(13.1) if (node.flags & NodeFlags.ParsedInStrictModeContext && isEvalOrArgumentsIdentifier(node.name)) { return reportInvalidUseInStrictMode(node.name); } } function checkTypeParameterList(typeParameters: NodeArray): boolean { if (checkForDisallowedTrailingComma(typeParameters)) { return true; } if (typeParameters && typeParameters.length === 0) { var start = typeParameters.pos - "<".length; var end = skipTrivia(sourceText, typeParameters.end) + ">".length; return grammarErrorAtPos(start, end - start, Diagnostics.Type_parameter_list_cannot_be_empty); } } function checkParameterList(parameters: NodeArray): boolean { if (checkForDisallowedTrailingComma(parameters)) { return true; } var seenOptionalParameter = false; var parameterCount = parameters.length; for (var i = 0; i < parameterCount; i++) { var parameter = parameters[i]; if (parameter.flags & NodeFlags.Rest) { if (i !== (parameterCount - 1)) { return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list); } if (parameter.flags & NodeFlags.QuestionMark) { return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_be_optional); } if (parameter.initializer) { return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_cannot_have_an_initializer); } } else if (parameter.flags & NodeFlags.QuestionMark || parameter.initializer) { seenOptionalParameter = true; if (parameter.flags & NodeFlags.QuestionMark && parameter.initializer) { return grammarErrorOnNode(parameter.name, Diagnostics.Parameter_cannot_have_question_mark_and_initializer); } } else { if (seenOptionalParameter) { return grammarErrorOnNode(parameter.name, Diagnostics.A_required_parameter_cannot_follow_an_optional_parameter); } } } } function checkPostfixOperator(node: UnaryExpression) { // The identifier eval or arguments may not appear as the LeftHandSideExpression of an // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator. if (node.flags & NodeFlags.ParsedInStrictModeContext && isEvalOrArgumentsIdentifier(node.operand)) { return reportInvalidUseInStrictMode(node.operand); } } function checkPrefixOperator(node: UnaryExpression) { if (node.flags & NodeFlags.ParsedInStrictModeContext) { // The identifier eval or arguments may not appear as the LeftHandSideExpression of an // Assignment operator(11.13) or of a PostfixExpression(11.3) or as the UnaryExpression // operated upon by a Prefix Increment(11.4.4) or a Prefix Decrement(11.4.5) operator if ((node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) && isEvalOrArgumentsIdentifier(node.operand)) { return reportInvalidUseInStrictMode(node.operand); } else if (node.operator === SyntaxKind.DeleteKeyword && node.operand.kind === SyntaxKind.Identifier) { // When a delete operator occurs within strict mode code, a SyntaxError is thrown if its // UnaryExpression is a direct reference to a variable, function argument, or function name return grammarErrorOnNode(node.operand, Diagnostics.delete_cannot_be_called_on_an_identifier_in_strict_mode); } } } function checkProperty(node: PropertyDeclaration) { return (node.parent.kind === SyntaxKind.ClassDeclaration && checkForInvalidQuestionMark(node, Diagnostics.A_class_member_cannot_be_declared_optional)) || checkForInitializerInAmbientContext(node); } function checkForInitializerInAmbientContext(node: PropertyDeclaration) { if (inAmbientContext && node.initializer) { return grammarErrorOnFirstToken(node.initializer, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } } function checkPropertyAssignment(node: PropertyDeclaration) { return checkForInvalidQuestionMark(node, Diagnostics.An_object_member_cannot_be_declared_optional); } function checkForInvalidQuestionMark(node: Declaration, message: DiagnosticMessage) { if (node.flags & NodeFlags.QuestionMark) { var pos = skipTrivia(sourceText, node.name.end); return grammarErrorAtPos(pos, "?".length, message); } } function checkReturnStatement(node: ReturnStatement) { if (!inFunctionBlock) { return grammarErrorOnFirstToken(node, Diagnostics.A_return_statement_can_only_be_used_within_a_function_body); } } function checkSetAccessor(node: MethodDeclaration) { return checkAnyParsedSignature(node) || checkAccessor(node); } function checkAccessor(accessor: MethodDeclaration): boolean { var kind = accessor.kind; if (languageVersion < ScriptTarget.ES5) { return grammarErrorOnNode(accessor.name, Diagnostics.Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher); } else if (inAmbientContext) { return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_be_declared_in_an_ambient_context); } else if (accessor.body === undefined) { return grammarErrorAtPos(accessor.end - 1, ";".length, Diagnostics._0_expected, "{"); } else if (accessor.typeParameters) { return grammarErrorOnNode(accessor.name, Diagnostics.An_accessor_cannot_have_type_parameters); } else if (kind === SyntaxKind.GetAccessor && accessor.parameters.length) { return grammarErrorOnNode(accessor.name, Diagnostics.A_get_accessor_cannot_have_parameters); } else if (kind === SyntaxKind.SetAccessor) { if (accessor.type) { return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_a_return_type_annotation); } else if (accessor.parameters.length !== 1) { return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_must_have_exactly_one_parameter); } else { var parameter = accessor.parameters[0]; if (parameter.flags & NodeFlags.Rest) { return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_rest_parameter); } else if (parameter.flags & NodeFlags.Modifier) { return grammarErrorOnNode(accessor.name, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation); } else if (parameter.flags & NodeFlags.QuestionMark) { return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_cannot_have_an_optional_parameter); } else if (parameter.initializer) { return grammarErrorOnNode(accessor.name, Diagnostics.A_set_accessor_parameter_cannot_have_an_initializer); } } } } function checkSourceFile(node: SourceFile): boolean { return inAmbientContext && checkTopLevelElementsForRequiredDeclareModifier(file); } function checkTopLevelElementsForRequiredDeclareModifier(file: SourceFile): boolean { for (var i = 0, n = file.statements.length; i < n; i++) { var decl = file.statements[i]; if (isDeclaration(decl) || decl.kind === SyntaxKind.VariableStatement) { if (checkTopLevelElementForRequiredDeclareModifier(decl)) { return true; } } } } function checkTopLevelElementForRequiredDeclareModifier(node: Node): boolean { // A declare modifier is required for any top level .d.ts declaration except export=, interfaces and imports: // categories: // // DeclarationElement: // ExportAssignment // export_opt InterfaceDeclaration // export_opt ImportDeclaration // export_opt ExternalImportDeclaration // export_opt AmbientDeclaration // if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ExportAssignment || (node.flags & NodeFlags.Ambient)) { return false; } return grammarErrorOnFirstToken(node, Diagnostics.A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file); } function checkShorthandPropertyAssignment(node: ShortHandPropertyDeclaration): boolean { return checkForInvalidQuestionMark(node, Diagnostics.An_object_member_cannot_be_declared_optional); } function checkSwitchStatement(node: SwitchStatement) { var firstDefaultClause: CaseOrDefaultClause; // Error on duplicate 'default' clauses. for (var i = 0, n = node.clauses.length; i < n; i++) { var clause = node.clauses[i]; if (clause.kind === SyntaxKind.DefaultClause) { if (firstDefaultClause === undefined) { firstDefaultClause = clause; } else { var start = skipTrivia(file.text, clause.pos); var end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end; return grammarErrorAtPos(start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement); } } } } function checkTaggedTemplateExpression(node: TaggedTemplateExpression) { if (languageVersion < ScriptTarget.ES6) { return grammarErrorOnFirstToken(node.template, Diagnostics.Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher); } } function checkTupleType(node: TupleTypeNode) { return checkForDisallowedTrailingComma(node.elementTypes) || checkForAtLeastOneType(node); } function checkForAtLeastOneType(node: TupleTypeNode): boolean { if (node.elementTypes.length === 0) { return grammarErrorOnNode(node, Diagnostics.A_tuple_type_element_list_cannot_be_empty) } } function checkTypeParameter(node: TypeParameterDeclaration) { if (node.expression) { return grammarErrorOnFirstToken(node.expression, Diagnostics.Type_expected); } } function checkTypeReference(node: TypeReferenceNode) { return checkTypeArguments(node.typeArguments); } function checkVariableDeclaration(node: VariableDeclaration) { if (inAmbientContext && node.initializer) { var equalsPos = node.type ? skipTrivia(sourceText, node.type.end) : skipTrivia(sourceText, node.name.end); return grammarErrorAtPos(equalsPos, "=".length, Diagnostics.Initializers_are_not_allowed_in_ambient_contexts); } if (!inAmbientContext && !node.initializer && isConst(node)) { return grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized); } if (node.flags & NodeFlags.ParsedInStrictModeContext && isEvalOrArgumentsIdentifier(node.name)) { // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code // and its Identifier is eval or arguments return reportInvalidUseInStrictMode(node.name); } } function checkVariableDeclarations(declarations: NodeArray): boolean { if (declarations) { if (checkForDisallowedTrailingComma(declarations)) { return true; } if (!declarations.length) { return grammarErrorAtPos(declarations.pos, declarations.end - declarations.pos, Diagnostics.Variable_declaration_list_cannot_be_empty); } var decl = declarations[0]; if (languageVersion < ScriptTarget.ES6) { if (isLet(decl)) { return grammarErrorOnFirstToken(decl, Diagnostics.let_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher); } else if (isConst(decl)) { return grammarErrorOnFirstToken(decl, Diagnostics.const_declarations_are_only_available_when_targeting_ECMAScript_6_and_higher); } } } } function checkVariableStatement(node: VariableStatement) { return checkVariableDeclarations(node.declarations) || checkForDisallowedLetOrConstStatement(node); } function checkForDisallowedLetOrConstStatement(node: VariableStatement) { if (!allowLetAndConstDeclarations(node.parent)) { if (isLet(node)) { return grammarErrorOnNode(node, Diagnostics.let_declarations_can_only_be_declared_inside_a_block); } else if (isConst(node)) { return grammarErrorOnNode(node, Diagnostics.const_declarations_can_only_be_declared_inside_a_block); } } } function allowLetAndConstDeclarations(parent: Node): boolean { switch (parent.kind) { case SyntaxKind.IfStatement: case SyntaxKind.DoStatement: case SyntaxKind.WhileStatement: case SyntaxKind.WithStatement: case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: return false; case SyntaxKind.LabeledStatement: return allowLetAndConstDeclarations(parent.parent); } return true; } function checkWithStatement(node: WithStatement): boolean { if (node.flags & NodeFlags.ParsedInStrictModeContext) { // Strict mode code may not include a WithStatement. The occurrence of a WithStatement in such // a context is an return grammarErrorOnFirstToken(node, Diagnostics.with_statements_are_not_allowed_in_strict_mode); } } } export function createProgram(rootNames: string[], options: CompilerOptions, host: CompilerHost): Program { var program: Program; var files: SourceFile[] = []; var filesByName: Map = {}; var errors: Diagnostic[] = []; var seenNoDefaultLib = options.noLib; var commonSourceDirectory: string; forEach(rootNames, name => processRootFile(name, false)); if (!seenNoDefaultLib) { processRootFile(host.getDefaultLibFilename(), true); } verifyCompilerOptions(); errors.sort(compareDiagnostics); program = { getSourceFile: getSourceFile, getSourceFiles: () => files, getCompilerOptions: () => options, getCompilerHost: () => host, getDiagnostics: getDiagnostics, getGlobalDiagnostics: getGlobalDiagnostics, getTypeChecker: fullTypeCheckMode => createTypeChecker(program, fullTypeCheckMode), getCommonSourceDirectory: () => commonSourceDirectory, }; return program; function getSourceFile(filename: string) { filename = host.getCanonicalFileName(filename); return hasProperty(filesByName, filename) ? filesByName[filename] : undefined; } function getDiagnostics(sourceFile?: SourceFile): Diagnostic[] { return sourceFile ? filter(errors, e => e.file === sourceFile) : errors; } function getGlobalDiagnostics(): Diagnostic[] { return filter(errors, e => !e.file); } function hasExtension(filename: string): boolean { return getBaseFilename(filename).indexOf(".") >= 0; } function processRootFile(filename: string, isDefaultLib: boolean) { processSourceFile(normalizePath(filename), isDefaultLib); } function processSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number) { if (refEnd !== undefined && refPos !== undefined) { var start = refPos; var length = refEnd - refPos; } var diagnostic: DiagnosticMessage; if (hasExtension(filename)) { if (!fileExtensionIs(filename, ".ts")) { diagnostic = Diagnostics.File_0_must_have_extension_ts_or_d_ts; } else if (!findSourceFile(filename, isDefaultLib, refFile, refPos, refEnd)) { diagnostic = Diagnostics.File_0_not_found; } else if (refFile && host.getCanonicalFileName(filename) === host.getCanonicalFileName(refFile.filename)) { diagnostic = Diagnostics.A_file_cannot_have_a_reference_to_itself; } } else { if (!(findSourceFile(filename + ".ts", isDefaultLib, refFile, refPos, refEnd) || findSourceFile(filename + ".d.ts", isDefaultLib, refFile, refPos, refEnd))) { diagnostic = Diagnostics.File_0_not_found; filename += ".ts"; } } if (diagnostic) { if (refFile) { errors.push(createFileDiagnostic(refFile, start, length, diagnostic, filename)); } else { errors.push(createCompilerDiagnostic(diagnostic, filename)); } } } // Get source file from normalized filename function findSourceFile(filename: string, isDefaultLib: boolean, refFile?: SourceFile, refStart?: number, refLength?: number): SourceFile { var canonicalName = host.getCanonicalFileName(filename); if (hasProperty(filesByName, canonicalName)) { // We've already looked for this file, use cached result return getSourceFileFromCache(filename, canonicalName, /*useAbsolutePath*/ false); } else { var normalizedAbsolutePath = getNormalizedAbsolutePath(filename, host.getCurrentDirectory()); var canonicalAbsolutePath = host.getCanonicalFileName(normalizedAbsolutePath); if (hasProperty(filesByName, canonicalAbsolutePath)) { return getSourceFileFromCache(normalizedAbsolutePath, canonicalAbsolutePath, /*useAbsolutePath*/ true); } // We haven't looked for this file, do so now and cache result var file = filesByName[canonicalName] = host.getSourceFile(filename, options.target, hostErrorMessage => { errors.push(createFileDiagnostic(refFile, refStart, refLength, Diagnostics.Cannot_read_file_0_Colon_1, filename, hostErrorMessage)); }); if (file) { seenNoDefaultLib = seenNoDefaultLib || file.hasNoDefaultLib; // Set the source file for normalized absolute path filesByName[canonicalAbsolutePath] = file; if (!options.noResolve) { var basePath = getDirectoryPath(filename); processReferencedFiles(file, basePath); processImportedModules(file, basePath); } if (isDefaultLib) { files.unshift(file); } else { files.push(file); } forEach(file.getSyntacticDiagnostics(), e => { errors.push(e); }); } } return file; function getSourceFileFromCache(filename: string, canonicalName: string, useAbsolutePath: boolean): SourceFile { var file = filesByName[canonicalName]; if (file && host.useCaseSensitiveFileNames()) { var sourceFileName = useAbsolutePath ? getNormalizedAbsolutePath(file.filename, host.getCurrentDirectory()) : file.filename; if (canonicalName !== sourceFileName) { errors.push(createFileDiagnostic(refFile, refStart, refLength, Diagnostics.Filename_0_differs_from_already_included_filename_1_only_in_casing, filename, sourceFileName)); } } return file; } } function processReferencedFiles(file: SourceFile, basePath: string) { forEach(file.referencedFiles, ref => { var referencedFilename = isRootedDiskPath(ref.filename) ? ref.filename : combinePaths(basePath, ref.filename); processSourceFile(normalizePath(referencedFilename), /* isDefaultLib */ false, file, ref.pos, ref.end); }); } function processImportedModules(file: SourceFile, basePath: string) { forEach(file.statements, node => { if (node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName) { var nameLiteral = (node).externalModuleName; var moduleName = nameLiteral.text; if (moduleName) { var searchPath = basePath; while (true) { var searchName = normalizePath(combinePaths(searchPath, moduleName)); if (findModuleSourceFile(searchName + ".ts", nameLiteral) || findModuleSourceFile(searchName + ".d.ts", nameLiteral)) { break; } var parentPath = getDirectoryPath(searchPath); if (parentPath === searchPath) { break; } searchPath = parentPath; } } } else if (node.kind === SyntaxKind.ModuleDeclaration && (node).name.kind === SyntaxKind.StringLiteral && (node.flags & NodeFlags.Ambient || isDeclarationFile(file))) { // TypeScript 1.0 spec (April 2014): 12.1.6 // An AmbientExternalModuleDeclaration declares an external module. // This type of declaration is permitted only in the global module. // The StringLiteral must specify a top - level external module name. // Relative external module names are not permitted forEachChild((node).body, node => { if (node.kind === SyntaxKind.ImportDeclaration && (node).externalModuleName) { var nameLiteral = (node).externalModuleName; var moduleName = nameLiteral.text; if (moduleName) { // TypeScript 1.0 spec (April 2014): 12.1.6 // An ExternalImportDeclaration in anAmbientExternalModuleDeclaration may reference other external modules // only through top - level external module names. Relative external module names are not permitted. var searchName = normalizePath(combinePaths(basePath, moduleName)); var tsFile = findModuleSourceFile(searchName + ".ts", nameLiteral); if (!tsFile) { findModuleSourceFile(searchName + ".d.ts", nameLiteral); } } } }); } }); function findModuleSourceFile(filename: string, nameLiteral: LiteralExpression) { return findSourceFile(filename, /* isDefaultLib */ false, file, nameLiteral.pos, nameLiteral.end - nameLiteral.pos); } } function verifyCompilerOptions() { if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) { // Error to specify --mapRoot or --sourceRoot without mapSourceFiles if (options.mapRoot) { errors.push(createCompilerDiagnostic(Diagnostics.Option_mapRoot_cannot_be_specified_without_specifying_sourcemap_option)); } if (options.sourceRoot) { errors.push(createCompilerDiagnostic(Diagnostics.Option_sourceRoot_cannot_be_specified_without_specifying_sourcemap_option)); } return; } var firstExternalModule = forEach(files, f => isExternalModule(f) ? f : undefined); if (firstExternalModule && options.module === ModuleKind.None) { // We cannot use createDiagnosticFromNode because nodes do not have parents yet var externalModuleErrorSpan = getErrorSpanForNode(firstExternalModule.externalModuleIndicator); var errorStart = skipTrivia(firstExternalModule.text, externalModuleErrorSpan.pos); var errorLength = externalModuleErrorSpan.end - errorStart; errors.push(createFileDiagnostic(firstExternalModule, errorStart, errorLength, Diagnostics.Cannot_compile_external_modules_unless_the_module_flag_is_provided)); } // there has to be common source directory if user specified --outdir || --sourcRoot // if user specified --mapRoot, there needs to be common source directory if there would be multiple files being emitted if (options.outDir || // there is --outDir specified options.sourceRoot || // there is --sourceRoot specified (options.mapRoot && // there is --mapRoot Specified and there would be multiple js files generated (!options.out || firstExternalModule !== undefined))) { var commonPathComponents: string[]; forEach(files, sourceFile => { // Each file contributes into common source file path if (!(sourceFile.flags & NodeFlags.DeclarationFile) && !fileExtensionIs(sourceFile.filename, ".js")) { var sourcePathComponents = getNormalizedPathComponents(sourceFile.filename, host.getCurrentDirectory()); sourcePathComponents.pop(); // FileName is not part of directory if (commonPathComponents) { for (var i = 0; i < Math.min(commonPathComponents.length, sourcePathComponents.length); i++) { if (commonPathComponents[i] !== sourcePathComponents[i]) { if (i === 0) { errors.push(createCompilerDiagnostic(Diagnostics.Cannot_find_the_common_subdirectory_path_for_the_input_files)); return; } // New common path found that is 0 -> i-1 commonPathComponents.length = i; break; } } // If the fileComponent path completely matched and less than already found update the length if (sourcePathComponents.length < commonPathComponents.length) { commonPathComponents.length = sourcePathComponents.length; } } else { // first file commonPathComponents = sourcePathComponents; } } }); commonSourceDirectory = getNormalizedPathFromPathComponents(commonPathComponents); if (commonSourceDirectory) { // Make sure directory path ends with directory separator so this string can directly // used to replace with "" to get the relative path of the source file and the relative path doesn't // start with / making it rooted path commonSourceDirectory += directorySeparator; } } } } }