diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 125e492cc3960..b527754e0e455 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21085,10 +21085,6 @@ namespace ts { } } - function isAccessor(kind: SyntaxKind): boolean { - return kind === SyntaxKind.GetAccessor || kind === SyntaxKind.SetAccessor; - } - function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean { const baseTypes = getBaseTypes(type); if (baseTypes.length < 2) { @@ -24558,7 +24554,7 @@ namespace ts { function checkGrammarStatementInAmbientContext(node: Node): boolean { if (isInAmbientContext(node)) { // An accessors is already reported about the ambient context - if (isAccessor(node.parent.kind)) { + if (isAccessor(node.parent)) { return getNodeLinks(node).hasReportedStatementInAmbientContext = true; } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 943d011c350df..7c690e9315528 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3908,7 +3908,7 @@ namespace ts { return bindingElement.right; } - if (isSpreadExpression(bindingElement)) { + if (isSpreadElement(bindingElement)) { // Recovery consistent with existing emit. return getInitializerOfBindingOrAssignmentElement(bindingElement.expression); } @@ -3976,7 +3976,7 @@ namespace ts { return getTargetOfBindingOrAssignmentElement(bindingElement.left); } - if (isSpreadExpression(bindingElement)) { + if (isSpreadElement(bindingElement)) { // `a` in `[...a] = ...` return getTargetOfBindingOrAssignmentElement(bindingElement.expression); } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index 63c1014516567..b9ea70d450663 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -3396,7 +3396,7 @@ namespace ts { else { if (segments.length === 1) { const firstElement = elements[0]; - return needsUniqueCopy && isSpreadExpression(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression + return needsUniqueCopy && isSpreadElement(firstElement) && firstElement.expression.kind !== SyntaxKind.ArrayLiteralExpression ? createArraySlice(segments[0]) : segments[0]; } @@ -3407,7 +3407,7 @@ namespace ts { } function partitionSpread(node: Expression) { - return isSpreadExpression(node) + return isSpreadElement(node) ? visitSpanOfSpreads : visitSpanOfNonSpreads; } diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 3355ad3563349..e6351c0965ea1 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1502,7 +1502,7 @@ namespace ts { if (isAssignmentExpression(node, /*excludeCompoundAssignment*/ true)) { return hasExportedReferenceInDestructuringTarget(node.left); } - else if (isSpreadExpression(node)) { + else if (isSpreadElement(node)) { return hasExportedReferenceInDestructuringTarget(node.expression); } else if (isObjectLiteralExpression(node)) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 134404c247b9d..f87abc124bf09 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -254,10 +254,6 @@ namespace ts { return !nodeIsMissing(node); } - export function isToken(n: Node): boolean { - return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken; - } - export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: boolean): 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. @@ -284,22 +280,6 @@ namespace ts { return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos); } - export function isJSDocNode(node: Node) { - return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode; - } - - export function isJSDoc(node: Node): node is JSDoc { - return node.kind === SyntaxKind.JSDocComment; - } - - export function isJSDocTypedefTag(node: Node): node is JSDocTypedefTag { - return node.kind === SyntaxKind.JSDocTypedefTag; - } - - export function isJSDocTag(node: Node) { - return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode; - } - export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFileLike): number { if (nodeIsMissing(node) || !node.decorators) { return getTokenPosOfNode(node, sourceFile); @@ -743,10 +723,6 @@ namespace ts { return false; } - export function isPrefixUnaryExpression(node: Node): node is PrefixUnaryExpression { - return node.kind === SyntaxKind.PrefixUnaryExpression; - } - // 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 { @@ -854,49 +830,6 @@ namespace ts { return false; } - export function isAccessor(node: Node): node is AccessorDeclaration { - return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor); - } - - export function isClassLike(node: Node): node is ClassLikeDeclaration { - return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression); - } - - export function isFunctionLike(node: Node): node is FunctionLikeDeclaration { - return node && isFunctionLikeKind(node.kind); - } - - export function isFunctionLikeKind(kind: SyntaxKind): boolean { - switch (kind) { - case SyntaxKind.Constructor: - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ArrowFunction: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: - case SyntaxKind.CallSignature: - case SyntaxKind.ConstructSignature: - case SyntaxKind.IndexSignature: - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - return true; - } - - return false; - } - - export function isFunctionOrConstructorTypeNode(node: Node): node is FunctionTypeNode | ConstructorTypeNode { - switch (node.kind) { - case SyntaxKind.FunctionType: - case SyntaxKind.ConstructorType: - return true; - } - - return false; - } - export function introducesArgumentsExoticObject(node: Node) { switch (node.kind) { case SyntaxKind.MethodDeclaration: @@ -911,21 +844,6 @@ namespace ts { return false; } - export function isIterationStatement(node: Node, lookInLabeledStatements: boolean): node is IterationStatement { - switch (node.kind) { - case SyntaxKind.ForStatement: - case SyntaxKind.ForInStatement: - case SyntaxKind.ForOfStatement: - case SyntaxKind.DoStatement: - case SyntaxKind.WhileStatement: - return true; - case SyntaxKind.LabeledStatement: - return lookInLabeledStatements && isIterationStatement((node).statement, lookInLabeledStatements); - } - - return false; - } - export function unwrapInnermostStatementOfLabel(node: LabeledStatement, beforeUnwrapLabelCallback?: (node: LabeledStatement) => void) { while (true) { if (beforeUnwrapLabelCallback) { @@ -1144,24 +1062,6 @@ namespace ts { return undefined; } - export function isCallLikeExpression(node: Node): node is CallLikeExpression { - switch (node.kind) { - case SyntaxKind.JsxOpeningElement: - case SyntaxKind.JsxSelfClosingElement: - case SyntaxKind.CallExpression: - case SyntaxKind.NewExpression: - case SyntaxKind.TaggedTemplateExpression: - case SyntaxKind.Decorator: - return true; - default: - return false; - } - } - - export function isCallOrNewExpression(node: Node): node is CallExpression | NewExpression { - return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression; - } - export function getInvokedExpression(node: CallLikeExpression): Expression { if (node.kind === SyntaxKind.TaggedTemplateExpression) { return (node).tag; @@ -2012,10 +1912,6 @@ namespace ts { return false; } - export function isNumericLiteral(node: Node): node is NumericLiteral { - return node.kind === SyntaxKind.NumericLiteral; - } - export function isStringOrNumericLiteral(node: Node): node is StringLiteral | NumericLiteral { const kind = node.kind; return kind === SyntaxKind.StringLiteral @@ -2082,24 +1978,6 @@ namespace ts { return node.text === "push" || node.text === "unshift"; } - export function isModifierKind(token: SyntaxKind): boolean { - switch (token) { - case SyntaxKind.AbstractKeyword: - case SyntaxKind.AsyncKeyword: - case SyntaxKind.ConstKeyword: - case SyntaxKind.DeclareKeyword: - case SyntaxKind.DefaultKeyword: - case SyntaxKind.ExportKeyword: - case SyntaxKind.PublicKeyword: - case SyntaxKind.PrivateKeyword: - case SyntaxKind.ProtectedKeyword: - case SyntaxKind.ReadonlyKeyword: - case SyntaxKind.StaticKeyword: - return true; - } - return false; - } - export function isParameterDeclaration(node: VariableLikeDeclaration) { const root = getRootDeclaration(node); return root.kind === SyntaxKind.Parameter; @@ -3563,1199 +3441,1826 @@ namespace ts { return node.symbol && getDeclarationOfKind(node.symbol, kind) === node; } - // Node tests - // - // All node tests in the following list should *not* reference parent pointers so that - // they may be used with transformations. - - // Node Arrays - - export function isNodeArray(array: T[]): array is NodeArray { - return array.hasOwnProperty("pos") - && array.hasOwnProperty("end"); + export function isWatchSet(options: CompilerOptions) { + // Firefox has Object.prototype.watch + return options.watch && options.hasOwnProperty("watch"); } - // Literals - - export function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression { - return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; + export function getCheckFlags(symbol: Symbol): CheckFlags { + return symbol.flags & SymbolFlags.Transient ? (symbol).checkFlags : 0; } - export function isLiteralKind(kind: SyntaxKind): boolean { - return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; + export function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags { + if (s.valueDeclaration) { + const flags = getCombinedModifierFlags(s.valueDeclaration); + return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; + } + if (getCheckFlags(s) & CheckFlags.Synthetic) { + const checkFlags = (s).checkFlags; + const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : + checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : + ModifierFlags.Protected; + const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; + return accessModifier | staticModifier; + } + if (s.flags & SymbolFlags.Prototype) { + return ModifierFlags.Public | ModifierFlags.Static; + } + return 0; } - export function isTextualLiteralKind(kind: SyntaxKind): boolean { - return kind === SyntaxKind.StringLiteral || kind === SyntaxKind.NoSubstitutionTemplateLiteral; + export function levenshtein(s1: string, s2: string): number { + let previous: number[] = new Array(s2.length + 1); + let current: number[] = new Array(s2.length + 1); + for (let i = 0; i < s2.length + 1; i++) { + previous[i] = i; + current[i] = -1; + } + for (let i = 1; i < s1.length + 1; i++) { + current[0] = i; + for (let j = 1; j < s2.length + 1; j++) { + current[j] = Math.min( + previous[j] + 1, + current[j - 1] + 1, + previous[j - 1] + (s1[i - 1] === s2[j - 1] ? 0 : 2)); + } + // shift current back to previous, and then reuse previous' array + const tmp = previous; + previous = current; + current = tmp; + } + return previous[previous.length - 1]; } +} - export function isLiteralExpression(node: Node): node is LiteralExpression { - return isLiteralKind(node.kind); +namespace ts { + export function getDefaultLibFileName(options: CompilerOptions): string { + switch (options.target) { + case ScriptTarget.ESNext: + return "lib.esnext.full.d.ts"; + case ScriptTarget.ES2017: + return "lib.es2017.full.d.ts"; + case ScriptTarget.ES2016: + return "lib.es2016.full.d.ts"; + case ScriptTarget.ES2015: + return "lib.es6.d.ts"; // We don't use lib.es2015.full.d.ts due to breaking change. + default: + return "lib.d.ts"; + } } - // Pseudo-literals - - export function isTemplateLiteralKind(kind: SyntaxKind): boolean { - return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken; + export function textSpanEnd(span: TextSpan) { + return span.start + span.length; } - export function isTemplateHead(node: Node): node is TemplateHead { - return node.kind === SyntaxKind.TemplateHead; + export function textSpanIsEmpty(span: TextSpan) { + return span.length === 0; } - export function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail { - const kind = node.kind; - return kind === SyntaxKind.TemplateMiddle - || kind === SyntaxKind.TemplateTail; + export function textSpanContainsPosition(span: TextSpan, position: number) { + return position >= span.start && position < textSpanEnd(span); } - // Identifiers - - export function isIdentifier(node: Node): node is Identifier { - return node.kind === SyntaxKind.Identifier; + // Returns true if 'span' contains 'other'. + export function textSpanContainsTextSpan(span: TextSpan, other: TextSpan) { + return other.start >= span.start && textSpanEnd(other) <= textSpanEnd(span); } - export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier { - // Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`. - return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None; + export function textSpanOverlapsWith(span: TextSpan, other: TextSpan) { + const overlapStart = Math.max(span.start, other.start); + const overlapEnd = Math.min(textSpanEnd(span), textSpanEnd(other)); + return overlapStart < overlapEnd; } - // Keywords - - export function isModifier(node: Node): node is Modifier { - return isModifierKind(node.kind); + export function textSpanOverlap(span1: TextSpan, span2: TextSpan) { + const overlapStart = Math.max(span1.start, span2.start); + const overlapEnd = Math.min(textSpanEnd(span1), textSpanEnd(span2)); + if (overlapStart < overlapEnd) { + return createTextSpanFromBounds(overlapStart, overlapEnd); + } + return undefined; } - // Names + export function textSpanIntersectsWithTextSpan(span: TextSpan, other: TextSpan) { + return other.start <= textSpanEnd(span) && textSpanEnd(other) >= span.start; + } - export function isQualifiedName(node: Node): node is QualifiedName { - return node.kind === SyntaxKind.QualifiedName; + export function textSpanIntersectsWith(span: TextSpan, start: number, length: number) { + const end = start + length; + return start <= textSpanEnd(span) && end >= span.start; } - export function isComputedPropertyName(node: Node): node is ComputedPropertyName { - return node.kind === SyntaxKind.ComputedPropertyName; + export function decodedTextSpanIntersectsWith(start1: number, length1: number, start2: number, length2: number) { + const end1 = start1 + length1; + const end2 = start2 + length2; + return start2 <= end1 && end2 >= start1; } - export function isEntityName(node: Node): node is EntityName { - const kind = node.kind; - return kind === SyntaxKind.QualifiedName - || kind === SyntaxKind.Identifier; + export function textSpanIntersectsWithPosition(span: TextSpan, position: number) { + return position <= textSpanEnd(span) && position >= span.start; } - export function isPropertyName(node: Node): node is PropertyName { - const kind = node.kind; - return kind === SyntaxKind.Identifier - || kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.NumericLiteral - || kind === SyntaxKind.ComputedPropertyName; + export function textSpanIntersection(span1: TextSpan, span2: TextSpan) { + const intersectStart = Math.max(span1.start, span2.start); + const intersectEnd = Math.min(textSpanEnd(span1), textSpanEnd(span2)); + if (intersectStart <= intersectEnd) { + return createTextSpanFromBounds(intersectStart, intersectEnd); + } + return undefined; } - export function isModuleName(node: Node): node is ModuleName { - const kind = node.kind; - return kind === SyntaxKind.Identifier - || kind === SyntaxKind.StringLiteral; - } + export function createTextSpan(start: number, length: number): TextSpan { + if (start < 0) { + throw new Error("start < 0"); + } + if (length < 0) { + throw new Error("length < 0"); + } - export function isBindingName(node: Node): node is BindingName { - const kind = node.kind; - return kind === SyntaxKind.Identifier - || kind === SyntaxKind.ObjectBindingPattern - || kind === SyntaxKind.ArrayBindingPattern; + return { start, length }; } - // Signature elements - - export function isTypeParameter(node: Node): node is TypeParameterDeclaration { - return node.kind === SyntaxKind.TypeParameter; + export function createTextSpanFromBounds(start: number, end: number) { + return createTextSpan(start, end - start); } - export function isParameter(node: Node): node is ParameterDeclaration { - return node.kind === SyntaxKind.Parameter; + export function textChangeRangeNewSpan(range: TextChangeRange) { + return createTextSpan(range.span.start, range.newLength); } - export function isDecorator(node: Node): node is Decorator { - return node.kind === SyntaxKind.Decorator; + export function textChangeRangeIsUnchanged(range: TextChangeRange) { + return textSpanIsEmpty(range.span) && range.newLength === 0; } - // Type members + export function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange { + if (newLength < 0) { + throw new Error("newLength < 0"); + } - export function isMethodDeclaration(node: Node): node is MethodDeclaration { - return node.kind === SyntaxKind.MethodDeclaration; + return { span, newLength }; } - export function isClassElement(node: Node): node is ClassElement { - const kind = node.kind; - return kind === SyntaxKind.Constructor - || kind === SyntaxKind.PropertyDeclaration - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.IndexSignature - || kind === SyntaxKind.SemicolonClassElement - || kind === SyntaxKind.MissingDeclaration; - } + export let unchangedTextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0); - export function isTypeElement(node: Node): node is TypeElement { - const kind = node.kind; - return kind === SyntaxKind.ConstructSignature - || kind === SyntaxKind.CallSignature - || kind === SyntaxKind.PropertySignature - || kind === SyntaxKind.MethodSignature - || kind === SyntaxKind.IndexSignature - || kind === SyntaxKind.MissingDeclaration; - } + /** + * Called to merge all the changes that occurred across several versions of a script snapshot + * into a single change. i.e. if a user keeps making successive edits to a script we will + * have a text change from V1 to V2, V2 to V3, ..., Vn. + * + * This function will then merge those changes into a single change range valid between V1 and + * Vn. + */ + export function collapseTextChangeRangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange { + if (changes.length === 0) { + return unchangedTextChangeRange; + } - export function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike { - const kind = node.kind; - return kind === SyntaxKind.PropertyAssignment - || kind === SyntaxKind.ShorthandPropertyAssignment - || kind === SyntaxKind.SpreadAssignment - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.MissingDeclaration; - } + if (changes.length === 1) { + return changes[0]; + } - // Type + // We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd } + // as it makes things much easier to reason about. + const change0 = changes[0]; - function isTypeNodeKind(kind: SyntaxKind) { - return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) - || kind === SyntaxKind.AnyKeyword - || kind === SyntaxKind.NumberKeyword - || kind === SyntaxKind.ObjectKeyword - || kind === SyntaxKind.BooleanKeyword - || kind === SyntaxKind.StringKeyword - || kind === SyntaxKind.SymbolKeyword - || kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.VoidKeyword - || kind === SyntaxKind.UndefinedKeyword - || kind === SyntaxKind.NullKeyword - || kind === SyntaxKind.NeverKeyword - || kind === SyntaxKind.ExpressionWithTypeArguments; - } + let oldStartN = change0.span.start; + let oldEndN = textSpanEnd(change0.span); + let newEndN = oldStartN + change0.newLength; - /** - * Node test that determines whether a node is a valid type node. - * This differs from the `isPartOfTypeNode` function which determines whether a node is *part* - * of a TypeNode. - */ - export function isTypeNode(node: Node): node is TypeNode { - return isTypeNodeKind(node.kind); - } + for (let i = 1; i < changes.length; i++) { + const nextChange = changes[i]; - // Binding patterns + // Consider the following case: + // i.e. two edits. The first represents the text change range { { 10, 50 }, 30 }. i.e. The span starting + // at 10, with length 50 is reduced to length 30. The second represents the text change range { { 30, 30 }, 40 }. + // i.e. the span starting at 30 with length 30 is increased to length 40. + // + // 0 10 20 30 40 50 60 70 80 90 100 + // ------------------------------------------------------------------------------------------------------- + // | / + // | /---- + // T1 | /---- + // | /---- + // | /---- + // ------------------------------------------------------------------------------------------------------- + // | \ + // | \ + // T2 | \ + // | \ + // | \ + // ------------------------------------------------------------------------------------------------------- + // + // Merging these turns out to not be too difficult. First, determining the new start of the change is trivial + // it's just the min of the old and new starts. i.e.: + // + // 0 10 20 30 40 50 60 70 80 90 100 + // ------------------------------------------------------------*------------------------------------------ + // | / + // | /---- + // T1 | /---- + // | /---- + // | /---- + // ----------------------------------------$-------------------$------------------------------------------ + // . | \ + // . | \ + // T2 . | \ + // . | \ + // . | \ + // ----------------------------------------------------------------------*-------------------------------- + // + // (Note the dots represent the newly inferred start. + // Determining the new and old end is also pretty simple. Basically it boils down to paying attention to the + // absolute positions at the asterisks, and the relative change between the dollar signs. Basically, we see + // which if the two $'s precedes the other, and we move that one forward until they line up. in this case that + // means: + // + // 0 10 20 30 40 50 60 70 80 90 100 + // --------------------------------------------------------------------------------*---------------------- + // | / + // | /---- + // T1 | /---- + // | /---- + // | /---- + // ------------------------------------------------------------$------------------------------------------ + // . | \ + // . | \ + // T2 . | \ + // . | \ + // . | \ + // ----------------------------------------------------------------------*-------------------------------- + // + // In other words (in this case), we're recognizing that the second edit happened after where the first edit + // ended with a delta of 20 characters (60 - 40). Thus, if we go back in time to where the first edit started + // that's the same as if we started at char 80 instead of 60. + // + // As it so happens, the same logic applies if the second edit precedes the first edit. In that case rather + // than pushing the first edit forward to match the second, we'll push the second edit forward to match the + // first. + // + // In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange + // semantics: { { start: 10, length: 70 }, newLength: 60 } + // + // The math then works out as follows. + // If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the + // final result like so: + // + // { + // oldStart3: Min(oldStart1, oldStart2), + // oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)), + // newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)) + // } - export function isArrayBindingPattern(node: Node): node is ArrayBindingPattern { - return node.kind === SyntaxKind.ArrayBindingPattern; - } + const oldStart1 = oldStartN; + const oldEnd1 = oldEndN; + const newEnd1 = newEndN; - export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { - return node.kind === SyntaxKind.ObjectBindingPattern; - } + const oldStart2 = nextChange.span.start; + const oldEnd2 = textSpanEnd(nextChange.span); + const newEnd2 = oldStart2 + nextChange.newLength; - export function isBindingPattern(node: Node): node is BindingPattern { - if (node) { - const kind = node.kind; - return kind === SyntaxKind.ArrayBindingPattern - || kind === SyntaxKind.ObjectBindingPattern; + oldStartN = Math.min(oldStart1, oldStart2); + oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)); + newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)); } - return false; + return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength*/ newEndN - oldStartN); } - export function isAssignmentPattern(node: Node): node is AssignmentPattern { - const kind = node.kind; - return kind === SyntaxKind.ArrayLiteralExpression - || kind === SyntaxKind.ObjectLiteralExpression; + export function getTypeParameterOwner(d: Declaration): Declaration { + if (d && d.kind === SyntaxKind.TypeParameter) { + for (let current: Node = d; current; current = current.parent) { + if (isFunctionLike(current) || isClassLike(current) || current.kind === SyntaxKind.InterfaceDeclaration) { + return current; + } + } + } } - export function isBindingElement(node: Node): node is BindingElement { - return node.kind === SyntaxKind.BindingElement; + export function isParameterPropertyDeclaration(node: Node): boolean { + return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent); } - export function isArrayBindingElement(node: Node): node is ArrayBindingElement { - const kind = node.kind; - return kind === SyntaxKind.BindingElement - || kind === SyntaxKind.OmittedExpression; + function walkUpBindingElementsAndPatterns(node: Node): Node { + while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) { + node = node.parent; + } + + return node; } + export function getCombinedModifierFlags(node: Node): ModifierFlags { + node = walkUpBindingElementsAndPatterns(node); + let flags = getModifierFlags(node); + if (node.kind === SyntaxKind.VariableDeclaration) { + node = node.parent; + } + + if (node && node.kind === SyntaxKind.VariableDeclarationList) { + flags |= getModifierFlags(node); + node = node.parent; + } + + if (node && node.kind === SyntaxKind.VariableStatement) { + flags |= getModifierFlags(node); + } + + return flags; + } + + // Returns the node flags for this node and all relevant parent nodes. This is done so that + // nodes like variable declarations and binding elements can returned a view of their flags + // that includes the modifiers from their container. i.e. flags like export/declare aren't + // stored on the variable declaration directly, but on the containing variable statement + // (if it has one). Similarly, flags for let/const are store on the variable declaration + // list. By calling this function, all those flags are combined so that the client can treat + // the node as if it actually had those flags. + export function getCombinedNodeFlags(node: Node): NodeFlags { + node = walkUpBindingElementsAndPatterns(node); + + let flags = node.flags; + if (node.kind === SyntaxKind.VariableDeclaration) { + node = node.parent; + } + + if (node && node.kind === SyntaxKind.VariableDeclarationList) { + flags |= node.flags; + node = node.parent; + } + + if (node && node.kind === SyntaxKind.VariableStatement) { + flags |= node.flags; + } + + return flags; + } /** - * Determines whether the BindingOrAssignmentElement is a BindingElement-like declaration + * Checks to see if the locale is in the appropriate format, + * and if it is, attempts to set the appropriate language. */ - export function isDeclarationBindingElement(bindingElement: BindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { - switch (bindingElement.kind) { - case SyntaxKind.VariableDeclaration: - case SyntaxKind.Parameter: - case SyntaxKind.BindingElement: - return true; + export function validateLocaleAndSetLanguage( + locale: string, + sys: { getExecutingFilePath(): string, resolvePath(path: string): string, fileExists(fileName: string): boolean, readFile(fileName: string): string }, + errors?: Diagnostic[]) { + const matchResult = /^([a-z]+)([_\-]([a-z]+))?$/.exec(locale.toLowerCase()); + + if (!matchResult) { + if (errors) { + errors.push(createCompilerDiagnostic(Diagnostics.Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1, "en", "ja-jp")); + } + return; } - return false; + const language = matchResult[1]; + const territory = matchResult[3]; + + // First try the entire locale, then fall back to just language if that's all we have. + // Either ways do not fail, and fallback to the English diagnostic strings. + if (!trySetLanguageAndTerritory(language, territory, errors)) { + trySetLanguageAndTerritory(language, /*territory*/ undefined, errors); + } + + function trySetLanguageAndTerritory(language: string, territory: string, errors?: Diagnostic[]): boolean { + const compilerFilePath = normalizePath(sys.getExecutingFilePath()); + const containingDirectoryPath = getDirectoryPath(compilerFilePath); + + let filePath = combinePaths(containingDirectoryPath, language); + + if (territory) { + filePath = filePath + "-" + territory; + } + + filePath = sys.resolvePath(combinePaths(filePath, "diagnosticMessages.generated.json")); + + if (!sys.fileExists(filePath)) { + return false; + } + + // TODO: Add codePage support for readFile? + let fileContents = ""; + try { + fileContents = sys.readFile(filePath); + } + catch (e) { + if (errors) { + errors.push(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, filePath)); + } + return false; + } + try { + ts.localizedDiagnosticMessages = JSON.parse(fileContents); + } + catch (e) { + if (errors) { + errors.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath)); + } + return false; + } + + return true; + } + } + + export function getOriginalNode(node: Node): Node; + export function getOriginalNode(node: Node, nodeTest: (node: Node) => node is T): T; + export function getOriginalNode(node: Node, nodeTest?: (node: Node) => boolean): Node { + if (node) { + while (node.original !== undefined) { + node = node.original; + } + } + + return !nodeTest || nodeTest(node) ? node : undefined; } /** - * Determines whether a node is a BindingOrAssignmentPattern + * Gets a value indicating whether a node originated in the parse tree. + * + * @param node The node to test. */ - export function isBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is BindingOrAssignmentPattern { - return isObjectBindingOrAssignmentPattern(node) - || isArrayBindingOrAssignmentPattern(node); + export function isParseTreeNode(node: Node): boolean { + return (node.flags & NodeFlags.Synthesized) === 0; } /** - * Determines whether a node is an ObjectBindingOrAssignmentPattern + * Gets the original parse tree node for a node. + * + * @param node The original node. + * @returns The original parse tree node if found; otherwise, undefined. */ - export function isObjectBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ObjectBindingOrAssignmentPattern { - switch (node.kind) { - case SyntaxKind.ObjectBindingPattern: - case SyntaxKind.ObjectLiteralExpression: - return true; + export function getParseTreeNode(node: Node): Node; + + /** + * Gets the original parse tree node for a node. + * + * @param node The original node. + * @param nodeTest A callback used to ensure the correct type of parse tree node is returned. + * @returns The original parse tree node if found; otherwise, undefined. + */ + export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => node is T): T; + export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node { + if (node === undefined || isParseTreeNode(node)) { + return node; } - return false; + node = getOriginalNode(node); + + if (isParseTreeNode(node) && (!nodeTest || nodeTest(node))) { + return node; + } + + return undefined; } /** - * Determines whether a node is an ArrayBindingOrAssignmentPattern + * Remove extra underscore from escaped identifier text content. + * + * @param identifier The escaped identifier text. + * @returns The unescaped identifier text. */ - export function isArrayBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ArrayBindingOrAssignmentPattern { - switch (node.kind) { - case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.ArrayLiteralExpression: - return true; + 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; + } + + export function getNameOfDeclaration(declaration: Declaration): DeclarationName | undefined { + if (!declaration) { + return undefined; + } + if (declaration.kind === SyntaxKind.BinaryExpression) { + const expr = declaration as BinaryExpression; + switch (getSpecialPropertyAssignmentKind(expr)) { + case SpecialPropertyAssignmentKind.ExportsProperty: + case SpecialPropertyAssignmentKind.ThisProperty: + case SpecialPropertyAssignmentKind.Property: + case SpecialPropertyAssignmentKind.PrototypeProperty: + return (expr.left as PropertyAccessExpression).name; + default: + return undefined; + } + } + else { + return (declaration as NamedDeclaration).name; } + } +} - return false; +// Simple node tests of the form `node.kind === SyntaxKind.Foo`. +namespace ts { + // Literals + export function isNumericLiteral(node: Node): node is NumericLiteral { + return node.kind === SyntaxKind.NumericLiteral; } - // Expression + export function isStringLiteral(node: Node): node is StringLiteral { + return node.kind === SyntaxKind.StringLiteral; + } - export function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression { - return node.kind === SyntaxKind.ArrayLiteralExpression; + export function isJsxText(node: Node): node is JsxText { + return node.kind === SyntaxKind.JsxText; } - export function isObjectLiteralExpression(node: Node): node is ObjectLiteralExpression { - return node.kind === SyntaxKind.ObjectLiteralExpression; + export function isRegularExpressionLiteral(node: Node): node is RegularExpressionLiteral { + return node.kind === SyntaxKind.RegularExpressionLiteral; } - export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { - return node.kind === SyntaxKind.PropertyAccessExpression; + export function isNoSubstitutionTemplateLiteral(node: Node): node is LiteralExpression { + return node.kind === SyntaxKind.NoSubstitutionTemplateLiteral; } - export function isPropertyAccessOrQualifiedName(node: Node): node is PropertyAccessExpression | QualifiedName { - const kind = node.kind; - return kind === SyntaxKind.PropertyAccessExpression - || kind === SyntaxKind.QualifiedName; + // Pseudo-literals + + export function isTemplateHead(node: Node): node is TemplateHead { + return node.kind === SyntaxKind.TemplateHead; } - export function isElementAccessExpression(node: Node): node is ElementAccessExpression { - return node.kind === SyntaxKind.ElementAccessExpression; + export function isTemplateMiddle(node: Node): node is TemplateMiddle { + return node.kind === SyntaxKind.TemplateMiddle; } - export function isBinaryExpression(node: Node): node is BinaryExpression { - return node.kind === SyntaxKind.BinaryExpression; + export function isTemplateTail(node: Node): node is TemplateTail { + return node.kind === SyntaxKind.TemplateTail; } - export function isConditionalExpression(node: Node): node is ConditionalExpression { - return node.kind === SyntaxKind.ConditionalExpression; + export function isIdentifier(node: Node): node is Identifier { + return node.kind === SyntaxKind.Identifier; } - export function isCallExpression(node: Node): node is CallExpression { - return node.kind === SyntaxKind.CallExpression; + // Names + + export function isQualifiedName(node: Node): node is QualifiedName { + return node.kind === SyntaxKind.QualifiedName; } - export function isTemplateLiteral(node: Node): node is TemplateLiteral { - const kind = node.kind; - return kind === SyntaxKind.TemplateExpression - || kind === SyntaxKind.NoSubstitutionTemplateLiteral; + export function isComputedPropertyName(node: Node): node is ComputedPropertyName { + return node.kind === SyntaxKind.ComputedPropertyName; } - export function isSpreadExpression(node: Node): node is SpreadElement { - return node.kind === SyntaxKind.SpreadElement; + // Signature elements + + export function isTypeParameter(node: Node): node is TypeParameterDeclaration { + return node.kind === SyntaxKind.TypeParameter; } - export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { - return node.kind === SyntaxKind.ExpressionWithTypeArguments; + export function isParameter(node: Node): node is ParameterDeclaration { + return node.kind === SyntaxKind.Parameter; } - function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { - return kind === SyntaxKind.PropertyAccessExpression - || kind === SyntaxKind.ElementAccessExpression - || kind === SyntaxKind.NewExpression - || kind === SyntaxKind.CallExpression - || kind === SyntaxKind.JsxElement - || kind === SyntaxKind.JsxSelfClosingElement - || kind === SyntaxKind.TaggedTemplateExpression - || kind === SyntaxKind.ArrayLiteralExpression - || kind === SyntaxKind.ParenthesizedExpression - || kind === SyntaxKind.ObjectLiteralExpression - || kind === SyntaxKind.ClassExpression - || kind === SyntaxKind.FunctionExpression - || kind === SyntaxKind.Identifier - || kind === SyntaxKind.RegularExpressionLiteral - || kind === SyntaxKind.NumericLiteral - || kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.NoSubstitutionTemplateLiteral - || kind === SyntaxKind.TemplateExpression - || kind === SyntaxKind.FalseKeyword - || kind === SyntaxKind.NullKeyword - || kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.TrueKeyword - || kind === SyntaxKind.SuperKeyword - || kind === SyntaxKind.NonNullExpression - || kind === SyntaxKind.MetaProperty; + export function isDecorator(node: Node): node is Decorator { + return node.kind === SyntaxKind.Decorator; } - export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { - return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node).kind); - } + // TypeMember - function isUnaryExpressionKind(kind: SyntaxKind): boolean { - return kind === SyntaxKind.PrefixUnaryExpression - || kind === SyntaxKind.PostfixUnaryExpression - || kind === SyntaxKind.DeleteExpression - || kind === SyntaxKind.TypeOfExpression - || kind === SyntaxKind.VoidExpression - || kind === SyntaxKind.AwaitExpression - || kind === SyntaxKind.TypeAssertionExpression - || isLeftHandSideExpressionKind(kind); + export function isPropertySignature(node: Node): node is PropertySignature { + return node.kind === SyntaxKind.PropertySignature; } - export function isUnaryExpression(node: Node): node is UnaryExpression { - return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind); + export function isPropertyDeclaration(node: Node): node is PropertyDeclaration { + return node.kind === SyntaxKind.PropertyDeclaration; } - function isExpressionKind(kind: SyntaxKind) { - return kind === SyntaxKind.ConditionalExpression - || kind === SyntaxKind.YieldExpression - || kind === SyntaxKind.ArrowFunction - || kind === SyntaxKind.BinaryExpression - || kind === SyntaxKind.SpreadElement - || kind === SyntaxKind.AsExpression - || kind === SyntaxKind.OmittedExpression - || kind === SyntaxKind.CommaListExpression - || isUnaryExpressionKind(kind); + export function isMethodSignature(node: Node): node is MethodSignature { + return node.kind === SyntaxKind.MethodSignature; } - export function isExpression(node: Node): node is Expression { - return isExpressionKind(skipPartiallyEmittedExpressions(node).kind); + export function isMethodDeclaration(node: Node): node is MethodDeclaration { + return node.kind === SyntaxKind.MethodDeclaration; } - export function isAssertionExpression(node: Node): node is AssertionExpression { - const kind = node.kind; - return kind === SyntaxKind.TypeAssertionExpression - || kind === SyntaxKind.AsExpression; + export function isConstructorDeclaration(node: Node): node is ConstructorDeclaration { + return node.kind === SyntaxKind.Constructor; } - export function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression { - return node.kind === SyntaxKind.PartiallyEmittedExpression; + export function isGetAccessorDeclaration(node: Node): node is GetAccessorDeclaration { + return node.kind === SyntaxKind.GetAccessor; } - export function isNotEmittedStatement(node: Node): node is NotEmittedStatement { - return node.kind === SyntaxKind.NotEmittedStatement; + export function isSetAccessorDeclaration(node: Node): node is SetAccessorDeclaration { + return node.kind === SyntaxKind.SetAccessor; } - export function isNotEmittedOrPartiallyEmittedNode(node: Node): node is NotEmittedStatement | PartiallyEmittedExpression { - return isNotEmittedStatement(node) - || isPartiallyEmittedExpression(node); + export function isCallSignatureDeclaration(node: Node): node is CallSignatureDeclaration { + return node.kind === SyntaxKind.CallSignature; } - export function isOmittedExpression(node: Node): node is OmittedExpression { - return node.kind === SyntaxKind.OmittedExpression; + export function isConstructSignatureDeclaration(node: Node): node is ConstructSignatureDeclaration { + return node.kind === SyntaxKind.ConstructSignature; } - // Misc - - export function isTemplateSpan(node: Node): node is TemplateSpan { - return node.kind === SyntaxKind.TemplateSpan; + export function isIndexSignatureDeclaration(node: Node): node is IndexSignatureDeclaration { + return node.kind === SyntaxKind.IndexSignature; } - // Element + // Type - export function isBlock(node: Node): node is Block { - return node.kind === SyntaxKind.Block; + export function isTypePredicateNode(node: Node): node is TypePredicateNode { + return node.kind === SyntaxKind.TypePredicate; } - export function isConciseBody(node: Node): node is ConciseBody { - return isBlock(node) - || isExpression(node); + export function isTypeReferenceNode(node: Node): node is TypeReferenceNode { + return node.kind === SyntaxKind.TypeReference; } - export function isFunctionBody(node: Node): node is FunctionBody { - return isBlock(node); + export function isFunctionTypeNode(node: Node): node is FunctionTypeNode { + return node.kind === SyntaxKind.FunctionType; } - export function isForInitializer(node: Node): node is ForInitializer { - return isVariableDeclarationList(node) - || isExpression(node); + export function isConstructorTypeNode(node: Node): node is ConstructorTypeNode { + return node.kind === SyntaxKind.ConstructorType; } - export function isVariableDeclaration(node: Node): node is VariableDeclaration { - return node.kind === SyntaxKind.VariableDeclaration; + export function isTypeQueryNode(node: Node): node is TypeQueryNode { + return node.kind === SyntaxKind.TypeQuery; } - export function isVariableDeclarationList(node: Node): node is VariableDeclarationList { - return node.kind === SyntaxKind.VariableDeclarationList; + export function isTypeLiteralNode(node: Node): node is TypeLiteralNode { + return node.kind === SyntaxKind.TypeLiteral; } - export function isCaseBlock(node: Node): node is CaseBlock { - return node.kind === SyntaxKind.CaseBlock; + export function isArrayTypeNode(node: Node): node is ArrayTypeNode { + return node.kind === SyntaxKind.ArrayType; } - export function isModuleBody(node: Node): node is ModuleBody { - const kind = node.kind; - return kind === SyntaxKind.ModuleBlock - || kind === SyntaxKind.ModuleDeclaration - || kind === SyntaxKind.Identifier; + export function isTupleTypeNode(node: Node): node is TupleTypeNode { + return node.kind === SyntaxKind.TupleType; } - export function isNamespaceBody(node: Node): node is NamespaceBody { - const kind = node.kind; - return kind === SyntaxKind.ModuleBlock - || kind === SyntaxKind.ModuleDeclaration; + export function isUnionTypeNode(node: Node): node is UnionTypeNode { + return node.kind === SyntaxKind.UnionType; } - export function isJSDocNamespaceBody(node: Node): node is JSDocNamespaceBody { - const kind = node.kind; - return kind === SyntaxKind.Identifier - || kind === SyntaxKind.ModuleDeclaration; + export function isIntersectionTypeNode(node: Node): node is IntersectionTypeNode { + return node.kind === SyntaxKind.IntersectionType; } - export function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { - return node.kind === SyntaxKind.ImportEqualsDeclaration; + export function isParenthesizedTypeNode(node: Node): node is ParenthesizedTypeNode { + return node.kind === SyntaxKind.ParenthesizedType; } - export function isImportDeclaration(node: Node): node is ImportDeclaration { - return node.kind === SyntaxKind.ImportDeclaration; + export function isThisTypeNode(node: Node): node is ThisTypeNode { + return node.kind === SyntaxKind.ThisType; } - export function isImportClause(node: Node): node is ImportClause { - return node.kind === SyntaxKind.ImportClause; + export function isTypeOperatorNode(node: Node): node is TypeOperatorNode { + return node.kind === SyntaxKind.TypeOperator; } - export function isNamedImportBindings(node: Node): node is NamedImportBindings { - const kind = node.kind; - return kind === SyntaxKind.NamedImports - || kind === SyntaxKind.NamespaceImport; + export function isIndexedAccessTypeNode(node: Node): node is IndexedAccessTypeNode { + return node.kind === SyntaxKind.IndexedAccessType; } - export function isImportSpecifier(node: Node): node is ImportSpecifier { - return node.kind === SyntaxKind.ImportSpecifier; + export function isMappedTypeNode(node: Node): node is MappedTypeNode { + return node.kind === SyntaxKind.MappedType; } - export function isNamedExports(node: Node): node is NamedExports { - return node.kind === SyntaxKind.NamedExports; + export function isLiteralTypeNode(node: Node): node is LiteralTypeNode { + return node.kind === SyntaxKind.LiteralType; } - export function isExportSpecifier(node: Node): node is ExportSpecifier { - return node.kind === SyntaxKind.ExportSpecifier; + // Binding patterns + + export function isObjectBindingPattern(node: Node): node is ObjectBindingPattern { + return node.kind === SyntaxKind.ObjectBindingPattern; } - export function isExportAssignment(node: Node): node is ExportAssignment { - return node.kind === SyntaxKind.ExportAssignment; + export function isArrayBindingPattern(node: Node): node is ArrayBindingPattern { + return node.kind === SyntaxKind.ArrayBindingPattern; } - export function isModuleOrEnumDeclaration(node: Node): node is ModuleDeclaration | EnumDeclaration { - return node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration; + export function isBindingElement(node: Node): node is BindingElement { + return node.kind === SyntaxKind.BindingElement; } - function isDeclarationKind(kind: SyntaxKind) { - return kind === SyntaxKind.ArrowFunction - || kind === SyntaxKind.BindingElement - || kind === SyntaxKind.ClassDeclaration - || kind === SyntaxKind.ClassExpression - || kind === SyntaxKind.Constructor - || kind === SyntaxKind.EnumDeclaration - || kind === SyntaxKind.EnumMember - || kind === SyntaxKind.ExportSpecifier - || kind === SyntaxKind.FunctionDeclaration - || kind === SyntaxKind.FunctionExpression - || kind === SyntaxKind.GetAccessor - || kind === SyntaxKind.ImportClause - || kind === SyntaxKind.ImportEqualsDeclaration - || kind === SyntaxKind.ImportSpecifier - || kind === SyntaxKind.InterfaceDeclaration - || kind === SyntaxKind.JsxAttribute - || kind === SyntaxKind.MethodDeclaration - || kind === SyntaxKind.MethodSignature - || kind === SyntaxKind.ModuleDeclaration - || kind === SyntaxKind.NamespaceExportDeclaration - || kind === SyntaxKind.NamespaceImport - || kind === SyntaxKind.Parameter - || kind === SyntaxKind.PropertyAssignment - || kind === SyntaxKind.PropertyDeclaration - || kind === SyntaxKind.PropertySignature - || kind === SyntaxKind.SetAccessor - || kind === SyntaxKind.ShorthandPropertyAssignment - || kind === SyntaxKind.TypeAliasDeclaration - || kind === SyntaxKind.TypeParameter - || kind === SyntaxKind.VariableDeclaration - || kind === SyntaxKind.JSDocTypedefTag; + // Expression + + export function isArrayLiteralExpression(node: Node): node is ArrayLiteralExpression { + return node.kind === SyntaxKind.ArrayLiteralExpression; } - function isDeclarationStatementKind(kind: SyntaxKind) { - return kind === SyntaxKind.FunctionDeclaration - || kind === SyntaxKind.MissingDeclaration - || kind === SyntaxKind.ClassDeclaration - || kind === SyntaxKind.InterfaceDeclaration - || kind === SyntaxKind.TypeAliasDeclaration - || kind === SyntaxKind.EnumDeclaration - || kind === SyntaxKind.ModuleDeclaration - || kind === SyntaxKind.ImportDeclaration - || kind === SyntaxKind.ImportEqualsDeclaration - || kind === SyntaxKind.ExportDeclaration - || kind === SyntaxKind.ExportAssignment - || kind === SyntaxKind.NamespaceExportDeclaration; + export function isObjectLiteralExpression(node: Node): node is ObjectLiteralExpression { + return node.kind === SyntaxKind.ObjectLiteralExpression; } - function isStatementKindButNotDeclarationKind(kind: SyntaxKind) { - return kind === SyntaxKind.BreakStatement - || kind === SyntaxKind.ContinueStatement - || kind === SyntaxKind.DebuggerStatement - || kind === SyntaxKind.DoStatement - || kind === SyntaxKind.ExpressionStatement - || kind === SyntaxKind.EmptyStatement - || kind === SyntaxKind.ForInStatement - || kind === SyntaxKind.ForOfStatement - || kind === SyntaxKind.ForStatement - || kind === SyntaxKind.IfStatement - || kind === SyntaxKind.LabeledStatement - || kind === SyntaxKind.ReturnStatement - || kind === SyntaxKind.SwitchStatement - || kind === SyntaxKind.ThrowStatement - || kind === SyntaxKind.TryStatement - || kind === SyntaxKind.VariableStatement - || kind === SyntaxKind.WhileStatement - || kind === SyntaxKind.WithStatement - || kind === SyntaxKind.NotEmittedStatement - || kind === SyntaxKind.EndOfDeclarationMarker - || kind === SyntaxKind.MergeDeclarationMarker; + export function isPropertyAccessExpression(node: Node): node is PropertyAccessExpression { + return node.kind === SyntaxKind.PropertyAccessExpression; } - export function isDeclaration(node: Node): node is NamedDeclaration { - return isDeclarationKind(node.kind); + export function isElementAccessExpression(node: Node): node is ElementAccessExpression { + return node.kind === SyntaxKind.ElementAccessExpression; } - export function isDeclarationStatement(node: Node): node is DeclarationStatement { - return isDeclarationStatementKind(node.kind); + export function isCallExpression(node: Node): node is CallExpression { + return node.kind === SyntaxKind.CallExpression; } - /** - * Determines whether the node is a statement that is not also a declaration - */ - export function isStatementButNotDeclaration(node: Node): node is Statement { - return isStatementKindButNotDeclarationKind(node.kind); + export function isNewExpression(node: Node): node is NewExpression { + return node.kind === SyntaxKind.NewExpression; } - export function isStatement(node: Node): node is Statement { - const kind = node.kind; - return isStatementKindButNotDeclarationKind(kind) - || isDeclarationStatementKind(kind) - || kind === SyntaxKind.Block; + export function isTaggedTemplateExpression(node: Node): node is TaggedTemplateExpression { + return node.kind === SyntaxKind.TaggedTemplateExpression; } - // Module references + export function isTypeAssertion(node: Node): node is TypeAssertion { + return node.kind === SyntaxKind.TypeAssertionExpression; + } - export function isModuleReference(node: Node): node is ModuleReference { - const kind = node.kind; - return kind === SyntaxKind.ExternalModuleReference - || kind === SyntaxKind.QualifiedName - || kind === SyntaxKind.Identifier; + export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression { + return node.kind === SyntaxKind.ParenthesizedExpression; } - // JSX + export function isFunctionExpression(node: Node): node is FunctionExpression { + return node.kind === SyntaxKind.FunctionExpression; + } - export function isJsxOpeningElement(node: Node): node is JsxOpeningElement { - return node.kind === SyntaxKind.JsxOpeningElement; + export function isArrowFunction(node: Node): node is ArrowFunction { + return node.kind === SyntaxKind.ArrowFunction; } - export function isJsxClosingElement(node: Node): node is JsxClosingElement { - return node.kind === SyntaxKind.JsxClosingElement; + export function isDeleteExpression(node: Node): node is DeleteExpression { + return node.kind === SyntaxKind.DeleteExpression; } - export function isJsxTagNameExpression(node: Node): node is JsxTagNameExpression { - const kind = node.kind; - return kind === SyntaxKind.ThisKeyword - || kind === SyntaxKind.Identifier - || kind === SyntaxKind.PropertyAccessExpression; + export function isTypeOfExpression(node: Node): node is TypeOfExpression { + return node.kind === SyntaxKind.AwaitExpression; } - export function isJsxChild(node: Node): node is JsxChild { - const kind = node.kind; - return kind === SyntaxKind.JsxElement - || kind === SyntaxKind.JsxExpression - || kind === SyntaxKind.JsxSelfClosingElement - || kind === SyntaxKind.JsxText; + export function isVoidExpression(node: Node): node is VoidExpression { + return node.kind === SyntaxKind.VoidExpression; } - export function isJsxAttributes(node: Node): node is JsxAttributes { - const kind = node.kind; - return kind === SyntaxKind.JsxAttributes; + export function isAwaitExpression(node: Node): node is AwaitExpression { + return node.kind === SyntaxKind.AwaitExpression; } - export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { - const kind = node.kind; - return kind === SyntaxKind.JsxAttribute - || kind === SyntaxKind.JsxSpreadAttribute; + export function isPrefixUnaryExpression(node: Node): node is PrefixUnaryExpression { + return node.kind === SyntaxKind.PrefixUnaryExpression; } - export function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute { - return node.kind === SyntaxKind.JsxSpreadAttribute; + export function isPostfixUnaryExpression(node: Node): node is PostfixUnaryExpression { + return node.kind === SyntaxKind.PostfixUnaryExpression; } - export function isJsxAttribute(node: Node): node is JsxAttribute { - return node.kind === SyntaxKind.JsxAttribute; + export function isBinaryExpression(node: Node): node is BinaryExpression { + return node.kind === SyntaxKind.BinaryExpression; } - export function isStringLiteralOrJsxExpression(node: Node): node is StringLiteral | JsxExpression { - const kind = node.kind; - return kind === SyntaxKind.StringLiteral - || kind === SyntaxKind.JsxExpression; + export function isConditionalExpression(node: Node): node is ConditionalExpression { + return node.kind === SyntaxKind.ConditionalExpression; } - export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement { - const kind = node.kind; - return kind === SyntaxKind.JsxOpeningElement - || kind === SyntaxKind.JsxSelfClosingElement; + export function isTemplateExpression(node: Node): node is TemplateExpression { + return node.kind === SyntaxKind.TemplateExpression; } - // Clauses + export function isYieldExpression(node: Node): node is YieldExpression { + return node.kind === SyntaxKind.YieldExpression; + } - export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { - const kind = node.kind; - return kind === SyntaxKind.CaseClause - || kind === SyntaxKind.DefaultClause; + export function isSpreadElement(node: Node): node is SpreadElement { + return node.kind === SyntaxKind.SpreadElement; } - export function isHeritageClause(node: Node): node is HeritageClause { - return node.kind === SyntaxKind.HeritageClause; + export function isClassExpression(node: Node): node is ClassExpression { + return node.kind === SyntaxKind.ClassExpression; } - export function isCatchClause(node: Node): node is CatchClause { - return node.kind === SyntaxKind.CatchClause; + export function isOmittedExpression(node: Node): node is OmittedExpression { + return node.kind === SyntaxKind.OmittedExpression; } + export function isExpressionWithTypeArguments(node: Node): node is ExpressionWithTypeArguments { + return node.kind === SyntaxKind.ExpressionWithTypeArguments; + } - // Property assignments + export function isAsExpression(node: Node): node is AsExpression { + return node.kind === SyntaxKind.AsExpression; + } - export function isPropertyAssignment(node: Node): node is PropertyAssignment { - return node.kind === SyntaxKind.PropertyAssignment; + export function isNonNullExpression(node: Node): node is NonNullExpression { + return node.kind === SyntaxKind.NonNullExpression; } - export function isShorthandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { - return node.kind === SyntaxKind.ShorthandPropertyAssignment; + export function isMetaProperty(node: Node): node is MetaProperty { + return node.kind === SyntaxKind.MetaProperty; } - // Enum + // Misc - export function isEnumMember(node: Node): node is EnumMember { - return node.kind === SyntaxKind.EnumMember; + export function isTemplateSpan(node: Node): node is TemplateSpan { + return node.kind === SyntaxKind.TemplateSpan; } - // Top-level nodes - export function isSourceFile(node: Node): node is SourceFile { - return node.kind === SyntaxKind.SourceFile; + export function isSemicolonClassElement(node: Node): node is SemicolonClassElement { + return node.kind === SyntaxKind.SemicolonClassElement; } - export function isWatchSet(options: CompilerOptions) { - // Firefox has Object.prototype.watch - return options.watch && options.hasOwnProperty("watch"); + // Block + + export function isBlock(node: Node): node is Block { + return node.kind === SyntaxKind.Block; } - export function getCheckFlags(symbol: Symbol): CheckFlags { - return symbol.flags & SymbolFlags.Transient ? (symbol).checkFlags : 0; + export function isVariableStatement(node: Node): node is VariableStatement { + return node.kind === SyntaxKind.VariableStatement; } - export function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags { - if (s.valueDeclaration) { - const flags = getCombinedModifierFlags(s.valueDeclaration); - return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; - } - if (getCheckFlags(s) & CheckFlags.Synthetic) { - const checkFlags = (s).checkFlags; - const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : - checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : - ModifierFlags.Protected; - const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; - return accessModifier | staticModifier; - } - if (s.flags & SymbolFlags.Prototype) { - return ModifierFlags.Public | ModifierFlags.Static; - } - return 0; + export function isEmptyStatement(node: Node): node is EmptyStatement { + return node.kind === SyntaxKind.EmptyStatement; } - export function levenshtein(s1: string, s2: string): number { - let previous: number[] = new Array(s2.length + 1); - let current: number[] = new Array(s2.length + 1); - for (let i = 0; i < s2.length + 1; i++) { - previous[i] = i; - current[i] = -1; - } - for (let i = 1; i < s1.length + 1; i++) { - current[0] = i; - for (let j = 1; j < s2.length + 1; j++) { - current[j] = Math.min( - previous[j] + 1, - current[j - 1] + 1, - previous[j - 1] + (s1[i - 1] === s2[j - 1] ? 0 : 2)); - } - // shift current back to previous, and then reuse previous' array - const tmp = previous; - previous = current; - current = tmp; - } - return previous[previous.length - 1]; + export function isExpressionStatement(node: Node): node is ExpressionStatement { + return node.kind === SyntaxKind.ExpressionStatement; } -} -namespace ts { - export function getDefaultLibFileName(options: CompilerOptions): string { - switch (options.target) { - case ScriptTarget.ESNext: - return "lib.esnext.full.d.ts"; - case ScriptTarget.ES2017: - return "lib.es2017.full.d.ts"; - case ScriptTarget.ES2016: - return "lib.es2016.full.d.ts"; - case ScriptTarget.ES2015: - return "lib.es6.d.ts"; // We don't use lib.es2015.full.d.ts due to breaking change. - default: - return "lib.d.ts"; - } + export function isIfStatement(node: Node): node is IfStatement { + return node.kind === SyntaxKind.IfStatement; } - export function textSpanEnd(span: TextSpan) { - return span.start + span.length; + export function isDoStatement(node: Node): node is DoStatement { + return node.kind === SyntaxKind.DoStatement; } - export function textSpanIsEmpty(span: TextSpan) { - return span.length === 0; + export function isWhileStatement(node: Node): node is WhileStatement { + return node.kind === SyntaxKind.WhileStatement; } - export function textSpanContainsPosition(span: TextSpan, position: number) { - return position >= span.start && position < textSpanEnd(span); + export function isForStatement(node: Node): node is ForStatement { + return node.kind === SyntaxKind.ForStatement; } - // Returns true if 'span' contains 'other'. - export function textSpanContainsTextSpan(span: TextSpan, other: TextSpan) { - return other.start >= span.start && textSpanEnd(other) <= textSpanEnd(span); + export function isForInStatement(node: Node): node is ForInStatement { + return node.kind === SyntaxKind.ForInStatement; } - export function textSpanOverlapsWith(span: TextSpan, other: TextSpan) { - const overlapStart = Math.max(span.start, other.start); - const overlapEnd = Math.min(textSpanEnd(span), textSpanEnd(other)); - return overlapStart < overlapEnd; + export function isForOfStatement(node: Node): node is ForOfStatement { + return node.kind === SyntaxKind.ForOfStatement; } - export function textSpanOverlap(span1: TextSpan, span2: TextSpan) { - const overlapStart = Math.max(span1.start, span2.start); - const overlapEnd = Math.min(textSpanEnd(span1), textSpanEnd(span2)); - if (overlapStart < overlapEnd) { - return createTextSpanFromBounds(overlapStart, overlapEnd); - } - return undefined; + export function isContinueStatement(node: Node): node is ContinueStatement { + return node.kind === SyntaxKind.ContinueStatement; } - export function textSpanIntersectsWithTextSpan(span: TextSpan, other: TextSpan) { - return other.start <= textSpanEnd(span) && textSpanEnd(other) >= span.start; + export function isBreakStatement(node: Node): node is BreakStatement { + return node.kind === SyntaxKind.BreakStatement; } - export function textSpanIntersectsWith(span: TextSpan, start: number, length: number) { - const end = start + length; - return start <= textSpanEnd(span) && end >= span.start; + export function isReturnStatement(node: Node): node is ReturnStatement { + return node.kind === SyntaxKind.ReturnStatement; } - export function decodedTextSpanIntersectsWith(start1: number, length1: number, start2: number, length2: number) { - const end1 = start1 + length1; - const end2 = start2 + length2; - return start2 <= end1 && end2 >= start1; + export function isWithStatement(node: Node): node is WithStatement { + return node.kind === SyntaxKind.WithStatement; } - export function textSpanIntersectsWithPosition(span: TextSpan, position: number) { - return position <= textSpanEnd(span) && position >= span.start; + export function isSwitchStatement(node: Node): node is SwitchStatement { + return node.kind === SyntaxKind.SwitchStatement; } - export function textSpanIntersection(span1: TextSpan, span2: TextSpan) { - const intersectStart = Math.max(span1.start, span2.start); - const intersectEnd = Math.min(textSpanEnd(span1), textSpanEnd(span2)); - if (intersectStart <= intersectEnd) { - return createTextSpanFromBounds(intersectStart, intersectEnd); - } - return undefined; + export function isLabeledStatement(node: Node): node is LabeledStatement { + return node.kind === SyntaxKind.LabeledStatement; } - export function createTextSpan(start: number, length: number): TextSpan { - if (start < 0) { - throw new Error("start < 0"); - } - if (length < 0) { - throw new Error("length < 0"); - } + export function isThrowStatement(node: Node): node is ThrowStatement { + return node.kind === SyntaxKind.ThrowStatement; + } - return { start, length }; + export function isTryStatement(node: Node): node is TryStatement { + return node.kind === SyntaxKind.TryStatement; } - export function createTextSpanFromBounds(start: number, end: number) { - return createTextSpan(start, end - start); + export function isDebuggerStatement(node: Node): node is DebuggerStatement { + return node.kind === SyntaxKind.DebuggerStatement; } - export function textChangeRangeNewSpan(range: TextChangeRange) { - return createTextSpan(range.span.start, range.newLength); + export function isVariableDeclaration(node: Node): node is VariableDeclaration { + return node.kind === SyntaxKind.VariableDeclaration; } - export function textChangeRangeIsUnchanged(range: TextChangeRange) { - return textSpanIsEmpty(range.span) && range.newLength === 0; + export function isVariableDeclarationList(node: Node): node is VariableDeclarationList { + return node.kind === SyntaxKind.VariableDeclarationList; } - export function createTextChangeRange(span: TextSpan, newLength: number): TextChangeRange { - if (newLength < 0) { - throw new Error("newLength < 0"); - } + export function isFunctionDeclaration(node: Node): node is FunctionDeclaration { + return node.kind === SyntaxKind.FunctionDeclaration; + } - return { span, newLength }; + export function isClassDeclaration(node: Node): node is ClassDeclaration { + return node.kind === SyntaxKind.ClassDeclaration; } - export let unchangedTextChangeRange = createTextChangeRange(createTextSpan(0, 0), 0); + export function isInterfaceDeclaration(node: Node): node is InterfaceDeclaration { + return node.kind === SyntaxKind.InterfaceDeclaration; + } - /** - * Called to merge all the changes that occurred across several versions of a script snapshot - * into a single change. i.e. if a user keeps making successive edits to a script we will - * have a text change from V1 to V2, V2 to V3, ..., Vn. - * - * This function will then merge those changes into a single change range valid between V1 and - * Vn. - */ - export function collapseTextChangeRangesAcrossMultipleVersions(changes: TextChangeRange[]): TextChangeRange { - if (changes.length === 0) { - return unchangedTextChangeRange; - } + export function isTypeAliasDeclaration(node: Node): node is TypeAliasDeclaration { + return node.kind === SyntaxKind.TypeAliasDeclaration; + } - if (changes.length === 1) { - return changes[0]; - } + export function isEnumDeclaration(node: Node): node is EnumDeclaration { + return node.kind === SyntaxKind.EnumDeclaration; + } - // We change from talking about { { oldStart, oldLength }, newLength } to { oldStart, oldEnd, newEnd } - // as it makes things much easier to reason about. - const change0 = changes[0]; + export function isModuleDeclaration(node: Node): node is ModuleDeclaration { + return node.kind === SyntaxKind.ModuleDeclaration; + } - let oldStartN = change0.span.start; - let oldEndN = textSpanEnd(change0.span); - let newEndN = oldStartN + change0.newLength; + export function isModuleBlock(node: Node): node is ModuleBlock { + return node.kind === SyntaxKind.ModuleBlock; + } - for (let i = 1; i < changes.length; i++) { - const nextChange = changes[i]; + export function isCaseBlock(node: Node): node is CaseBlock { + return node.kind === SyntaxKind.CaseBlock; + } - // Consider the following case: - // i.e. two edits. The first represents the text change range { { 10, 50 }, 30 }. i.e. The span starting - // at 10, with length 50 is reduced to length 30. The second represents the text change range { { 30, 30 }, 40 }. - // i.e. the span starting at 30 with length 30 is increased to length 40. - // - // 0 10 20 30 40 50 60 70 80 90 100 - // ------------------------------------------------------------------------------------------------------- - // | / - // | /---- - // T1 | /---- - // | /---- - // | /---- - // ------------------------------------------------------------------------------------------------------- - // | \ - // | \ - // T2 | \ - // | \ - // | \ - // ------------------------------------------------------------------------------------------------------- - // - // Merging these turns out to not be too difficult. First, determining the new start of the change is trivial - // it's just the min of the old and new starts. i.e.: - // - // 0 10 20 30 40 50 60 70 80 90 100 - // ------------------------------------------------------------*------------------------------------------ - // | / - // | /---- - // T1 | /---- - // | /---- - // | /---- - // ----------------------------------------$-------------------$------------------------------------------ - // . | \ - // . | \ - // T2 . | \ - // . | \ - // . | \ - // ----------------------------------------------------------------------*-------------------------------- - // - // (Note the dots represent the newly inferred start. - // Determining the new and old end is also pretty simple. Basically it boils down to paying attention to the - // absolute positions at the asterisks, and the relative change between the dollar signs. Basically, we see - // which if the two $'s precedes the other, and we move that one forward until they line up. in this case that - // means: - // - // 0 10 20 30 40 50 60 70 80 90 100 - // --------------------------------------------------------------------------------*---------------------- - // | / - // | /---- - // T1 | /---- - // | /---- - // | /---- - // ------------------------------------------------------------$------------------------------------------ - // . | \ - // . | \ - // T2 . | \ - // . | \ - // . | \ - // ----------------------------------------------------------------------*-------------------------------- - // - // In other words (in this case), we're recognizing that the second edit happened after where the first edit - // ended with a delta of 20 characters (60 - 40). Thus, if we go back in time to where the first edit started - // that's the same as if we started at char 80 instead of 60. - // - // As it so happens, the same logic applies if the second edit precedes the first edit. In that case rather - // than pushing the first edit forward to match the second, we'll push the second edit forward to match the - // first. - // - // In this case that means we have { oldStart: 10, oldEnd: 80, newEnd: 70 } or, in TextChangeRange - // semantics: { { start: 10, length: 70 }, newLength: 60 } - // - // The math then works out as follows. - // If we have { oldStart1, oldEnd1, newEnd1 } and { oldStart2, oldEnd2, newEnd2 } then we can compute the - // final result like so: - // - // { - // oldStart3: Min(oldStart1, oldStart2), - // oldEnd3 : Max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)), - // newEnd3 : Max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)) - // } + export function isNamespaceExportDeclaration(node: Node): node is NamespaceExportDeclaration { + return node.kind === SyntaxKind.NamespaceExportDeclaration; + } - const oldStart1 = oldStartN; - const oldEnd1 = oldEndN; - const newEnd1 = newEndN; + export function isImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration { + return node.kind === SyntaxKind.ImportEqualsDeclaration; + } - const oldStart2 = nextChange.span.start; - const oldEnd2 = textSpanEnd(nextChange.span); - const newEnd2 = oldStart2 + nextChange.newLength; + export function isImportDeclaration(node: Node): node is ImportDeclaration { + return node.kind === SyntaxKind.ImportDeclaration; + } - oldStartN = Math.min(oldStart1, oldStart2); - oldEndN = Math.max(oldEnd1, oldEnd1 + (oldEnd2 - newEnd1)); - newEndN = Math.max(newEnd2, newEnd2 + (newEnd1 - oldEnd2)); - } + export function isImportClause(node: Node): node is ImportClause { + return node.kind === SyntaxKind.ImportClause; + } - return createTextChangeRange(createTextSpanFromBounds(oldStartN, oldEndN), /*newLength*/ newEndN - oldStartN); + export function isNamespaceImport(node: Node): node is NamespaceImport { + return node.kind === SyntaxKind.NamespaceImport; } - export function getTypeParameterOwner(d: Declaration): Declaration { - if (d && d.kind === SyntaxKind.TypeParameter) { - for (let current: Node = d; current; current = current.parent) { - if (isFunctionLike(current) || isClassLike(current) || current.kind === SyntaxKind.InterfaceDeclaration) { - return current; - } - } - } + export function isNamedImports(node: Node): node is NamedImports { + return node.kind === SyntaxKind.NamedImports; } - export function isParameterPropertyDeclaration(node: Node): boolean { - return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent); + export function isImportSpecifier(node: Node): node is ImportSpecifier { + return node.kind === SyntaxKind.ImportSpecifier; } - function walkUpBindingElementsAndPatterns(node: Node): Node { - while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) { - node = node.parent; - } + export function isExportAssignment(node: Node): node is ExportAssignment { + return node.kind === SyntaxKind.ExportAssignment; + } - return node; + export function isExportDeclaration(node: Node): node is ExportDeclaration { + return node.kind === SyntaxKind.ExportDeclaration; } - export function getCombinedModifierFlags(node: Node): ModifierFlags { - node = walkUpBindingElementsAndPatterns(node); - let flags = getModifierFlags(node); - if (node.kind === SyntaxKind.VariableDeclaration) { - node = node.parent; - } + export function isNamedExports(node: Node): node is NamedExports { + return node.kind === SyntaxKind.NamedExports; + } - if (node && node.kind === SyntaxKind.VariableDeclarationList) { - flags |= getModifierFlags(node); - node = node.parent; - } + export function isExportSpecifier(node: Node): node is ExportSpecifier { + return node.kind === SyntaxKind.ExportSpecifier; + } - if (node && node.kind === SyntaxKind.VariableStatement) { - flags |= getModifierFlags(node); - } + export function isMissingDeclaration(node: Node): node is MissingDeclaration { + return node.kind === SyntaxKind.MissingDeclaration; + } - return flags; + // Module References + + export function isExternalModuleReference(node: Node): node is ExternalModuleReference { + return node.kind === SyntaxKind.ExternalModuleReference; } - // Returns the node flags for this node and all relevant parent nodes. This is done so that - // nodes like variable declarations and binding elements can returned a view of their flags - // that includes the modifiers from their container. i.e. flags like export/declare aren't - // stored on the variable declaration directly, but on the containing variable statement - // (if it has one). Similarly, flags for let/const are store on the variable declaration - // list. By calling this function, all those flags are combined so that the client can treat - // the node as if it actually had those flags. - export function getCombinedNodeFlags(node: Node): NodeFlags { - node = walkUpBindingElementsAndPatterns(node); + // JSX - let flags = node.flags; - if (node.kind === SyntaxKind.VariableDeclaration) { - node = node.parent; - } + export function isJsxElement(node: Node): node is JsxElement { + return node.kind === SyntaxKind.JsxElement; + } - if (node && node.kind === SyntaxKind.VariableDeclarationList) { - flags |= node.flags; - node = node.parent; - } + export function isJsxSelfClosingElement(node: Node): node is JsxSelfClosingElement { + return node.kind === SyntaxKind.JsxSelfClosingElement; + } - if (node && node.kind === SyntaxKind.VariableStatement) { - flags |= node.flags; - } + export function isJsxOpeningElement(node: Node): node is JsxOpeningElement { + return node.kind === SyntaxKind.JsxOpeningElement; + } - return flags; + export function isJsxClosingElement(node: Node): node is JsxClosingElement { + return node.kind === SyntaxKind.JsxClosingElement; } - /** - * Checks to see if the locale is in the appropriate format, - * and if it is, attempts to set the appropriate language. - */ - export function validateLocaleAndSetLanguage( - locale: string, - sys: { getExecutingFilePath(): string, resolvePath(path: string): string, fileExists(fileName: string): boolean, readFile(fileName: string): string }, - errors?: Diagnostic[]) { - const matchResult = /^([a-z]+)([_\-]([a-z]+))?$/.exec(locale.toLowerCase()); + export function isJsxAttribute(node: Node): node is JsxAttribute { + return node.kind === SyntaxKind.JsxAttribute; + } - if (!matchResult) { - if (errors) { - errors.push(createCompilerDiagnostic(Diagnostics.Locale_must_be_of_the_form_language_or_language_territory_For_example_0_or_1, "en", "ja-jp")); - } - return; - } + export function isJsxAttributes(node: Node): node is JsxAttributes { + return node.kind === SyntaxKind.JsxAttributes; + } - const language = matchResult[1]; - const territory = matchResult[3]; + export function isJsxSpreadAttribute(node: Node): node is JsxSpreadAttribute { + return node.kind === SyntaxKind.JsxSpreadAttribute; + } - // First try the entire locale, then fall back to just language if that's all we have. - // Either ways do not fail, and fallback to the English diagnostic strings. - if (!trySetLanguageAndTerritory(language, territory, errors)) { - trySetLanguageAndTerritory(language, /*territory*/ undefined, errors); - } + export function isJsxExpression(node: Node): node is JsxExpression { + return node.kind === SyntaxKind.JsxExpression; + } - function trySetLanguageAndTerritory(language: string, territory: string, errors?: Diagnostic[]): boolean { - const compilerFilePath = normalizePath(sys.getExecutingFilePath()); - const containingDirectoryPath = getDirectoryPath(compilerFilePath); + // Clauses - let filePath = combinePaths(containingDirectoryPath, language); + export function isCaseClause(node: Node): node is CaseClause { + return node.kind === SyntaxKind.CaseClause; + } - if (territory) { - filePath = filePath + "-" + territory; - } + export function isDefaultClause(node: Node): node is DefaultClause { + return node.kind === SyntaxKind.DefaultClause; + } - filePath = sys.resolvePath(combinePaths(filePath, "diagnosticMessages.generated.json")); + export function isHeritageClause(node: Node): node is HeritageClause { + return node.kind === SyntaxKind.HeritageClause; + } - if (!sys.fileExists(filePath)) { - return false; - } + export function isCatchClause(node: Node): node is CatchClause { + return node.kind === SyntaxKind.CatchClause; + } - // TODO: Add codePage support for readFile? - let fileContents = ""; - try { - fileContents = sys.readFile(filePath); - } - catch (e) { - if (errors) { - errors.push(createCompilerDiagnostic(Diagnostics.Unable_to_open_file_0, filePath)); - } - return false; - } - try { - ts.localizedDiagnosticMessages = JSON.parse(fileContents); - } - catch (e) { - if (errors) { - errors.push(createCompilerDiagnostic(Diagnostics.Corrupted_locale_file_0, filePath)); - } - return false; - } + // Property assignments - return true; - } + export function isPropertyAssignment(node: Node): node is PropertyAssignment { + return node.kind === SyntaxKind.PropertyAssignment; } - export function getOriginalNode(node: Node): Node; - export function getOriginalNode(node: Node, nodeTest: (node: Node) => node is T): T; - export function getOriginalNode(node: Node, nodeTest?: (node: Node) => boolean): Node { - if (node) { - while (node.original !== undefined) { - node = node.original; - } - } + export function isShorthandPropertyAssignment(node: Node): node is ShorthandPropertyAssignment { + return node.kind === SyntaxKind.ShorthandPropertyAssignment; + } - return !nodeTest || nodeTest(node) ? node : undefined; + export function isSpreadAssignment(node: Node): node is SpreadAssignment { + return node.kind === SyntaxKind.SpreadAssignment; } - /** - * Gets a value indicating whether a node originated in the parse tree. - * - * @param node The node to test. - */ - export function isParseTreeNode(node: Node): boolean { - return (node.flags & NodeFlags.Synthesized) === 0; + // Enum + + export function isEnumMember(node: Node): node is EnumMember { + return node.kind === SyntaxKind.EnumMember; } - /** - * Gets the original parse tree node for a node. - * - * @param node The original node. - * @returns The original parse tree node if found; otherwise, undefined. - */ - export function getParseTreeNode(node: Node): Node; + // Top-level nodes + export function isSourceFile(node: Node): node is SourceFile { + return node.kind === SyntaxKind.SourceFile; + } - /** - * Gets the original parse tree node for a node. - * - * @param node The original node. - * @param nodeTest A callback used to ensure the correct type of parse tree node is returned. - * @returns The original parse tree node if found; otherwise, undefined. - */ - export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => node is T): T; - export function getParseTreeNode(node: Node, nodeTest?: (node: Node) => boolean): Node { - if (node === undefined || isParseTreeNode(node)) { - return node; - } + export function isBundle(node: Node): node is Bundle { + return node.kind === SyntaxKind.Bundle; + } - node = getOriginalNode(node); + // JSDoc - if (isParseTreeNode(node) && (!nodeTest || nodeTest(node))) { - return node; - } + export function isJSDocTypeExpression(node: Node): node is JSDocTypeExpression { + return node.kind === SyntaxKind.JSDocTypeExpression; + } - return undefined; + export function isJSDocAllType(node: JSDocAllType): node is JSDocAllType { + return node.kind === SyntaxKind.JSDocAllType; } - /** - * Remove extra underscore from escaped identifier text content. - * - * @param identifier The escaped identifier text. - * @returns The unescaped identifier text. - */ - 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; + export function isJSDocUnknownType(node: Node): node is JSDocUnknownType { + return node.kind === SyntaxKind.JSDocUnknownType; } - export function getNameOfDeclaration(declaration: Declaration): DeclarationName | undefined { - if (!declaration) { - return undefined; - } - if (declaration.kind === SyntaxKind.BinaryExpression) { - const expr = declaration as BinaryExpression; - switch (getSpecialPropertyAssignmentKind(expr)) { - case SpecialPropertyAssignmentKind.ExportsProperty: - case SpecialPropertyAssignmentKind.ThisProperty: - case SpecialPropertyAssignmentKind.Property: - case SpecialPropertyAssignmentKind.PrototypeProperty: - return (expr.left as PropertyAccessExpression).name; - default: - return undefined; - } + export function isJSDocArrayType(node: Node): node is JSDocArrayType { + return node.kind === SyntaxKind.JSDocArrayType; + } + + export function isJSDocUnionType(node: Node): node is JSDocUnionType { + return node.kind === SyntaxKind.JSDocUnionType; + } + + export function isJSDocTupleType(node: Node): node is JSDocTupleType { + return node.kind === SyntaxKind.JSDocTupleType; + } + + export function isJSDocNullableType(node: Node): node is JSDocNullableType { + return node.kind === SyntaxKind.JSDocNullableType; + } + + export function isJSDocNonNullableType(node: Node): node is JSDocNonNullableType { + return node.kind === SyntaxKind.JSDocNonNullableType; + } + + export function isJSDocRecordType(node: Node): node is JSDocRecordType { + return node.kind === SyntaxKind.JSDocRecordType; + } + + export function isJSDocRecordMember(node: Node): node is JSDocRecordMember { + return node.kind === SyntaxKind.JSDocRecordMember; + } + + export function isJSDocTypeReference(node: Node): node is JSDocTypeReference { + return node.kind === SyntaxKind.JSDocTypeReference; + } + + export function isJSDocOptionalType(node: Node): node is JSDocOptionalType { + return node.kind === SyntaxKind.JSDocOptionalType; + } + + export function isJSDocFunctionType(node: Node): node is JSDocFunctionType { + return node.kind === SyntaxKind.JSDocFunctionType; + } + + export function isJSDocVariadicType(node: Node): node is JSDocVariadicType { + return node.kind === SyntaxKind.JSDocVariadicType; + } + + export function isJSDocConstructorType(node: Node): node is JSDocConstructorType { + return node.kind === SyntaxKind.JSDocConstructorType; + } + + export function isJSDocThisType(node: Node): node is JSDocThisType { + return node.kind === SyntaxKind.JSDocThisType; + } + + export function isJSDoc(node: Node): node is JSDoc { + return node.kind === SyntaxKind.JSDocComment; + } + + export function isJSDocAugmentsTag(node: Node): node is JSDocAugmentsTag { + return node.kind === SyntaxKind.JSDocAugmentsTag; + } + + export function isJSDocParameterTag(node: Node): node is JSDocParameterTag { + return node.kind === SyntaxKind.JSDocParameterTag; + } + + export function isJSDocReturnTag(node: Node): node is JSDocReturnTag { + return node.kind === SyntaxKind.JSDocReturnTag; + } + + export function isJSDocTypeTag(node: Node): node is JSDocTypeTag { + return node.kind === SyntaxKind.JSDocTypeTag; + } + + export function isJSDocTemplateTag(node: Node): node is JSDocTemplateTag { + return node.kind === SyntaxKind.JSDocTemplateTag; + } + + export function isJSDocTypedefTag(node: Node): node is JSDocTypedefTag { + return node.kind === SyntaxKind.JSDocTypedefTag; + } + + export function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag { + return node.kind === SyntaxKind.JSDocPropertyTag; + } + + export function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral { + return node.kind === SyntaxKind.JSDocTypeLiteral; + } + + export function isJSDocLiteralType(node: Node): node is JSDocLiteralType { + return node.kind === SyntaxKind.JSDocLiteralType; + } +} + +// Node tests +// +// All node tests in the following list should *not* reference parent pointers so that +// they may be used with transformations. +namespace ts { + /** + * True if node is of some token syntax kind. + * For example, this is true for an IfKeyword but not for an IfStatement. + */ + export function isToken(n: Node): boolean { + return n.kind >= SyntaxKind.FirstToken && n.kind <= SyntaxKind.LastToken; + } + + // Node Arrays + + /* @internal */ + export function isNodeArray(array: T[]): array is NodeArray { + return array.hasOwnProperty("pos") + && array.hasOwnProperty("end"); + } + + // Literals + + /* @internal */ + export function isLiteralKind(kind: SyntaxKind): boolean { + return SyntaxKind.FirstLiteralToken <= kind && kind <= SyntaxKind.LastLiteralToken; + } + + export function isLiteralExpression(node: Node): node is LiteralExpression { + return isLiteralKind(node.kind); + } + + // Pseudo-literals + + /* @internal */ + export function isTemplateLiteralKind(kind: SyntaxKind): boolean { + return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken; + } + + export function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail { + const kind = node.kind; + return kind === SyntaxKind.TemplateMiddle + || kind === SyntaxKind.TemplateTail; + } + + // Identifiers + + /* @internal */ + export function isGeneratedIdentifier(node: Node): node is GeneratedIdentifier { + // Using `>` here catches both `GeneratedIdentifierKind.None` and `undefined`. + return isIdentifier(node) && node.autoGenerateKind > GeneratedIdentifierKind.None; + } + + // Keywords + + /* @internal */ + export function isModifierKind(token: SyntaxKind): boolean { + switch (token) { + case SyntaxKind.AbstractKeyword: + case SyntaxKind.AsyncKeyword: + case SyntaxKind.ConstKeyword: + case SyntaxKind.DeclareKeyword: + case SyntaxKind.DefaultKeyword: + case SyntaxKind.ExportKeyword: + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.StaticKeyword: + return true; } - else { - return (declaration as NamedDeclaration).name; + return false; + } + + export function isModifier(node: Node): node is Modifier { + return isModifierKind(node.kind); + } + + export function isEntityName(node: Node): node is EntityName { + const kind = node.kind; + return kind === SyntaxKind.QualifiedName + || kind === SyntaxKind.Identifier; + } + + export function isPropertyName(node: Node): node is PropertyName { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.NumericLiteral + || kind === SyntaxKind.ComputedPropertyName; + } + + export function isBindingName(node: Node): node is BindingName { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.ObjectBindingPattern + || kind === SyntaxKind.ArrayBindingPattern; + } + + // Functions + + export function isFunctionLike(node: Node): node is FunctionLikeDeclaration { + return node && isFunctionLikeKind(node.kind); + } + + /* @internal */ + export function isFunctionLikeKind(kind: SyntaxKind): boolean { + switch (kind) { + case SyntaxKind.Constructor: + case SyntaxKind.FunctionExpression: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.ArrowFunction: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: + case SyntaxKind.CallSignature: + case SyntaxKind.ConstructSignature: + case SyntaxKind.IndexSignature: + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return true; } + + return false; + } + + // Classes + export function isClassElement(node: Node): node is ClassElement { + const kind = node.kind; + return kind === SyntaxKind.Constructor + || kind === SyntaxKind.PropertyDeclaration + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor + || kind === SyntaxKind.IndexSignature + || kind === SyntaxKind.SemicolonClassElement + || kind === SyntaxKind.MissingDeclaration; + } + + export function isClassLike(node: Node): node is ClassLikeDeclaration { + return node && (node.kind === SyntaxKind.ClassDeclaration || node.kind === SyntaxKind.ClassExpression); + } + + export function isAccessor(node: Node): node is AccessorDeclaration { + return node && (node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.SetAccessor); + } + + // Type members + + export function isTypeElement(node: Node): node is TypeElement { + const kind = node.kind; + return kind === SyntaxKind.ConstructSignature + || kind === SyntaxKind.CallSignature + || kind === SyntaxKind.PropertySignature + || kind === SyntaxKind.MethodSignature + || kind === SyntaxKind.IndexSignature + || kind === SyntaxKind.MissingDeclaration; + } + + export function isObjectLiteralElementLike(node: Node): node is ObjectLiteralElementLike { + const kind = node.kind; + return kind === SyntaxKind.PropertyAssignment + || kind === SyntaxKind.ShorthandPropertyAssignment + || kind === SyntaxKind.SpreadAssignment + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.SetAccessor + || kind === SyntaxKind.MissingDeclaration; + } + + // Type + + function isTypeNodeKind(kind: SyntaxKind) { + return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode) + || kind === SyntaxKind.AnyKeyword + || kind === SyntaxKind.NumberKeyword + || kind === SyntaxKind.ObjectKeyword + || kind === SyntaxKind.BooleanKeyword + || kind === SyntaxKind.StringKeyword + || kind === SyntaxKind.SymbolKeyword + || kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.VoidKeyword + || kind === SyntaxKind.UndefinedKeyword + || kind === SyntaxKind.NullKeyword + || kind === SyntaxKind.NeverKeyword + || kind === SyntaxKind.ExpressionWithTypeArguments; + } + + /** + * Node test that determines whether a node is a valid type node. + * This differs from the `isPartOfTypeNode` function which determines whether a node is *part* + * of a TypeNode. + */ + export function isTypeNode(node: Node): node is TypeNode { + return isTypeNodeKind(node.kind); + } + + export function isFunctionOrConstructorTypeNode(node: Node): node is FunctionTypeNode | ConstructorTypeNode { + switch (node.kind) { + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return true; + } + + return false; + } + + // Binding patterns + + /* @internal */ + export function isBindingPattern(node: Node): node is BindingPattern { + if (node) { + const kind = node.kind; + return kind === SyntaxKind.ArrayBindingPattern + || kind === SyntaxKind.ObjectBindingPattern; + } + + return false; + } + + /* @internal */ + export function isAssignmentPattern(node: Node): node is AssignmentPattern { + const kind = node.kind; + return kind === SyntaxKind.ArrayLiteralExpression + || kind === SyntaxKind.ObjectLiteralExpression; + } + + + /* @internal */ + export function isArrayBindingElement(node: Node): node is ArrayBindingElement { + const kind = node.kind; + return kind === SyntaxKind.BindingElement + || kind === SyntaxKind.OmittedExpression; + } + + + /** + * Determines whether the BindingOrAssignmentElement is a BindingElement-like declaration + */ + /* @internal */ + export function isDeclarationBindingElement(bindingElement: BindingOrAssignmentElement): bindingElement is VariableDeclaration | ParameterDeclaration | BindingElement { + switch (bindingElement.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + return true; + } + + return false; + } + + /** + * Determines whether a node is a BindingOrAssignmentPattern + */ + /* @internal */ + export function isBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is BindingOrAssignmentPattern { + return isObjectBindingOrAssignmentPattern(node) + || isArrayBindingOrAssignmentPattern(node); + } + + /** + * Determines whether a node is an ObjectBindingOrAssignmentPattern + */ + /* @internal */ + export function isObjectBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ObjectBindingOrAssignmentPattern { + switch (node.kind) { + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ObjectLiteralExpression: + return true; + } + + return false; + } + + /** + * Determines whether a node is an ArrayBindingOrAssignmentPattern + */ + /* @internal */ + export function isArrayBindingOrAssignmentPattern(node: BindingOrAssignmentElementTarget): node is ArrayBindingOrAssignmentPattern { + switch (node.kind) { + case SyntaxKind.ArrayBindingPattern: + case SyntaxKind.ArrayLiteralExpression: + return true; + } + + return false; + } + + // Expression + + export function isPropertyAccessOrQualifiedName(node: Node): node is PropertyAccessExpression | QualifiedName { + const kind = node.kind; + return kind === SyntaxKind.PropertyAccessExpression + || kind === SyntaxKind.QualifiedName; + } + + export function isCallLikeExpression(node: Node): node is CallLikeExpression { + switch (node.kind) { + case SyntaxKind.JsxOpeningElement: + case SyntaxKind.JsxSelfClosingElement: + case SyntaxKind.CallExpression: + case SyntaxKind.NewExpression: + case SyntaxKind.TaggedTemplateExpression: + case SyntaxKind.Decorator: + return true; + default: + return false; + } + } + + export function isCallOrNewExpression(node: Node): node is CallExpression | NewExpression { + return node.kind === SyntaxKind.CallExpression || node.kind === SyntaxKind.NewExpression; + } + + export function isTemplateLiteral(node: Node): node is TemplateLiteral { + const kind = node.kind; + return kind === SyntaxKind.TemplateExpression + || kind === SyntaxKind.NoSubstitutionTemplateLiteral; + } + + function isLeftHandSideExpressionKind(kind: SyntaxKind): boolean { + return kind === SyntaxKind.PropertyAccessExpression + || kind === SyntaxKind.ElementAccessExpression + || kind === SyntaxKind.NewExpression + || kind === SyntaxKind.CallExpression + || kind === SyntaxKind.JsxElement + || kind === SyntaxKind.JsxSelfClosingElement + || kind === SyntaxKind.TaggedTemplateExpression + || kind === SyntaxKind.ArrayLiteralExpression + || kind === SyntaxKind.ParenthesizedExpression + || kind === SyntaxKind.ObjectLiteralExpression + || kind === SyntaxKind.ClassExpression + || kind === SyntaxKind.FunctionExpression + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.RegularExpressionLiteral + || kind === SyntaxKind.NumericLiteral + || kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.NoSubstitutionTemplateLiteral + || kind === SyntaxKind.TemplateExpression + || kind === SyntaxKind.FalseKeyword + || kind === SyntaxKind.NullKeyword + || kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.TrueKeyword + || kind === SyntaxKind.SuperKeyword + || kind === SyntaxKind.NonNullExpression + || kind === SyntaxKind.MetaProperty; + } + + /* @internal */ + export function isLeftHandSideExpression(node: Node): node is LeftHandSideExpression { + return isLeftHandSideExpressionKind(skipPartiallyEmittedExpressions(node).kind); + } + + function isUnaryExpressionKind(kind: SyntaxKind): boolean { + return kind === SyntaxKind.PrefixUnaryExpression + || kind === SyntaxKind.PostfixUnaryExpression + || kind === SyntaxKind.DeleteExpression + || kind === SyntaxKind.TypeOfExpression + || kind === SyntaxKind.VoidExpression + || kind === SyntaxKind.AwaitExpression + || kind === SyntaxKind.TypeAssertionExpression + || isLeftHandSideExpressionKind(kind); + } + + /* @internal */ + export function isUnaryExpression(node: Node): node is UnaryExpression { + return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind); + } + + function isExpressionKind(kind: SyntaxKind) { + return kind === SyntaxKind.ConditionalExpression + || kind === SyntaxKind.YieldExpression + || kind === SyntaxKind.ArrowFunction + || kind === SyntaxKind.BinaryExpression + || kind === SyntaxKind.SpreadElement + || kind === SyntaxKind.AsExpression + || kind === SyntaxKind.OmittedExpression + || kind === SyntaxKind.CommaListExpression + || isUnaryExpressionKind(kind); + } + + /* @internal */ + export function isExpression(node: Node): node is Expression { + return isExpressionKind(skipPartiallyEmittedExpressions(node).kind); + } + + export function isAssertionExpression(node: Node): node is AssertionExpression { + const kind = node.kind; + return kind === SyntaxKind.TypeAssertionExpression + || kind === SyntaxKind.AsExpression; + } + + /* @internal */ + export function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression { + return node.kind === SyntaxKind.PartiallyEmittedExpression; + } + + /* @internal */ + export function isNotEmittedStatement(node: Node): node is NotEmittedStatement { + return node.kind === SyntaxKind.NotEmittedStatement; + } + + /* @internal */ + export function isNotEmittedOrPartiallyEmittedNode(node: Node): node is NotEmittedStatement | PartiallyEmittedExpression { + return isNotEmittedStatement(node) + || isPartiallyEmittedExpression(node); + } + + // Statement + + export function isIterationStatement(node: Node, lookInLabeledStatements: boolean): node is IterationStatement { + switch (node.kind) { + case SyntaxKind.ForStatement: + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: + case SyntaxKind.DoStatement: + case SyntaxKind.WhileStatement: + return true; + case SyntaxKind.LabeledStatement: + return lookInLabeledStatements && isIterationStatement((node).statement, lookInLabeledStatements); + } + + return false; + } + + // Element + + /* @internal */ + export function isConciseBody(node: Node): node is ConciseBody { + return isBlock(node) + || isExpression(node); + } + + /* @internal */ + export function isFunctionBody(node: Node): node is FunctionBody { + return isBlock(node); + } + + /* @internal */ + export function isForInitializer(node: Node): node is ForInitializer { + return isVariableDeclarationList(node) + || isExpression(node); + } + + /* @internal */ + export function isModuleBody(node: Node): node is ModuleBody { + const kind = node.kind; + return kind === SyntaxKind.ModuleBlock + || kind === SyntaxKind.ModuleDeclaration + || kind === SyntaxKind.Identifier; + } + + /* @internal */ + export function isNamespaceBody(node: Node): node is NamespaceBody { + const kind = node.kind; + return kind === SyntaxKind.ModuleBlock + || kind === SyntaxKind.ModuleDeclaration; + } + + /* @internal */ + export function isJSDocNamespaceBody(node: Node): node is JSDocNamespaceBody { + const kind = node.kind; + return kind === SyntaxKind.Identifier + || kind === SyntaxKind.ModuleDeclaration; + } + + /* @internal */ + export function isNamedImportBindings(node: Node): node is NamedImportBindings { + const kind = node.kind; + return kind === SyntaxKind.NamedImports + || kind === SyntaxKind.NamespaceImport; + } + + /* @internal */ + export function isModuleOrEnumDeclaration(node: Node): node is ModuleDeclaration | EnumDeclaration { + return node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration; + } + + function isDeclarationKind(kind: SyntaxKind) { + return kind === SyntaxKind.ArrowFunction + || kind === SyntaxKind.BindingElement + || kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.ClassExpression + || kind === SyntaxKind.Constructor + || kind === SyntaxKind.EnumDeclaration + || kind === SyntaxKind.EnumMember + || kind === SyntaxKind.ExportSpecifier + || kind === SyntaxKind.FunctionDeclaration + || kind === SyntaxKind.FunctionExpression + || kind === SyntaxKind.GetAccessor + || kind === SyntaxKind.ImportClause + || kind === SyntaxKind.ImportEqualsDeclaration + || kind === SyntaxKind.ImportSpecifier + || kind === SyntaxKind.InterfaceDeclaration + || kind === SyntaxKind.JsxAttribute + || kind === SyntaxKind.MethodDeclaration + || kind === SyntaxKind.MethodSignature + || kind === SyntaxKind.ModuleDeclaration + || kind === SyntaxKind.NamespaceExportDeclaration + || kind === SyntaxKind.NamespaceImport + || kind === SyntaxKind.Parameter + || kind === SyntaxKind.PropertyAssignment + || kind === SyntaxKind.PropertyDeclaration + || kind === SyntaxKind.PropertySignature + || kind === SyntaxKind.SetAccessor + || kind === SyntaxKind.ShorthandPropertyAssignment + || kind === SyntaxKind.TypeAliasDeclaration + || kind === SyntaxKind.TypeParameter + || kind === SyntaxKind.VariableDeclaration + || kind === SyntaxKind.JSDocTypedefTag; + } + + function isDeclarationStatementKind(kind: SyntaxKind) { + return kind === SyntaxKind.FunctionDeclaration + || kind === SyntaxKind.MissingDeclaration + || kind === SyntaxKind.ClassDeclaration + || kind === SyntaxKind.InterfaceDeclaration + || kind === SyntaxKind.TypeAliasDeclaration + || kind === SyntaxKind.EnumDeclaration + || kind === SyntaxKind.ModuleDeclaration + || kind === SyntaxKind.ImportDeclaration + || kind === SyntaxKind.ImportEqualsDeclaration + || kind === SyntaxKind.ExportDeclaration + || kind === SyntaxKind.ExportAssignment + || kind === SyntaxKind.NamespaceExportDeclaration; + } + + function isStatementKindButNotDeclarationKind(kind: SyntaxKind) { + return kind === SyntaxKind.BreakStatement + || kind === SyntaxKind.ContinueStatement + || kind === SyntaxKind.DebuggerStatement + || kind === SyntaxKind.DoStatement + || kind === SyntaxKind.ExpressionStatement + || kind === SyntaxKind.EmptyStatement + || kind === SyntaxKind.ForInStatement + || kind === SyntaxKind.ForOfStatement + || kind === SyntaxKind.ForStatement + || kind === SyntaxKind.IfStatement + || kind === SyntaxKind.LabeledStatement + || kind === SyntaxKind.ReturnStatement + || kind === SyntaxKind.SwitchStatement + || kind === SyntaxKind.ThrowStatement + || kind === SyntaxKind.TryStatement + || kind === SyntaxKind.VariableStatement + || kind === SyntaxKind.WhileStatement + || kind === SyntaxKind.WithStatement + || kind === SyntaxKind.NotEmittedStatement + || kind === SyntaxKind.EndOfDeclarationMarker + || kind === SyntaxKind.MergeDeclarationMarker; + } + + /* @internal */ + export function isDeclaration(node: Node): node is NamedDeclaration { + return isDeclarationKind(node.kind); + } + + /* @internal */ + export function isDeclarationStatement(node: Node): node is DeclarationStatement { + return isDeclarationStatementKind(node.kind); + } + + /** + * Determines whether the node is a statement that is not also a declaration + */ + /* @internal */ + export function isStatementButNotDeclaration(node: Node): node is Statement { + return isStatementKindButNotDeclarationKind(node.kind); + } + + /* @internal */ + export function isStatement(node: Node): node is Statement { + const kind = node.kind; + return isStatementKindButNotDeclarationKind(kind) + || isDeclarationStatementKind(kind) + || kind === SyntaxKind.Block; + } + + // Module references + + /* @internal */ + export function isModuleReference(node: Node): node is ModuleReference { + const kind = node.kind; + return kind === SyntaxKind.ExternalModuleReference + || kind === SyntaxKind.QualifiedName + || kind === SyntaxKind.Identifier; + } + + // JSX + + /* @internal */ + export function isJsxTagNameExpression(node: Node): node is JsxTagNameExpression { + const kind = node.kind; + return kind === SyntaxKind.ThisKeyword + || kind === SyntaxKind.Identifier + || kind === SyntaxKind.PropertyAccessExpression; + } + + /* @internal */ + export function isJsxChild(node: Node): node is JsxChild { + const kind = node.kind; + return kind === SyntaxKind.JsxElement + || kind === SyntaxKind.JsxExpression + || kind === SyntaxKind.JsxSelfClosingElement + || kind === SyntaxKind.JsxText; + } + + /* @internal */ + export function isJsxAttributeLike(node: Node): node is JsxAttributeLike { + const kind = node.kind; + return kind === SyntaxKind.JsxAttribute + || kind === SyntaxKind.JsxSpreadAttribute; + } + + /* @internal */ + export function isStringLiteralOrJsxExpression(node: Node): node is StringLiteral | JsxExpression { + const kind = node.kind; + return kind === SyntaxKind.StringLiteral + || kind === SyntaxKind.JsxExpression; + } + + export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement { + const kind = node.kind; + return kind === SyntaxKind.JsxOpeningElement + || kind === SyntaxKind.JsxSelfClosingElement; + } + + // Clauses + + export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause { + const kind = node.kind; + return kind === SyntaxKind.CaseClause + || kind === SyntaxKind.DefaultClause; + } + + // JSDoc + + /** True if node is of some JSDoc syntax kind. */ + /* @internal */ + export function isJSDocNode(node: Node): boolean { + return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode; + } + + // TODO: determine what this does before making it public. + /* @internal */ + export function isJSDocTag(node: Node): boolean { + return node.kind >= SyntaxKind.FirstJSDocTagNode && node.kind <= SyntaxKind.LastJSDocTagNode; } }