From df91ef0b382464c7c05419ffb2d35b50252a3a3a Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 28 Mar 2016 12:37:37 -0700 Subject: [PATCH 01/90] Simple case and scoping --- src/compiler/binder.ts | 14 +++-- src/compiler/checker.ts | 24 +++++--- src/compiler/diagnosticMessages.json | 4 ++ src/compiler/parser.ts | 86 +++++++++++++++++++++++----- src/compiler/types.ts | 29 +++++++++- 5 files changed, 130 insertions(+), 27 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6957be484f3..881ad81cb2e 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -404,6 +404,13 @@ namespace ts { // This node will now be set as the parent of all of its children as we recurse into them. parent = node; + // Binding of JsDocComment should be done before the current block scope container changes. + // because the scope of JsDocComment should not be affected by whether the current node is a + // container or not. + if (isInJavaScriptFile(node) && node.jsDocComment) { + bind(node.jsDocComment); + } + // Depending on what kind of node this is, we may have to adjust the current container // and block-container. If the current node is a container, then it is automatically // considered the current block-container as well. Also, for containers that we know @@ -468,10 +475,6 @@ namespace ts { labelStack = labelIndexMap = implicitLabels = undefined; } - if (isInJavaScriptFile(node) && node.jsDocComment) { - bind(node.jsDocComment); - } - bindReachableStatement(node); if (currentReachabilityState === Reachability.Reachable && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { @@ -857,7 +860,7 @@ namespace ts { // their container in the tree. To accomplish this, we simply add their declared // symbol to the 'locals' of the container. These symbols can then be found as // the type checker walks up the containers, checking them for matching names. - return declareSymbol(container.locals, undefined, node, symbolFlags, symbolExcludes); + return declareSymbol(container.locals, /*parent*/ undefined, node, symbolFlags, symbolExcludes); } } @@ -1343,6 +1346,7 @@ namespace ts { return bindClassLikeDeclaration(node); case SyntaxKind.InterfaceDeclaration: return bindBlockScopedDeclaration(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); + case SyntaxKind.JSDocTypedefTag: case SyntaxKind.TypeAliasDeclaration: return bindBlockScopedDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); case SyntaxKind.EnumDeclaration: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9509c286236..ff416ec2cc5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3403,7 +3403,7 @@ namespace ts { return links.declaredType; } - function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type { + function getDeclaredTypeOfTypeAlias(symbol: Symbol, node?: Node): Type { const links = getSymbolLinks(symbol); if (!links.declaredType) { // Note that we use the links object as the target here because the symbol object is used as the unique @@ -3411,8 +3411,18 @@ namespace ts { if (!pushTypeResolution(symbol, TypeSystemPropertyName.DeclaredType)) { return unknownType; } - const declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); - let type = getTypeFromTypeNode(declaration.type); + + let typeNode: TypeNode; + let name: Identifier; + if (node && (node.flags & NodeFlags.JavaScriptFile)) { + const declaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); + typeNode = declaration.typeExpression.type; + } + if (!typeNode) { + const declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); + typeNode = declaration.type; + } + let type = getTypeFromTypeNode(typeNode); if (popTypeResolution()) { links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); if (links.typeParameters) { @@ -3424,7 +3434,7 @@ namespace ts { } else { type = unknownType; - error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); + error(name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); } links.declaredType = type; } @@ -3462,13 +3472,13 @@ namespace ts { return links.declaredType; } - function getDeclaredTypeOfSymbol(symbol: Symbol): Type { + function getDeclaredTypeOfSymbol(symbol: Symbol, node?: Node): Type { Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0); if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return getDeclaredTypeOfClassOrInterface(symbol); } if (symbol.flags & SymbolFlags.TypeAlias) { - return getDeclaredTypeOfTypeAlias(symbol); + return getDeclaredTypeOfTypeAlias(symbol, node); } if (symbol.flags & SymbolFlags.Enum) { return getDeclaredTypeOfEnum(symbol); @@ -4564,7 +4574,7 @@ namespace ts { // references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the // declared type. Instantiations are cached using the type identities of the type arguments as the key. function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { - const type = getDeclaredTypeOfSymbol(symbol); + const type = getDeclaredTypeOfSymbol(symbol, node); const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters; if (typeParameters) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d7c2fac2710..c887a9c7112 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -811,6 +811,10 @@ "category": "Error", "code": 1249 }, + "'{0}' tag cannot be used independently as a top level JSDoc tag.": { + "category": "Error", + "code": 1250 + }, "'with' statements are not allowed in an async function block.": { "category": "Error", "code": 1300 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b7c011631ee..91f225931d2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -401,6 +401,8 @@ namespace ts { return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNodes, (node).typeParameters); + case SyntaxKind.JSDocTypedefTag: + return visitNode(cbNode, (node).typeExpression); } } @@ -886,7 +888,7 @@ namespace ts { /** Invokes the provided callback then unconditionally restores the parser to the state it * was in immediately prior to invoking the callback. The result of invoking the callback - * is returned from this function. + * is returned from this function. */ function lookAhead(callback: () => T): T { return speculationHelper(callback, /*isLookAhead*/ true); @@ -2068,11 +2070,11 @@ namespace ts { } function fillSignature( - returnToken: SyntaxKind, - yieldContext: boolean, - awaitContext: boolean, - requireCompleteParameterList: boolean, - signature: SignatureDeclaration): void { + returnToken: SyntaxKind, + yieldContext: boolean, + awaitContext: boolean, + requireCompleteParameterList: boolean, + signature: SignatureDeclaration): void { const returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; signature.typeParameters = parseTypeParameters(); @@ -3341,8 +3343,8 @@ namespace ts { if (sourceFile.languageVariant !== LanguageVariant.JSX) { return false; } - // We are in JSX context and the token is part of JSXElement. - // Fall through + // We are in JSX context and the token is part of JSXElement. + // Fall through default: return true; } @@ -4044,9 +4046,9 @@ namespace ts { const isAsync = !!(node.flags & NodeFlags.Async); node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : - isGenerator ? doInYieldContext(parseOptionalIdentifier) : - isAsync ? doInAwaitContext(parseOptionalIdentifier) : - parseOptionalIdentifier(); + isGenerator ? doInYieldContext(parseOptionalIdentifier) : + isAsync ? doInAwaitContext(parseOptionalIdentifier) : + parseOptionalIdentifier(); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); @@ -4984,7 +4986,7 @@ namespace ts { if (token === SyntaxKind.ConstKeyword && permitInvalidConstAsModifier) { // We need to ensure that any subsequent modifiers appear on the same line - // so that when 'const' is a standalone declaration, we don't issue an error. + // so that when 'const' is a standalone declaration, we don't issue an error. if (!tryParse(nextTokenIsOnSameLineAndCanFollowModifier)) { break; } @@ -5247,7 +5249,7 @@ namespace ts { node.decorators = decorators; setModifiers(node, modifiers); if (token === SyntaxKind.GlobalKeyword) { - // parse 'global' as name of global scope augmentation + // parse 'global' as name of global scope augmentation node.name = parseIdentifier(); node.flags |= NodeFlags.GlobalAugmentation; } @@ -5829,7 +5831,7 @@ namespace ts { } function checkForEmptyTypeArgumentList(typeArguments: NodeArray) { - if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) { + if (parseDiagnostics.length === 0 && typeArguments && typeArguments.length === 0) { const start = typeArguments.pos - "<".length; const end = skipTrivia(sourceText, typeArguments.end) + ">".length; return parseErrorAtPosition(start, end - start, Diagnostics.Type_argument_list_cannot_be_empty); @@ -5990,6 +5992,7 @@ namespace ts { Debug.assert(end <= content.length); let tags: NodeArray; + let currentParentJSDocDeclaration: Declaration; let result: JSDocComment; @@ -6097,6 +6100,11 @@ namespace ts { return handleTemplateTag(atToken, tagName); case "type": return handleTypeTag(atToken, tagName); + case "typedef": + return handleTypedefTag(atToken, tagName); + case "property": + case "prop": + return handlePropertyTag(atToken, tagName); } } @@ -6204,6 +6212,56 @@ namespace ts { return finishNode(result); } + function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag { + if (!currentParentJSDocDeclaration) { + parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_cannot_be_used_independently_as_a_top_level_JSDoc_tag, tagName.text); + return undefined; + } + + const typeExpression = tryParseTypeExpression(); + skipWhitespace(); + const name = parseJSDocIdentifier(); + if (!name) { + parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected); + return undefined; + } + + const result = createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); + result.atToken = atToken; + result.tagName = tagName; + result.name = name; + result.typeExpression = typeExpression; + return finishNode(result); + } + + function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { + const typeExpression = tryParseTypeExpression(); + skipWhitespace(); + const name = parseJSDocIdentifier(); + if (!name) { + parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected); + return undefined; + } + + const result = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); + result.atToken = atToken; + result.tagName = tagName; + result.name = name; + result.typeExpression = typeExpression; + + // if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { + // const jsDocTypeReference = typeExpression.type; + // if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { + // const name = jsDocTypeReference.name; + // if (name.text === "Object") { + // currentParentJSDocDeclaration = declaration; + // } + // } + // } + + return result; + } + function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag { if (forEach(tags, t => t.kind === SyntaxKind.JSDocTemplateTag)) { parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 546d1631945..a0cc8d054e1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -342,6 +342,10 @@ namespace ts { JSDocReturnTag, JSDocTypeTag, JSDocTemplateTag, + JSDocTypedefTag, + JSDocPropertyTag, + JSDocTypedefDeclaration, + JSDocTypeLiteral, // Synthesized list SyntaxList, @@ -1510,6 +1514,29 @@ namespace ts { typeExpression: JSDocTypeExpression; } + // @kind(SyntaxKind.JSDocTypedefTag) + export interface JSDocTypedefTag extends JSDocTag, Declaration { + name: Identifier; + typeExpression: JSDocTypeExpression; + } + + // @kind(SyntaxKind.JSDocPropertyTag) + export interface JSDocPropertyTag extends JSDocTag, TypeElement { + name: Identifier; + typeExpression: JSDocTypeExpression; + } + + // @kind(SyntaxKind.JSDocTypedefDeclaration) + export interface JSDocTypedefDeclaration extends Declaration { + name: Identifier; + type: TypeNode; + } + + // @kind(SyntaxKind.JSDocTypeLiteral) + export interface JSDocTypeLiteral extends TypeNode { + members: NodeArray; + } + // @kind(SyntaxKind.JSDocParameterTag) export interface JSDocParameterTag extends JSDocTag { preParameterName?: Identifier; @@ -2094,7 +2121,7 @@ namespace ts { jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. - superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing + superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing } export const enum TypeFlags { From 23bca4eda6b220a88771a6c922842f8da1c3b366 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Mon, 4 Apr 2016 00:44:51 -0700 Subject: [PATCH 02/90] Support the case with @property and @type --- src/compiler/binder.ts | 4 + src/compiler/checker.ts | 16 +- src/compiler/parser.ts | 142 +++++++++++++++--- src/compiler/types.ts | 19 +-- .../fourslash/server/jsdocCompletions.ts | 15 ++ 5 files changed, 160 insertions(+), 36 deletions(-) create mode 100644 tests/cases/fourslash/server/jsdocCompletions.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 881ad81cb2e..b3f163bbef1 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -744,6 +744,7 @@ namespace ts { case SyntaxKind.EnumDeclaration: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocRecordType: return ContainerFlags.IsContainer; @@ -832,6 +833,7 @@ namespace ts { case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.JSDocRecordType: + case SyntaxKind.JSDocTypeLiteral: // Interface/Object-types always have their children added to the 'members' of // their container. They are only accessible through an instance of their // container, and are never in scope otherwise (even inside the body of the @@ -1294,6 +1296,7 @@ namespace ts { case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.JSDocRecordMember: + case SyntaxKind.JSDocPropertyTag: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: @@ -1326,6 +1329,7 @@ namespace ts { case SyntaxKind.JSDocFunctionType: return bindFunctionOrConstructorType(node); case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocRecordType: return bindAnonymousDeclaration(node, SymbolFlags.TypeLiteral, "__type"); case SyntaxKind.ObjectLiteralExpression: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff416ec2cc5..e66e5e6a9d8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3412,17 +3412,14 @@ namespace ts { return unknownType; } - let typeNode: TypeNode; - let name: Identifier; + let declaration: JSDocTypedefTag | TypeAliasDeclaration; if (node && (node.flags & NodeFlags.JavaScriptFile)) { - const declaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); - typeNode = declaration.typeExpression.type; + declaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); } - if (!typeNode) { - const declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); - typeNode = declaration.type; + if (!declaration) { + declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); } - let type = getTypeFromTypeNode(typeNode); + let type = getTypeFromTypeNode(declaration.type); if (popTypeResolution()) { links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); if (links.typeParameters) { @@ -3434,7 +3431,7 @@ namespace ts { } else { type = unknownType; - error(name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); + error(declaration.name, Diagnostics.Type_alias_0_circularly_references_itself, symbolToString(symbol)); } links.declaredType = type; } @@ -5067,6 +5064,7 @@ namespace ts { case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: case SyntaxKind.TypeLiteral: + case SyntaxKind.JSDocTypeLiteral: case SyntaxKind.JSDocFunctionType: case SyntaxKind.JSDocRecordType: return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 91f225931d2..5de1369ee09 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -402,7 +402,15 @@ namespace ts { case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: - return visitNode(cbNode, (node).typeExpression); + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).typeExpression) || + visitNode(cbNode, (node).type); + case SyntaxKind.JSDocTypeLiteral: + return visitNodes(cbNodes, (node).members); + case SyntaxKind.JSDocPropertyTag: + return visitNode(cbNode, (node).name) || + visitNode(cbNode, (node).typeExpression) || + visitNode(cbNode, (node).type); } } @@ -5992,7 +6000,8 @@ namespace ts { Debug.assert(end <= content.length); let tags: NodeArray; - let currentParentJSDocDeclaration: Declaration; + let currentParentJSDocTag: JSDocParentTag; + let currentParentJSDocTagEnd: number; let result: JSDocComment; @@ -6050,6 +6059,10 @@ namespace ts { nextJSDocToken(); } + if (currentParentJSDocTag) { + finishCurrentParentTag(); + } + result = createJSDocComment(); }); @@ -6073,6 +6086,40 @@ namespace ts { } } + function finishCurrentParentTag(): void { + if (!currentParentJSDocTag) { + return; + } + + if (currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) { + const typedefTag = currentParentJSDocTag; + if (typedefTag.jsDocTypeTag) { + if (typedefTag.jsDocTypeTag.typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { + const typeTagtype = typedefTag.jsDocTypeTag.typeExpression.type; + if ((typeTagtype.name.kind !== SyntaxKind.Identifier) || + (typeTagtype.name).text !== "Object") { + typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type; + } + } + else { + typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type; + } + } + if (!typedefTag.type) { + const tagType = createNode(SyntaxKind.JSDocTypeLiteral, currentParentJSDocTag.pos); + if (typedefTag.jsDocPropertyTags){ + tagType.members = >[]; + addRange(tagType.members, typedefTag.jsDocPropertyTags); + } + typedefTag.type = finishNode(tagType, currentParentJSDocTagEnd); + } + } + + addTag(finishNode(currentParentJSDocTag, currentParentJSDocTagEnd)); + currentParentJSDocTag = undefined; + currentParentJSDocTagEnd = undefined; + } + function parseTag(): void { Debug.assert(token === SyntaxKind.AtToken); const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos()); @@ -6085,22 +6132,30 @@ namespace ts { } const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName); - addTag(tag); + if (!currentParentJSDocTag) { + addTag(tag); + } } function handleTag(atToken: Node, tagName: Identifier): JSDocTag { if (tagName) { switch (tagName.text) { case "param": + finishCurrentParentTag(); return handleParamTag(atToken, tagName); case "return": case "returns": + finishCurrentParentTag(); return handleReturnTag(atToken, tagName); case "template": + finishCurrentParentTag(); return handleTemplateTag(atToken, tagName); case "type": + // @typedef tag is allowed to have one @type tag, therefore seeing + // a @type tag may not indicate the end of the current parent tag. return handleTypeTag(atToken, tagName); case "typedef": + finishCurrentParentTag(); return handleTypedefTag(atToken, tagName); case "property": case "prop": @@ -6130,6 +6185,25 @@ namespace ts { } } + function addToCurrentParentTag(tag: JSDocTag): void { + if (!currentParentJSDocTag) { + return; + } + switch (tag.kind) { + case SyntaxKind.JSDocPropertyTag: + if (!currentParentJSDocTag.jsDocPropertyTags) { + currentParentJSDocTag.jsDocPropertyTags = >[]; + } + currentParentJSDocTag.jsDocPropertyTags.push(tag); + break; + case SyntaxKind.JSDocTypeTag: + if (!currentParentJSDocTag.jsDocTypeTag) { + currentParentJSDocTag.jsDocTypeTag = tag; + } + break; + } + } + function tryParseTypeExpression(): JSDocTypeExpression { if (token !== SyntaxKind.OpenBraceToken) { return undefined; @@ -6205,15 +6279,33 @@ namespace ts { parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text); } - const result = createNode(SyntaxKind.JSDocTypeTag, atToken.pos); + let result = createNode(SyntaxKind.JSDocTypeTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; result.typeExpression = tryParseTypeExpression(); - return finishNode(result); + result = finishNode(result); + + let typeTagPartOfParentTag = false; + if (currentParentJSDocTag && currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) { + const parentTag = currentParentJSDocTag; + if (!parentTag.typeExpression && !parentTag.jsDocTypeTag) { + typeTagPartOfParentTag = true; + parentTag.jsDocTypeTag = result; + currentParentJSDocTagEnd = scanner.getStartPos(); + } + } + if (!typeTagPartOfParentTag) { + // If this @type tag is not part of the current parent tag, then + // it denotes the end of the current parent tag. + finishCurrentParentTag(); + return result; + } + + return undefined; } function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag { - if (!currentParentJSDocDeclaration) { + if (!currentParentJSDocTag) { parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_cannot_be_used_independently_as_a_top_level_JSDoc_tag, tagName.text); return undefined; } @@ -6226,12 +6318,17 @@ namespace ts { return undefined; } - const result = createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); + let result = createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; result.name = name; result.typeExpression = typeExpression; - return finishNode(result); + result.type = typeExpression.type; + result = finishNode(result); + + addToCurrentParentTag(result); + currentParentJSDocTagEnd = scanner.getStartPos(); + return undefined; } function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { @@ -6249,17 +6346,26 @@ namespace ts { result.name = name; result.typeExpression = typeExpression; - // if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { - // const jsDocTypeReference = typeExpression.type; - // if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { - // const name = jsDocTypeReference.name; - // if (name.text === "Object") { - // currentParentJSDocDeclaration = declaration; - // } - // } - // } + if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { + const jsDocTypeReference = typeExpression.type; + if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { + const name = jsDocTypeReference.name; + if (name.text === "Object") { + currentParentJSDocTag = result; + } + } + } + else if (!typeExpression) { + currentParentJSDocTag = result; + } - return result; + if (!currentParentJSDocTag) { + result.type = result.typeExpression.type; + return finishNode(result); + } + + currentParentJSDocTagEnd = scanner.getStartPos(); + return undefined; } function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a0cc8d054e1..31c2c8eb068 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -344,7 +344,6 @@ namespace ts { JSDocTemplateTag, JSDocTypedefTag, JSDocPropertyTag, - JSDocTypedefDeclaration, JSDocTypeLiteral, // Synthesized list @@ -616,6 +615,7 @@ namespace ts { // SyntaxKind.PropertyAssignment // SyntaxKind.ShorthandPropertyAssignment // SyntaxKind.EnumMember + // SyntaxKind.JSDocPropertyTag export interface VariableLikeDeclaration extends Declaration { propertyName?: PropertyName; dotDotDotToken?: Node; @@ -1515,25 +1515,26 @@ namespace ts { } // @kind(SyntaxKind.JSDocTypedefTag) - export interface JSDocTypedefTag extends JSDocTag, Declaration { + export interface JSDocTypedefTag extends JSDocTag, Declaration, JSDocParentTag { name: Identifier; - typeExpression: JSDocTypeExpression; + typeExpression?: JSDocTypeExpression; + type: JSDocType; + } + + export interface JSDocParentTag extends JSDocTag { + jsDocPropertyTags?: NodeArray; + jsDocTypeTag?: JSDocTypeTag; } // @kind(SyntaxKind.JSDocPropertyTag) export interface JSDocPropertyTag extends JSDocTag, TypeElement { name: Identifier; typeExpression: JSDocTypeExpression; - } - - // @kind(SyntaxKind.JSDocTypedefDeclaration) - export interface JSDocTypedefDeclaration extends Declaration { - name: Identifier; type: TypeNode; } // @kind(SyntaxKind.JSDocTypeLiteral) - export interface JSDocTypeLiteral extends TypeNode { + export interface JSDocTypeLiteral extends JSDocType { members: NodeArray; } diff --git a/tests/cases/fourslash/server/jsdocCompletions.ts b/tests/cases/fourslash/server/jsdocCompletions.ts new file mode 100644 index 00000000000..c8dc1bbe481 --- /dev/null +++ b/tests/cases/fourslash/server/jsdocCompletions.ts @@ -0,0 +1,15 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsdocCompletion_typedef.js +//// /** +//// * @typedef {Object} Person +//// * @property {string} personName +//// * @type {Person} +//// */ +//// var x1; +//// x1/**/ + +goTo.marker(); +edit.insert('.'); +verify.memberListContains('personName'); \ No newline at end of file From 9a8f6395e44ed494876f7a671b2d2750b336b929 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Wed, 6 Apr 2016 20:22:23 -0700 Subject: [PATCH 03/90] Support the case with variable name as @typedef type name --- src/compiler/checker.ts | 13 +++++-------- src/compiler/parser.ts | 22 +++++++++++++++++----- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e66e5e6a9d8..35ae6777fe2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3403,7 +3403,7 @@ namespace ts { return links.declaredType; } - function getDeclaredTypeOfTypeAlias(symbol: Symbol, node?: Node): Type { + function getDeclaredTypeOfTypeAlias(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.declaredType) { // Note that we use the links object as the target here because the symbol object is used as the unique @@ -3412,10 +3412,7 @@ namespace ts { return unknownType; } - let declaration: JSDocTypedefTag | TypeAliasDeclaration; - if (node && (node.flags & NodeFlags.JavaScriptFile)) { - declaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); - } + let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); if (!declaration) { declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); } @@ -3469,13 +3466,13 @@ namespace ts { return links.declaredType; } - function getDeclaredTypeOfSymbol(symbol: Symbol, node?: Node): Type { + function getDeclaredTypeOfSymbol(symbol: Symbol): Type { Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0); if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return getDeclaredTypeOfClassOrInterface(symbol); } if (symbol.flags & SymbolFlags.TypeAlias) { - return getDeclaredTypeOfTypeAlias(symbol, node); + return getDeclaredTypeOfTypeAlias(symbol); } if (symbol.flags & SymbolFlags.Enum) { return getDeclaredTypeOfEnum(symbol); @@ -4571,7 +4568,7 @@ namespace ts { // references to the type parameters of the alias. We replace those with the actual type arguments by instantiating the // declared type. Instantiations are cached using the type identities of the type arguments as the key. function getTypeFromTypeAliasReference(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, symbol: Symbol): Type { - const type = getDeclaredTypeOfSymbol(symbol, node); + const type = getDeclaredTypeOfSymbol(symbol); const links = getSymbolLinks(symbol); const typeParameters = links.typeParameters; if (typeParameters) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5de1369ee09..0b4a98bd986 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5977,7 +5977,7 @@ namespace ts { const saveParseDiagnosticsLength = parseDiagnostics.length; const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; - const comment = parseJSDocCommentWorker(start, length); + const comment = parseJSDocCommentWorker(start, length, parent); if (comment) { comment.parent = parent; } @@ -5989,7 +5989,7 @@ namespace ts { return comment; } - export function parseJSDocCommentWorker(start: number, length: number): JSDocComment { + export function parseJSDocCommentWorker(start: number, length: number, parentNode?: Node): JSDocComment { const content = sourceText; start = start || 0; const end = length === undefined ? content.length : start + length; @@ -6334,10 +6334,22 @@ namespace ts { function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { const typeExpression = tryParseTypeExpression(); skipWhitespace(); - const name = parseJSDocIdentifier(); + let name = parseJSDocIdentifier(); if (!name) { - parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected); - return undefined; + let foundNameFromParentNode = false; + if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { + if ((parentNode).declarationList.declarations.length > 0) { + const nameFromParentNode = (parentNode).declarationList.declarations[0].name; + if (nameFromParentNode.kind === SyntaxKind.Identifier) { + foundNameFromParentNode = true; + name = nameFromParentNode; + } + } + } + if (!foundNameFromParentNode) { + parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected); + return undefined; + } } const result = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); From 2945f64e3b88f8658ed7863d4d3146ad9d5c6b52 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 8 Apr 2016 20:19:39 -0700 Subject: [PATCH 04/90] Add tests for renaming and incremental parsing. Changed how the AST was hydrated with jsdocComment nodes. --- src/compiler/binder.ts | 6 +- src/compiler/checker.ts | 4 +- src/compiler/parser.ts | 44 +-- src/compiler/scanner.ts | 5 +- src/compiler/types.ts | 9 +- src/compiler/utilities.ts | 89 ++++-- src/harness/harness.ts | 22 +- src/services/services.ts | 53 +++- src/services/utilities.ts | 61 ++-- tests/cases/fourslash/commentsVariables.ts | 80 ++--- tests/cases/fourslash/doesnotwork.ts | 45 +++ .../fourslash/server/jsdocCompletions.ts | 15 - .../cases/fourslash/server/jsdocTypedefTag.ts | 64 ++++ .../server/jsdocTypedefTagGoToDefinition.ts | 29 ++ .../server/jsdocTypedefTagRename01.ts | 20 ++ .../server/jsdocTypedefTagRename02.ts | 15 + .../server/jsdocTypedefTagRename03.ts | 20 ++ .../server/jsdocTypedefTagRename04.ts | 24 ++ tests/cases/unittests/jsDocParsing.ts | 3 +- tests/webTestServer.js | 288 ++++++++++++++++++ tests/webTestServer.js.map | 1 + 21 files changed, 757 insertions(+), 140 deletions(-) create mode 100644 tests/cases/fourslash/doesnotwork.ts delete mode 100644 tests/cases/fourslash/server/jsdocCompletions.ts create mode 100644 tests/cases/fourslash/server/jsdocTypedefTag.ts create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagGoToDefinition.ts create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagRename01.ts create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagRename02.ts create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagRename03.ts create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagRename04.ts create mode 100644 tests/webTestServer.js create mode 100644 tests/webTestServer.js.map diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index b3f163bbef1..45629da8adf 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -407,8 +407,10 @@ namespace ts { // Binding of JsDocComment should be done before the current block scope container changes. // because the scope of JsDocComment should not be affected by whether the current node is a // container or not. - if (isInJavaScriptFile(node) && node.jsDocComment) { - bind(node.jsDocComment); + if (isInJavaScriptFile(node) && node.jsDocComments) { + for (const jsDocComment of node.jsDocComments) { + bind(jsDocComment); + } } // Depending on what kind of node this is, we may have to adjust the current container diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 35ae6777fe2..c903696f88f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16117,7 +16117,7 @@ namespace ts { node = node.parent; } - return node.parent && node.parent.kind === SyntaxKind.TypeReference; + return node.parent && (node.parent.kind === SyntaxKind.TypeReference || node.parent.kind === SyntaxKind.JSDocTypeReference) ; } function isHeritageClauseElementIdentifier(entityName: Node): boolean { @@ -16251,7 +16251,7 @@ namespace ts { } } else if (isTypeReferenceIdentifier(entityName)) { - let meaning = entityName.parent.kind === SyntaxKind.TypeReference ? SymbolFlags.Type : SymbolFlags.Namespace; + let meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace; // Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead // return the alias symbol. meaning |= SymbolFlags.Alias; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0b4a98bd986..009a3e13fa2 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -402,15 +402,15 @@ namespace ts { case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: - return visitNode(cbNode, (node).name) || - visitNode(cbNode, (node).typeExpression) || + return visitNode(cbNode, (node).typeExpression) || + visitNode(cbNode, (node).name) || visitNode(cbNode, (node).type); case SyntaxKind.JSDocTypeLiteral: return visitNodes(cbNodes, (node).members); case SyntaxKind.JSDocPropertyTag: - return visitNode(cbNode, (node).name) || - visitNode(cbNode, (node).typeExpression) || - visitNode(cbNode, (node).type); + return visitNode(cbNode, (node).typeExpression) || + visitNode(cbNode, (node).type) || + visitNode(cbNode, (node).name); } } @@ -638,9 +638,14 @@ namespace ts { if (comments) { for (const comment of comments) { const jsDocComment = JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos); - if (jsDocComment) { - node.jsDocComment = jsDocComment; + if (!jsDocComment) { + continue; } + + if (!node.jsDocComments) { + node.jsDocComments = []; + } + node.jsDocComments.push(jsDocComment); } } } @@ -6105,13 +6110,15 @@ namespace ts { typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type; } } - if (!typedefTag.type) { - const tagType = createNode(SyntaxKind.JSDocTypeLiteral, currentParentJSDocTag.pos); - if (typedefTag.jsDocPropertyTags){ - tagType.members = >[]; - addRange(tagType.members, typedefTag.jsDocPropertyTags); - } - typedefTag.type = finishNode(tagType, currentParentJSDocTagEnd); + if (!typedefTag.type && typedefTag.jsDocPropertyTags) { + const childrenTagPoses = ts.map(typedefTag.jsDocPropertyTags, tag => tag.pos); + const childrenTagEnds = ts.map(typedefTag.jsDocPropertyTags, tag => tag.end); + const pos = Math.min(...childrenTagPoses); + const end = Math.max(...childrenTagEnds); + const tagType = createNode(SyntaxKind.JSDocTypeLiteral, pos); + tagType.members = >[]; + addRange(tagType.members, typedefTag.jsDocPropertyTags); + typedefTag.type = finishNode(tagType, end); } } @@ -6545,10 +6552,6 @@ namespace ts { node._children = undefined; } - if (node.jsDocComment) { - node.jsDocComment = undefined; - } - node.pos += delta; node.end += delta; @@ -6557,6 +6560,11 @@ namespace ts { } forEachChild(node, visitNode, visitArray); + if (node.jsDocComments) { + for (const jsDocComment of node.jsDocComments) { + forEachChild(jsDocComment, visitNode, visitArray); + } + } checkNodePositions(node, aggressiveChecks); } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 8979814a7a2..d4206749fae 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -433,7 +433,7 @@ namespace ts { } /* @internal */ - export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean): number { + export function skipTrivia(text: string, pos: number, stopAfterLineBreak?: boolean, stopAtComments = false): number { // Using ! with a greater than test is a fast way of testing the following conditions: // pos === undefined || pos === null || isNaN(pos) || pos < 0; if (!(pos >= 0)) { @@ -461,6 +461,9 @@ namespace ts { pos++; continue; case CharacterCodes.slash: + if (stopAtComments) { + break; + } if (text.charCodeAt(pos + 1) === CharacterCodes.slash) { pos += 2; while (pos < text.length) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 31c2c8eb068..838b5d951f2 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -374,6 +374,8 @@ namespace ts { FirstBinaryOperator = LessThanToken, LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, + FirstJSDocNode = JSDocTypeExpression, + LastJSDocNode = JSDocTypeLiteral } export const enum NodeFlags { @@ -447,7 +449,7 @@ namespace ts { modifiers?: ModifiersArray; // Array of modifiers /* @internal */ id?: number; // Unique id (used to look up NodeLinks) parent?: Node; // Parent node (initialized by binding - /* @internal */ jsDocComment?: JSDocComment; // JSDoc for the node, if it has any. Only for .js files. + /* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files. /* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding) /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) @@ -2827,4 +2829,9 @@ namespace ts { /* @internal */ reattachFileDiagnostics(newFile: SourceFile): void; } + + // SyntaxKind.SyntaxList + export interface SyntaxList extends Node { + _children: Node[]; + } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4356486d3b5..fab5e9f8f8b 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -203,16 +203,54 @@ namespace ts { return !nodeIsMissing(node); } - export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile): number { + export function getTokenPosOfNode(node: Node, sourceFile?: SourceFile, includeJsDocComment?: 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. if (nodeIsMissing(node)) { return node.pos; } + if (isJSDocNode(node)) { + return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); + } + + if (node.jsDocComments && node.jsDocComments.length > 0 && includeJsDocComment) { + return getTokenPosOfNode(node.jsDocComments[0]); + } + + if (node.kind === SyntaxKind.SyntaxList) { + const childrenPoses = ts.map((node)._children, child => getTokenPosOfNode(child, sourceFile, includeJsDocComment)); + return Math.min(...childrenPoses); + } + return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos); } + export function isJSDocNode(node: Node) { + return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode; + } + export function isJSDocType(node: Node) { + switch (node.kind) { + case SyntaxKind.JSDocAllType: + case SyntaxKind.JSDocUnknownType: + case SyntaxKind.JSDocArrayType: + case SyntaxKind.JSDocUnionType: + case SyntaxKind.JSDocTupleType: + case SyntaxKind.JSDocNullableType: + case SyntaxKind.JSDocNonNullableType: + case SyntaxKind.JSDocRecordType: + case SyntaxKind.JSDocRecordMember: + case SyntaxKind.JSDocTypeReference: + case SyntaxKind.JSDocOptionalType: + case SyntaxKind.JSDocFunctionType: + case SyntaxKind.JSDocVariadicType: + case SyntaxKind.JSDocConstructorType: + case SyntaxKind.JSDocThisType: + return true; + } + return false; + } + export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number { if (nodeIsMissing(node) || !node.decorators) { return getTokenPosOfNode(node, sourceFile); @@ -1199,26 +1237,28 @@ namespace ts { return undefined; } - const jsDocComment = getJSDocComment(node, checkParentVariableStatement); - if (!jsDocComment) { + const jsDocComments = getJSDocComments(node, checkParentVariableStatement); + if (!jsDocComments) { return undefined; } - for (const tag of jsDocComment.tags) { - if (tag.kind === kind) { - return tag; + for (const jsDocComment of jsDocComments) { + for (const tag of jsDocComment.tags) { + if (tag.kind === kind) { + return tag; + } } } } - function getJSDocComment(node: Node, checkParentVariableStatement: boolean): JSDocComment { - if (node.jsDocComment) { - return node.jsDocComment; + function getJSDocComments(node: Node, checkParentVariableStatement: boolean): JSDocComment[] { + if (node.jsDocComments) { + return node.jsDocComments; } - // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement. - // /** + // Try to recognize this pattern when node is initializer of variable declaration and JSDoc comments are on containing variable statement. + // /** // * @param {number} name - // * @returns {number} + // * @returns {number} // */ // var x = function(name) { return name.length; } if (checkParentVariableStatement) { @@ -1229,7 +1269,7 @@ namespace ts { const variableStatementNode = isInitializerOfVariableDeclarationInStatement ? node.parent.parent.parent : undefined; if (variableStatementNode) { - return variableStatementNode.jsDocComment; + return variableStatementNode.jsDocComments; } // Also recognize when the node is the RHS of an assignment expression @@ -1240,12 +1280,12 @@ namespace ts { (parent as BinaryExpression).operatorToken.kind === SyntaxKind.EqualsToken && parent.parent.kind === SyntaxKind.ExpressionStatement; if (isSourceOfAssignmentExpressionStatement) { - return parent.parent.jsDocComment; + return parent.parent.jsDocComments; } const isPropertyAssignmentExpression = parent && parent.kind === SyntaxKind.PropertyAssignment; if (isPropertyAssignmentExpression) { - return parent.jsDocComment; + return parent.jsDocComments; } } @@ -1270,14 +1310,16 @@ namespace ts { // annotation. const parameterName = (parameter.name).text; - const jsDocComment = getJSDocComment(parameter.parent, /*checkParentVariableStatement*/ true); - if (jsDocComment) { - for (const tag of jsDocComment.tags) { - if (tag.kind === SyntaxKind.JSDocParameterTag) { - const parameterTag = tag; - const name = parameterTag.preParameterName || parameterTag.postParameterName; - if (name.text === parameterName) { - return parameterTag; + const jsDocComments = getJSDocComments(parameter.parent, /*checkParentVariableStatement*/ true); + if (jsDocComments) { + for (const jsDocComment of jsDocComments) { + for (const tag of jsDocComment.tags) { + if (tag.kind === SyntaxKind.JSDocParameterTag) { + const parameterTag = tag; + const name = parameterTag.preParameterName || parameterTag.postParameterName; + if (name.text === parameterName) { + return parameterTag; + } } } } @@ -1374,6 +1416,7 @@ namespace ts { case SyntaxKind.TypeAliasDeclaration: case SyntaxKind.TypeParameter: case SyntaxKind.VariableDeclaration: + case SyntaxKind.JSDocTypedefTag: return true; } return false; diff --git a/src/harness/harness.ts b/src/harness/harness.ts index ddaca642094..330f57cf5c9 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1,7 +1,7 @@ // // Copyright (c) Microsoft Corporation. All rights reserved. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -217,6 +217,14 @@ namespace Utils { return k; } + if (k === (ts).SyntaxKind.FirstJSDocCommentNode || k === (ts).SyntaxKind.LastJSDocCommentNode) { + for (const kindName in (ts).SyntaxKind) { + if ((ts).SyntaxKind[kindName] === k) { + return kindName; + } + } + } + return (ts).SyntaxKind[k]; } @@ -346,7 +354,7 @@ namespace Utils { assert.equal(node1.end, node2.end, "node1.end !== node2.end"); assert.equal(node1.kind, node2.kind, "node1.kind !== node2.kind"); - // call this on both nodes to ensure all propagated flags have been set (and thus can be + // call this on both nodes to ensure all propagated flags have been set (and thus can be // compared). assert.equal(ts.containsParseError(node1), ts.containsParseError(node2)); assert.equal(node1.flags, node2.flags, "node1.flags !== node2.flags"); @@ -753,7 +761,7 @@ namespace Harness { (emittedFile: string, emittedLine: number, emittedColumn: number, sourceFile: string, sourceLine: number, sourceColumn: number, sourceName: string): void; } - // Settings + // Settings export let userSpecifiedRoot = ""; export let lightMode = false; @@ -792,7 +800,7 @@ namespace Harness { fileName: string, sourceText: string, languageVersion: ts.ScriptTarget) { - // We'll only assert invariants outside of light mode. + // We'll only assert invariants outside of light mode. const shouldAssertInvariants = !Harness.lightMode; // Only set the parent nodes if we're asserting invariants. We don't need them otherwise. @@ -887,7 +895,7 @@ namespace Harness { libFiles?: string; } - // Additional options not already in ts.optionDeclarations + // Additional options not already in ts.optionDeclarations const harnessOptionDeclarations: ts.CommandLineOption[] = [ { name: "allowNonTsExtensions", type: "boolean" }, { name: "useCaseSensitiveFileNames", type: "boolean" }, @@ -1135,7 +1143,7 @@ namespace Harness { errLines.forEach(e => outputLines.push(e)); // do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics - // if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers) + // if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers) // then they will be added twice thus triggering 'total errors' assertion with condition // 'totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length @@ -1445,7 +1453,7 @@ namespace Harness { }; testUnitData.push(newTestFile2); - // unit tests always list files explicitly + // unit tests always list files explicitly const parseConfigHost: ts.ParseConfigHost = { readDirectory: (name) => [] }; diff --git a/src/services/services.ts b/src/services/services.ts index a634b2baf1a..d85d478fcbe 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -20,7 +20,7 @@ namespace ts { getChildCount(sourceFile?: SourceFile): number; getChildAt(index: number, sourceFile?: SourceFile): Node; getChildren(sourceFile?: SourceFile): Node[]; - getStart(sourceFile?: SourceFile): number; + getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number; getFullStart(): number; getEnd(): number; getWidth(sourceFile?: SourceFile): number; @@ -170,6 +170,8 @@ namespace ts { "static", "throws", "type", + "typedef", + "property", "version" ]; let jsDocCompletionEntries: CompletionEntry[]; @@ -187,6 +189,7 @@ namespace ts { public end: number; public flags: NodeFlags; public parent: Node; + public jsDocComments: JSDocComment[]; private _children: Node[]; constructor(kind: SyntaxKind, pos: number, end: number) { @@ -201,8 +204,8 @@ namespace ts { return getSourceFileOfNode(this); } - public getStart(sourceFile?: SourceFile): number { - return getTokenPosOfNode(this, sourceFile); + public getStart(sourceFile?: SourceFile, includeJsDocComment?: boolean): number { + return getTokenPosOfNode(this, sourceFile, includeJsDocComment); } public getFullStart(): number { @@ -233,12 +236,14 @@ namespace ts { return (sourceFile || this.getSourceFile()).text.substring(this.getStart(), this.getEnd()); } - private addSyntheticNodes(nodes: Node[], pos: number, end: number): number { + private addSyntheticNodes(nodes: Node[], pos: number, end: number, useJSDocScanner?: boolean): number { scanner.setTextPos(pos); while (pos < end) { - const token = scanner.scan(); + const token = useJSDocScanner ? scanner.scanJSDocToken() : scanner.scan(); const textPos = scanner.getTextPos(); - nodes.push(createNode(token, pos, textPos, 0, this)); + if (textPos <= end) { + nodes.push(createNode(token, pos, textPos, 0, this)); + } pos = textPos; } return pos; @@ -268,20 +273,36 @@ namespace ts { scanner.setText((sourceFile || this.getSourceFile()).text); children = []; let pos = this.pos; + const isJSDocTag = + this.kind === SyntaxKind.JSDocComment || + this.kind === SyntaxKind.JSDocParameterTag || + this.kind === SyntaxKind.JSDocTag || + this.kind === SyntaxKind.JSDocParameterTag || + this.kind === SyntaxKind.JSDocReturnTag || + this.kind === SyntaxKind.JSDocTypeTag || + this.kind === SyntaxKind.JSDocTemplateTag || + this.kind === SyntaxKind.JSDocTypedefTag || + this.kind === SyntaxKind.JSDocPropertyTag; const processNode = (node: Node) => { if (pos < node.pos) { - pos = this.addSyntheticNodes(children, pos, node.pos); + pos = this.addSyntheticNodes(children, pos, node.pos, /*useJSDocScanner*/ isJSDocTag); } children.push(node); pos = node.end; }; const processNodes = (nodes: NodeArray) => { if (pos < nodes.pos) { - pos = this.addSyntheticNodes(children, pos, nodes.pos); + pos = this.addSyntheticNodes(children, pos, nodes.pos, /*useJSDocScanner*/ isJSDocTag); } children.push(this.createSyntaxList(>nodes)); pos = nodes.end; }; + // jsDocComments need to be the first children + if (this.jsDocComments) { + for (const jsDocComment of this.jsDocComments) { + processNode(jsDocComment); + } + } forEachChild(this, processNode, processNodes); if (pos < this.end) { this.addSyntheticNodes(children, pos, this.end); @@ -1883,7 +1904,7 @@ namespace ts { options.isolatedModules = true; - // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. + // transpileModule does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths. options.suppressOutputPathCheck = true; // Filename can be non-ts file. @@ -5499,7 +5520,7 @@ namespace ts { const sourceFile = getValidSourceFile(fileName); - const node = getTouchingPropertyName(sourceFile, position); + const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true); if (!node) { return undefined; } @@ -5808,7 +5829,8 @@ namespace ts { const sourceFile = container.getSourceFile(); const tripleSlashDirectivePrefixRegex = /^\/\/\/\s* 0) { + for (const jsDocComment of node.jsDocComments) { + forEachChild(jsDocComment, walk); + } + } } } } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 8006fea2c07..5ea011f2226 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -260,29 +260,29 @@ namespace ts { /* Gets the token whose text has range [start, end) and * position >= start and (position < end or (position === end && token is keyword or identifier)) */ - export function getTouchingWord(sourceFile: SourceFile, position: number): Node { - return getTouchingToken(sourceFile, position, n => isWord(n.kind)); + export function getTouchingWord(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node { + return getTouchingToken(sourceFile, position, n => isWord(n.kind), includeJsDocComment); } /* Gets the token whose text has range [start, end) and position >= start * and (position < end or (position === end && token is keyword or identifier or numeric/string literal)) */ - export function getTouchingPropertyName(sourceFile: SourceFile, position: number): Node { - return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind)); + export function getTouchingPropertyName(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node { + return getTouchingToken(sourceFile, position, n => isPropertyName(n.kind), includeJsDocComment); } /** Returns the token if position is in [start, end) or if position === end and includeItemAtEndPosition(token) === true */ - export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean): Node { - return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition); + export function getTouchingToken(sourceFile: SourceFile, position: number, includeItemAtEndPosition?: (n: Node) => boolean, includeJsDocComment = false): Node { + return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ false, includeItemAtEndPosition, includeJsDocComment); } /** Returns a token if position is in [start-of-leading-trivia, end) */ - export function getTokenAtPosition(sourceFile: SourceFile, position: number): Node { - return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined); + export function getTokenAtPosition(sourceFile: SourceFile, position: number, includeJsDocComment = false): Node { + return getTokenAtPositionWorker(sourceFile, position, /*allowPositionInLeadingTrivia*/ true, /*includeItemAtEndPosition*/ undefined, includeJsDocComment); } /** Get the token whose text contains the position */ - function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean): Node { + function getTokenAtPositionWorker(sourceFile: SourceFile, position: number, allowPositionInLeadingTrivia: boolean, includeItemAtEndPosition: (n: Node) => boolean, includeJsDocComment = false): Node { let current: Node = sourceFile; outer: while (true) { if (isToken(current)) { @@ -290,10 +290,34 @@ namespace ts { return current; } + if (includeJsDocComment) { + const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode); + for (const jsDocChild of jsDocChildren) { + let start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment); + if (start <= position) { + let end = jsDocChild.getEnd(); + if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) { + current = jsDocChild; + continue outer; + } + else if (includeItemAtEndPosition && end === position) { + let previousToken = findPrecedingToken(position, sourceFile, jsDocChild); + if (previousToken && includeItemAtEndPosition(previousToken)) { + return previousToken; + } + } + } + } + } + // find the child that contains 'position' for (let i = 0, n = current.getChildCount(sourceFile); i < n; i++) { let child = current.getChildAt(i); - let start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile); + // all jsDocComment nodes were already visited + if (isJSDocNode(child)) { + continue; + } + let start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment); if (start <= position) { let end = child.getEnd(); if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) { @@ -308,6 +332,7 @@ namespace ts { } } } + return current; } } @@ -501,11 +526,13 @@ namespace ts { } if (node) { - let jsDocComment = node.jsDocComment; - if (jsDocComment) { - for (let tag of jsDocComment.tags) { - if (tag.pos <= position && position <= tag.end) { - return tag; + let jsDocComments = node.jsDocComments; + if (jsDocComments) { + for (const jsDocComment of jsDocComments) { + for (const tag of jsDocComment.tags) { + if (tag.pos <= position && position <= tag.end) { + return tag; + } } } } @@ -615,7 +642,7 @@ namespace ts { // [a,b,c] from: // [a, b, c] = someExpression; if (node.parent.kind === SyntaxKind.BinaryExpression && - (node.parent).left === node && + (node.parent).left === node && (node.parent).operatorToken.kind === SyntaxKind.EqualsToken) { return true; } @@ -629,7 +656,7 @@ namespace ts { // [a, b, c] of // [x, [a, b, c] ] = someExpression - // or + // or // {x, a: {a, b, c} } = someExpression if (isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.kind === SyntaxKind.PropertyAssignment ? node.parent.parent : node.parent)) { return true; diff --git a/tests/cases/fourslash/commentsVariables.ts b/tests/cases/fourslash/commentsVariables.ts index d62e1e9bee8..b2cf6c9cb9b 100644 --- a/tests/cases/fourslash/commentsVariables.ts +++ b/tests/cases/fourslash/commentsVariables.ts @@ -41,60 +41,60 @@ ////} ////var x = fo/*15*/o2; -goTo.marker('1'); -verify.quickInfoIs("var myVariable: number", "This is my variable"); +// goTo.marker('1'); +// verify.quickInfoIs("var myVariable: number", "This is my variable"); -goTo.marker('2'); -verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); +// goTo.marker('2'); +// verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); -goTo.marker('3'); -verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); -verify.completionListContains("d", "var d: number", "d variable"); +// goTo.marker('3'); +// verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); +// verify.completionListContains("d", "var d: number", "d variable"); goTo.marker('4'); verify.completionListContains("foo", "function foo(): void", "foos comment"); verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); -goTo.marker('5'); -verify.currentSignatureHelpDocCommentIs("foos comment"); -goTo.marker('5q'); -verify.quickInfoIs("function foo(): void", "foos comment"); +// goTo.marker('5'); +// verify.currentSignatureHelpDocCommentIs("foos comment"); +// goTo.marker('5q'); +// verify.quickInfoIs("function foo(): void", "foos comment"); -goTo.marker('6'); -verify.currentSignatureHelpDocCommentIs(""); -goTo.marker('6q'); -verify.quickInfoIs("var fooVar: () => void", ""); +// goTo.marker('6'); +// verify.currentSignatureHelpDocCommentIs(""); +// goTo.marker('6q'); +// verify.quickInfoIs("var fooVar: () => void", ""); -goTo.marker('7'); -verify.completionListContains("foo", "function foo(): void", "foos comment"); -verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); +// goTo.marker('7'); +// verify.completionListContains("foo", "function foo(): void", "foos comment"); +// verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); -goTo.marker('8'); -verify.currentSignatureHelpDocCommentIs("foos comment"); -goTo.marker('8q'); -verify.quickInfoIs("function foo(): void", "foos comment"); +// goTo.marker('8'); +// verify.currentSignatureHelpDocCommentIs("foos comment"); +// goTo.marker('8q'); +// verify.quickInfoIs("function foo(): void", "foos comment"); -goTo.marker('9'); -verify.currentSignatureHelpDocCommentIs(""); -goTo.marker('9q'); -verify.quickInfoIs("var fooVar: () => void", ""); -goTo.marker('9aq'); -verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); +// goTo.marker('9'); +// verify.currentSignatureHelpDocCommentIs(""); +// goTo.marker('9q'); +// verify.quickInfoIs("var fooVar: () => void", ""); +// goTo.marker('9aq'); +// verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); -goTo.marker('10'); -verify.completionListContains("i", "var i: c", "instance comment"); +// goTo.marker('10'); +// verify.completionListContains("i", "var i: c", "instance comment"); -goTo.marker('11'); -verify.completionListContains("i1_i", "var i1_i: i1", "interface instance comments"); +// goTo.marker('11'); +// verify.completionListContains("i1_i", "var i1_i: i1", "interface instance comments"); -goTo.marker('12'); -verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); +// goTo.marker('12'); +// verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); -goTo.marker('13'); -verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); +// goTo.marker('13'); +// verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); -goTo.marker('14'); -verify.quickInfoIs("function foo(): void", "foos comment"); +// goTo.marker('14'); +// verify.quickInfoIs("function foo(): void", "foos comment"); -goTo.marker('15'); -verify.quickInfoIs("function foo2(a: number): void (+1 overload)", ""); +// goTo.marker('15'); +// verify.quickInfoIs("function foo2(a: number): void (+1 overload)", ""); diff --git a/tests/cases/fourslash/doesnotwork.ts b/tests/cases/fourslash/doesnotwork.ts new file mode 100644 index 00000000000..2f415563c21 --- /dev/null +++ b/tests/cases/fourslash/doesnotwork.ts @@ -0,0 +1,45 @@ +/// + +/////** This is my variable*/ +////var myVariable = 10; +//// +/////** d variable*/ +////var d = 10; +////myVariable = d; +//// +/////** foos comment*/ +////function foo() { +////} +/////** fooVar comment*/ +////var fooVar: () => void; +/////*4*/ +////foo(); +////fooVar(); +////fooVar = foo; +//// +////foo(); +////fooVar(); +////var fooVarVar = fooVar; +/////**class comment*/ +////class c { +//// /** constructor comment*/ +//// constructor() { +//// } +////} +/////**instance comment*/ +////var i = new c(); +//// +/////** interface comments*/ +////interface i1 { +////} +/////**interface instance comments*/ +////var i1_i: i1; +//// +////function foo2(a: number): void; +////function foo2(b: string): void; +////function foo2(aOrb) { +////} +////var x = foo2; + +goTo.marker('4'); +verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); diff --git a/tests/cases/fourslash/server/jsdocCompletions.ts b/tests/cases/fourslash/server/jsdocCompletions.ts deleted file mode 100644 index c8dc1bbe481..00000000000 --- a/tests/cases/fourslash/server/jsdocCompletions.ts +++ /dev/null @@ -1,15 +0,0 @@ -/// - -// @allowNonTsExtensions: true -// @Filename: jsdocCompletion_typedef.js -//// /** -//// * @typedef {Object} Person -//// * @property {string} personName -//// * @type {Person} -//// */ -//// var x1; -//// x1/**/ - -goTo.marker(); -edit.insert('.'); -verify.memberListContains('personName'); \ No newline at end of file diff --git a/tests/cases/fourslash/server/jsdocTypedefTag.ts b/tests/cases/fourslash/server/jsdocTypedefTag.ts new file mode 100644 index 00000000000..e645b518020 --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTag.ts @@ -0,0 +1,64 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsdocCompletion_typedef.js + +//// /** @typedef {(string | number)} NumberLike */ +//// +//// /** +//// * @typedef Animal +//// * @type {Object} +//// * @property {string} animalName +//// * @property {number} animalAge +//// */ +//// +//// /** +//// * @typedef {Object} Person +//// * @property {string} personName +//// * @property {number} personAge +//// */ +//// +//// /** +//// * @typedef {Object} +//// * @property {string} catName +//// * @property {number} catAge +//// */ +//// var Cat; +//// +//// /** @typedef {{ dogName: string, dogAge: number }} */ +//// var Dog; +//// +//// /** @type {NumberLike} */ +//// var numberLike; numberLike./*numberLike*/ +//// +//// /** @type {Person} */ +//// var p;p./*person*/ +//// +//// /** @type {Animal} */ +//// var a;a./*animal*/ +//// +//// /** @type {Cat} */ +//// var c;c./*cat*/ +//// +//// /** @type {Dog} */ +//// var d;d./*dog*/ + +goTo.marker('numberLike'); +verify.memberListContains('charAt'); +verify.memberListContains('toExponential'); + +goTo.marker('person'); +verify.memberListContains('personName'); +verify.memberListContains('personAge'); + +goTo.marker('animal'); +verify.memberListContains('animalName'); +verify.memberListContains('animalAge'); + +goTo.marker('dog'); +verify.memberListContains('dogName'); +verify.memberListContains('dogAge'); + +goTo.marker('cat'); +verify.memberListContains('catName'); +verify.memberListContains('catAge'); \ No newline at end of file diff --git a/tests/cases/fourslash/server/jsdocTypedefTagGoToDefinition.ts b/tests/cases/fourslash/server/jsdocTypedefTagGoToDefinition.ts new file mode 100644 index 00000000000..4db14611938 --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagGoToDefinition.ts @@ -0,0 +1,29 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsdocCompletion_typedef.js + +//// /** +//// * @typedef {Object} Person +//// * /*1*/@property {string} personName +//// * @property {number} personAge +//// */ +//// +//// /** +//// * @typedef {{ /*2*/animalName: string, animalAge: number }} Animal +//// */ +//// +//// /** @type {Person} */ +//// var person; person.personName/*3*/ +//// +//// /** @type {Animal} */ +//// var animal; animal.animalName/*4*/ + +goTo.file('jsdocCompletion_typedef.js'); +goTo.marker('3'); +goTo.definition(); +verify.caretAtMarker('1'); + +goTo.marker('4'); +goTo.definition(); +verify.caretAtMarker('2'); diff --git a/tests/cases/fourslash/server/jsdocTypedefTagRename01.ts b/tests/cases/fourslash/server/jsdocTypedefTagRename01.ts new file mode 100644 index 00000000000..776d0180b06 --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagRename01.ts @@ -0,0 +1,20 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsDocTypedef_form1.js +//// +//// /** @typedef {(string | number)} */ +//// var /*1*/[|NumberLike|]; +//// +//// /*2*/[|NumberLike|] = 10; +//// +//// /** @type {/*3*/[|NumberLike|]} */ +//// var numberLike; + +goTo.file('jsDocTypedef_form1.js') +goTo.marker('1'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); +goTo.marker('2'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); +goTo.marker('3'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); \ No newline at end of file diff --git a/tests/cases/fourslash/server/jsdocTypedefTagRename02.ts b/tests/cases/fourslash/server/jsdocTypedefTagRename02.ts new file mode 100644 index 00000000000..7f1d422d971 --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagRename02.ts @@ -0,0 +1,15 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsDocTypedef_form2.js +//// +//// /** @typedef {(string | number)} /*1*/[|NumberLike|] */ +//// +//// /** @type {/*2*/[|NumberLike|]} */ +//// var numberLike; + +goTo.file('jsDocTypedef_form2.js') +goTo.marker('1'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); +goTo.marker('2'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); \ No newline at end of file diff --git a/tests/cases/fourslash/server/jsdocTypedefTagRename03.ts b/tests/cases/fourslash/server/jsdocTypedefTagRename03.ts new file mode 100644 index 00000000000..c1b38945806 --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagRename03.ts @@ -0,0 +1,20 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsDocTypedef_form3.js +//// +//// /** +//// * @typedef /*1*/[|Person|] +//// * @type {Object} +//// * @property {number} age +//// * @property {string} name +//// */ +//// +//// /** @type {/*2*/[|Person|]} */ +//// var person; + +goTo.file('jsDocTypedef_form3.js') +goTo.marker('1'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); +goTo.marker('2'); +verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ true); \ No newline at end of file diff --git a/tests/cases/fourslash/server/jsdocTypedefTagRename04.ts b/tests/cases/fourslash/server/jsdocTypedefTagRename04.ts new file mode 100644 index 00000000000..b7bf220512a --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagRename04.ts @@ -0,0 +1,24 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsDocTypedef_form2.js +//// +//// function test1() { +//// /** @typedef {(string | number)} NumberLike */ +//// +//// /** @type {/*1*/NumberLike} */ +//// var numberLike; +//// } +//// function test2() { +//// /** @typedef {(string | number)} NumberLike2 */ +//// +//// /** @type {NumberLike2} */ +//// var n/*2*/umberLike2; +//// } + +goTo.marker('2'); +verify.quickInfoExists(); +goTo.marker('1'); +edit.insert('111'); +goTo.marker('2'); +verify.quickInfoExists(); \ No newline at end of file diff --git a/tests/cases/unittests/jsDocParsing.ts b/tests/cases/unittests/jsDocParsing.ts index 1bd7b1147ca..0540c02d135 100644 --- a/tests/cases/unittests/jsDocParsing.ts +++ b/tests/cases/unittests/jsDocParsing.ts @@ -1004,7 +1004,8 @@ module ts { if (result !== expected) { // Turn on a human-readable diff if (typeof require !== 'undefined') { - require('chai').config.showDiff = true; + const chai = require('chai'); + chai.config.showDiff = true; chai.expect(JSON.parse(result)).equal(JSON.parse(expected)); } else { diff --git a/tests/webTestServer.js b/tests/webTestServer.js new file mode 100644 index 00000000000..b47da7bfe76 --- /dev/null +++ b/tests/webTestServer.js @@ -0,0 +1,288 @@ +/// +"use strict"; +var http = require("http"); +var fs = require("fs"); +var path = require("path"); +var url = require("url"); +var child_process = require("child_process"); +var os = require("os"); +/// Command line processing /// +if (process.argv[2] == '--help') { + console.log('Runs a node server on port 8888 by default, looking for tests folder in the current directory\n'); + console.log('Syntax: node nodeServer.js [port] [typescriptEnlistmentDirectory] [tests] [--browser] [--verbose]\n'); + console.log('Examples: \n\tnode nodeServer.js 8888 .'); + console.log('\tnode nodeServer.js 3000 D:/src/typescript/public --verbose IE'); +} +function switchToForwardSlashes(path) { + return path.replace(/\\/g, "/").replace(/\/\//g, '/'); +} +var defaultPort = 8888; +var port = process.argv[2] || defaultPort; +var rootDir = switchToForwardSlashes(__dirname + '/../'); +var browser; +if (process.argv[3]) { + browser = process.argv[3]; + if (browser !== 'chrome' && browser !== 'IE') { + console.log('Invalid command line arguments. Got ' + browser + ' but expected chrome, IE or nothing.'); + } +} +var grep = process.argv[4]; +var verbose = false; +if (process.argv[5] == '--verbose') { + verbose = true; +} +else if (process.argv[5] && process.argv[5] !== '--verbose') { + console.log('Invalid command line arguments. Got ' + process.argv[5] + ' but expected --verbose or nothing.'); +} +/// Utils /// +function log(msg) { + if (verbose) { + console.log(msg); + } +} +// Copied from the compiler sources +function dir(path, spec, options) { + options = options || {}; + function filesInFolder(folder) { + var folder = switchToForwardSlashes(folder); + var paths = []; + // Everything after the current directory is relative + var baseDirectoryLength = process.cwd().length + 1; + try { + var files = fs.readdirSync(folder); + for (var i = 0; i < files.length; i++) { + var stat = fs.statSync(folder + "/" + files[i]); + if (options.recursive && stat.isDirectory()) { + paths = paths.concat(filesInFolder(folder + "/" + files[i])); + } + else if (stat.isFile() && (!spec || files[i].match(spec))) { + var relativePath = folder.substring(baseDirectoryLength); + paths.push(relativePath + "/" + files[i]); + } + } + } + catch (err) { + } + return paths; + } + return filesInFolder(path); +} +// fs.rmdirSync won't delete directories with files in it +function deleteFolderRecursive(path) { + if (fs.existsSync(path)) { + fs.readdirSync(path).forEach(function (file, index) { + var curPath = path + "/" + file; + if (fs.statSync(curPath).isDirectory()) { + deleteFolderRecursive(curPath); + } + else { + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } +} +; +function writeFile(path, data, opts) { + try { + fs.writeFileSync(path, data); + } + catch (e) { + // assume file was written to a directory that exists, if not, start recursively creating them as necessary + var parts = switchToForwardSlashes(path).split('/'); + for (var i = 0; i < parts.length; i++) { + var subDir = parts.slice(0, i).join('/'); + if (!fs.existsSync(subDir)) { + fs.mkdir(subDir); + } + } + fs.writeFileSync(path, data); + } +} +/// Request Handling /// +function handleResolutionRequest(filePath, res) { + var resolvedPath = path.resolve(filePath, ''); + resolvedPath = resolvedPath.substring(resolvedPath.indexOf('tests')); + resolvedPath = switchToForwardSlashes(resolvedPath); + send('success', res, resolvedPath); + return; +} +function send(result, res, contents, contentType) { + if (contentType === void 0) { contentType = "binary"; } + var responseCode = result === "success" ? 200 : result === "fail" ? 500 : result === 'unknown' ? 404 : parseInt(result); + res.writeHead(responseCode, { "Content-Type": contentType }); + res.end(contents); + return; +} +// Reads the data from a post request and passes it to the given callback +function processPost(req, res, callback) { + var queryData = ""; + if (typeof callback !== 'function') + return null; + if (req.method == 'POST') { + req.on('data', function (data) { + queryData += data; + if (queryData.length > 1e8) { + queryData = ""; + send("413", res, null); + console.log("ERROR: destroying connection"); + req.connection.destroy(); + } + }); + req.on('end', function () { + //res.post = url.parse(req.url).query; + callback(queryData); + }); + } + else { + send("405", res, null); + } +} +var RequestType; +(function (RequestType) { + RequestType[RequestType["GetFile"] = 0] = "GetFile"; + RequestType[RequestType["GetDir"] = 1] = "GetDir"; + RequestType[RequestType["ResolveFile"] = 2] = "ResolveFile"; + RequestType[RequestType["WriteFile"] = 3] = "WriteFile"; + RequestType[RequestType["DeleteFile"] = 4] = "DeleteFile"; + RequestType[RequestType["WriteDir"] = 5] = "WriteDir"; + RequestType[RequestType["DeleteDir"] = 6] = "DeleteDir"; + RequestType[RequestType["AppendFile"] = 7] = "AppendFile"; + RequestType[RequestType["Unknown"] = 8] = "Unknown"; +})(RequestType || (RequestType = {})); +function getRequestOperation(req, filename) { + if (req.method === 'GET' && req.url.indexOf('?') === -1) { + if (req.url.indexOf('.') !== -1) + return RequestType.GetFile; + else + return RequestType.GetDir; + } + else { + var queryData = url.parse(req.url, true).query; + if (req.method === 'GET' && queryData.resolve !== undefined) + return RequestType.ResolveFile; + // mocha uses ?grep= query string as equivalent to the --grep command line option used to filter tests + if (req.method === 'GET' && queryData.grep !== undefined) + return RequestType.GetFile; + if (req.method === 'POST' && queryData.action) { + var path = req.url.substr(0, req.url.lastIndexOf('?')); + var isFile = path.substring(path.lastIndexOf('/')).indexOf('.') !== -1; + switch (queryData.action.toUpperCase()) { + case 'WRITE': + return isFile ? RequestType.WriteFile : RequestType.WriteDir; + case 'DELETE': + return isFile ? RequestType.DeleteFile : RequestType.DeleteDir; + case 'APPEND': + return isFile ? RequestType.AppendFile : RequestType.Unknown; + } + } + return RequestType.Unknown; + } +} +function handleRequestOperation(req, res, operation, reqPath) { + switch (operation) { + case RequestType.GetDir: + var filesInFolder = dir(reqPath, "", { recursive: true }); + send('success', res, filesInFolder.join(',')); + break; + case RequestType.GetFile: + fs.readFile(reqPath, function (err, file) { + var ext = reqPath.substr(reqPath.lastIndexOf('.')); + var contentType = 'binary'; + if (ext === '.js') + contentType = 'text/javascript'; + else if (ext === '.css') + contentType = 'text/javascript'; + else if (ext === '.html') + contentType = 'text/html'; + err + ? send('fail', res, err.message, contentType) + : send('success', res, file, contentType); + }); + break; + case RequestType.ResolveFile: + var resolveRequest = req.url.match(/(.*)\?resolve/); + handleResolutionRequest(resolveRequest[1], res); + break; + case RequestType.WriteFile: + processPost(req, res, function (data) { + writeFile(reqPath, data, { recursive: true }); + }); + send('success', res, null); + break; + case RequestType.WriteDir: + fs.mkdirSync(reqPath); + send('success', res, null); + break; + case RequestType.DeleteFile: + if (fs.existsSync(reqPath)) { + fs.unlinkSync(reqPath); + } + send('success', res, null); + break; + case RequestType.DeleteDir: + if (fs.existsSync(reqPath)) { + fs.rmdirSync(reqPath); + } + send('success', res, null); + break; + case RequestType.AppendFile: + processPost(req, res, function (data) { + fs.appendFileSync(reqPath, data); + }); + send('success', res, null); + break; + case RequestType.Unknown: + default: + send('unknown', res, null); + break; + } +} +console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); +http.createServer(function (req, res) { + log(req.method + ' ' + req.url); + var uri = url.parse(req.url).pathname; + var reqPath = path.join(process.cwd(), uri); + var operation = getRequestOperation(req, reqPath); + handleRequestOperation(req, res, operation, reqPath); +}).listen(8888); +var browserPath; +if ((browser && browser === 'chrome')) { + var defaultChromePath = ""; + switch (os.platform()) { + case "win32": + case "win64": + defaultChromePath = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"; + break; + case "darwin": + defaultChromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; + break; + case "linux": + defaultChromePath = "/opt/google/chrome/chrome"; + break; + default: + console.log("default Chrome location is unknown for platform '" + os.platform() + "'"); + break; + } + if (fs.existsSync(defaultChromePath)) { + browserPath = defaultChromePath; + } + else { + browserPath = browser; + } +} +else { + var defaultIEPath = 'C:/Program Files/Internet Explorer/iexplore.exe'; + if (fs.existsSync(defaultIEPath)) { + browserPath = defaultIEPath; + } + else { + browserPath = browser; + } +} +console.log('Using browser: ' + browserPath); +var queryString = grep ? "?grep=" + grep : ''; +child_process.spawn(browserPath, ['http://localhost:' + port + '/tests/webTestResults.html' + queryString], { + stdio: 'inherit' +}); +//# sourceMappingURL=file:///C:/Users/lizhe/Documents/github/TypeScript/tests/webTestServer.js.map \ No newline at end of file diff --git a/tests/webTestServer.js.map b/tests/webTestServer.js.map new file mode 100644 index 00000000000..1c924a872f4 --- /dev/null +++ b/tests/webTestServer.js.map @@ -0,0 +1 @@ +{"version":3,"file":"webTestServer.js","sourceRoot":"","sources":["file:///C:/Users/lizhe/Documents/github/TypeScript/tests/webTestServer.ts"],"names":[],"mappings":"AAAA,yDAAyD;;AAEzD,IAAO,IAAI,WAAW,MAAM,CAAC,CAAC;AAC9B,IAAO,EAAE,WAAW,IAAI,CAAC,CAAC;AAC1B,IAAO,IAAI,WAAW,MAAM,CAAC,CAAC;AAC9B,IAAO,GAAG,WAAW,KAAK,CAAC,CAAC;AAC5B,IAAO,aAAa,WAAW,eAAe,CAAC,CAAC;AAChD,IAAO,EAAE,WAAW,IAAI,CAAC,CAAC;AAE1B,+BAA+B;AAE/B,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,iGAAiG,CAAC,CAAC;IAC/G,OAAO,CAAC,GAAG,CAAC,qGAAqG,CAAC,CAAC;IACnH,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;AACnF,CAAC;AAED,gCAAgC,IAAY;IACxC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,IAAI,WAAW,GAAG,IAAI,CAAC;AACvB,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC;AAC1C,IAAI,OAAO,GAAG,sBAAsB,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;AAEzD,IAAI,OAAe,CAAC;AACpB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClB,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,EAAE,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAG,OAAO,GAAG,sCAAsC,CAAC,CAAC;IAC3G,CAAC;AACL,CAAC;AAED,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE3B,IAAI,OAAO,GAAG,KAAK,CAAC;AACpB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC;IACjC,OAAO,GAAG,IAAI,CAAC;AACnB,CAAC;AAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,qCAAqC,CAAC,CAAC;AAClH,CAAC;AAED,aAAa;AACb,aAAa,GAAW;IACpB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;AACL,CAAC;AAED,mCAAmC;AACnC,aAAa,IAAY,EAAE,IAAa,EAAE,OAAa;IACnD,OAAO,GAAG,OAAO,IAA8B,EAAE,CAAC;IAElD,uBAAuB,MAAc;QACjC,IAAI,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,qDAAqD;QACrD,IAAI,mBAAmB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QAEnD,IAAI,CAAC;YACD,IAAI,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACnC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpC,IAAI,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;oBAC1C,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjE,CAAC;gBAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC1D,IAAI,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;oBACzD,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9C,CAAC;YACL,CAAC;QACL,CAAE;QAAA,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEf,CAAC;QACD,MAAM,CAAC,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,yDAAyD;AACzD,+BAA+B,IAAY;IACvC,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,KAAK;YAC9C,IAAI,OAAO,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;YAChC,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;gBACrC,qBAAqB,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YAAC,IAAI,CAAC,CAAC;gBACJ,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;QACL,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;AACL,CAAC;AAAA,CAAC;AAEF,mBAAmB,IAAY,EAAE,IAAS,EAAE,IAA4B;IACpE,IAAI,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,CAAE;IAAA,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACT,2GAA2G;QAC3G,IAAI,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACzB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;QACL,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;AACL,CAAC;AAED,wBAAwB;AAExB,iCAAiC,QAAgB,EAAE,GAAwB;IACvE,IAAI,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC9C,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,YAAY,GAAG,sBAAsB,CAAC,YAAY,CAAC,CAAC;IACpD,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;IACnC,MAAM,CAAC;AACX,CAAC;AAMD,cAAc,MAAc,EAAE,GAAwB,EAAE,QAAgB,EAAE,WAAsB;IAAtB,2BAAsB,GAAtB,sBAAsB;IAC5F,IAAI,YAAY,GAAG,MAAM,KAAK,SAAS,GAAG,GAAG,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,GAAG,MAAM,KAAK,SAAS,GAAG,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxH,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7D,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClB,MAAM,CAAC;AACX,CAAC;AAED,yEAAyE;AACzE,qBAAqB,GAAuB,EAAE,GAAwB,EAAE,QAA+B;IACnG,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,EAAE,CAAC,CAAC,OAAO,QAAQ,KAAK,UAAU,CAAC;QAAC,MAAM,CAAC,IAAI,CAAC;IAEhD,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC;QACvB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,IAAY;YACjC,SAAS,IAAI,IAAI,CAAC;YAClB,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;gBACzB,SAAS,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;gBAC5C,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE;YACV,sCAAsC;YACtC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IAEP,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;AACL,CAAC;AAED,IAAK,WAUJ;AAVD,WAAK,WAAW;IACZ,mDAAO,CAAA;IACP,iDAAM,CAAA;IACN,2DAAW,CAAA;IACX,uDAAS,CAAA;IACT,yDAAU,CAAA;IACV,qDAAQ,CAAA;IACR,uDAAS,CAAA;IACT,yDAAU,CAAA;IACV,mDAAO,CAAA;AACX,CAAC,EAVI,WAAW,KAAX,WAAW,QAUf;AAED,6BAA6B,GAAuB,EAAE,QAAgB;IAClE,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;QAC5D,IAAI;YAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,CAAC;QACF,IAAI,SAAS,GAAQ,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC;QACpD,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC;YAAC,MAAM,CAAC,WAAW,CAAC,WAAW,CAAA;QAC3F,8GAA8G;QAC9G,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;YAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAA;QACpF,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5C,IAAI,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YACvD,IAAI,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;gBACrC,KAAK,OAAO;oBACR,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC;gBACjE,KAAK,QAAQ;oBACT,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC;gBACnE,KAAK,QAAQ;oBACT,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC;YACrE,CAAC;QACL,CAAC;QACD,MAAM,CAAC,WAAW,CAAC,OAAO,CAAA;IAC9B,CAAC;AACL,CAAC;AAED,gCAAgC,GAAuB,EAAE,GAAwB,EAAE,SAAsB,EAAE,OAAe;IACtH,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAChB,KAAK,WAAW,CAAC,MAAM;YACnB,IAAI,aAAa,GAAG,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9C,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,OAAO;YACpB,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,EAAE,IAAI;gBACpC,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;gBACnD,IAAI,WAAW,GAAG,QAAQ,CAAC;gBAC3B,EAAE,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC;oBAAC,WAAW,GAAG,iBAAiB,CAAA;gBAClD,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC;oBAAC,WAAW,GAAG,iBAAiB,CAAA;gBACxD,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC;oBAAC,WAAW,GAAG,WAAW,CAAA;gBACnD,GAAG;sBACD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC;sBAC3C,IAAI,CAAC,SAAS,EAAE,GAAG,EAAQ,IAAK,EAAE,WAAW,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;YACH,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,WAAW;YACxB,IAAI,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YACpD,uBAAuB,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAChD,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,SAAS;YACtB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,UAAC,IAAI;gBACvB,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,QAAQ;YACrB,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACtB,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,UAAU;YACvB,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACzB,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,SAAS;YACtB,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACzB,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,UAAU;YACvB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,UAAC,IAAI;gBACvB,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,OAAO,CAAC;QACzB;YACI,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;IACd,CAAC;AACL,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,uDAAuD,GAAG,IAAI,GAAG,yBAAyB,CAAC,CAAC;AAExG,IAAI,CAAC,YAAY,CAAC,UAAU,GAAuB,EAAE,GAAwB;IACzE,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;IACrC,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC5C,IAAI,SAAS,GAAG,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAClD,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAEhB,IAAI,WAAmB,CAAC;AACxB,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,iBAAiB,GAAG,EAAE,CAAC;IAC3B,MAAM,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACpB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO;YACR,iBAAiB,GAAG,6DAA6D,CAAC;YAClF,KAAK,CAAC;QACV,KAAK,QAAQ;YACT,iBAAiB,GAAG,8DAA8D,CAAC;YACnF,KAAK,CAAC;QACV,KAAK,OAAO;YACR,iBAAiB,GAAG,2BAA2B,CAAA;YAC/C,KAAK,CAAC;QACV;YACI,OAAO,CAAC,GAAG,CAAC,sDAAoD,EAAE,CAAC,QAAQ,EAAE,MAAG,CAAC,CAAC;YAClF,KAAK,CAAC;IACd,CAAC;IACD,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACnC,WAAW,GAAG,iBAAiB,CAAC;IACpC,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,WAAW,GAAG,OAAO,CAAC;IAC1B,CAAC;AACL,CAAC;AAAC,IAAI,CAAC,CAAC;IACJ,IAAI,aAAa,GAAG,iDAAiD,CAAC;IACtE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC/B,WAAW,GAAG,aAAa,CAAC;IAChC,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,WAAW,GAAG,OAAO,CAAC;IAC1B,CAAC;AACL,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,WAAW,CAAC,CAAC;AAE7C,IAAI,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;AAC9C,aAAa,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,mBAAmB,GAAG,IAAI,GAAG,4BAA4B,GAAG,WAAW,CAAC,EAAE;IACxG,KAAK,EAAE,SAAS;CACnB,CAAC,CAAC"} \ No newline at end of file From 0dddcf4b84ee3eba1413a1591984605ef4940708 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 14 Apr 2016 16:51:22 -0700 Subject: [PATCH 05/90] code clean up --- src/compiler/parser.ts | 38 ++- src/compiler/types.ts | 2 + src/compiler/utilities.ts | 32 +-- src/harness/harness.ts | 4 +- src/services/services.ts | 9 +- tests/cases/fourslash/commentsVariables.ts | 80 +++--- tests/cases/fourslash/doesnotwork.ts | 45 ---- tests/webTestServer.js | 288 --------------------- tests/webTestServer.js.map | 1 - 9 files changed, 72 insertions(+), 427 deletions(-) delete mode 100644 tests/cases/fourslash/doesnotwork.ts delete mode 100644 tests/webTestServer.js delete mode 100644 tests/webTestServer.js.map diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 058098eff1d..3a1c3c62f41 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -409,7 +409,6 @@ namespace ts { return visitNodes(cbNodes, (node).members); case SyntaxKind.JSDocPropertyTag: return visitNode(cbNode, (node).typeExpression) || - visitNode(cbNode, (node).type) || visitNode(cbNode, (node).name); } } @@ -4063,9 +4062,9 @@ namespace ts { const isAsync = !!(node.flags & NodeFlags.Async); node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : - isGenerator ? doInYieldContext(parseOptionalIdentifier) : - isAsync ? doInAwaitContext(parseOptionalIdentifier) : - parseOptionalIdentifier(); + isGenerator ? doInYieldContext(parseOptionalIdentifier) : + isAsync ? doInAwaitContext(parseOptionalIdentifier) : + parseOptionalIdentifier(); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); @@ -6121,15 +6120,13 @@ namespace ts { typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type; } } - if (!typedefTag.type && typedefTag.jsDocPropertyTags) { - const childrenTagPoses = ts.map(typedefTag.jsDocPropertyTags, tag => tag.pos); - const childrenTagEnds = ts.map(typedefTag.jsDocPropertyTags, tag => tag.end); - const pos = Math.min(...childrenTagPoses); - const end = Math.max(...childrenTagEnds); - const tagType = createNode(SyntaxKind.JSDocTypeLiteral, pos); - tagType.members = >[]; - addRange(tagType.members, typedefTag.jsDocPropertyTags); - typedefTag.type = finishNode(tagType, end); + if (!typedefTag.type && typedefTag.jsDocPropertyTags && typedefTag.jsDocPropertyTags.length > 0) { + const pos = typedefTag.jsDocPropertyTags[0].pos; + const end = typedefTag.jsDocPropertyTags[typedefTag.jsDocPropertyTags.length - 1].end; + const jsdocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, pos); + jsdocTypeLiteral.members = >[]; + addRange(jsdocTypeLiteral.members, typedefTag.jsDocPropertyTags); + typedefTag.type = finishNode(jsdocTypeLiteral, end); } } @@ -6303,23 +6300,18 @@ namespace ts { result.typeExpression = tryParseTypeExpression(); result = finishNode(result); - let typeTagPartOfParentTag = false; if (currentParentJSDocTag && currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) { const parentTag = currentParentJSDocTag; if (!parentTag.typeExpression && !parentTag.jsDocTypeTag) { - typeTagPartOfParentTag = true; parentTag.jsDocTypeTag = result; currentParentJSDocTagEnd = scanner.getStartPos(); + return result; } } - if (!typeTagPartOfParentTag) { - // If this @type tag is not part of the current parent tag, then - // it denotes the end of the current parent tag. - finishCurrentParentTag(); - return result; - } - - return undefined; + // If this @type tag is not part of the current parent tag, then + // it denotes the end of the current parent tag. + finishCurrentParentTag(); + return result; } function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7743f7c35de..91ce1f1022f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1533,6 +1533,8 @@ namespace ts { export interface JSDocPropertyTag extends JSDocTag, TypeElement { name: Identifier; typeExpression: JSDocTypeExpression; + // Add a "type" property here to make the interface compatible + // with the "VariableLikeDeclaration" definition type: TypeNode; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ebf6b42581a..2f29e477d69 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -279,13 +279,16 @@ namespace ts { return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos, /*stopAfterLineBreak*/ false, /*stopAtComments*/ true); } - if (node.jsDocComments && node.jsDocComments.length > 0 && includeJsDocComment) { + if (includeJsDocComment && node.jsDocComments && node.jsDocComments.length > 0) { return getTokenPosOfNode(node.jsDocComments[0]); } - if (node.kind === SyntaxKind.SyntaxList) { - const childrenPoses = ts.map((node)._children, child => getTokenPosOfNode(child, sourceFile, includeJsDocComment)); - return Math.min(...childrenPoses); + // For a syntax list, it is possible that one of its children has JSDocComment nodes, while + // the syntax list itself considers them as normal trivia. Therefore if we simply skip + // trivia for the list, we may have skipped the JSDocComment as well. So we should process its + // first child to determine the actual position of its first token. + if (node.kind === SyntaxKind.SyntaxList && (node)._children.length > 0) { + return getTokenPosOfNode((node)._children[0], sourceFile, includeJsDocComment); } return skipTrivia((sourceFile || getSourceFileOfNode(node)).text, node.pos); @@ -294,27 +297,6 @@ namespace ts { export function isJSDocNode(node: Node) { return node.kind >= SyntaxKind.FirstJSDocNode && node.kind <= SyntaxKind.LastJSDocNode; } - export function isJSDocType(node: Node) { - switch (node.kind) { - case SyntaxKind.JSDocAllType: - case SyntaxKind.JSDocUnknownType: - case SyntaxKind.JSDocArrayType: - case SyntaxKind.JSDocUnionType: - case SyntaxKind.JSDocTupleType: - case SyntaxKind.JSDocNullableType: - case SyntaxKind.JSDocNonNullableType: - case SyntaxKind.JSDocRecordType: - case SyntaxKind.JSDocRecordMember: - case SyntaxKind.JSDocTypeReference: - case SyntaxKind.JSDocOptionalType: - case SyntaxKind.JSDocFunctionType: - case SyntaxKind.JSDocVariadicType: - case SyntaxKind.JSDocConstructorType: - case SyntaxKind.JSDocThisType: - return true; - } - return false; - } export function getNonDecoratorTokenPosOfNode(node: Node, sourceFile?: SourceFile): number { if (nodeIsMissing(node) || !node.decorators) { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 790ecebb75f..e07fe60ba86 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -222,7 +222,9 @@ namespace Utils { return k; } - if (k === (ts).SyntaxKind.FirstJSDocCommentNode || k === (ts).SyntaxKind.LastJSDocCommentNode) { + // For some markers in SyntaxKind, we should print its original syntax name instead of + // the marker name in tests. + if (k === (ts).SyntaxKind.FirstJSDocNode || k === (ts).SyntaxKind.LastJSDocNode) { for (const kindName in (ts).SyntaxKind) { if ((ts).SyntaxKind[kindName] === k) { return kindName; diff --git a/src/services/services.ts b/src/services/services.ts index f2906aaa8b4..ffbb174fe8c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -173,6 +173,7 @@ namespace ts { "type", "typedef", "property", + "prop", "version" ]; let jsDocCompletionEntries: CompletionEntry[]; @@ -274,7 +275,7 @@ namespace ts { scanner.setText((sourceFile || this.getSourceFile()).text); children = []; let pos = this.pos; - const isJSDocTag = + const useJSDocScanner = this.kind === SyntaxKind.JSDocComment || this.kind === SyntaxKind.JSDocParameterTag || this.kind === SyntaxKind.JSDocTag || @@ -286,14 +287,14 @@ namespace ts { this.kind === SyntaxKind.JSDocPropertyTag; const processNode = (node: Node) => { if (pos < node.pos) { - pos = this.addSyntheticNodes(children, pos, node.pos, /*useJSDocScanner*/ isJSDocTag); + pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); } children.push(node); pos = node.end; }; const processNodes = (nodes: NodeArray) => { if (pos < nodes.pos) { - pos = this.addSyntheticNodes(children, pos, nodes.pos, /*useJSDocScanner*/ isJSDocTag); + pos = this.addSyntheticNodes(children, pos, nodes.pos, useJSDocScanner); } children.push(this.createSyntaxList(>nodes)); pos = nodes.end; @@ -7803,7 +7804,7 @@ namespace ts { break; default: forEachChild(node, walk); - if (node.jsDocComments && node.jsDocComments.length > 0) { + if (node.jsDocComments) { for (const jsDocComment of node.jsDocComments) { forEachChild(jsDocComment, walk); } diff --git a/tests/cases/fourslash/commentsVariables.ts b/tests/cases/fourslash/commentsVariables.ts index b2cf6c9cb9b..d62e1e9bee8 100644 --- a/tests/cases/fourslash/commentsVariables.ts +++ b/tests/cases/fourslash/commentsVariables.ts @@ -41,60 +41,60 @@ ////} ////var x = fo/*15*/o2; -// goTo.marker('1'); -// verify.quickInfoIs("var myVariable: number", "This is my variable"); +goTo.marker('1'); +verify.quickInfoIs("var myVariable: number", "This is my variable"); -// goTo.marker('2'); -// verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); +goTo.marker('2'); +verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); -// goTo.marker('3'); -// verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); -// verify.completionListContains("d", "var d: number", "d variable"); +goTo.marker('3'); +verify.completionListContains("myVariable", "var myVariable: number", "This is my variable"); +verify.completionListContains("d", "var d: number", "d variable"); goTo.marker('4'); verify.completionListContains("foo", "function foo(): void", "foos comment"); verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); -// goTo.marker('5'); -// verify.currentSignatureHelpDocCommentIs("foos comment"); -// goTo.marker('5q'); -// verify.quickInfoIs("function foo(): void", "foos comment"); +goTo.marker('5'); +verify.currentSignatureHelpDocCommentIs("foos comment"); +goTo.marker('5q'); +verify.quickInfoIs("function foo(): void", "foos comment"); -// goTo.marker('6'); -// verify.currentSignatureHelpDocCommentIs(""); -// goTo.marker('6q'); -// verify.quickInfoIs("var fooVar: () => void", ""); +goTo.marker('6'); +verify.currentSignatureHelpDocCommentIs(""); +goTo.marker('6q'); +verify.quickInfoIs("var fooVar: () => void", ""); -// goTo.marker('7'); -// verify.completionListContains("foo", "function foo(): void", "foos comment"); -// verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); +goTo.marker('7'); +verify.completionListContains("foo", "function foo(): void", "foos comment"); +verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); -// goTo.marker('8'); -// verify.currentSignatureHelpDocCommentIs("foos comment"); -// goTo.marker('8q'); -// verify.quickInfoIs("function foo(): void", "foos comment"); +goTo.marker('8'); +verify.currentSignatureHelpDocCommentIs("foos comment"); +goTo.marker('8q'); +verify.quickInfoIs("function foo(): void", "foos comment"); -// goTo.marker('9'); -// verify.currentSignatureHelpDocCommentIs(""); -// goTo.marker('9q'); -// verify.quickInfoIs("var fooVar: () => void", ""); -// goTo.marker('9aq'); -// verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); +goTo.marker('9'); +verify.currentSignatureHelpDocCommentIs(""); +goTo.marker('9q'); +verify.quickInfoIs("var fooVar: () => void", ""); +goTo.marker('9aq'); +verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); -// goTo.marker('10'); -// verify.completionListContains("i", "var i: c", "instance comment"); +goTo.marker('10'); +verify.completionListContains("i", "var i: c", "instance comment"); -// goTo.marker('11'); -// verify.completionListContains("i1_i", "var i1_i: i1", "interface instance comments"); +goTo.marker('11'); +verify.completionListContains("i1_i", "var i1_i: i1", "interface instance comments"); -// goTo.marker('12'); -// verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); +goTo.marker('12'); +verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); -// goTo.marker('13'); -// verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); +goTo.marker('13'); +verify.quickInfoIs("var fooVar: () => void", "fooVar comment"); -// goTo.marker('14'); -// verify.quickInfoIs("function foo(): void", "foos comment"); +goTo.marker('14'); +verify.quickInfoIs("function foo(): void", "foos comment"); -// goTo.marker('15'); -// verify.quickInfoIs("function foo2(a: number): void (+1 overload)", ""); +goTo.marker('15'); +verify.quickInfoIs("function foo2(a: number): void (+1 overload)", ""); diff --git a/tests/cases/fourslash/doesnotwork.ts b/tests/cases/fourslash/doesnotwork.ts deleted file mode 100644 index 2f415563c21..00000000000 --- a/tests/cases/fourslash/doesnotwork.ts +++ /dev/null @@ -1,45 +0,0 @@ -/// - -/////** This is my variable*/ -////var myVariable = 10; -//// -/////** d variable*/ -////var d = 10; -////myVariable = d; -//// -/////** foos comment*/ -////function foo() { -////} -/////** fooVar comment*/ -////var fooVar: () => void; -/////*4*/ -////foo(); -////fooVar(); -////fooVar = foo; -//// -////foo(); -////fooVar(); -////var fooVarVar = fooVar; -/////**class comment*/ -////class c { -//// /** constructor comment*/ -//// constructor() { -//// } -////} -/////**instance comment*/ -////var i = new c(); -//// -/////** interface comments*/ -////interface i1 { -////} -/////**interface instance comments*/ -////var i1_i: i1; -//// -////function foo2(a: number): void; -////function foo2(b: string): void; -////function foo2(aOrb) { -////} -////var x = foo2; - -goTo.marker('4'); -verify.completionListContains("fooVar", "var fooVar: () => void", "fooVar comment"); diff --git a/tests/webTestServer.js b/tests/webTestServer.js deleted file mode 100644 index b47da7bfe76..00000000000 --- a/tests/webTestServer.js +++ /dev/null @@ -1,288 +0,0 @@ -/// -"use strict"; -var http = require("http"); -var fs = require("fs"); -var path = require("path"); -var url = require("url"); -var child_process = require("child_process"); -var os = require("os"); -/// Command line processing /// -if (process.argv[2] == '--help') { - console.log('Runs a node server on port 8888 by default, looking for tests folder in the current directory\n'); - console.log('Syntax: node nodeServer.js [port] [typescriptEnlistmentDirectory] [tests] [--browser] [--verbose]\n'); - console.log('Examples: \n\tnode nodeServer.js 8888 .'); - console.log('\tnode nodeServer.js 3000 D:/src/typescript/public --verbose IE'); -} -function switchToForwardSlashes(path) { - return path.replace(/\\/g, "/").replace(/\/\//g, '/'); -} -var defaultPort = 8888; -var port = process.argv[2] || defaultPort; -var rootDir = switchToForwardSlashes(__dirname + '/../'); -var browser; -if (process.argv[3]) { - browser = process.argv[3]; - if (browser !== 'chrome' && browser !== 'IE') { - console.log('Invalid command line arguments. Got ' + browser + ' but expected chrome, IE or nothing.'); - } -} -var grep = process.argv[4]; -var verbose = false; -if (process.argv[5] == '--verbose') { - verbose = true; -} -else if (process.argv[5] && process.argv[5] !== '--verbose') { - console.log('Invalid command line arguments. Got ' + process.argv[5] + ' but expected --verbose or nothing.'); -} -/// Utils /// -function log(msg) { - if (verbose) { - console.log(msg); - } -} -// Copied from the compiler sources -function dir(path, spec, options) { - options = options || {}; - function filesInFolder(folder) { - var folder = switchToForwardSlashes(folder); - var paths = []; - // Everything after the current directory is relative - var baseDirectoryLength = process.cwd().length + 1; - try { - var files = fs.readdirSync(folder); - for (var i = 0; i < files.length; i++) { - var stat = fs.statSync(folder + "/" + files[i]); - if (options.recursive && stat.isDirectory()) { - paths = paths.concat(filesInFolder(folder + "/" + files[i])); - } - else if (stat.isFile() && (!spec || files[i].match(spec))) { - var relativePath = folder.substring(baseDirectoryLength); - paths.push(relativePath + "/" + files[i]); - } - } - } - catch (err) { - } - return paths; - } - return filesInFolder(path); -} -// fs.rmdirSync won't delete directories with files in it -function deleteFolderRecursive(path) { - if (fs.existsSync(path)) { - fs.readdirSync(path).forEach(function (file, index) { - var curPath = path + "/" + file; - if (fs.statSync(curPath).isDirectory()) { - deleteFolderRecursive(curPath); - } - else { - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(path); - } -} -; -function writeFile(path, data, opts) { - try { - fs.writeFileSync(path, data); - } - catch (e) { - // assume file was written to a directory that exists, if not, start recursively creating them as necessary - var parts = switchToForwardSlashes(path).split('/'); - for (var i = 0; i < parts.length; i++) { - var subDir = parts.slice(0, i).join('/'); - if (!fs.existsSync(subDir)) { - fs.mkdir(subDir); - } - } - fs.writeFileSync(path, data); - } -} -/// Request Handling /// -function handleResolutionRequest(filePath, res) { - var resolvedPath = path.resolve(filePath, ''); - resolvedPath = resolvedPath.substring(resolvedPath.indexOf('tests')); - resolvedPath = switchToForwardSlashes(resolvedPath); - send('success', res, resolvedPath); - return; -} -function send(result, res, contents, contentType) { - if (contentType === void 0) { contentType = "binary"; } - var responseCode = result === "success" ? 200 : result === "fail" ? 500 : result === 'unknown' ? 404 : parseInt(result); - res.writeHead(responseCode, { "Content-Type": contentType }); - res.end(contents); - return; -} -// Reads the data from a post request and passes it to the given callback -function processPost(req, res, callback) { - var queryData = ""; - if (typeof callback !== 'function') - return null; - if (req.method == 'POST') { - req.on('data', function (data) { - queryData += data; - if (queryData.length > 1e8) { - queryData = ""; - send("413", res, null); - console.log("ERROR: destroying connection"); - req.connection.destroy(); - } - }); - req.on('end', function () { - //res.post = url.parse(req.url).query; - callback(queryData); - }); - } - else { - send("405", res, null); - } -} -var RequestType; -(function (RequestType) { - RequestType[RequestType["GetFile"] = 0] = "GetFile"; - RequestType[RequestType["GetDir"] = 1] = "GetDir"; - RequestType[RequestType["ResolveFile"] = 2] = "ResolveFile"; - RequestType[RequestType["WriteFile"] = 3] = "WriteFile"; - RequestType[RequestType["DeleteFile"] = 4] = "DeleteFile"; - RequestType[RequestType["WriteDir"] = 5] = "WriteDir"; - RequestType[RequestType["DeleteDir"] = 6] = "DeleteDir"; - RequestType[RequestType["AppendFile"] = 7] = "AppendFile"; - RequestType[RequestType["Unknown"] = 8] = "Unknown"; -})(RequestType || (RequestType = {})); -function getRequestOperation(req, filename) { - if (req.method === 'GET' && req.url.indexOf('?') === -1) { - if (req.url.indexOf('.') !== -1) - return RequestType.GetFile; - else - return RequestType.GetDir; - } - else { - var queryData = url.parse(req.url, true).query; - if (req.method === 'GET' && queryData.resolve !== undefined) - return RequestType.ResolveFile; - // mocha uses ?grep= query string as equivalent to the --grep command line option used to filter tests - if (req.method === 'GET' && queryData.grep !== undefined) - return RequestType.GetFile; - if (req.method === 'POST' && queryData.action) { - var path = req.url.substr(0, req.url.lastIndexOf('?')); - var isFile = path.substring(path.lastIndexOf('/')).indexOf('.') !== -1; - switch (queryData.action.toUpperCase()) { - case 'WRITE': - return isFile ? RequestType.WriteFile : RequestType.WriteDir; - case 'DELETE': - return isFile ? RequestType.DeleteFile : RequestType.DeleteDir; - case 'APPEND': - return isFile ? RequestType.AppendFile : RequestType.Unknown; - } - } - return RequestType.Unknown; - } -} -function handleRequestOperation(req, res, operation, reqPath) { - switch (operation) { - case RequestType.GetDir: - var filesInFolder = dir(reqPath, "", { recursive: true }); - send('success', res, filesInFolder.join(',')); - break; - case RequestType.GetFile: - fs.readFile(reqPath, function (err, file) { - var ext = reqPath.substr(reqPath.lastIndexOf('.')); - var contentType = 'binary'; - if (ext === '.js') - contentType = 'text/javascript'; - else if (ext === '.css') - contentType = 'text/javascript'; - else if (ext === '.html') - contentType = 'text/html'; - err - ? send('fail', res, err.message, contentType) - : send('success', res, file, contentType); - }); - break; - case RequestType.ResolveFile: - var resolveRequest = req.url.match(/(.*)\?resolve/); - handleResolutionRequest(resolveRequest[1], res); - break; - case RequestType.WriteFile: - processPost(req, res, function (data) { - writeFile(reqPath, data, { recursive: true }); - }); - send('success', res, null); - break; - case RequestType.WriteDir: - fs.mkdirSync(reqPath); - send('success', res, null); - break; - case RequestType.DeleteFile: - if (fs.existsSync(reqPath)) { - fs.unlinkSync(reqPath); - } - send('success', res, null); - break; - case RequestType.DeleteDir: - if (fs.existsSync(reqPath)) { - fs.rmdirSync(reqPath); - } - send('success', res, null); - break; - case RequestType.AppendFile: - processPost(req, res, function (data) { - fs.appendFileSync(reqPath, data); - }); - send('success', res, null); - break; - case RequestType.Unknown: - default: - send('unknown', res, null); - break; - } -} -console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); -http.createServer(function (req, res) { - log(req.method + ' ' + req.url); - var uri = url.parse(req.url).pathname; - var reqPath = path.join(process.cwd(), uri); - var operation = getRequestOperation(req, reqPath); - handleRequestOperation(req, res, operation, reqPath); -}).listen(8888); -var browserPath; -if ((browser && browser === 'chrome')) { - var defaultChromePath = ""; - switch (os.platform()) { - case "win32": - case "win64": - defaultChromePath = "C:/Program Files (x86)/Google/Chrome/Application/chrome.exe"; - break; - case "darwin": - defaultChromePath = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; - break; - case "linux": - defaultChromePath = "/opt/google/chrome/chrome"; - break; - default: - console.log("default Chrome location is unknown for platform '" + os.platform() + "'"); - break; - } - if (fs.existsSync(defaultChromePath)) { - browserPath = defaultChromePath; - } - else { - browserPath = browser; - } -} -else { - var defaultIEPath = 'C:/Program Files/Internet Explorer/iexplore.exe'; - if (fs.existsSync(defaultIEPath)) { - browserPath = defaultIEPath; - } - else { - browserPath = browser; - } -} -console.log('Using browser: ' + browserPath); -var queryString = grep ? "?grep=" + grep : ''; -child_process.spawn(browserPath, ['http://localhost:' + port + '/tests/webTestResults.html' + queryString], { - stdio: 'inherit' -}); -//# sourceMappingURL=file:///C:/Users/lizhe/Documents/github/TypeScript/tests/webTestServer.js.map \ No newline at end of file diff --git a/tests/webTestServer.js.map b/tests/webTestServer.js.map deleted file mode 100644 index 1c924a872f4..00000000000 --- a/tests/webTestServer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"webTestServer.js","sourceRoot":"","sources":["file:///C:/Users/lizhe/Documents/github/TypeScript/tests/webTestServer.ts"],"names":[],"mappings":"AAAA,yDAAyD;;AAEzD,IAAO,IAAI,WAAW,MAAM,CAAC,CAAC;AAC9B,IAAO,EAAE,WAAW,IAAI,CAAC,CAAC;AAC1B,IAAO,IAAI,WAAW,MAAM,CAAC,CAAC;AAC9B,IAAO,GAAG,WAAW,KAAK,CAAC,CAAC;AAC5B,IAAO,aAAa,WAAW,eAAe,CAAC,CAAC;AAChD,IAAO,EAAE,WAAW,IAAI,CAAC,CAAC;AAE1B,+BAA+B;AAE/B,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC;IAC9B,OAAO,CAAC,GAAG,CAAC,iGAAiG,CAAC,CAAC;IAC/G,OAAO,CAAC,GAAG,CAAC,qGAAqG,CAAC,CAAC;IACnH,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAC;AACnF,CAAC;AAED,gCAAgC,IAAY;IACxC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AAC1D,CAAC;AAED,IAAI,WAAW,GAAG,IAAI,CAAC;AACvB,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC;AAC1C,IAAI,OAAO,GAAG,sBAAsB,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;AAEzD,IAAI,OAAe,CAAC;AACpB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAClB,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,EAAE,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAG,OAAO,GAAG,sCAAsC,CAAC,CAAC;IAC3G,CAAC;AACL,CAAC;AAED,IAAI,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE3B,IAAI,OAAO,GAAG,KAAK,CAAC;AACpB,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC,CAAC,CAAC;IACjC,OAAO,GAAG,IAAI,CAAC;AACnB,CAAC;AAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC;IAC5D,OAAO,CAAC,GAAG,CAAC,sCAAsC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,qCAAqC,CAAC,CAAC;AAClH,CAAC;AAED,aAAa;AACb,aAAa,GAAW;IACpB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;AACL,CAAC;AAED,mCAAmC;AACnC,aAAa,IAAY,EAAE,IAAa,EAAE,OAAa;IACnD,OAAO,GAAG,OAAO,IAA8B,EAAE,CAAC;IAElD,uBAAuB,MAAc;QACjC,IAAI,MAAM,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,qDAAqD;QACrD,IAAI,mBAAmB,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;QAEnD,IAAI,CAAC;YACD,IAAI,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YACnC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACpC,IAAI,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAChD,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;oBAC1C,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjE,CAAC;gBAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;oBAC1D,IAAI,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;oBACzD,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC9C,CAAC;YACL,CAAC;QACL,CAAE;QAAA,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAEf,CAAC;QACD,MAAM,CAAC,KAAK,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,yDAAyD;AACzD,+BAA+B,IAAY;IACvC,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,UAAU,IAAI,EAAE,KAAK;YAC9C,IAAI,OAAO,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,CAAC;YAChC,EAAE,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;gBACrC,qBAAqB,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YAAC,IAAI,CAAC,CAAC;gBACJ,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;QACL,CAAC,CAAC,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;AACL,CAAC;AAAA,CAAC;AAEF,mBAAmB,IAAY,EAAE,IAAS,EAAE,IAA4B;IACpE,IAAI,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,CAAE;IAAA,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACT,2GAA2G;QAC3G,IAAI,KAAK,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpD,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACzC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACzB,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;QACL,CAAC;QACD,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;AACL,CAAC;AAED,wBAAwB;AAExB,iCAAiC,QAAgB,EAAE,GAAwB;IACvE,IAAI,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC9C,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,YAAY,GAAG,sBAAsB,CAAC,YAAY,CAAC,CAAC;IACpD,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC;IACnC,MAAM,CAAC;AACX,CAAC;AAMD,cAAc,MAAc,EAAE,GAAwB,EAAE,QAAgB,EAAE,WAAsB;IAAtB,2BAAsB,GAAtB,sBAAsB;IAC5F,IAAI,YAAY,GAAG,MAAM,KAAK,SAAS,GAAG,GAAG,GAAG,MAAM,KAAK,MAAM,GAAG,GAAG,GAAG,MAAM,KAAK,SAAS,GAAG,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxH,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7D,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClB,MAAM,CAAC;AACX,CAAC;AAED,yEAAyE;AACzE,qBAAqB,GAAuB,EAAE,GAAwB,EAAE,QAA+B;IACnG,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,EAAE,CAAC,CAAC,OAAO,QAAQ,KAAK,UAAU,CAAC;QAAC,MAAM,CAAC,IAAI,CAAC;IAEhD,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC;QACvB,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,IAAY;YACjC,SAAS,IAAI,IAAI,CAAC;YAClB,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;gBACzB,SAAS,GAAG,EAAE,CAAC;gBACf,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;gBACvB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;gBAC5C,GAAG,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;YAC7B,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE;YACV,sCAAsC;YACtC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IAEP,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;AACL,CAAC;AAED,IAAK,WAUJ;AAVD,WAAK,WAAW;IACZ,mDAAO,CAAA;IACP,iDAAM,CAAA;IACN,2DAAW,CAAA;IACX,uDAAS,CAAA;IACT,yDAAU,CAAA;IACV,qDAAQ,CAAA;IACR,uDAAS,CAAA;IACT,yDAAU,CAAA;IACV,mDAAO,CAAA;AACX,CAAC,EAVI,WAAW,KAAX,WAAW,QAUf;AAED,6BAA6B,GAAuB,EAAE,QAAgB;IAClE,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC;QAC5D,IAAI;YAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,CAAC;QACF,IAAI,SAAS,GAAQ,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC;QACpD,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC;YAAC,MAAM,CAAC,WAAW,CAAC,WAAW,CAAA;QAC3F,8GAA8G;QAC9G,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;YAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAA;QACpF,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,MAAM,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;YAC5C,IAAI,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YACvD,IAAI,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;gBACrC,KAAK,OAAO;oBACR,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,SAAS,GAAG,WAAW,CAAC,QAAQ,CAAC;gBACjE,KAAK,QAAQ;oBACT,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,UAAU,GAAG,WAAW,CAAC,SAAS,CAAC;gBACnE,KAAK,QAAQ;oBACT,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC;YACrE,CAAC;QACL,CAAC;QACD,MAAM,CAAC,WAAW,CAAC,OAAO,CAAA;IAC9B,CAAC;AACL,CAAC;AAED,gCAAgC,GAAuB,EAAE,GAAwB,EAAE,SAAsB,EAAE,OAAe;IACtH,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;QAChB,KAAK,WAAW,CAAC,MAAM;YACnB,IAAI,aAAa,GAAG,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC1D,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9C,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,OAAO;YACpB,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,EAAE,IAAI;gBACpC,IAAI,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;gBACnD,IAAI,WAAW,GAAG,QAAQ,CAAC;gBAC3B,EAAE,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC;oBAAC,WAAW,GAAG,iBAAiB,CAAA;gBAClD,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,MAAM,CAAC;oBAAC,WAAW,GAAG,iBAAiB,CAAA;gBACxD,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,OAAO,CAAC;oBAAC,WAAW,GAAG,WAAW,CAAA;gBACnD,GAAG;sBACD,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC;sBAC3C,IAAI,CAAC,SAAS,EAAE,GAAG,EAAQ,IAAK,EAAE,WAAW,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;YACH,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,WAAW;YACxB,IAAI,cAAc,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;YACpD,uBAAuB,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAChD,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,SAAS;YACtB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,UAAC,IAAI;gBACvB,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClD,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,QAAQ;YACrB,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YACtB,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,UAAU;YACvB,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACzB,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC3B,CAAC;YACD,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,SAAS;YACtB,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;gBACzB,EAAE,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC1B,CAAC;YACD,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,UAAU;YACvB,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,UAAC,IAAI;gBACvB,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;QACV,KAAK,WAAW,CAAC,OAAO,CAAC;QACzB;YACI,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;YAC3B,KAAK,CAAC;IACd,CAAC;AACL,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,uDAAuD,GAAG,IAAI,GAAG,yBAAyB,CAAC,CAAC;AAExG,IAAI,CAAC,YAAY,CAAC,UAAU,GAAuB,EAAE,GAAwB;IACzE,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAA;IACrC,IAAI,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,CAAC,CAAC;IAC5C,IAAI,SAAS,GAAG,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAClD,sBAAsB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;AACzD,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAEhB,IAAI,WAAmB,CAAC;AACxB,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,iBAAiB,GAAG,EAAE,CAAC;IAC3B,MAAM,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QACpB,KAAK,OAAO,CAAC;QACb,KAAK,OAAO;YACR,iBAAiB,GAAG,6DAA6D,CAAC;YAClF,KAAK,CAAC;QACV,KAAK,QAAQ;YACT,iBAAiB,GAAG,8DAA8D,CAAC;YACnF,KAAK,CAAC;QACV,KAAK,OAAO;YACR,iBAAiB,GAAG,2BAA2B,CAAA;YAC/C,KAAK,CAAC;QACV;YACI,OAAO,CAAC,GAAG,CAAC,sDAAoD,EAAE,CAAC,QAAQ,EAAE,MAAG,CAAC,CAAC;YAClF,KAAK,CAAC;IACd,CAAC;IACD,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;QACnC,WAAW,GAAG,iBAAiB,CAAC;IACpC,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,WAAW,GAAG,OAAO,CAAC;IAC1B,CAAC;AACL,CAAC;AAAC,IAAI,CAAC,CAAC;IACJ,IAAI,aAAa,GAAG,iDAAiD,CAAC;IACtE,EAAE,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC/B,WAAW,GAAG,aAAa,CAAC;IAChC,CAAC;IAAC,IAAI,CAAC,CAAC;QACJ,WAAW,GAAG,OAAO,CAAC;IAC1B,CAAC;AACL,CAAC;AAED,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,WAAW,CAAC,CAAC;AAE7C,IAAI,WAAW,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;AAC9C,aAAa,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,mBAAmB,GAAG,IAAI,GAAG,4BAA4B,GAAG,WAAW,CAAC,EAAE;IACxG,KAAK,EAAE,SAAS;CACnB,CAAC,CAAC"} \ No newline at end of file From 15cbb0216202eec3e9fa5f04c124607d4bf8c020 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 22 Apr 2016 14:26:30 -0700 Subject: [PATCH 06/90] refactor --- src/compiler/types.ts | 2 +- src/services/utilities.ts | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 91ce1f1022f..f40237ba47e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -450,7 +450,7 @@ namespace ts { modifiers?: ModifiersArray; // Array of modifiers /* @internal */ id?: number; // Unique id (used to look up NodeLinks) parent?: Node; // Parent node (initialized by binding - /* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files. + /* @internal */ jsDocComments?: JSDocComment[]; // JSDoc for the node, if it has any. Only for .js files. /* @internal */ symbol?: Symbol; // Symbol declared by node (initialized by binding) /* @internal */ locals?: SymbolTable; // Locals associated with node (initialized by binding) /* @internal */ nextContainer?: Node; // Next container in declaration order (initialized by binding) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 73ac43911b5..0230195b0cc 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -267,7 +267,7 @@ namespace ts { if (includeJsDocComment) { const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode); for (const jsDocChild of jsDocChildren) { - let start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment); + const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment); if (start <= position) { let end = jsDocChild.getEnd(); if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) { @@ -500,9 +500,8 @@ namespace ts { } if (node) { - let jsDocComments = node.jsDocComments; - if (jsDocComments) { - for (const jsDocComment of jsDocComments) { + if (node.jsDocComments) { + for (const jsDocComment of node.jsDocComments) { for (const tag of jsDocComment.tags) { if (tag.pos <= position && position <= tag.end) { return tag; From d568d79b495d256e93907b28c3aeec912b41594f Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 22 Apr 2016 15:19:57 -0700 Subject: [PATCH 07/90] resolve build error --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b9662126e65..612602f5658 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -6322,7 +6322,7 @@ namespace ts { const typeExpression = tryParseTypeExpression(); skipWhitespace(); - const name = parseJSDocIdentifier(); + const name = parseJSDocIdentifierName(); if (!name) { parseErrorAtPosition(scanner.getStartPos(), /*length*/ 0, Diagnostics.Identifier_expected); return undefined; @@ -6344,7 +6344,7 @@ namespace ts { function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { const typeExpression = tryParseTypeExpression(); skipWhitespace(); - let name = parseJSDocIdentifier(); + let name = parseJSDocIdentifierName(); if (!name) { let foundNameFromParentNode = false; if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { From 79bb4ba5b42d58c2f8ff8955f26f58a9ef24cddb Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Mon, 25 Apr 2016 11:56:16 -0700 Subject: [PATCH 08/90] Fix broken test --- src/services/utilities.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index bf90a023c14..12baca1af54 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -445,6 +445,10 @@ namespace ts { return false; } + if (token.kind === SyntaxKind.JsxText) { + return true; + } + //
Hello |
if (token.kind === SyntaxKind.LessThanToken && token.parent.kind === SyntaxKind.JsxText) { return true; @@ -455,7 +459,7 @@ namespace ts { return true; } - //
{ + //
{ // | // } < /div> if (token && token.kind === SyntaxKind.CloseBraceToken && token.parent.kind === SyntaxKind.JsxExpression) { From 20d8a94d66ce467053bce183b0a1e0538a1ea96d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 25 Feb 2016 03:32:15 -0500 Subject: [PATCH 09/90] Remove check narrowing only certain types, add test showing issues with this --- .../typeGuardNarrowsPrimitiveIntersection.js | 38 ++++++++++ ...eGuardNarrowsPrimitiveIntersection.symbols | 70 +++++++++++++++++ ...ypeGuardNarrowsPrimitiveIntersection.types | 76 +++++++++++++++++++ .../typeGuardNarrowsPrimitiveIntersection.ts | 21 +++++ 4 files changed, 205 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js new file mode 100644 index 00000000000..a4cba6f4ab5 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js @@ -0,0 +1,38 @@ +//// [typeGuardNarrowsPrimitiveIntersection.ts] +type Tag = {__tag: any}; +declare function isNonBlank(value: string) : value is (string & Tag); +declare function doThis(value: string & Tag): void; +declare function doThat(value: string) : void; +let value: string; +if (isNonBlank(value)) { + doThis(value); +} else { + doThat(value); +} + + +const enum Tag2 {} +declare function isNonBlank2(value: string) : value is (string & Tag2); +declare function doThis2(value: string & Tag2): void; +declare function doThat2(value: string) : void; +if (isNonBlank2(value)) { + doThis2(value); +} else { + doThat2(value); +} + + +//// [typeGuardNarrowsPrimitiveIntersection.js] +var value; +if (isNonBlank(value)) { + doThis(value); +} +else { + doThat(value); +} +if (isNonBlank2(value)) { + doThis2(value); +} +else { + doThat2(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols new file mode 100644 index 00000000000..da507fb94c5 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols @@ -0,0 +1,70 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === +type Tag = {__tag: any}; +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) +>__tag : Symbol(__tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 12)) + +declare function isNonBlank(value: string) : value is (string & Tag); +>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28)) +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) + +declare function doThis(value: string & Tag): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 24)) +>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 3, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +if (isNonBlank(value)) { +>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) +} + + +const enum Tag2 {} +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function isNonBlank2(value: string) : value is (string & Tag2); +>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29)) +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function doThis2(value: string & Tag2): void; +>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 25)) +>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1)) + +declare function doThat2(value: string) : void; +>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 15, 25)) + +if (isNonBlank2(value)) { +>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + + doThis2(value); +>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) + +} else { + doThat2(value); +>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53)) +>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3)) +} + diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types new file mode 100644 index 00000000000..478363669b4 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types @@ -0,0 +1,76 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts === +type Tag = {__tag: any}; +>Tag : { __tag: any; } +>__tag : any + +declare function isNonBlank(value: string) : value is (string & Tag); +>isNonBlank : (value: string) => value is string & { __tag: any; } +>value : string +>value : any +>Tag : { __tag: any; } + +declare function doThis(value: string & Tag): void; +>doThis : (value: string & { __tag: any; }) => void +>value : string & { __tag: any; } +>Tag : { __tag: any; } + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isNonBlank(value)) { +>isNonBlank(value) : boolean +>isNonBlank : (value: string) => value is string & { __tag: any; } +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: string & { __tag: any; }) => void +>value : string & { __tag: any; } + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + +const enum Tag2 {} +>Tag2 : Tag2 + +declare function isNonBlank2(value: string) : value is (string & Tag2); +>isNonBlank2 : (value: string) => value is string & Tag2 +>value : string +>value : any +>Tag2 : Tag2 + +declare function doThis2(value: string & Tag2): void; +>doThis2 : (value: string & Tag2) => void +>value : string & Tag2 +>Tag2 : Tag2 + +declare function doThat2(value: string) : void; +>doThat2 : (value: string) => void +>value : string + +if (isNonBlank2(value)) { +>isNonBlank2(value) : boolean +>isNonBlank2 : (value: string) => value is string & Tag2 +>value : string + + doThis2(value); +>doThis2(value) : void +>doThis2 : (value: string & Tag2) => void +>value : string & Tag2 + +} else { + doThat2(value); +>doThat2(value) : void +>doThat2 : (value: string) => void +>value : string +} + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts new file mode 100644 index 00000000000..376a78275c8 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts @@ -0,0 +1,21 @@ +type Tag = {__tag: any}; +declare function isNonBlank(value: string) : value is (string & Tag); +declare function doThis(value: string & Tag): void; +declare function doThat(value: string) : void; +let value: string; +if (isNonBlank(value)) { + doThis(value); +} else { + doThat(value); +} + + +const enum Tag2 {} +declare function isNonBlank2(value: string) : value is (string & Tag2); +declare function doThis2(value: string & Tag2): void; +declare function doThat2(value: string) : void; +if (isNonBlank2(value)) { + doThis2(value); +} else { + doThat2(value); +} From a2feb0ee7dc07aad3ef9d05e2ea151c547fd460d Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 11 Mar 2016 02:24:26 -0500 Subject: [PATCH 10/90] string literal case test --- .../typeGuardNarrowsToLiteralType.js | 21 +++++++++++ .../typeGuardNarrowsToLiteralType.symbols | 32 +++++++++++++++++ .../typeGuardNarrowsToLiteralType.types | 35 +++++++++++++++++++ .../typeGuardNarrowsToLiteralType.ts | 10 ++++++ 4 files changed, 98 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.js create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js new file mode 100644 index 00000000000..fdd2ee3fb7f --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js @@ -0,0 +1,21 @@ +//// [typeGuardNarrowsToLiteralType.ts] +declare function isFoo(value: string) : value is "foo"; +declare function doThis(value: "foo"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + + + +//// [typeGuardNarrowsToLiteralType.js] +var value; +if (isFoo(value)) { + doThis(value); +} +else { + doThat(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols new file mode 100644 index 00000000000..0d0ddc5f00d --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts === +declare function isFoo(value: string) : value is "foo"; +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23)) + +declare function doThis(value: "foo"): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 1, 24)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 2, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + +if (isFoo(value)) { +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3)) +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types new file mode 100644 index 00000000000..9835206deb9 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types @@ -0,0 +1,35 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts === +declare function isFoo(value: string) : value is "foo"; +>isFoo : (value: string) => value is "foo" +>value : string +>value : any + +declare function doThis(value: "foo"): void; +>doThis : (value: "foo") => void +>value : "foo" + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isFoo(value)) { +>isFoo(value) : boolean +>isFoo : (value: string) => value is "foo" +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: "foo") => void +>value : "foo" + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts new file mode 100644 index 00000000000..3b7d5bba21a --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts @@ -0,0 +1,10 @@ +declare function isFoo(value: string) : value is "foo"; +declare function doThis(value: "foo"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + From 92bf91dd581b98e9c9fd0c8d69dae6132af762ef Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:14:00 -0400 Subject: [PATCH 11/90] Reconcile fix with CFA work --- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6a42dc43d51..ca0d61c8106 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7555,7 +7555,7 @@ namespace ts { } function getNarrowedTypeOfReference(type: Type, reference: Node) { - if (!(type.flags & TypeFlags.Narrowable) || !isNarrowableReference(reference)) { + if (type.flags & TypeFlags.Defaultable || !isNarrowableReference(reference)) { return type; } const leftmostNode = getLeftmostIdentifierOrThis(reference); @@ -7975,7 +7975,7 @@ namespace ts { const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - if (defaultsToDeclaredType && !(type.flags & TypeFlags.Narrowable)) { + if (defaultsToDeclaredType && type.flags & TypeFlags.Defaultable) { return type; } const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d0d411a710e..5f181bc982e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2175,7 +2175,13 @@ namespace ts { ObjectType = Class | Interface | Reference | Tuple | Anonymous, UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, - Narrowable = Any | ObjectType | Union | TypeParameter, + + // 'Defaultable' types are types where narrowing reverts to the original type, rather than actually narrow. + // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep + // Void as the only defaultable type, since it's a non-value type construct (representing a lack of a value) + // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing + // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules) + Defaultable = Void, /* @internal */ RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, /* @internal */ From 73d4aaef46eea2323587edb650678c2fe70aa170 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:17:40 -0400 Subject: [PATCH 12/90] Defaultable -> NotNarrowable to align with use --- src/compiler/checker.ts | 4 ++-- src/compiler/types.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca0d61c8106..2e2f59f6c15 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7555,7 +7555,7 @@ namespace ts { } function getNarrowedTypeOfReference(type: Type, reference: Node) { - if (type.flags & TypeFlags.Defaultable || !isNarrowableReference(reference)) { + if (type.flags & TypeFlags.NotNarrowable || !isNarrowableReference(reference)) { return type; } const leftmostNode = getLeftmostIdentifierOrThis(reference); @@ -7975,7 +7975,7 @@ namespace ts { const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - if (defaultsToDeclaredType && type.flags & TypeFlags.Defaultable) { + if (defaultsToDeclaredType && type.flags & TypeFlags.NotNarrowable) { return type; } const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5f181bc982e..47f9d1b752e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2176,12 +2176,12 @@ namespace ts { UnionOrIntersection = Union | Intersection, StructuredType = ObjectType | Union | Intersection, - // 'Defaultable' types are types where narrowing reverts to the original type, rather than actually narrow. + // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow. // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep // Void as the only defaultable type, since it's a non-value type construct (representing a lack of a value) // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules) - Defaultable = Void, + NotNarrowable = Void, /* @internal */ RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, /* @internal */ From 70733da2f2013ef786486d64a6dce7efae504298 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:23:54 -0400 Subject: [PATCH 13/90] Missed a defaultable in comments --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 47f9d1b752e..044cf3f4e8b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2178,7 +2178,7 @@ namespace ts { // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow. // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep - // Void as the only defaultable type, since it's a non-value type construct (representing a lack of a value) + // Void as the only non-narrowable type, since it's a non-value type construct (representing a lack of a value) // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules) NotNarrowable = Void, From a80529b9899ebe105f6e72a176f4bdf6c5eb1586 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 26 Apr 2016 04:37:00 -0400 Subject: [PATCH 14/90] Add test for narrowing to unions of string literals --- .../typeGuardNarrowsToLiteralTypeUnion.js | 21 +++++++++++ ...typeGuardNarrowsToLiteralTypeUnion.symbols | 32 +++++++++++++++++ .../typeGuardNarrowsToLiteralTypeUnion.types | 35 +++++++++++++++++++ .../typeGuardNarrowsToLiteralTypeUnion.ts | 10 ++++++ 4 files changed, 98 insertions(+) create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js new file mode 100644 index 00000000000..715b362c8e0 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js @@ -0,0 +1,21 @@ +//// [typeGuardNarrowsToLiteralTypeUnion.ts] +declare function isFoo(value: string) : value is ("foo" | "bar"); +declare function doThis(value: "foo" | "bar"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + + + +//// [typeGuardNarrowsToLiteralTypeUnion.js] +var value; +if (isFoo(value)) { + doThis(value); +} +else { + doThat(value); +} diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols new file mode 100644 index 00000000000..356fa06a06c --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts === +declare function isFoo(value: string) : value is ("foo" | "bar"); +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23)) + +declare function doThis(value: "foo" | "bar"): void; +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 24)) + +declare function doThat(value: string) : void; +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 2, 24)) + +let value: string; +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + +if (isFoo(value)) { +>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + + doThis(value); +>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) + +} else { + doThat(value); +>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52)) +>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3)) +} + + diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types new file mode 100644 index 00000000000..321a8861d55 --- /dev/null +++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types @@ -0,0 +1,35 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts === +declare function isFoo(value: string) : value is ("foo" | "bar"); +>isFoo : (value: string) => value is "foo" | "bar" +>value : string +>value : any + +declare function doThis(value: "foo" | "bar"): void; +>doThis : (value: "foo" | "bar") => void +>value : "foo" | "bar" + +declare function doThat(value: string) : void; +>doThat : (value: string) => void +>value : string + +let value: string; +>value : string + +if (isFoo(value)) { +>isFoo(value) : boolean +>isFoo : (value: string) => value is "foo" | "bar" +>value : string + + doThis(value); +>doThis(value) : void +>doThis : (value: "foo" | "bar") => void +>value : "foo" | "bar" + +} else { + doThat(value); +>doThat(value) : void +>doThat : (value: string) => void +>value : string +} + + diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts new file mode 100644 index 00000000000..8f4726160f4 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts @@ -0,0 +1,10 @@ +declare function isFoo(value: string) : value is ("foo" | "bar"); +declare function doThis(value: "foo" | "bar"): void; +declare function doThat(value: string) : void; +let value: string; +if (isFoo(value)) { + doThis(value); +} else { + doThat(value); +} + From 81ee9687e76c3624ac683d3150f18a5258964683 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Sun, 22 May 2016 21:51:21 +0700 Subject: [PATCH 15/90] Add failing test for #8738 --- .../es6ExportClauseWithAssignmentInEs5.js | 20 ++++++++++++++ ...es6ExportClauseWithAssignmentInEs5.symbols | 20 ++++++++++++++ .../es6ExportClauseWithAssignmentInEs5.types | 26 +++++++++++++++++++ .../es6ExportClauseWithAssignmentInEs5.ts | 11 ++++++++ 4 files changed, 77 insertions(+) create mode 100644 tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js create mode 100644 tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols create mode 100644 tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types create mode 100644 tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js new file mode 100644 index 00000000000..4eeabebd0a9 --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -0,0 +1,20 @@ +//// [server.ts] + +var foo = 2; +foo = 3; + +var baz = 3; +baz = 4; + +export { foo, baz, baz as quux }; + + +//// [server.js] +"use strict"; +var foo = 2; +exports.foo = foo; +exports.foo = foo = 3; +var baz = 3; +exports.baz = baz; +exports.quux = baz; +exports.quux = exports.baz = baz = 4; diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols new file mode 100644 index 00000000000..64a41c93cc6 --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/server.ts === + +var foo = 2; +>foo : Symbol(foo, Decl(server.ts, 1, 3)) + +foo = 3; +>foo : Symbol(foo, Decl(server.ts, 1, 3)) + +var baz = 3; +>baz : Symbol(baz, Decl(server.ts, 4, 3)) + +baz = 4; +>baz : Symbol(baz, Decl(server.ts, 4, 3)) + +export { foo, baz, baz as quux }; +>foo : Symbol(foo, Decl(server.ts, 7, 8)) +>baz : Symbol(baz, Decl(server.ts, 7, 13)) +>baz : Symbol(quux, Decl(server.ts, 7, 18)) +>quux : Symbol(quux, Decl(server.ts, 7, 18)) + diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types new file mode 100644 index 00000000000..28ce1a1562e --- /dev/null +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -0,0 +1,26 @@ +=== tests/cases/compiler/server.ts === + +var foo = 2; +>foo : number +>2 : number + +foo = 3; +>foo = 3 : number +>foo : number +>3 : number + +var baz = 3; +>baz : number +>3 : number + +baz = 4; +>baz = 4 : number +>baz : number +>4 : number + +export { foo, baz, baz as quux }; +>foo : number +>baz : number +>baz : number +>quux : number + diff --git a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts new file mode 100644 index 00000000000..b79e34e3de1 --- /dev/null +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -0,0 +1,11 @@ +// @target: es5 +// @module: commonjs + +// @filename: server.ts +var foo = 2; +foo = 3; + +var baz = 3; +baz = 4; + +export { foo, baz, baz as quux }; From 63291d1de0a22de6160cb650d6d25d74304deab1 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Sun, 22 May 2016 22:23:41 +0700 Subject: [PATCH 16/90] Sort baseline reference identifier by name --- tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 4eeabebd0a9..636b4ff0909 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -17,4 +17,4 @@ exports.foo = foo = 3; var baz = 3; exports.baz = baz; exports.quux = baz; -exports.quux = exports.baz = baz = 4; +exports.baz = exports.quux = baz = 4; From 9a4b6ab6262c2fd3c0c9d0765861bb7c8a828dde Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Sun, 22 May 2016 22:24:31 +0700 Subject: [PATCH 17/90] Detects assignment to internal module export clause, fixes #8738 --- src/compiler/emitter.ts | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f27557a9136..2ff6ca97bfc 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2613,6 +2613,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true); } + function isNameOfExportedSourceLevelDeclarationInClauseModule(node: Node): boolean { + if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) { + return false; + } + + return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (node).text); + } + function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { const exportChanged = (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); @@ -2783,18 +2791,34 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement); } else { - const exportChanged = + const externalExportChanged = node.operatorToken.kind >= SyntaxKind.FirstAssignment && node.operatorToken.kind <= SyntaxKind.LastAssignment && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left); - if (exportChanged) { + if (externalExportChanged) { // emit assignment 'x y' as 'exports("x", x y)' write(`${exportFunctionForFile}("`); emitNodeWithoutSourceMap(node.left); write(`", `); } + const internalExportClauseMemberChanged = + node.operatorToken.kind >= SyntaxKind.FirstAssignment && + node.operatorToken.kind <= SyntaxKind.LastAssignment && + isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); + + if (internalExportClauseMemberChanged) { + for (const specifier of exportSpecifiers[(node.left).text]) { + emitStart(specifier.name); + emitContainingModuleName(specifier); + write("."); + emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + emitEnd(specifier.name); + write(" = "); + } + } + if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { // Downleveled emit exponentiation operator using Math.pow emitExponentiationOperator(node); @@ -2815,7 +2839,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge decreaseIndentIf(indentedBeforeOperator, indentedAfterOperator); } - if (exportChanged) { + if (externalExportChanged) { write(")"); } } From 269ebda7fc11d07c8305628e31d69dbe5c0aa2e0 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 04:21:43 +0700 Subject: [PATCH 18/90] Factor out assignment op check --- src/compiler/emitter.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 2ff6ca97bfc..06ed0b18d34 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2791,9 +2791,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitDestructuring(node, node.parent.kind === SyntaxKind.ExpressionStatement); } else { - const externalExportChanged = - node.operatorToken.kind >= SyntaxKind.FirstAssignment && - node.operatorToken.kind <= SyntaxKind.LastAssignment && + const isAssignment = isAssignmentOperator(node.operatorToken.kind); + + const externalExportChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.left); if (externalExportChanged) { @@ -2803,9 +2803,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(`", `); } - const internalExportClauseMemberChanged = - node.operatorToken.kind >= SyntaxKind.FirstAssignment && - node.operatorToken.kind <= SyntaxKind.LastAssignment && + const internalExportClauseMemberChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); if (internalExportClauseMemberChanged) { From ec60ac94ab5ebb619a6fc59ba96ad933459ea0b5 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 04:24:38 +0700 Subject: [PATCH 19/90] Add test for composite assignment --- .../es6ExportClauseWithAssignmentInEs5.js | 8 +++++++- .../es6ExportClauseWithAssignmentInEs5.symbols | 17 ++++++++++++----- .../es6ExportClauseWithAssignmentInEs5.types | 12 +++++++++++- .../es6ExportClauseWithAssignmentInEs5.ts | 5 ++++- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 636b4ff0909..9387f508999 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -6,7 +6,10 @@ foo = 3; var baz = 3; baz = 4; -export { foo, baz, baz as quux }; +var buzz = 10; +buzz += 3; + +export { foo, baz, baz as quux, buzz }; //// [server.js] @@ -18,3 +21,6 @@ var baz = 3; exports.baz = baz; exports.quux = baz; exports.baz = exports.quux = baz = 4; +var buzz = 10; +exports.buzz = buzz; +exports.buzz = buzz += 3; diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols index 64a41c93cc6..489636a5d7b 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -12,9 +12,16 @@ var baz = 3; baz = 4; >baz : Symbol(baz, Decl(server.ts, 4, 3)) -export { foo, baz, baz as quux }; ->foo : Symbol(foo, Decl(server.ts, 7, 8)) ->baz : Symbol(baz, Decl(server.ts, 7, 13)) ->baz : Symbol(quux, Decl(server.ts, 7, 18)) ->quux : Symbol(quux, Decl(server.ts, 7, 18)) +var buzz = 10; +>buzz : Symbol(buzz, Decl(server.ts, 7, 3)) + +buzz += 3; +>buzz : Symbol(buzz, Decl(server.ts, 7, 3)) + +export { foo, baz, baz as quux, buzz }; +>foo : Symbol(foo, Decl(server.ts, 10, 8)) +>baz : Symbol(baz, Decl(server.ts, 10, 13)) +>baz : Symbol(quux, Decl(server.ts, 10, 18)) +>quux : Symbol(quux, Decl(server.ts, 10, 18)) +>buzz : Symbol(buzz, Decl(server.ts, 10, 31)) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types index 28ce1a1562e..98e8b5528c0 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -18,9 +18,19 @@ baz = 4; >baz : number >4 : number -export { foo, baz, baz as quux }; +var buzz = 10; +>buzz : number +>10 : number + +buzz += 3; +>buzz += 3 : number +>buzz : number +>3 : number + +export { foo, baz, baz as quux, buzz }; >foo : number >baz : number >baz : number >quux : number +>buzz : number diff --git a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts index b79e34e3de1..e61660f886e 100644 --- a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -8,4 +8,7 @@ foo = 3; var baz = 3; baz = 4; -export { foo, baz, baz as quux }; +var buzz = 10; +buzz += 3; + +export { foo, baz, baz as quux, buzz }; From ebd4ce6e7adf5ab0265d32eb8d37c7c25ed5d388 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 04:51:46 +0700 Subject: [PATCH 20/90] Factor out the behaviour and handles x++ and ++x --- src/compiler/emitter.ts | 57 ++++++++++++++----- .../es6ExportClauseWithAssignmentInEs5.js | 12 +++- ...es6ExportClauseWithAssignmentInEs5.symbols | 25 ++++++-- .../es6ExportClauseWithAssignmentInEs5.types | 19 ++++++- .../es6ExportClauseWithAssignmentInEs5.ts | 7 ++- 5 files changed, 96 insertions(+), 24 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 06ed0b18d34..010fc2f2266 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2622,10 +2622,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { - const exportChanged = (node.operator === SyntaxKind.PlusPlusToken || node.operator === SyntaxKind.MinusMinusToken) && + const isPlusPlusOrMinusMinus = (node.operator === SyntaxKind.PlusPlusToken + || node.operator === SyntaxKind.MinusMinusToken); + const externalExportChanged = isPlusPlusOrMinusMinus && isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); - if (exportChanged) { + if (externalExportChanged) { // emit // ++x // as @@ -2634,6 +2636,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge emitNodeWithoutSourceMap(node.operand); write(`", `); } + const internalExportChanged = isPlusPlusOrMinusMinus && + isNameOfExportedSourceLevelDeclarationInClauseModule(node.operand); + + if (internalExportChanged) { + emitAliasEqual( node.operand); + } write(tokenToString(node.operator)); // In some cases, we need to emit a space between the operator and the operand. One obvious case @@ -2659,14 +2667,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } emit(node.operand); - if (exportChanged) { + if (externalExportChanged) { write(")"); } } function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { - const exportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); - if (exportChanged) { + const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); + const internalExportChanged = isNameOfExportedSourceLevelDeclarationInClauseModule(node.operand); + + if (externalExportChanged) { // export function returns the value that was passes as the second argument // however for postfix unary expressions result value should be the value before modification. // emit 'x++' as '(export('x', ++x) - 1)' and 'x--' as '(export('x', --x) + 1)' @@ -2684,6 +2694,16 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(") + 1)"); } } + else if (internalExportChanged) { + emitAliasEqual( node.operand); + emit(node.operand); + if (node.operator === SyntaxKind.PlusPlusToken) { + write(" += 1"); + } + else { + write(" -= 1"); + } + } else { emit(node.operand); write(tokenToString(node.operator)); @@ -2785,6 +2805,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } } + function emitAliasEqual(name: Identifier): boolean { + for (const specifier of exportSpecifiers[name.text]) { + emitStart(specifier.name); + emitContainingModuleName(specifier); + write("."); + emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + emitEnd(specifier.name); + write(" = "); + } + return true; + } + function emitBinaryExpression(node: BinaryExpression) { if (languageVersion < ScriptTarget.ES6 && node.operatorToken.kind === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { @@ -2803,18 +2835,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(`", `); } - const internalExportClauseMemberChanged = isAssignment && + const internalExportChanged = isAssignment && isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); - if (internalExportClauseMemberChanged) { - for (const specifier of exportSpecifiers[(node.left).text]) { - emitStart(specifier.name); - emitContainingModuleName(specifier); - write("."); - emitNodeWithCommentsAndWithoutSourcemap(specifier.name); - emitEnd(specifier.name); - write(" = "); - } + if (internalExportChanged) { + // export { foo } + // emit foo = 2 as exports.foo = foo = 2 + emitAliasEqual(node.left); } if (node.operatorToken.kind === SyntaxKind.AsteriskAsteriskToken || node.operatorToken.kind === SyntaxKind.AsteriskAsteriskEqualsToken) { diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js index 9387f508999..94bc5d9281f 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.js @@ -9,7 +9,12 @@ baz = 4; var buzz = 10; buzz += 3; -export { foo, baz, baz as quux, buzz }; +var bizz = 8; +bizz++; // compiles to exports.bizz = bizz += 1 +bizz--; // similarly +++bizz; // compiles to exports.bizz = ++bizz + +export { foo, baz, baz as quux, buzz, bizz }; //// [server.js] @@ -24,3 +29,8 @@ exports.baz = exports.quux = baz = 4; var buzz = 10; exports.buzz = buzz; exports.buzz = buzz += 3; +var bizz = 8; +exports.bizz = bizz; +exports.bizz = bizz += 1; // compiles to exports.bizz = bizz += 1 +exports.bizz = bizz -= 1; // similarly +exports.bizz = ++bizz; // compiles to exports.bizz = ++bizz diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols index 489636a5d7b..6f8dbb01ec5 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.symbols @@ -18,10 +18,23 @@ var buzz = 10; buzz += 3; >buzz : Symbol(buzz, Decl(server.ts, 7, 3)) -export { foo, baz, baz as quux, buzz }; ->foo : Symbol(foo, Decl(server.ts, 10, 8)) ->baz : Symbol(baz, Decl(server.ts, 10, 13)) ->baz : Symbol(quux, Decl(server.ts, 10, 18)) ->quux : Symbol(quux, Decl(server.ts, 10, 18)) ->buzz : Symbol(buzz, Decl(server.ts, 10, 31)) +var bizz = 8; +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +bizz++; // compiles to exports.bizz = bizz += 1 +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +bizz--; // similarly +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +++bizz; // compiles to exports.bizz = ++bizz +>bizz : Symbol(bizz, Decl(server.ts, 10, 3)) + +export { foo, baz, baz as quux, buzz, bizz }; +>foo : Symbol(foo, Decl(server.ts, 15, 8)) +>baz : Symbol(baz, Decl(server.ts, 15, 13)) +>baz : Symbol(quux, Decl(server.ts, 15, 18)) +>quux : Symbol(quux, Decl(server.ts, 15, 18)) +>buzz : Symbol(buzz, Decl(server.ts, 15, 31)) +>bizz : Symbol(bizz, Decl(server.ts, 15, 37)) diff --git a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types index 98e8b5528c0..3b6752d89db 100644 --- a/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types +++ b/tests/baselines/reference/es6ExportClauseWithAssignmentInEs5.types @@ -27,10 +27,27 @@ buzz += 3; >buzz : number >3 : number -export { foo, baz, baz as quux, buzz }; +var bizz = 8; +>bizz : number +>8 : number + +bizz++; // compiles to exports.bizz = bizz += 1 +>bizz++ : number +>bizz : number + +bizz--; // similarly +>bizz-- : number +>bizz : number + +++bizz; // compiles to exports.bizz = ++bizz +>++bizz : number +>bizz : number + +export { foo, baz, baz as quux, buzz, bizz }; >foo : number >baz : number >baz : number >quux : number >buzz : number +>bizz : number diff --git a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts index e61660f886e..2e86ba7aeea 100644 --- a/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts +++ b/tests/cases/compiler/es6ExportClauseWithAssignmentInEs5.ts @@ -11,4 +11,9 @@ baz = 4; var buzz = 10; buzz += 3; -export { foo, baz, baz as quux, buzz }; +var bizz = 8; +bizz++; // compiles to exports.bizz = bizz += 1 +bizz--; // similarly +++bizz; // compiles to exports.bizz = ++bizz + +export { foo, baz, baz as quux, buzz, bizz }; From f1ac06f30d3a6f44f521a8fcec8e1d9241bcaab7 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 05:42:11 +0700 Subject: [PATCH 21/90] Handles ES3 default as identifier name --- src/compiler/emitter.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 010fc2f2266..ddf10c912ec 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2809,6 +2809,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge for (const specifier of exportSpecifiers[name.text]) { emitStart(specifier.name); emitContainingModuleName(specifier); + if (languageVersion === ScriptTarget.ES3 && name.text === "default") { + write('["default"]'); + } write("."); emitNodeWithCommentsAndWithoutSourcemap(specifier.name); emitEnd(specifier.name); From eca94375e38bf00bf6e3b3973db5676a84e116df Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Tue, 24 May 2016 05:52:15 +0700 Subject: [PATCH 22/90] Fix missing else statement --- src/compiler/emitter.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index ddf10c912ec..e6638102c40 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2812,8 +2812,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (languageVersion === ScriptTarget.ES3 && name.text === "default") { write('["default"]'); } - write("."); - emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + else { + write("."); + emitNodeWithCommentsAndWithoutSourcemap(specifier.name); + } emitEnd(specifier.name); write(" = "); } From f69ecb5b90746ad6e87c6cff8044eb02f7e1b7ca Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 23 May 2016 16:00:56 -0700 Subject: [PATCH 23/90] run tests in parallel by equally dividing them between workers --- Jakefile.js | 99 +++++++++++++++++-------- src/harness/compilerRunner.ts | 12 +++- src/harness/fourslashRunner.ts | 12 +++- src/harness/projectsRunner.ts | 12 +++- src/harness/runner.ts | 127 ++++++++++++++++++++++++++++++--- src/harness/runnerbase.ts | 11 ++- src/harness/rwcRunner.ts | 10 ++- src/harness/test262Runner.ts | 12 +++- 8 files changed, 246 insertions(+), 49 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 5112a8db597..73bfdfdaedf 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -680,9 +680,9 @@ function cleanTestDirs() { } // used to pass data from jake command line directly to run.js -function writeTestConfigFile(tests, light, testConfigFile) { - console.log('Running test(s): ' + tests); - var testConfigContents = JSON.stringify({ test: [tests], light: light }); +function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, testConfigFile) { + var testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder }); + console.log('Running tests with config: ' + testConfigContents); fs.writeFileSync('test.config', testConfigContents); } @@ -692,7 +692,7 @@ function deleteTemporaryProjectOutput() { } } -function runConsoleTests(defaultReporter, defaultSubsets) { +function runConsoleTests(defaultReporter, runInParallel) { cleanTestDirs(); var debug = process.env.debug || process.env.d; tests = process.env.test || process.env.tests || process.env.t; @@ -701,9 +701,22 @@ function runConsoleTests(defaultReporter, defaultSubsets) { if(fs.existsSync(testConfigFile)) { fs.unlinkSync(testConfigFile); } + var workerCount, taskConfigsFolder; + if (runInParallel) { + // generate name to store task configuration files + var prefix = os.tmpdir() + "/ts-tests"; + var i = 1; + do { + taskConfigsFolder = prefix + i; + i++; + } while (fs.existsSync(taskConfigsFolder)); + fs.mkdirSync(taskConfigsFolder); - if (tests || light) { - writeTestConfigFile(tests, light, testConfigFile); + workerCount = process.env.workerCount || os.cpus().length; + } + + if (tests || light || taskConfigsFolder) { + writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, testConfigFile); } if (tests && tests.toLocaleLowerCase() === "rwc") { @@ -717,45 +730,71 @@ function runConsoleTests(defaultReporter, defaultSubsets) { // timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer - var subsetRegexes; - if(defaultSubsets.length === 0) { - subsetRegexes = [tests]; - } - else { - var subsets = tests ? tests.split("|") : defaultSubsets; - subsetRegexes = subsets.map(function (sub) { return "^" + sub + ".*$"; }); - subsetRegexes.push("^(?!" + subsets.join("|") + ").*$"); - } - subsetRegexes.forEach(function (subsetRegex, i) { - tests = subsetRegex ? ' -g "' + subsetRegex + '"' : ''; + if(!runInParallel) { + tests = tests ? ' -g "' + tests + '"' : ''; var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + ' -t ' + testTimeout + ' ' + run; console.log(cmd); - function finish() { - deleteTemporaryProjectOutput(); - complete(); - } exec(cmd, function () { - if (lintFlag && i === 0) { - var lint = jake.Task['lint']; - lint.addListener('complete', function () { - complete(); - }); - lint.invoke(); + if (i === 0) { + runLinter(); } finish(); }, finish); - }); + + } + else { + // run task to load all tests and partition then between workers + var cmd = "mocha " + " -R min " + colors + run; + console.log(cmd); + exec(cmd, function() { + // read all configuration files and spawn a worker for every config + var configFiles = fs.readdirSync(taskConfigsFolder); + var counter = configFiles.length; + // schedule work for chunks + configFiles.forEach(function (f) { + var configPath = path.join(taskConfigsFolder, f); + var workerCmd = "mocha" + " -t " + testTimeout + " -R " + reporter + " " + colors + " " + run + " --config='" + configPath + "'"; + console.log(workerCmd); + exec(workerCmd, finishWorker, finishWorker) + }); + + function finishWorker() { + counter--; + if (counter === 0) { + // last worker clean everything and runs linter + runLinter(); + deleteTemporaryProjectOutput(); + jake.rmRf(taskConfigsFolder); + } + complete(); + } + }); + } + function finish() { + deleteTemporaryProjectOutput(); + complete(); + } + function runLinter() { + if (!lintFlag) { + return; + } + var lint = jake.Task['lint']; + lint.addListener('complete', function () { + complete(); + }); + lint.invoke(); + } } var testTimeout = 20000; desc("Runs all the tests in parallel using the built run.js file. Optional arguments are: t[ests]=category1|category2|... d[ebug]=true."); task("runtests-parallel", ["build-rules", "tests", builtLocalDirectory], function() { - runConsoleTests('min', ['compiler', 'conformance', 'Projects', 'fourslash']); + runConsoleTests('min', /*runInParallel*/ true); }, {async: true}); desc("Runs the tests using the built run.js file. Optional arguments are: t[ests]=regex r[eporter]=[list|spec|json|] d[ebug]=true color[s]=false lint=true."); task("runtests", ["build-rules", "tests", builtLocalDirectory], function() { - runConsoleTests('mocha-fivemat-progress-reporter', []); + runConsoleTests('mocha-fivemat-progress-reporter', /*runInParallel*/ false); }, {async: true}); desc("Generates code coverage data via instanbul"); diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index 1ddbd1ea331..fefea00b807 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -11,7 +11,7 @@ const enum CompilerTestType { class CompilerBaselineRunner extends RunnerBase { private basePath = "tests/cases"; - private testSuiteName: string; + private testSuiteName: TestRunnerKind; private errors: boolean; private emit: boolean; private decl: boolean; @@ -40,6 +40,14 @@ class CompilerBaselineRunner extends RunnerBase { this.basePath += "/" + this.testSuiteName; } + public kind() { + return this.testSuiteName; + } + + public enumerateTestFiles() { + return this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true }); + } + private makeUnitName(name: string, root: string) { return ts.isRootedDiskPath(name) ? name : ts.combinePaths(root, name); }; @@ -390,7 +398,7 @@ class CompilerBaselineRunner extends RunnerBase { // this will set up a series of describe/it blocks to run between the setup and cleanup phases if (this.tests.length === 0) { - const testFiles = this.enumerateFiles(this.basePath, /\.tsx?$/, { recursive: true }); + const testFiles = this.enumerateTestFiles(); testFiles.forEach(fn => { fn = fn.replace(/\\/g, "/"); this.checkTestCodeOutput(fn); diff --git a/src/harness/fourslashRunner.ts b/src/harness/fourslashRunner.ts index 386bd9e340c..a6174342acd 100644 --- a/src/harness/fourslashRunner.ts +++ b/src/harness/fourslashRunner.ts @@ -12,7 +12,7 @@ const enum FourSlashTestType { class FourSlashRunner extends RunnerBase { protected basePath: string; - protected testSuiteName: string; + protected testSuiteName: TestRunnerKind; constructor(private testType: FourSlashTestType) { super(); @@ -36,9 +36,17 @@ class FourSlashRunner extends RunnerBase { } } + public enumerateTestFiles() { + return this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false }); + } + + public kind() { + return this.testSuiteName; + } + public initializeTests() { if (this.tests.length === 0) { - this.tests = this.enumerateFiles(this.basePath, /\.ts/i, { recursive: false }); + this.tests = this.enumerateTestFiles(); } describe(this.testSuiteName + " tests", () => { diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 9ee4359d8d3..777ef63145d 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -37,11 +37,19 @@ interface BatchCompileProjectTestCaseResult extends CompileProjectFilesResult { } class ProjectRunner extends RunnerBase { + + public enumerateTestFiles() { + return this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true }); + } + + public kind(): TestRunnerKind { + return "project"; + } + public initializeTests() { if (this.tests.length === 0) { - const testFiles = this.enumerateFiles("tests/cases/project", /\.json$/, { recursive: true }); + const testFiles = this.enumerateTestFiles(); testFiles.forEach(fn => { - fn = fn.replace(/\\/g, "/"); this.runProjectTestCase(fn); }); } diff --git a/src/harness/runner.ts b/src/harness/runner.ts index bb3cafea0a8..07723913040 100644 --- a/src/harness/runner.ts +++ b/src/harness/runner.ts @@ -33,18 +33,90 @@ function runTests(runners: RunnerBase[]) { } } -// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options -let mytestconfig = "mytest.config"; -let testconfig = "test.config"; -let testConfigFile = - Harness.IO.fileExists(mytestconfig) ? Harness.IO.readFile(mytestconfig) : - (Harness.IO.fileExists(testconfig) ? Harness.IO.readFile(testconfig) : ""); +function tryGetConfig(args: string[]) { + const prefix = "--config="; + const configPath = ts.forEach(args, arg => arg.lastIndexOf(prefix, 0) === 0 && arg.substr(prefix.length)); + // strip leading and trailing quotes from the path (necessary on Windows since shell does not do it automatically) + return configPath && configPath.replace(/(^[\"'])|([\"']$)/g, ""); +} -if (testConfigFile !== "") { - const testConfig = JSON.parse(testConfigFile); +function createRunner(kind: TestRunnerKind): RunnerBase { + switch (kind) { + case "conformance": + return new CompilerBaselineRunner(CompilerTestType.Conformance); + case "compiler": + return new CompilerBaselineRunner(CompilerTestType.Regressions); + case "fourslash": + return new FourSlashRunner(FourSlashTestType.Native); + case "fourslash-shims": + return new FourSlashRunner(FourSlashTestType.Shims); + case "fourslash-shims-pp": + return new FourSlashRunner(FourSlashTestType.ShimsWithPreprocess); + case "fourslash-server": + return new FourSlashRunner(FourSlashTestType.Server); + case "project": + return new ProjectRunner(); + case "rwc": + return new RWCRunner(); + case "test262": + return new Test262BaselineRunner(); + } +} + +// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options + +const mytestconfigFileName = "mytest.config"; +const testconfigFileName = "test.config"; + +const customConfig = tryGetConfig(Harness.IO.args()); +let testConfigContent = + customConfig && Harness.IO.fileExists(customConfig) + ? Harness.IO.readFile(customConfig) + : Harness.IO.fileExists(mytestconfigFileName) + ? Harness.IO.readFile(mytestconfigFileName) + : Harness.IO.fileExists(testconfigFileName) ? Harness.IO.readFile(testconfigFileName) : ""; + +let taskConfigsFolder: string; +let workerCount: number; +let runUnitTests = true; + +interface TestConfig { + light?: boolean; + taskConfigsFolder?: string; + workerCount?: number; + tasks?: TaskSet[]; + test?: string[]; + runUnitTests?: boolean; +} + +interface TaskSet { + runner: TestRunnerKind; + files: string[]; +} + +if (testConfigContent !== "") { + const testConfig = JSON.parse(testConfigContent); if (testConfig.light) { Harness.lightMode = true; } + if (testConfig.taskConfigsFolder) { + taskConfigsFolder = testConfig.taskConfigsFolder; + } + if (testConfig.runUnitTests !== undefined) { + runUnitTests = testConfig.runUnitTests; + } + if (testConfig.workerCount) { + workerCount = testConfig.workerCount; + } + if (testConfig.tasks) { + for (const taskSet of testConfig.tasks) { + const runner = createRunner(taskSet.runner); + for (const file of taskSet.files) { + runner.addTest(file); + } + runners.push(runner); + } + } if (testConfig.test && testConfig.test.length > 0) { for (const option of testConfig.test) { @@ -108,4 +180,41 @@ if (runners.length === 0) { // runners.push(new GeneratedFourslashRunner()); } -runTests(runners); +if (taskConfigsFolder) { + // this instance of mocha should only partition work but not run actual tests + runUnitTests = false; + const workerConfigs: TestConfig[] = []; + for (let i = 0; i < workerCount; i++) { + // pass light mode settings to workers + workerConfigs.push({ light: Harness.lightMode, tasks: [] }); + } + + for (const runner of runners) { + const files = runner.enumerateTestFiles(); + const chunkSize = Math.floor(files.length / workerCount) + 1; // add extra 1 to prevent missing tests due to rounding + for (let i = 0; i < workerCount; i++) { + const startPos = i * chunkSize; + const len = Math.min(chunkSize, files.length - startPos); + if (len !== 0) { + workerConfigs[i].tasks.push({ + runner: runner.kind(), + files: files.slice(startPos, startPos + len) + }); + } + } + } + + for (let i = 0; i < workerCount; i++) { + const config = workerConfigs[i]; + // use last worker to run unit tests + config.runUnitTests = i === workerCount - 1; + Harness.IO.writeFile(ts.combinePaths(taskConfigsFolder, `task-config${i}.json`), JSON.stringify(workerConfigs[i])); + } +} +else { + runTests(runners); +} +if (!runUnitTests) { + // patch `describe` to skip unit tests + describe = describe.skip; +} \ No newline at end of file diff --git a/src/harness/runnerbase.ts b/src/harness/runnerbase.ts index a114e8c3201..81da0907714 100644 --- a/src/harness/runnerbase.ts +++ b/src/harness/runnerbase.ts @@ -1,5 +1,10 @@ /// + +type TestRunnerKind = CompilerTestKind | FourslashTestKind | "project" | "rwc" | "test262"; +type CompilerTestKind = "conformance" | "compiler"; +type FourslashTestKind = "fourslash" | "fourslash-shims" | "fourslash-shims-pp" | "fourslash-server"; + abstract class RunnerBase { constructor() { } @@ -12,9 +17,13 @@ abstract class RunnerBase { } public enumerateFiles(folder: string, regex?: RegExp, options?: { recursive: boolean }): string[] { - return Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) }); + return ts.map(Harness.IO.listFiles(Harness.userSpecifiedRoot + folder, regex, { recursive: (options ? options.recursive : false) }), ts.normalizeSlashes); } + abstract kind(): TestRunnerKind; + + abstract enumerateTestFiles(): string[]; + /** Setup the runner's tests so that they are ready to be executed by the harness * The first test should be a describe/it block that sets up the harness's compiler instance appropriately */ diff --git a/src/harness/rwcRunner.ts b/src/harness/rwcRunner.ts index 39231435bf5..ff1623b9051 100644 --- a/src/harness/rwcRunner.ts +++ b/src/harness/rwcRunner.ts @@ -223,12 +223,20 @@ namespace RWC { class RWCRunner extends RunnerBase { private static sourcePath = "internal/cases/rwc/"; + public enumerateTestFiles() { + return Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/); + } + + public kind(): TestRunnerKind { + return "rwc"; + } + /** Setup the runner's tests so that they are ready to be executed by the harness * The first test should be a describe/it block that sets up the harness's compiler instance appropriately */ public initializeTests(): void { // Read in and evaluate the test list - const testList = Harness.IO.listFiles(RWCRunner.sourcePath, /.+\.json$/); + const testList = this.enumerateTestFiles(); for (let i = 0; i < testList.length; i++) { this.runTest(testList[i]); } diff --git a/src/harness/test262Runner.ts b/src/harness/test262Runner.ts index cc9957c1fac..a3276cc65b3 100644 --- a/src/harness/test262Runner.ts +++ b/src/harness/test262Runner.ts @@ -97,12 +97,20 @@ class Test262BaselineRunner extends RunnerBase { }); } + public kind(): TestRunnerKind { + return "test262"; + } + + public enumerateTestFiles() { + return ts.map(this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }), ts.normalizePath); + } + public initializeTests() { // this will set up a series of describe/it blocks to run between the setup and cleanup phases if (this.tests.length === 0) { - const testFiles = this.enumerateFiles(Test262BaselineRunner.basePath, Test262BaselineRunner.testFileExtensionRegex, { recursive: true }); + const testFiles = this.enumerateTestFiles(); testFiles.forEach(fn => { - this.runTest(ts.normalizePath(fn)); + this.runTest(fn); }); } else { From 47eac4f4af32c8b0d2ba74e836df0505915d3b81 Mon Sep 17 00:00:00 2001 From: Evan Sebastian Date: Wed, 25 May 2016 01:42:17 +0700 Subject: [PATCH 24/90] isNameOfExportedDeclarationInNonES6Module --- src/compiler/emitter.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index e6638102c40..f7eda2d0e7e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2613,7 +2613,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return isSourceFileLevelDeclarationInSystemJsModule(targetDeclaration, /*isExported*/ true); } - function isNameOfExportedSourceLevelDeclarationInClauseModule(node: Node): boolean { + function isNameOfExportedDeclarationInNonES6Module(node: Node): boolean { if (modulekind === ModuleKind.System || node.kind !== SyntaxKind.Identifier || nodeIsSynthesized(node)) { return false; } @@ -2637,7 +2637,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge write(`", `); } const internalExportChanged = isPlusPlusOrMinusMinus && - isNameOfExportedSourceLevelDeclarationInClauseModule(node.operand); + isNameOfExportedDeclarationInNonES6Module(node.operand); if (internalExportChanged) { emitAliasEqual( node.operand); @@ -2674,7 +2674,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function emitPostfixUnaryExpression(node: PostfixUnaryExpression) { const externalExportChanged = isNameOfExportedSourceLevelDeclarationInSystemExternalModule(node.operand); - const internalExportChanged = isNameOfExportedSourceLevelDeclarationInClauseModule(node.operand); + const internalExportChanged = isNameOfExportedDeclarationInNonES6Module(node.operand); if (externalExportChanged) { // export function returns the value that was passes as the second argument @@ -2841,7 +2841,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge } const internalExportChanged = isAssignment && - isNameOfExportedSourceLevelDeclarationInClauseModule(node.left); + isNameOfExportedDeclarationInNonES6Module(node.left); if (internalExportChanged) { // export { foo } @@ -5464,7 +5464,7 @@ const _super = (function (geti, seti) { // // NOTE: we reuse the same rewriting logic for cases when targeting ES6 and module kind is System. - // Because of hoisting top level class declaration need to be emitted as class expressions. + // Because of hoisting top level class declaration need to be emitted as class expressions. // Double bind case is only required if node is decorated. if (isDecorated && resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithBodyScopedClassBinding) { decoratedClassAlias = unescapeIdentifier(makeUniqueName(node.name ? node.name.text : "default")); From b8b38f3b86882d2f7ba94ce53be09cedd821a238 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 24 May 2016 12:11:15 -0700 Subject: [PATCH 25/90] Do not resolve alias when getting symbol of import equal's right hand side Fixes #5501 --- src/compiler/checker.ts | 22 ++++++++----------- .../reference/acceptableAlias1.symbols | 2 +- .../amdImportNotAsPrimaryExpression.symbols | 2 +- .../reference/chainedImportAlias.symbols | 2 +- ...mmonJSImportNotAsPrimaryExpression.symbols | 2 +- .../declFileForExportedImport.symbols | 2 +- ...lFileImportChainInExportAssignment.symbols | 2 +- .../declarationEmit_nameConflicts.symbols | 2 +- .../dependencyViaImportAlias.symbols | 2 +- ...edImportInIndirectExportAssignment.symbols | 2 +- ...alModuleReferenceDoubleUnderscore1.symbols | 2 +- ...ternalModuleInsideAnInternalModule.symbols | 2 +- .../import_reference-exported-alias.symbols | 4 ++-- .../import_reference-to-type-alias.symbols | 2 +- .../importedAliasesInTypePositions.symbols | 2 +- .../reference/tsxPreserveEmit1.symbols | 2 +- tests/cases/fourslash/renameAlias.ts | 11 ++++++++++ tests/cases/fourslash/renameAlias2.ts | 11 ++++++++++ tests/cases/fourslash/renameAlias3.ts | 11 ++++++++++ .../fourslash/renameAliasExternalModule.ts | 16 ++++++++++++++ .../fourslash/renameAliasExternalModule2.ts | 16 ++++++++++++++ .../fourslash/renameAliasExternalModule3.ts | 16 ++++++++++++++ 22 files changed, 106 insertions(+), 29 deletions(-) create mode 100644 tests/cases/fourslash/renameAlias.ts create mode 100644 tests/cases/fourslash/renameAlias2.ts create mode 100644 tests/cases/fourslash/renameAlias3.ts create mode 100644 tests/cases/fourslash/renameAliasExternalModule.ts create mode 100644 tests/cases/fourslash/renameAliasExternalModule2.ts create mode 100644 tests/cases/fourslash/renameAliasExternalModule3.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6a42dc43d51..8a510825673 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1154,11 +1154,7 @@ namespace ts { } // This function is only for imports with entity names - function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, importDeclaration?: ImportEqualsDeclaration): Symbol { - if (!importDeclaration) { - importDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); - Debug.assert(importDeclaration !== undefined); - } + function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, importDeclaration: ImportEqualsDeclaration, dontResolveAlias?: boolean): Symbol { // There are three things we might try to look for. In the following examples, // the search term is enclosed in |...|: // @@ -1170,13 +1166,13 @@ namespace ts { } // Check for case 1 and 3 in the above example if (entityName.kind === SyntaxKind.Identifier || entityName.parent.kind === SyntaxKind.QualifiedName) { - return resolveEntityName(entityName, SymbolFlags.Namespace); + return resolveEntityName(entityName, SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); } else { // Case 2 in above example // entityName.kind could be a QualifiedName or a Missing identifier Debug.assert(entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration); - return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); + return resolveEntityName(entityName, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace, /*ignoreErrors*/ false, dontResolveAlias); } } @@ -1185,7 +1181,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean): Symbol { + function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol { if (nodeIsMissing(name)) { return undefined; } @@ -1219,7 +1215,7 @@ namespace ts { Debug.fail("Unknown entity name kind."); } Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); - return symbol.flags & meaning ? symbol : resolveAlias(symbol); + return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); } function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol { @@ -16441,7 +16437,9 @@ namespace ts { if (entityName.kind !== SyntaxKind.PropertyAccessExpression) { if (isInRightSideOfImportOrExportAssignment(entityName)) { // Since we already checked for ExportAssignment, this really could only be an Import - return getSymbolOfPartOfRightHandSideOfImportEquals(entityName); + const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); + Debug.assert(importEqualsDeclaration !== undefined); + return getSymbolOfPartOfRightHandSideOfImportEquals(entityName, importEqualsDeclaration, /*dontResolveAlias*/ true); } } @@ -16531,9 +16529,7 @@ namespace ts { if (node.kind === SyntaxKind.Identifier) { if (isInRightSideOfImportOrExportAssignment(node)) { - return node.parent.kind === SyntaxKind.ExportAssignment - ? getSymbolOfEntityNameOrPropertyAccessExpression(node) - : getSymbolOfPartOfRightHandSideOfImportEquals(node); + return getSymbolOfEntityNameOrPropertyAccessExpression(node); } else if (node.parent.kind === SyntaxKind.BindingElement && node.parent.parent.kind === SyntaxKind.ObjectBindingPattern && diff --git a/tests/baselines/reference/acceptableAlias1.symbols b/tests/baselines/reference/acceptableAlias1.symbols index cffbc70290f..31679fb6a52 100644 --- a/tests/baselines/reference/acceptableAlias1.symbols +++ b/tests/baselines/reference/acceptableAlias1.symbols @@ -13,5 +13,5 @@ module M { import r = M.X; >r : Symbol(r, Decl(acceptableAlias1.ts, 4, 1)) >M : Symbol(M, Decl(acceptableAlias1.ts, 0, 0)) ->X : Symbol(r, Decl(acceptableAlias1.ts, 0, 10)) +>X : Symbol(M.X, Decl(acceptableAlias1.ts, 2, 5)) diff --git a/tests/baselines/reference/amdImportNotAsPrimaryExpression.symbols b/tests/baselines/reference/amdImportNotAsPrimaryExpression.symbols index ae67c0a3175..a04bd7077c1 100644 --- a/tests/baselines/reference/amdImportNotAsPrimaryExpression.symbols +++ b/tests/baselines/reference/amdImportNotAsPrimaryExpression.symbols @@ -5,7 +5,7 @@ import foo = require("./foo_0"); // None of the below should cause a runtime dependency on foo_0 import f = foo.M1; >f : Symbol(f, Decl(foo_1.ts, 0, 32)) ->foo : Symbol(foo, Decl(foo_0.ts, 0, 0)) +>foo : Symbol(foo, Decl(foo_1.ts, 0, 0)) >M1 : Symbol(foo.M1, Decl(foo_0.ts, 8, 1)) var i: f.I2; diff --git a/tests/baselines/reference/chainedImportAlias.symbols b/tests/baselines/reference/chainedImportAlias.symbols index 0f374305aeb..d10e22af119 100644 --- a/tests/baselines/reference/chainedImportAlias.symbols +++ b/tests/baselines/reference/chainedImportAlias.symbols @@ -4,7 +4,7 @@ import x = require('./chainedImportAlias_file0'); import y = x; >y : Symbol(y, Decl(chainedImportAlias_file1.ts, 0, 49)) ->x : Symbol(x, Decl(chainedImportAlias_file0.ts, 0, 0)) +>x : Symbol(x, Decl(chainedImportAlias_file1.ts, 0, 0)) y.m.foo(); >y.m.foo : Symbol(x.m.foo, Decl(chainedImportAlias_file0.ts, 0, 17)) diff --git a/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.symbols b/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.symbols index ae67c0a3175..a04bd7077c1 100644 --- a/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.symbols +++ b/tests/baselines/reference/commonJSImportNotAsPrimaryExpression.symbols @@ -5,7 +5,7 @@ import foo = require("./foo_0"); // None of the below should cause a runtime dependency on foo_0 import f = foo.M1; >f : Symbol(f, Decl(foo_1.ts, 0, 32)) ->foo : Symbol(foo, Decl(foo_0.ts, 0, 0)) +>foo : Symbol(foo, Decl(foo_1.ts, 0, 0)) >M1 : Symbol(foo.M1, Decl(foo_0.ts, 8, 1)) var i: f.I2; diff --git a/tests/baselines/reference/declFileForExportedImport.symbols b/tests/baselines/reference/declFileForExportedImport.symbols index fe14836199c..c9c36796826 100644 --- a/tests/baselines/reference/declFileForExportedImport.symbols +++ b/tests/baselines/reference/declFileForExportedImport.symbols @@ -11,7 +11,7 @@ var y = a.x; export import b = a; >b : Symbol(b, Decl(declFileForExportedImport_1.ts, 2, 12)) ->a : Symbol(a, Decl(declFileForExportedImport_0.ts, 0, 0)) +>a : Symbol(a, Decl(declFileForExportedImport_1.ts, 0, 0)) var z = b.x; >z : Symbol(z, Decl(declFileForExportedImport_1.ts, 5, 3)) diff --git a/tests/baselines/reference/declFileImportChainInExportAssignment.symbols b/tests/baselines/reference/declFileImportChainInExportAssignment.symbols index 12e05bed75a..a47db44a02e 100644 --- a/tests/baselines/reference/declFileImportChainInExportAssignment.symbols +++ b/tests/baselines/reference/declFileImportChainInExportAssignment.symbols @@ -17,7 +17,7 @@ import a = m.c; import b = a; >b : Symbol(b, Decl(declFileImportChainInExportAssignment.ts, 6, 15)) ->a : Symbol(a, Decl(declFileImportChainInExportAssignment.ts, 0, 10)) +>a : Symbol(a, Decl(declFileImportChainInExportAssignment.ts, 5, 1)) export = b; >b : Symbol(b, Decl(declFileImportChainInExportAssignment.ts, 6, 15)) diff --git a/tests/baselines/reference/declarationEmit_nameConflicts.symbols b/tests/baselines/reference/declarationEmit_nameConflicts.symbols index 1c5bb35b756..43657e85baf 100644 --- a/tests/baselines/reference/declarationEmit_nameConflicts.symbols +++ b/tests/baselines/reference/declarationEmit_nameConflicts.symbols @@ -37,7 +37,7 @@ export module M { export import d = im; >d : Symbol(d, Decl(declarationEmit_nameConflicts_0.ts, 11, 24)) ->im : Symbol(d, Decl(declarationEmit_nameConflicts_1.ts, 0, 0)) +>im : Symbol(im, Decl(declarationEmit_nameConflicts_0.ts, 0, 0)) } export module M.P { diff --git a/tests/baselines/reference/dependencyViaImportAlias.symbols b/tests/baselines/reference/dependencyViaImportAlias.symbols index 8855b8d6451..04c0a9a1a13 100644 --- a/tests/baselines/reference/dependencyViaImportAlias.symbols +++ b/tests/baselines/reference/dependencyViaImportAlias.symbols @@ -4,7 +4,7 @@ import a = require('A'); import A = a.A; >A : Symbol(A, Decl(B.ts, 0, 24)) ->a : Symbol(a, Decl(A.ts, 0, 0)) +>a : Symbol(a, Decl(B.ts, 0, 0)) >A : Symbol(a.A, Decl(A.ts, 0, 0)) export = A; diff --git a/tests/baselines/reference/es6ImportNamedImportInIndirectExportAssignment.symbols b/tests/baselines/reference/es6ImportNamedImportInIndirectExportAssignment.symbols index 24eab96f54b..77b93c3ef00 100644 --- a/tests/baselines/reference/es6ImportNamedImportInIndirectExportAssignment.symbols +++ b/tests/baselines/reference/es6ImportNamedImportInIndirectExportAssignment.symbols @@ -14,7 +14,7 @@ import { a } from "./es6ImportNamedImportInIndirectExportAssignment_0"; import x = a; >x : Symbol(x, Decl(es6ImportNamedImportInIndirectExportAssignment_1.ts, 0, 71)) ->a : Symbol(a, Decl(es6ImportNamedImportInIndirectExportAssignment_0.ts, 0, 0)) +>a : Symbol(a, Decl(es6ImportNamedImportInIndirectExportAssignment_1.ts, 0, 8)) export = x; >x : Symbol(x, Decl(es6ImportNamedImportInIndirectExportAssignment_1.ts, 0, 71)) diff --git a/tests/baselines/reference/externalModuleReferenceDoubleUnderscore1.symbols b/tests/baselines/reference/externalModuleReferenceDoubleUnderscore1.symbols index 02e5e6cfc5a..d4b995af6b2 100644 --- a/tests/baselines/reference/externalModuleReferenceDoubleUnderscore1.symbols +++ b/tests/baselines/reference/externalModuleReferenceDoubleUnderscore1.symbols @@ -5,7 +5,7 @@ declare module 'timezonecomplete' { export import TimeUnit = basics.TimeUnit; >TimeUnit : Symbol(TimeUnit, Decl(externalModuleReferenceDoubleUnderscore1.ts, 1, 57)) ->basics : Symbol(basics, Decl(externalModuleReferenceDoubleUnderscore1.ts, 3, 1)) +>basics : Symbol(basics, Decl(externalModuleReferenceDoubleUnderscore1.ts, 0, 35)) >TimeUnit : Symbol(basics.TimeUnit, Decl(externalModuleReferenceDoubleUnderscore1.ts, 5, 44)) } diff --git a/tests/baselines/reference/importAliasAnExternalModuleInsideAnInternalModule.symbols b/tests/baselines/reference/importAliasAnExternalModuleInsideAnInternalModule.symbols index ffaca4a0ec5..ecc81c727e1 100644 --- a/tests/baselines/reference/importAliasAnExternalModuleInsideAnInternalModule.symbols +++ b/tests/baselines/reference/importAliasAnExternalModuleInsideAnInternalModule.symbols @@ -8,7 +8,7 @@ module m_private { //import r2 = require('m'); // would be error export import C = r; // no error >C : Symbol(C, Decl(importAliasAnExternalModuleInsideAnInternalModule_file1.ts, 1, 18)) ->r : Symbol(C, Decl(importAliasAnExternalModuleInsideAnInternalModule_file0.ts, 0, 0)) +>r : Symbol(r, Decl(importAliasAnExternalModuleInsideAnInternalModule_file1.ts, 0, 0)) C.m.foo(); >C.m.foo : Symbol(C.m.foo, Decl(importAliasAnExternalModuleInsideAnInternalModule_file0.ts, 0, 17)) diff --git a/tests/baselines/reference/import_reference-exported-alias.symbols b/tests/baselines/reference/import_reference-exported-alias.symbols index 5a7e8b8c5c7..10ccb7a9256 100644 --- a/tests/baselines/reference/import_reference-exported-alias.symbols +++ b/tests/baselines/reference/import_reference-exported-alias.symbols @@ -4,12 +4,12 @@ import appJs = require("file1"); import Services = appJs.Services; >Services : Symbol(Services, Decl(file2.ts, 0, 32)) ->appJs : Symbol(appJs, Decl(file1.ts, 0, 0)) +>appJs : Symbol(appJs, Decl(file2.ts, 0, 0)) >Services : Symbol(appJs.Services, Decl(file1.ts, 0, 12)) import UserServices = Services.UserServices; >UserServices : Symbol(UserServices, Decl(file2.ts, 1, 33)) ->Services : Symbol(appJs.Services, Decl(file1.ts, 0, 12)) +>Services : Symbol(Services, Decl(file2.ts, 0, 32)) >UserServices : Symbol(Services.UserServices, Decl(file1.ts, 1, 28)) var x = new UserServices().getUserName(); diff --git a/tests/baselines/reference/import_reference-to-type-alias.symbols b/tests/baselines/reference/import_reference-to-type-alias.symbols index 6d096c1cba3..996cf05be71 100644 --- a/tests/baselines/reference/import_reference-to-type-alias.symbols +++ b/tests/baselines/reference/import_reference-to-type-alias.symbols @@ -4,7 +4,7 @@ import appJs = require("file1"); import Services = appJs.App.Services; >Services : Symbol(Services, Decl(file2.ts, 0, 32)) ->appJs : Symbol(appJs, Decl(file1.ts, 0, 0)) +>appJs : Symbol(appJs, Decl(file2.ts, 0, 0)) >App : Symbol(appJs.App, Decl(file1.ts, 0, 0)) >Services : Symbol(Services, Decl(file1.ts, 0, 19)) diff --git a/tests/baselines/reference/importedAliasesInTypePositions.symbols b/tests/baselines/reference/importedAliasesInTypePositions.symbols index 1d28900c22f..1ad3c4254f8 100644 --- a/tests/baselines/reference/importedAliasesInTypePositions.symbols +++ b/tests/baselines/reference/importedAliasesInTypePositions.symbols @@ -4,7 +4,7 @@ import RT_ALIAS = require("file1"); import ReferredTo = RT_ALIAS.elaborate.nested.mod.name.ReferredTo; >ReferredTo : Symbol(ReferredTo, Decl(file2.ts, 0, 35)) ->RT_ALIAS : Symbol(RT_ALIAS, Decl(file1.ts, 0, 0)) +>RT_ALIAS : Symbol(RT_ALIAS, Decl(file2.ts, 0, 0)) >elaborate : Symbol(RT_ALIAS.elaborate, Decl(file1.ts, 0, 0)) >nested : Symbol(RT_ALIAS.elaborate.nested, Decl(file1.ts, 0, 24)) >mod : Symbol(RT_ALIAS.elaborate.nested.mod, Decl(file1.ts, 0, 31)) diff --git a/tests/baselines/reference/tsxPreserveEmit1.symbols b/tests/baselines/reference/tsxPreserveEmit1.symbols index 53707b86104..b5651d7c38e 100644 --- a/tests/baselines/reference/tsxPreserveEmit1.symbols +++ b/tests/baselines/reference/tsxPreserveEmit1.symbols @@ -8,7 +8,7 @@ import ReactRouter = require('react-router'); import Route = ReactRouter.Route; >Route : Symbol(Route, Decl(test.tsx, 2, 45)) ->ReactRouter : Symbol(ReactRouter, Decl(react.d.ts, 4, 1)) +>ReactRouter : Symbol(ReactRouter, Decl(test.tsx, 1, 32)) >Route : Symbol(ReactRouter.Route, Decl(react.d.ts, 7, 4)) var routes1 = ; diff --git a/tests/cases/fourslash/renameAlias.ts b/tests/cases/fourslash/renameAlias.ts new file mode 100644 index 00000000000..e3f57ac7b41 --- /dev/null +++ b/tests/cases/fourslash/renameAlias.ts @@ -0,0 +1,11 @@ +/// + +////module SomeModule { export class SomeClass { } } +////import [|M|] = SomeModule; +////import C = [|M|].SomeClass; + +let ranges = test.ranges() +for (let range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameAlias2.ts b/tests/cases/fourslash/renameAlias2.ts new file mode 100644 index 00000000000..da1bcf8248d --- /dev/null +++ b/tests/cases/fourslash/renameAlias2.ts @@ -0,0 +1,11 @@ +/// + +////module [|SomeModule|] { export class SomeClass { } } +////import M = [|SomeModule|]; +////import C = M.SomeClass; + +let ranges = test.ranges() +for (let range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameAlias3.ts b/tests/cases/fourslash/renameAlias3.ts new file mode 100644 index 00000000000..9172f052abb --- /dev/null +++ b/tests/cases/fourslash/renameAlias3.ts @@ -0,0 +1,11 @@ +/// + +////module SomeModule { export class [|SomeClass|] { } } +////import M = SomeModule; +////import C = M.[|SomeClass|]; + +let ranges = test.ranges() +for (let range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameAliasExternalModule.ts b/tests/cases/fourslash/renameAliasExternalModule.ts new file mode 100644 index 00000000000..54277fd6e64 --- /dev/null +++ b/tests/cases/fourslash/renameAliasExternalModule.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: a.ts +////module SomeModule { export class SomeClass { } } +////export = SomeModule; + +// @Filename: b.ts +////import [|M|] = require("./a"); +////import C = [|M|].SomeClass; + +let ranges = test.ranges() +for (let range of ranges) { + goTo.file(range.fileName); + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameAliasExternalModule2.ts b/tests/cases/fourslash/renameAliasExternalModule2.ts new file mode 100644 index 00000000000..2d9d07efb68 --- /dev/null +++ b/tests/cases/fourslash/renameAliasExternalModule2.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: a.ts +////module [|SomeModule|] { export class SomeClass { } } +////export = [|SomeModule|]; + +// @Filename: b.ts +////import M = require("./a"); +////import C = M.SomeClass; + +let ranges = test.ranges() +for (let range of ranges) { + goTo.file(range.fileName); + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameAliasExternalModule3.ts b/tests/cases/fourslash/renameAliasExternalModule3.ts new file mode 100644 index 00000000000..acff9371c77 --- /dev/null +++ b/tests/cases/fourslash/renameAliasExternalModule3.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: a.ts +////module SomeModule { export class [|SomeClass|] { } } +////export = SomeModule; + +// @Filename: b.ts +////import M = require("./a"); +////import C = M.[|SomeClass|]; + +let ranges = test.ranges() +for (let range of ranges) { + goTo.file(range.fileName); + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file From 5f7bbbf8fdb949c32e1d68e2dc1efb45a59f4a1e Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 24 May 2016 14:29:52 -0700 Subject: [PATCH 26/90] unconditionnaly use linter for non-parallel run --- Jakefile.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 73bfdfdaedf..7be070bc492 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -735,9 +735,7 @@ function runConsoleTests(defaultReporter, runInParallel) { var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + ' -t ' + testTimeout + ' ' + run; console.log(cmd); exec(cmd, function () { - if (i === 0) { - runLinter(); - } + runLinter(); finish(); }, finish); From fe77f541f600492025680e82b58895f3f3ceedd4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 25 May 2016 07:24:14 -0700 Subject: [PATCH 27/90] Always include a root node in the navigation bar. This lets us change the navigation bar counting algorithm to traverse from the root only, so it never has duplicate nodes. --- src/harness/fourslash.ts | 19 +++++++++---------- src/services/navigationBar.ts | 13 +------------ 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 3bce13820d7..048beb21e09 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1961,23 +1961,22 @@ namespace FourSlash { public verifyNavigationBarCount(expected: number) { const items = this.languageService.getNavigationBarItems(this.activeFile.fileName); - const actual = this.getNavigationBarItemsCount(items); + const actual = this.getNavigationBarItemsCount(items[0]); if (expected !== actual) { this.raiseError(`verifyNavigationBarCount failed - found: ${actual} navigation items, expected: ${expected}.`); } } - private getNavigationBarItemsCount(items: ts.NavigationBarItem[]) { - let result = 0; - if (items) { - for (let i = 0, n = items.length; i < n; i++) { - result++; - result += this.getNavigationBarItemsCount(items[i].childItems); - } + private getNavigationBarItemsCount(root: ts.NavigationBarItem) { + ts.Debug.assert(root.kind === ts.ScriptElementKind.moduleElement); + function recur(item: ts.NavigationBarItem) { + let count = 1; + for (const child of item.childItems) + count += recur(child); + return count; } - - return result; + return recur(root); } public verifyNavigationBarContains(name: string, kind: string, fileName?: string, parentName?: string, isAdditionalSpan?: boolean, markerPosition?: number) { diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index bcf0726ffd5..f6f7b741a56 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -9,16 +9,10 @@ namespace ts.NavigationBar { return getJsNavigationBarItems(sourceFile, compilerOptions); } - // If the source file has any child items, then it included in the tree - // and takes lexical ownership of all other top-level items. - let hasGlobalNode = false; - return getItemsWorker(getTopLevelNodes(sourceFile), createTopLevelItem); function getIndent(node: Node): number { - // If we have a global node in the tree, - // then it adds an extra layer of depth to all subnodes. - let indent = hasGlobalNode ? 1 : 0; + let indent = 1; // Global node is the only one with indent 0. let current = node.parent; while (current) { @@ -508,11 +502,6 @@ namespace ts.NavigationBar { function createSourceFileItem(node: SourceFile): ts.NavigationBarItem { const childItems = getItemsWorker(getChildNodes(node.statements), createChildItem); - if (childItems === undefined || childItems.length === 0) { - return undefined; - } - - hasGlobalNode = true; const rootName = isExternalModule(node) ? "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(node.fileName)))) + "\"" : ""; From fe970abc81e04cfc21b01db6138289fccbfbeef8 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 26 May 2016 07:30:31 -0700 Subject: [PATCH 28/90] Change tests to use the full JSON output of the navigation bar. This reduces the confusion of verify.navigationBarCount() counting duplicate items. --- src/harness/fourslash.ts | 113 ++++------- .../fourslash/deleteClassWithEnumPresent.ts | 33 +++- tests/cases/fourslash/fourslash.ts | 4 +- .../cases/fourslash/getNavigationBarItems.ts | 32 +++- tests/cases/fourslash/navbar_const.ts | 24 ++- .../navbar_contains-no-duplicates.ts | 152 ++++++++++++--- tests/cases/fourslash/navbar_exportDefault.ts | 97 ++++++++-- tests/cases/fourslash/navbar_let.ts | 24 ++- .../navigationBarItemsBindingPatterns.ts | 66 +++++-- ...ionBarItemsBindingPatternsInConstructor.ts | 48 ++++- .../navigationBarItemsEmptyConstructors.ts | 29 ++- .../fourslash/navigationBarItemsExports.ts | 33 +++- .../fourslash/navigationBarItemsFunctions.ts | 60 ++++-- .../navigationBarItemsFunctionsBroken.ts | 25 ++- .../navigationBarItemsFunctionsBroken2.ts | 25 ++- .../fourslash/navigationBarItemsImports.ts | 60 ++++-- ...ionBarItemsInsideMethodsAndConstructors.ts | 159 +++++++++++++--- .../fourslash/navigationBarItemsItems.ts | 174 ++++++++++++++--- .../fourslash/navigationBarItemsItems2.ts | 29 ++- ...rItemsItemsContainsNoAnonymousFunctions.ts | 58 +++++- .../navigationBarItemsItemsExternalModules.ts | 36 +++- ...navigationBarItemsItemsExternalModules2.ts | 43 ++++- ...navigationBarItemsItemsExternalModules3.ts | 43 ++++- .../navigationBarItemsItemsModuleVariables.ts | 56 +++++- .../navigationBarItemsMissingName1.ts | 39 ++-- .../navigationBarItemsMissingName2.ts | 26 ++- .../fourslash/navigationBarItemsModules.ts | 138 +++++++++++--- ...ationBarItemsMultilineStringIdentifiers.ts | 98 ++++++++-- ...BarItemsPropertiesDefinedInConstructors.ts | 41 +++- .../fourslash/navigationBarItemsSymbols1.ts | 42 ++++- .../fourslash/navigationBarItemsSymbols2.ts | 37 +++- .../fourslash/navigationBarItemsSymbols3.ts | 25 ++- .../fourslash/navigationBarItemsTypeAlias.ts | 16 +- tests/cases/fourslash/server/navbar01.ts | 175 +++++++++++++++--- .../shims-pp/getNavigationBarItems.ts | 22 ++- .../fourslash/shims/getNavigationBarItems.ts | 22 ++- 36 files changed, 1656 insertions(+), 448 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 048beb21e09..c33c9107ad7 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1959,70 +1959,42 @@ namespace FourSlash { } } - public verifyNavigationBarCount(expected: number) { - const items = this.languageService.getNavigationBarItems(this.activeFile.fileName); - const actual = this.getNavigationBarItemsCount(items[0]); - - if (expected !== actual) { - this.raiseError(`verifyNavigationBarCount failed - found: ${actual} navigation items, expected: ${expected}.`); + public verifyNavigationBar(json: any) { + let items = this.languageService.getNavigationBarItems(this.activeFile.fileName); + items = this.simplifyNavigationBar(items); + if (JSON.stringify(items) !== JSON.stringify(json)) { + this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, undefined, 2)}`) } } - private getNavigationBarItemsCount(root: ts.NavigationBarItem) { - ts.Debug.assert(root.kind === ts.ScriptElementKind.moduleElement); - function recur(item: ts.NavigationBarItem) { - let count = 1; - for (const child of item.childItems) - count += recur(child); - return count; - } - return recur(root); - } - - public verifyNavigationBarContains(name: string, kind: string, fileName?: string, parentName?: string, isAdditionalSpan?: boolean, markerPosition?: number) { - fileName = fileName || this.activeFile.fileName; - const items = this.languageService.getNavigationBarItems(fileName); - - if (!items || items.length === 0) { - this.raiseError("verifyNavigationBarContains failed - found 0 navigation items, expected at least one."); - } - - if (this.navigationBarItemsContains(items, name, kind, parentName)) { - return; - } - - const missingItem = { name, kind, parentName }; - this.raiseError(`verifyNavigationBarContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`); - } - - private navigationBarItemsContains(items: ts.NavigationBarItem[], name: string, kind: string, parentName?: string) { - function recur(items: ts.NavigationBarItem[], curParentName: string) { - for (let i = 0; i < items.length; i++) { - const item = items[i]; - if (item && item.text === name && item.kind === kind && (!parentName || curParentName === parentName)) { - return true; - } - if (recur(item.childItems, item.text)) { - return true; - } - } - return false; - } - return recur(items, ""); - } - - public verifyNavigationBarChildItem(parent: string, name: string, kind: string) { - const items = this.languageService.getNavigationBarItems(this.activeFile.fileName); - - for (let i = 0; i < items.length; i++) { - const item = items[i]; - if (item.text === parent) { - if (this.navigationBarItemsContains(item.childItems, name, kind)) - return; - const missingItem = { name, kind }; - this.raiseError(`verifyNavigationBarChildItem failed - could not find the item: ${JSON.stringify(missingItem)} in the children list: (${JSON.stringify(item.childItems, undefined, 2)})`); - } - } + // Remove any properties that tend to all have the same value so that test data is easier to read. + private simplifyNavigationBar(items: ts.NavigationBarItem[]): any { + return items.map(item => { + item = ts.clone(item); + if (item.kindModifiers === "") + delete item.kindModifiers; + delete item.spans; + item.childItems = item.childItems.map(child => { + child = ts.clone(child); + ts.Debug.assert(child.childItems.length === 0); + ts.Debug.assert(child.indent === 0); + ts.Debug.assert(child.bolded === false); + ts.Debug.assert(child.grayed === false); + delete child.childItems; + delete child.indent; + delete child.bolded; + delete child.grayed; + delete child.spans; + if (child.kindModifiers === "") + delete child.kindModifiers; + return child; + }); + if (item.bolded === false) + delete item.bolded; + if (item.grayed === false) + delete item.grayed; + return item; + }) } public printNavigationItems(searchValue: string) { @@ -3041,23 +3013,8 @@ namespace FourSlashInterface { this.DocCommentTemplate(/*expectedText*/ undefined, /*expectedOffset*/ undefined, /*empty*/ true); } - public navigationBarCount(count: number) { - this.state.verifyNavigationBarCount(count); - } - - // TODO: figure out what to do with the unused arguments. - public navigationBarContains( - name: string, - kind: string, - fileName?: string, - parentName?: string, - isAdditionalSpan?: boolean, - markerPosition?: number) { - this.state.verifyNavigationBarContains(name, kind, fileName, parentName, isAdditionalSpan, markerPosition); - } - - public navigationBarChildItem(parent: string, name: string, kind: string) { - this.state.verifyNavigationBarChildItem(parent, name, kind); + public navigationBar(json: any) { + this.state.verifyNavigationBar(json); } public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) { diff --git a/tests/cases/fourslash/deleteClassWithEnumPresent.ts b/tests/cases/fourslash/deleteClassWithEnumPresent.ts index 29f7c61e259..d8d4478afe7 100644 --- a/tests/cases/fourslash/deleteClassWithEnumPresent.ts +++ b/tests/cases/fourslash/deleteClassWithEnumPresent.ts @@ -5,4 +5,35 @@ goTo.marker(); edit.deleteAtCaret('class Bar { }'.length); -verify.navigationBarContains('Foo', 'enum', 'tests/cases/fourslash/deleteClassWithEnumPresent.ts', ''); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "Foo", + "kind": "enum" + } + ], + "indent": 0 + }, + { + "text": "Foo", + "kind": "enum", + "childItems": [ + { + "text": "a", + "kind": "property" + }, + { + "text": "b", + "kind": "property" + }, + { + "text": "c", + "kind": "property" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 08883f163f2..8a0d2d36364 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -175,9 +175,7 @@ declare namespace FourSlashInterface { DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void; noDocCommentTemplate(): void; - navigationBarCount(count: number): void; - navigationBarContains(name: string, kind: string, fileName?: string, parentName?: string, isAdditionalSpan?: boolean, markerPosition?: number): void; - navigationBarChildItem(parent: string, text: string, kind: string): void; + navigationBar(json: any): void; navigationItemsListCount(count: number, searchValue: string, matchKind?: string): void; navigationItemsListContains(name: string, kind: string, searchValue: string, matchKind: string, fileName?: string, parentName?: string): void; occurrencesAtPositionContains(range: Range, isWriteAccess?: boolean): void; diff --git a/tests/cases/fourslash/getNavigationBarItems.ts b/tests/cases/fourslash/getNavigationBarItems.ts index cd09f61011e..a2b0492514a 100644 --- a/tests/cases/fourslash/getNavigationBarItems.ts +++ b/tests/cases/fourslash/getNavigationBarItems.ts @@ -5,7 +5,31 @@ //// ["bar"]: string; ////} -verify.navigationBarCount(5); -verify.navigationBarContains("C", "class"); -verify.navigationBarChildItem("C", "[\"bar\"]", "property"); -verify.navigationBarChildItem("C", "foo", "property"); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "C", + "kind": "class", + "childItems": [ + { + "text": "[\"bar\"]", + "kind": "property" + }, + { + "text": "foo", + "kind": "property" + } + ], + "indent": 1 + } +]) diff --git a/tests/cases/fourslash/navbar_const.ts b/tests/cases/fourslash/navbar_const.ts index 5d8b42607c8..02a10f46a74 100644 --- a/tests/cases/fourslash/navbar_const.ts +++ b/tests/cases/fourslash/navbar_const.ts @@ -1,13 +1,17 @@ /// -//// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0; +//// const c = 0; -test.markers().forEach(marker => { - verify.navigationBarContains( - marker.data.itemName, - marker.data.kind, - marker.fileName, - marker.data.parentName, - marker.data.isAdditionalRange, - marker.position); -}); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "c", + "kind": "const" + } + ], + "indent": 0 + } +]); diff --git a/tests/cases/fourslash/navbar_contains-no-duplicates.ts b/tests/cases/fourslash/navbar_contains-no-duplicates.ts index 42b33a722ad..333130ff795 100644 --- a/tests/cases/fourslash/navbar_contains-no-duplicates.ts +++ b/tests/cases/fourslash/navbar_contains-no-duplicates.ts @@ -1,42 +1,142 @@ /// -//// {| "itemName": "Windows", "kind": "module", "parentName": "" |}declare module Windows { -//// {| "itemName": "Foundation", "kind": "module", "parentName": "Windows" |}export module Foundation { -//// export var {| "itemName": "A", "kind": "var" |}A; -//// {| "itemName": "Test", "kind": "class", "parentName": "Foundation" |}export class Test { -//// {| "itemName": "wow", "kind": "method" |}public wow(); +//// declare module Windows { +//// export module Foundation { +//// export var A; +//// export class Test { +//// public wow(); //// } //// } //// } //// -//// {| "itemName": "Windows", "kind": "module", "parentName": "", "isAdditionalRange": true |}declare module Windows { -//// {| "itemName": "Foundation", "kind": "module", "parentName": "Windows", "isAdditionalRange": true |}export module Foundation { -//// export var {| "itemName": "B", "kind": "var" |}B; -//// {| "itemName": "Test", "kind": "module" |}export module Test { -//// {| "itemName": "Boom", "kind": "function" |}export function Boom(): number; +//// declare module Windows { +//// export module Foundation { +//// export var B; +//// export module Test { +//// export function Boom(): number; //// } //// } //// } //// -//// {| "itemName": "ABC", "kind": "class", "parentName": "" |}class ABC { -//// {| "itemName": "foo", "kind": "method" |}public foo() { +//// class ABC { +//// public foo() { //// return 3; //// } //// } //// -//// {| "itemName": "ABC", "kind": "module", "parentName": "" |}module ABC { -//// export var {| "itemName": "x", "kind": "var" |}x = 3; +//// module ABC { +//// export var x = 3; //// } -test.markers().forEach(marker => { - if (marker.data) { - verify.navigationBarContains( - marker.data.itemName, - marker.data.kind, - marker.fileName, - marker.data.parentName, - marker.data.isAdditionalRange, - marker.position); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "ABC", + "kind": "class" + }, + { + "text": "ABC", + "kind": "module" + }, + { + "text": "Windows", + "kind": "module", + "kindModifiers": "declare" + } + ], + "indent": 0 + }, + { + "text": "ABC", + "kind": "class", + "childItems": [ + { + "text": "foo", + "kind": "method", + "kindModifiers": "public" + } + ], + "indent": 1 + }, + { + "text": "ABC", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + }, + { + "text": "Windows", + "kind": "module", + "kindModifiers": "declare", + "childItems": [ + { + "text": "Foundation", + "kind": "module", + "kindModifiers": "export,declare" + } + ], + "indent": 1 + }, + { + "text": "Foundation", + "kind": "module", + "kindModifiers": "export,declare", + "childItems": [ + { + "text": "A", + "kind": "var", + "kindModifiers": "export,declare" + }, + { + "text": "Test", + "kind": "class", + "kindModifiers": "export,declare" + }, + { + "text": "B", + "kind": "var", + "kindModifiers": "export,declare" + }, + { + "text": "Test", + "kind": "module", + "kindModifiers": "export,declare" + } + ], + "indent": 2 + }, + { + "text": "Test", + "kind": "class", + "kindModifiers": "export,declare", + "childItems": [ + { + "text": "wow", + "kind": "method", + "kindModifiers": "public,declare" + } + ], + "indent": 3 + }, + { + "text": "Test", + "kind": "module", + "kindModifiers": "export,declare", + "childItems": [ + { + "text": "Boom", + "kind": "function", + "kindModifiers": "export,declare" + } + ], + "indent": 3 } -}); - -verify.navigationBarCount(19); +]); diff --git a/tests/cases/fourslash/navbar_exportDefault.ts b/tests/cases/fourslash/navbar_exportDefault.ts index a56eeb8b226..7c666589f12 100644 --- a/tests/cases/fourslash/navbar_exportDefault.ts +++ b/tests/cases/fourslash/navbar_exportDefault.ts @@ -1,24 +1,93 @@ /// // @Filename: a.ts -//// {| "itemName": "default", "kind": "class", "parentName": "" |}export default class { } +////export default class { } // @Filename: b.ts -//// {| "itemName": "C", "kind": "class", "parentName": "" |}export default class C { } +////export default class C { } // @Filename: c.ts -//// {| "itemName": "default", "kind": "function", "parentName": "" |}export default function { } +////export default function { } // @Filename: d.ts -//// {| "itemName": "Func", "kind": "function", "parentName": "" |}export default function Func { } +////export default function Func { } -test.markers().forEach(marker => { - goTo.file(marker.fileName); - verify.navigationBarContains( - marker.data.itemName, - marker.data.kind, - marker.fileName, - marker.data.parentName, - marker.data.isAdditionalRange, - marker.position); -}); \ No newline at end of file +goTo.file("a.ts"); +verify.navigationBar([ + { + "text": "\"a\"", + "kind": "module", + "childItems": [], + "indent": 0 + }, + { + "text": "default", + "kind": "class", + "kindModifiers": "export", + "childItems": [], + "indent": 1 + } +]); + +goTo.file("b.ts"); +verify.navigationBar([ + { + "text": "\"b\"", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "class", + "kindModifiers": "export" + } + ], + "indent": 0 + }, + { + "text": "C", + "kind": "class", + "kindModifiers": "export", + "childItems": [], + "indent": 1 + } +]); + +goTo.file("c.ts"); +verify.navigationBar([ + { + "text": "\"c\"", + "kind": "module", + "childItems": [], + "indent": 0 + }, + { + "text": "default", + "kind": "function", + "kindModifiers": "export", + "childItems": [], + "indent": 1 + } +]); + +goTo.file("d.ts"); +verify.navigationBar([ + { + "text": "\"d\"", + "kind": "module", + "childItems": [ + { + "text": "Func", + "kind": "function", + "kindModifiers": "export" + } + ], + "indent": 0 + }, + { + "text": "Func", + "kind": "function", + "kindModifiers": "export", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navbar_let.ts b/tests/cases/fourslash/navbar_let.ts index c3b125526ef..dc9d9c07857 100644 --- a/tests/cases/fourslash/navbar_let.ts +++ b/tests/cases/fourslash/navbar_let.ts @@ -1,13 +1,17 @@ /// -//// {| "itemName": "c", "kind": "let", "parentName": "" |}let c = 0; +////let c = 0; -test.markers().forEach(marker => { - verify.navigationBarContains( - marker.data.itemName, - marker.data.kind, - marker.fileName, - marker.data.parentName, - marker.data.isAdditionalRange, - marker.position); -}); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "c", + "kind": "let" + } + ], + "indent": 0 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts b/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts index afac15daacc..007a9c9b804 100644 --- a/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts +++ b/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts @@ -6,16 +6,56 @@ ////const bar1, [c, d] ////var {e, x: [f, g]} = {a:1, x:[]}; -verify.navigationBarCount(12); // global (1) + variable declarations (4) + binding patterns (7) -verify.navigationBarContains("foo", "var"); -verify.navigationBarContains("bar", "var"); -verify.navigationBarContains("foo1", "let") -verify.navigationBarContains("a", "let"); -verify.navigationBarContains("b", "let"); -verify.navigationBarContains("bar1", "const"); -verify.navigationBarContains("c", "const"); -verify.navigationBarContains("d", "const"); -verify.navigationBarContains("e", "var"); -verify.navigationBarContains("f", "var"); -verify.navigationBarContains("g", "var"); - +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "let" + }, + { + "text": "b", + "kind": "let" + }, + { + "text": "bar", + "kind": "var" + }, + { + "text": "bar1", + "kind": "const" + }, + { + "text": "c", + "kind": "const" + }, + { + "text": "d", + "kind": "const" + }, + { + "text": "e", + "kind": "var" + }, + { + "text": "f", + "kind": "var" + }, + { + "text": "foo", + "kind": "var" + }, + { + "text": "foo1", + "kind": "let" + }, + { + "text": "g", + "kind": "var" + } + ], + "indent": 0 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts b/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts index 24979e141e5..41751f5c74b 100644 --- a/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts +++ b/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts @@ -11,4 +11,50 @@ //// } ////} -verify.navigationBarCount(9); // global + 2 children + 2x(class + field + constructor) +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "A", + "kind": "class" + }, + { + "text": "B", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "A", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "x", + "kind": "property" + } + ], + "indent": 1 + }, + { + "text": "B", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "x", + "kind": "property" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts b/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts index a80f4c596d4..27c33f6afb2 100644 --- a/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts @@ -5,8 +5,27 @@ //// } ////} -verify.navigationBarContains("Test", "class"); -verify.navigationBarContains("constructor", "constructor"); - -// no other items -verify.navigationBarCount(4); // global + 1 child, Test + 1 child \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "Test", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "Test", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsExports.ts b/tests/cases/fourslash/navigationBarItemsExports.ts index b88b60d0efb..79422f96274 100644 --- a/tests/cases/fourslash/navigationBarItemsExports.ts +++ b/tests/cases/fourslash/navigationBarItemsExports.ts @@ -1,18 +1,33 @@ /// -////export { {| "itemName": "a", "kind": "alias", "parentName": "" |}a } from "a"; +////export { a } from "a"; //// -////export { {| "itemName": "B", "kind": "alias", "parentName": "" |}b as B } from "a" +////export { b as B } from "a" //// -////{| "itemName": "e", "kind": "alias", "parentName": "" |} export import e = require("a"); +////export import e = require("a"); //// ////export * from "a"; // no bindings here -test.markers().forEach((marker) => { - if (marker.data) { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); +verify.navigationBar([ + { + "text": "\"navigationBarItemsExports\"", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "alias" + }, + { + "text": "B", + "kind": "alias" + }, + { + "text": "e", + "kind": "alias", + "kindModifiers": "export" + } + ], + "indent": 0 } -}); - -verify.navigationBarCount(4); +]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctions.ts b/tests/cases/fourslash/navigationBarItemsFunctions.ts index 0948923c046..e2260f2ff2b 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctions.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctions.ts @@ -1,23 +1,61 @@ /// -////{| "itemName": "", "kind": "module" |} -//// -////{| "itemName": "foo", "kind": "function" |}function foo() { +////function foo() { //// var x = 10; -//// {| "itemName": "bar", "kind": "function", "parentName": "foo" |}function bar() { +//// function bar() { //// var y = 10; -//// {| "itemName": "biz", "kind": "function", "parentName": "bar" |}function biz() { +//// function biz() { //// var z = 10; //// } //// } ////} //// -////{| "itemName": "baz", "kind": "function", "parentName": "" |}function baz() { +////function baz() { //// var v = 10; ////} -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(8); // 4 functions + global. Note: there are 8 because of the functions show up at the top level and as child items. +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "baz", + "kind": "function" + }, + { + "text": "foo", + "kind": "function" + } + ], + "indent": 0 + }, + { + "text": "baz", + "kind": "function", + "childItems": [], + "indent": 1 + }, + { + "text": "foo", + "kind": "function", + "childItems": [ + { + "text": "bar", + "kind": "function" + } + ], + "indent": 1 + }, + { + "text": "bar", + "kind": "function", + "childItems": [ + { + "text": "biz", + "kind": "function" + } + ], + "indent": 2 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts index e8bf1d33fc3..dc84b50ff30 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts @@ -1,12 +1,25 @@ /// -////{| "itemName": "f", "kind": "function" |} ////function f() { //// function; ////} -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(3); // and 'f'. \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "f", + "kind": "function" + } + ], + "indent": 0 + }, + { + "text": "f", + "kind": "function", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts index 9576d4dd789..b4612efcac0 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts @@ -1,13 +1,26 @@ /// ////function; -////{| "itemName": "f", "kind": "function" |} ////function f() { //// function; ////} -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(3); // and 'f' \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "f", + "kind": "function" + } + ], + "indent": 0 + }, + { + "text": "f", + "kind": "function", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsImports.ts b/tests/cases/fourslash/navigationBarItemsImports.ts index 53619da310a..d6526a7a68c 100644 --- a/tests/cases/fourslash/navigationBarItemsImports.ts +++ b/tests/cases/fourslash/navigationBarItemsImports.ts @@ -1,25 +1,57 @@ /// -////import {| "itemName": "d1", "kind": "alias", "parentName": "" |}d1 from "a"; +////import d1 from "a"; //// -////import { {| "itemName": "a", "kind": "alias", "parentName": "" |}a } from "a"; +////import { a } from "a"; //// -////import { {| "itemName": "B", "kind": "alias", "parentName": "" |}b as B } from "a" +////import { b as B } from "a" //// -////import {| "itemName": "d2", "kind": "alias", "parentName": "" |}d2, -//// { {| "itemName": "c", "kind": "alias", "parentName": "" |}c, -//// {| "itemName": "D", "kind": "alias", "parentName": "" |} d as D } from "a" +////import d2, { c, d as D } from "a" //// -////{| "itemName": "e", "kind": "alias", "parentName": "" |}import e = require("a"); +////import e = require("a"); //// -////import {| "itemName": "ns", "kind": "alias", "parentName": "" |}* as ns from "a"; +////import * as ns from "a"; -test.markers().forEach((marker) => { - if (marker.data) { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); +verify.navigationBar([ + { + "text": "\"navigationBarItemsImports\"", + "kind": "module", + "childItems": [ + { + "text": "a", + "kind": "alias" + }, + { + "text": "B", + "kind": "alias" + }, + { + "text": "c", + "kind": "alias" + }, + { + "text": "D", + "kind": "alias" + }, + { + "text": "d1", + "kind": "alias" + }, + { + "text": "d2", + "kind": "alias" + }, + { + "text": "e", + "kind": "alias" + }, + { + "text": "ns", + "kind": "alias" + } + ], + "indent": 0 } -}); - -verify.navigationBarCount(9); +]); diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts index 8d93d241ae1..7d2fb4ed127 100644 --- a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -2,41 +2,140 @@ ////class Class { //// constructor() { -//// {| "itemName": "LocalFunctionInConstructor", "kind": "function", "parentName": "constructor"|}function LocalFunctionInConstructor() { -//// -//// } -//// -//// {| "itemName": "LocalInterfaceInConstrcutor", "kind": "interface", "parentName": "constructor"|}interface LocalInterfaceInConstrcutor { -//// } -//// -//// {| "itemName": "LocalEnumInConstructor", "kind": "enum", "parentName": "constructor"|}enum LocalEnumInConstructor { -//// {| "itemName": "LocalEnumMemberInConstructor", "kind": "property", "parentName": "LocalEnumInConstructor"|}LocalEnumMemberInConstructor, -//// } +//// function LocalFunctionInConstructor() {} +//// interface LocalInterfaceInConstrcutor {} +//// enum LocalEnumInConstructor { LocalEnumMemberInConstructor } //// } //// //// method() { -//// {| "itemName": "LocalFunctionInMethod", "kind": "function", "parentName": "method"|}function LocalFunctionInMethod() { -//// {| "itemName": "LocalFunctionInLocalFunctionInMethod", "kind": "function", "parentName": "LocalFunctionInMethod"|}function LocalFunctionInLocalFunctionInMethod() { -//// -//// } -//// } -//// -//// {| "itemName": "LocalInterfaceInMethod", "kind": "interface", "parentName": "method"|}interface LocalInterfaceInMethod { -//// } -//// -//// {| "itemName": "LocalEnumInMethod", "kind": "enum", "parentName": "method"|}enum LocalEnumInMethod { -//// {| "itemName": "LocalEnumMemberInMethod", "kind": "property", "parentName": "LocalEnumInMethod"|}LocalEnumMemberInMethod, +//// function LocalFunctionInMethod() { +//// function LocalFunctionInLocalFunctionInMethod() {} //// } +//// interface LocalInterfaceInMethod {} +//// enum LocalEnumInMethod { LocalEnumMemberInMethod } //// } //// -//// emptyMethod() { // Non child functions method should not be duplicated -//// -//// } +//// emptyMethod() { } // Non child functions method should not be duplicated ////} -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -// no other items -verify.navigationBarCount(23); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "Class", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "Class", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "emptyMethod", + "kind": "method" + }, + { + "text": "method", + "kind": "method" + } + ], + "indent": 1 + }, + { + "text": "constructor", + "kind": "constructor", + "childItems": [ + { + "text": "LocalEnumInConstructor", + "kind": "enum" + }, + { + "text": "LocalFunctionInConstructor", + "kind": "function" + }, + { + "text": "LocalInterfaceInConstrcutor", + "kind": "interface" + } + ], + "indent": 2 + }, + { + "text": "LocalEnumInConstructor", + "kind": "enum", + "childItems": [ + { + "text": "LocalEnumMemberInConstructor", + "kind": "property" + } + ], + "indent": 2 + }, + { + "text": "LocalFunctionInConstructor", + "kind": "function", + "childItems": [], + "indent": 2 + }, + { + "text": "LocalInterfaceInConstrcutor", + "kind": "interface", + "childItems": [], + "indent": 2 + }, + { + "text": "method", + "kind": "method", + "childItems": [ + { + "text": "LocalEnumInMethod", + "kind": "enum" + }, + { + "text": "LocalFunctionInMethod", + "kind": "function" + }, + { + "text": "LocalInterfaceInMethod", + "kind": "interface" + } + ], + "indent": 2 + }, + { + "text": "LocalEnumInMethod", + "kind": "enum", + "childItems": [ + { + "text": "LocalEnumMemberInMethod", + "kind": "property" + } + ], + "indent": 2 + }, + { + "text": "LocalFunctionInMethod", + "kind": "function", + "childItems": [ + { + "text": "LocalFunctionInLocalFunctionInMethod", + "kind": "function" + } + ], + "indent": 2 + }, + { + "text": "LocalInterfaceInMethod", + "kind": "interface", + "childItems": [], + "indent": 2 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsItems.ts b/tests/cases/fourslash/navigationBarItemsItems.ts index e8e9fd40824..013d28cd402 100644 --- a/tests/cases/fourslash/navigationBarItemsItems.ts +++ b/tests/cases/fourslash/navigationBarItemsItems.ts @@ -1,52 +1,172 @@ /// ////// Interface -////{| "itemName": "IPoint", "kind": "interface", "parentName": "" |}interface IPoint { -//// {| "itemName": "getDist", "kind": "method", "parentName": "IPoint" |}getDist(): number; -//// {| "itemName": "new()", "kind": "construct", "parentName": "IPoint" |}new(): IPoint; -//// {| "itemName": "()", "kind": "call", "parentName": "IPoint" |}(): any; -//// {| "itemName": "[]", "kind": "index", "parentName": "IPoint" |}[x:string]: number; -//// {| "itemName": "prop", "kind": "property", "parentName": "IPoint" |}prop: string; +////interface IPoint { +//// getDist(): number; +//// new(): IPoint; +//// (): any; +//// [x:string]: number; +//// prop: string; ////} //// /////// Module -////{| "itemName": "Shapes", "kind": "module", "parentName": "" |}module Shapes { +////module Shapes { //// //// // Class -//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes" |}export class Point implements IPoint { -//// {| "itemName": "constructor", "kind": "constructor", "parentName": "Point" |}constructor (public x: number, public y: number) { } +//// export class Point implements IPoint { +//// constructor (public x: number, public y: number) { } //// //// // Instance member -//// {| "itemName": "getDist", "kind": "method", "parentName": "Point" |}getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); } +//// getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); } //// //// // Getter -//// {| "itemName": "value", "kind": "getter", "parentName": "Point" |}get value(): number { return 0; } +//// get value(): number { return 0; } //// //// // Setter -//// {| "itemName": "value", "kind": "setter", "parentName": "Point" |}set value(newValue: number) { return; } +//// set value(newValue: number) { return; } //// //// // Static member -//// {| "itemName": "origin", "kind": "property", "parentName": "Point" |}static origin = new Point(0, 0); +//// static origin = new Point(0, 0); //// //// // Static method -//// {| "itemName": "getOrigin", "kind": "method", "parentName": "Point" |}private static getOrigin() { return Point.origin;} +//// private static getOrigin() { return Point.origin; } //// } //// -//// {| "itemName": "Values", "kind": "enum", "parentName": "Shapes" |}enum Values { -//// value1, -//// {| "itemName": "value2", "kind": "property", "parentName": "Values" |}value2, -//// value3, -//// } +//// enum Values { value1, value2, value3 } ////} //// ////// Local variables -////{| "itemName": "p", "kind": "var", "parentName": "" |}var p: IPoint = new Shapes.Point(3, 4); -////{| "itemName": "dist", "kind": "var", "parentName": "" |}var dist = p.getDist(); +////var p: IPoint = new Shapes.Point(3, 4); +////var dist = p.getDist(); -test.markers().forEach((marker) => { - if (marker.data) { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "dist", + "kind": "var" + }, + { + "text": "IPoint", + "kind": "interface" + }, + { + "text": "p", + "kind": "var" + }, + { + "text": "Shapes", + "kind": "module" + } + ], + "indent": 0 + }, + { + "text": "IPoint", + "kind": "interface", + "childItems": [ + { + "text": "()", + "kind": "call" + }, + { + "text": "new()", + "kind": "construct" + }, + { + "text": "[]", + "kind": "index" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "prop", + "kind": "property" + } + ], + "indent": 1 + }, + { + "text": "Shapes", + "kind": "module", + "childItems": [ + { + "text": "Point", + "kind": "class", + "kindModifiers": "export" + }, + { + "text": "Values", + "kind": "enum" + } + ], + "indent": 1 + }, + { + "text": "Point", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "getOrigin", + "kind": "method", + "kindModifiers": "private,static" + }, + { + "text": "origin", + "kind": "property", + "kindModifiers": "static" + }, + { + "text": "value", + "kind": "getter" + }, + { + "text": "value", + "kind": "setter" + }, + { + "text": "x", + "kind": "property", + "kindModifiers": "public" + }, + { + "text": "y", + "kind": "property", + "kindModifiers": "public" + } + ], + "indent": 2 + }, + { + "text": "Values", + "kind": "enum", + "childItems": [ + { + "text": "value1", + "kind": "property" + }, + { + "text": "value2", + "kind": "property" + }, + { + "text": "value3", + "kind": "property" + } + ], + "indent": 2 } -}); - -verify.navigationBarCount(27); +]); diff --git a/tests/cases/fourslash/navigationBarItemsItems2.ts b/tests/cases/fourslash/navigationBarItemsItems2.ts index 013f27b4d71..27c32531d6b 100644 --- a/tests/cases/fourslash/navigationBarItemsItems2.ts +++ b/tests/cases/fourslash/navigationBarItemsItems2.ts @@ -1,6 +1,5 @@ /// - /////**/ goTo.marker(); @@ -8,5 +7,29 @@ edit.insertLine("module A"); edit.insert("export class "); // should not crash -verify.navigationBarCount(4); - +verify.navigationBar([ + { + "text": "\"navigationBarItemsItems2\"", + "kind": "module", + "childItems": [ + { + "text": "A", + "kind": "module" + } + ], + "indent": 0 + }, + { + "text": "default", + "kind": "class", + "kindModifiers": "export", + "childItems": [], + "indent": 1 + }, + { + "text": "A", + "kind": "module", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts b/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts index dac6e96ab93..34e4a1c0c89 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts @@ -30,15 +30,57 @@ ////} goTo.marker("file1"); -verify.navigationBarCount(0); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [], + "indent": 0 + } +]); goTo.marker("file2"); -verify.navigationBarContains("", "module"); -verify.navigationBarContains("x", "var"); -verify.navigationBarCount(2); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var" + } + ], + "indent": 0 + } +]); goTo.marker("file3"); -verify.navigationBarContains("", "module"); -verify.navigationBarContains("foo", "function"); -verify.navigationBarContains("bar", "function"); -verify.navigationBarCount(5); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "bar", + "kind": "function" + }, + { + "text": "foo", + "kind": "function" + } + ], + "indent": 0 + }, + { + "text": "bar", + "kind": "function", + "childItems": [], + "indent": 1 + }, + { + "text": "foo", + "kind": "function", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts index 7b2e0def4bf..f2df772fb19 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts @@ -1,11 +1,33 @@ /// -////{| "itemName": "Bar", "kind": "class", "parentName": "\"navigationBarItemsItemsExternalModules\"" |}export class Bar { -//// {| "itemName": "s", "kind": "property", "parentName": "Bar" |}public s: string; +////export class Bar { +//// public s: string; ////} -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(4); // external module node + class + property +verify.navigationBar([ + { + "text": "\"navigationBarItemsItemsExternalModules\"", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export" + } + ], + "indent": 0 + }, + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "s", + "kind": "property", + "kindModifiers": "public" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts index 9087493b298..54d4759afe6 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts @@ -1,15 +1,40 @@ /// // @Filename: test/file.ts -////{| "itemName": "Bar", "kind": "class", "parentName": "\"file\"" |}export class Bar { -//// {| "itemName": "s", "kind": "property", "parentName": "Bar" |}public s: string; +////export class Bar { +//// public s: string; ////} -////{| "itemName": "\"file\"", "kind": "module" |} -////{| "itemName": "x", "kind": "var", "parentName": "\"file\"" |} ////export var x: number; -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(5); // external module node + variable in module + class + property +verify.navigationBar([ + { + "text": "\"file\"", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export" + }, + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 0 + }, + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "s", + "kind": "property", + "kindModifiers": "public" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts index a00b3a131dd..2db04c980d2 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts @@ -1,15 +1,40 @@ /// // @Filename: test/my fil"e.ts -////{| "itemName": "Bar", "kind": "class", "parentName": "\"my fil\\\"e\"" |}export class Bar { -//// {| "itemName": "s", "kind": "property", "parentName": "Bar" |}public s: string; +////export class Bar { +//// public s: string; ////} -////{| "itemName": "\"my fil\\\"e\"", "kind": "module" |} -////{| "itemName": "x", "kind": "var", "parentName": "\"my fil\\\"e\"" |} ////export var x: number; -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(5); // external module node + 2 children + class + property +verify.navigationBar([ + { + "text": "\"my fil\\\"e\"", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export" + }, + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 0 + }, + { + "text": "Bar", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "s", + "kind": "property", + "kindModifiers": "public" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts b/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts index 5429011888c..ef4022ce046 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts @@ -19,12 +19,56 @@ //// export var z = 0; ////} goTo.marker("file1"); -verify.navigationBarContains("Module1", "module"); -verify.navigationBarContains("x", "var"); // nothing else should show up -verify.navigationBarCount(4); // , its child, Module1, its child +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "Module1", + "kind": "module" + } + ], + "indent": 0 + }, + { + "text": "Module1", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + } +]); goTo.marker("file2"); -verify.navigationBarContains("Module1.SubModule", "module"); -verify.navigationBarContains("y", "var"); -verify.navigationBarCount(4); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "Module1.SubModule", + "kind": "module" + } + ], + "indent": 0 + }, + { + "text": "Module1.SubModule", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsMissingName1.ts b/tests/cases/fourslash/navigationBarItemsMissingName1.ts index 59f2911f3aa..4738cc45063 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName1.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName1.ts @@ -1,16 +1,29 @@ ////export function -/////** -//// * This is a class. -//// */ -////{| "itemName": "C", "kind": "class", "parentName": "\"navigationBarItemsMissingName1\"" |} class C { -//// {| "itemName": "foo", "kind": "method" |} foo() { -//// } +////class C { +//// foo() {} ////} - -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -/// Root + 1 child, class + 1 child -verify.navigationBarCount(4); +verify.navigationBar([ + { + "text": "\"navigationBarItemsMissingName1\"", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "C", + "kind": "class", + "childItems": [ + { + "text": "foo", + "kind": "method" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsMissingName2.ts b/tests/cases/fourslash/navigationBarItemsMissingName2.ts index d26aa0b553d..f4a37cf1154 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName2.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName2.ts @@ -2,10 +2,26 @@ //// * This is a class. //// */ ////class /* But it has no name! */ { -//// foo() { -//// } +//// foo() {} ////} - -// The class is unnamed, so its method is not included either. -verify.navigationBarCount(2); +// Anonymous classes are still included. +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [], + "indent": 0 + }, + { + "text": "default", + "kind": "class", + "childItems": [ + { + "text": "foo", + "kind": "method" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsModules.ts b/tests/cases/fourslash/navigationBarItemsModules.ts index 237ec8eb275..c3b8aef08eb 100644 --- a/tests/cases/fourslash/navigationBarItemsModules.ts +++ b/tests/cases/fourslash/navigationBarItemsModules.ts @@ -1,49 +1,137 @@ /// -////{| "itemName": "\"X.Y.Z\"", "kind": "module", "parentName": "" |} -////declare module "X.Y.Z" { -////} +////declare module "X.Y.Z" {} //// -////{| "itemName": "'X2.Y2.Z2'", "kind": "module", "parentName": "" |} -////declare module 'X2.Y2.Z2' { -////} +////declare module 'X2.Y2.Z2' {} //// -////{| "itemName": "A.B.C", "kind": "module", "parentName": "" |} ////module A.B.C { -//// {| "itemName": "x", "kind": "var", "parentName": "A.B.C" |} //// export var x; ////} //// -////{| "itemName": "A.B", "kind": "module", "parentName": "" |} ////module A.B { -//// {| "itemName": "y", "kind": "var", "parentName": "A.B" |} //// export var y; ////} //// -////{| "itemName": "A", "kind": "module", "parentName": "" |} ////module A { -//// {| "itemName": "z", "kind": "var", "parentName": "A" |} //// export var z; ////} //// -////{| "itemName": "A", "kind": "module", "parentName": "" |} ////module A { -//// {| "itemName": "B", "kind": "module", "parentName": "A" |} //// module B { -//// {| "itemName": "C", "kind": "module", "parentName": "B" |} //// module C { -//// {| "itemName": "x", "kind": "var", "parentName": "C" |} //// declare var x; //// } //// } ////} - -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -/// We have 8 module keywords, and 4 var keywords. -/// The declarations of A.B.C.x do not get merged, so the 4 vars are independent. -/// The two 'A' modules, however, do get merged, so in reality we have 7 modules. -verify.navigationBarCount(19); +//We have 8 module keywords, and 4 var keywords. +//The declarations of A.B.C.x do not get merged, so the 4 vars are independent. +//The two 'A' modules, however, do get merged, so in reality we have 7 modules. +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "A.B.C", + "kind": "module" + }, + { + "text": "A.B", + "kind": "module" + }, + { + "text": "A", + "kind": "module" + }, + { + "text": "\"X.Y.Z\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "'X2.Y2.Z2'", + "kind": "module", + "kindModifiers": "declare" + } + ], + "indent": 0 + }, + { + "text": "A.B.C", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + }, + { + "text": "A.B", + "kind": "module", + "childItems": [ + { + "text": "y", + "kind": "var", + "kindModifiers": "export" + } + ], + "indent": 1 + }, + { + "text": "A", + "kind": "module", + "childItems": [ + { + "text": "z", + "kind": "var", + "kindModifiers": "export" + }, + { + "text": "B", + "kind": "module" + } + ], + "indent": 1 + }, + { + "text": "B", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "module" + } + ], + "indent": 2 + }, + { + "text": "C", + "kind": "module", + "childItems": [ + { + "text": "x", + "kind": "var", + "kindModifiers": "declare" + } + ], + "indent": 3 + }, + { + "text": "\"X.Y.Z\"", + "kind": "module", + "kindModifiers": "declare", + "childItems": [], + "indent": 1 + }, + { + "text": "'X2.Y2.Z2'", + "kind": "module", + "kindModifiers": "declare", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts index 6bd3d799171..c2e43f5f7b7 100644 --- a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts +++ b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts @@ -1,31 +1,22 @@ -////{| "itemName": "\"Multiline\\r\\nMadness\"", "kind": "module", "parentName": "" |} ////declare module "Multiline\r\nMadness" { ////} //// -////{| "itemName": "\"Multiline\\\nMadness\"", "kind": "module", "parentName": "" |} ////declare module "Multiline\ ////Madness" { ////} -////{| "itemName": "\"MultilineMadness\"", "kind": "module", "parentName": "" |} ////declare module "MultilineMadness" {} //// -////{| "itemName": "Foo", "kind": "interface", "parentName": "" |} ////interface Foo { -//// {| "itemName": "\"a1\\\\\\r\\nb\"", "kind": "property", "parentName": "Foo" |} //// "a1\\\r\nb"; -//// {| "itemName": "\"a2\\\n \\\n b\"", "kind": "method", "parentName": "Foo" |} //// "a2\ //// \ //// b"(): Foo; ////} //// -////{| "itemName": "Bar", "kind": "class", "parentName": "" |} ////class Bar implements Foo { -//// {| "itemName": "'a1\\\\\\r\\nb'", "kind": "property", "parentName": "Bar" |} //// 'a1\\\r\nb': Foo; //// -//// {| "itemName": "'a2\\\n \\\n b'", "kind": "method", "parentName": "Bar" |} //// 'a2\ //// \ //// b'(): Foo { @@ -33,9 +24,86 @@ //// } ////} - -test.markers().forEach((marker) => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(15); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "Bar", + "kind": "class" + }, + { + "text": "Foo", + "kind": "interface" + }, + { + "text": "\"Multiline\\r\\nMadness\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "\"Multiline\\\nMadness\"", + "kind": "module", + "kindModifiers": "declare" + }, + { + "text": "\"MultilineMadness\"", + "kind": "module", + "kindModifiers": "declare" + } + ], + "indent": 0 + }, + { + "text": "Bar", + "kind": "class", + "childItems": [ + { + "text": "'a1\\\\\\r\\nb'", + "kind": "property" + }, + { + "text": "'a2\\\n \\\n b'", + "kind": "method" + } + ], + "indent": 1 + }, + { + "text": "Foo", + "kind": "interface", + "childItems": [ + { + "text": "\"a1\\\\\\r\\nb\"", + "kind": "property" + }, + { + "text": "\"a2\\\n \\\n b\"", + "kind": "method" + } + ], + "indent": 1 + }, + { + "text": "\"Multiline\\r\\nMadness\"", + "kind": "module", + "kindModifiers": "declare", + "childItems": [], + "indent": 1 + }, + { + "text": "\"Multiline\\\nMadness\"", + "kind": "module", + "kindModifiers": "declare", + "childItems": [], + "indent": 1 + }, + { + "text": "\"MultilineMadness\"", + "kind": "module", + "kindModifiers": "declare", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts index 8e1981f6101..d99d3bce9eb 100644 --- a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts @@ -6,10 +6,37 @@ //// } ////} -verify.navigationBarContains("List", "class"); -verify.navigationBarContains("constructor", "constructor"); -verify.navigationBarContains("a", "property"); -verify.navigationBarContains("b", "property"); - -// no other items -verify.navigationBarCount(6); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "List", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "List", + "kind": "class", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "a", + "kind": "property", + "kindModifiers": "public" + }, + { + "text": "b", + "kind": "property", + "kindModifiers": "public" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsSymbols1.ts b/tests/cases/fourslash/navigationBarItemsSymbols1.ts index 6d55812fa0e..8c565447c19 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols1.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols1.ts @@ -1,18 +1,40 @@ /// -////{| "itemName": "C", "kind": "class", "parentName": "" |} ////class C { -//// {| "itemName": "[Symbol.isRegExp]", "kind": "property", "parentName": "C" |} //// [Symbol.isRegExp] = 0; -//// {| "itemName": "[Symbol.iterator]", "kind": "method", "parentName": "C" |} //// [Symbol.iterator]() { } -//// {| "itemName": "[Symbol.isConcatSpreadable]", "kind": "getter", "parentName": "C" |} //// get [Symbol.isConcatSpreadable]() { } ////} -test.markers().forEach(marker => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -// 2 lack markers: and its child -verify.navigationBarCount(2 + test.markers().length); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "C", + "kind": "class" + } + ], + "indent": 0 + }, + { + "text": "C", + "kind": "class", + "childItems": [ + { + "text": "[Symbol.isConcatSpreadable]", + "kind": "getter" + }, + { + "text": "[Symbol.isRegExp]", + "kind": "property" + }, + { + "text": "[Symbol.iterator]", + "kind": "method" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsSymbols2.ts b/tests/cases/fourslash/navigationBarItemsSymbols2.ts index 5c398f77bbd..d747b5dadaa 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols2.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols2.ts @@ -1,16 +1,35 @@ /// -////{| "itemName": "I", "kind": "interface", "parentName": "" |} ////interface I { -//// {| "itemName": "[Symbol.isRegExp]", "kind": "property", "parentName": "I" |} //// [Symbol.isRegExp]: string; -//// {| "itemName": "[Symbol.iterator]", "kind": "method", "parentName": "I" |} //// [Symbol.iterator](): string; ////} -test.markers().forEach(marker => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -// 2 are not marked: and its child. -verify.navigationBarCount(2 + test.markers().length); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "I", + "kind": "interface" + } + ], + "indent": 0 + }, + { + "text": "I", + "kind": "interface", + "childItems": [ + { + "text": "[Symbol.isRegExp]", + "kind": "property" + }, + { + "text": "[Symbol.iterator]", + "kind": "method" + } + ], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsSymbols3.ts b/tests/cases/fourslash/navigationBarItemsSymbols3.ts index 23451588ecd..29ff9f277a3 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols3.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols3.ts @@ -1,13 +1,26 @@ /// -////{| "itemName": "E", "kind": "enum", "parentName": "" |} ////enum E { //// // No nav bar entry for this //// [Symbol.isRegExp] = 0 ////} -test.markers().forEach(marker => { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); -}); - -verify.navigationBarCount(3); // and E appearing both toplevel and under \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "E", + "kind": "enum" + } + ], + "indent": 0 + }, + { + "text": "E", + "kind": "enum", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/navigationBarItemsTypeAlias.ts b/tests/cases/fourslash/navigationBarItemsTypeAlias.ts index 50923256533..8a422651bf3 100644 --- a/tests/cases/fourslash/navigationBarItemsTypeAlias.ts +++ b/tests/cases/fourslash/navigationBarItemsTypeAlias.ts @@ -2,5 +2,17 @@ ////type T = number | string; -verify.navigationBarCount(1); -verify.navigationBarContains("T", "type"); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [], + "indent": 0 + }, + { + "text": "T", + "kind": "type", + "childItems": [], + "indent": 1 + } +]); diff --git a/tests/cases/fourslash/server/navbar01.ts b/tests/cases/fourslash/server/navbar01.ts index fea424317db..ed8ed2d8563 100644 --- a/tests/cases/fourslash/server/navbar01.ts +++ b/tests/cases/fourslash/server/navbar01.ts @@ -1,52 +1,171 @@ /// ////// Interface -////{| "itemName": "IPoint", "kind": "interface", "parentName": "" |}interface IPoint { -//// {| "itemName": "getDist", "kind": "method", "parentName": "IPoint" |}getDist(): number; -//// {| "itemName": "new()", "kind": "construct", "parentName": "IPoint" |}new(): IPoint; -//// {| "itemName": "()", "kind": "call", "parentName": "IPoint" |}(): any; -//// {| "itemName": "[]", "kind": "index", "parentName": "IPoint" |}[x:string]: number; -//// {| "itemName": "prop", "kind": "property", "parentName": "IPoint" |}prop: string; +////interface IPoint { +//// getDist(): number; +//// new(): IPoint; +//// (): any; +//// [x:string]: number; +//// prop: string; ////} //// /////// Module -////{| "itemName": "Shapes", "kind": "module", "parentName": "" |}module Shapes { -//// +////module Shapes { //// // Class -//// {| "itemName": "Point", "kind": "class", "parentName": "Shapes" |}export class Point implements IPoint { -//// {| "itemName": "constructor", "kind": "constructor", "parentName": "Point" |}constructor (public x: number, public y: number) { } +//// export class Point implements IPoint { +//// constructor (public x: number, public y: number) { } //// //// // Instance member -//// {| "itemName": "getDist", "kind": "method", "parentName": "Point" |}getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); } +//// getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); } //// //// // Getter -//// {| "itemName": "value", "kind": "getter", "parentName": "Point" |}get value(): number { return 0; } +//// get value(): number { return 0; } //// //// // Setter -//// {| "itemName": "value", "kind": "setter", "parentName": "Point" |}set value(newValue: number) { return; } +//// set value(newValue: number) { return; } //// //// // Static member -//// {| "itemName": "origin", "kind": "property", "parentName": "Point" |}static origin = new Point(0, 0); +//// static origin = new Point(0, 0); //// //// // Static method -//// {| "itemName": "getOrigin", "kind": "method", "parentName": "Point" |}private static getOrigin() { return Point.origin;} +//// private static getOrigin() { return Point.origin;} //// } //// -//// {| "itemName": "Values", "kind": "enum", "parentName": "Shapes" |}enum Values { -//// value1, -//// {| "itemName": "value2", "kind": "property", "parentName": "Values" |}value2, -//// value3, -//// } +//// enum Values { value1, value2, value3 } ////} //// ////// Local variables -////{| "itemName": "p", "kind": "var", "parentName": "" |}var p: IPoint = new Shapes.Point(3, 4); -////{| "itemName": "dist", "kind": "var", "parentName": "" |}var dist = p.getDist(); +////var p: IPoint = new Shapes.Point(3, 4); +////var dist = p.getDist(); -test.markers().forEach((marker) => { - if (marker.data) { - verify.navigationBarContains(marker.data.itemName, marker.data.kind, marker.fileName, marker.data.parentName); +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "dist", + "kind": "var" + }, + { + "text": "IPoint", + "kind": "interface" + }, + { + "text": "p", + "kind": "var" + }, + { + "text": "Shapes", + "kind": "module" + } + ], + "indent": 0 + }, + { + "text": "IPoint", + "kind": "interface", + "childItems": [ + { + "text": "()", + "kind": "call" + }, + { + "text": "new()", + "kind": "construct" + }, + { + "text": "[]", + "kind": "index" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "prop", + "kind": "property" + } + ], + "indent": 0 + }, + { + "text": "Shapes", + "kind": "module", + "childItems": [ + { + "text": "Point", + "kind": "class", + "kindModifiers": "export" + }, + { + "text": "Values", + "kind": "enum" + } + ], + "indent": 0 + }, + { + "text": "Point", + "kind": "class", + "kindModifiers": "export", + "childItems": [ + { + "text": "constructor", + "kind": "constructor" + }, + { + "text": "getDist", + "kind": "method" + }, + { + "text": "getOrigin", + "kind": "method", + "kindModifiers": "private,static" + }, + { + "text": "origin", + "kind": "property", + "kindModifiers": "static" + }, + { + "text": "value", + "kind": "getter" + }, + { + "text": "value", + "kind": "setter" + }, + { + "text": "x", + "kind": "property", + "kindModifiers": "public" + }, + { + "text": "y", + "kind": "property", + "kindModifiers": "public" + } + ], + "indent": 0 + }, + { + "text": "Values", + "kind": "enum", + "childItems": [ + { + "text": "value1", + "kind": "property" + }, + { + "text": "value2", + "kind": "property" + }, + { + "text": "value3", + "kind": "property" + } + ], + "indent": 0 } -}); - -verify.navigationBarCount(27); +]); diff --git a/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts b/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts index a2cdb80baef..fe852e03a28 100644 --- a/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts +++ b/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts @@ -2,12 +2,16 @@ //// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0; -test.markers().forEach(marker => { - verify.navigationBarContains( - marker.data.itemName, - marker.data.kind, - marker.fileName, - marker.data.parentName, - marker.data.isAdditionalRange, - marker.position); -}); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "c", + "kind": "const" + } + ], + "indent": 0 + } +]); diff --git a/tests/cases/fourslash/shims/getNavigationBarItems.ts b/tests/cases/fourslash/shims/getNavigationBarItems.ts index a2cdb80baef..fe852e03a28 100644 --- a/tests/cases/fourslash/shims/getNavigationBarItems.ts +++ b/tests/cases/fourslash/shims/getNavigationBarItems.ts @@ -2,12 +2,16 @@ //// {| "itemName": "c", "kind": "const", "parentName": "" |}const c = 0; -test.markers().forEach(marker => { - verify.navigationBarContains( - marker.data.itemName, - marker.data.kind, - marker.fileName, - marker.data.parentName, - marker.data.isAdditionalRange, - marker.position); -}); \ No newline at end of file +verify.navigationBar([ + { + "text": "", + "kind": "module", + "childItems": [ + { + "text": "c", + "kind": "const" + } + ], + "indent": 0 + } +]); From 12914eacc5300c5cd9a0aa44df4bef85591288be Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 26 May 2016 09:01:56 -0700 Subject: [PATCH 29/90] Fix lint errors --- src/harness/fourslash.ts | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index c33c9107ad7..53aaa8c7d6e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1963,7 +1963,7 @@ namespace FourSlash { let items = this.languageService.getNavigationBarItems(this.activeFile.fileName); items = this.simplifyNavigationBar(items); if (JSON.stringify(items) !== JSON.stringify(json)) { - this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, undefined, 2)}`) + this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, undefined, 2)}`); } } @@ -1971,30 +1971,35 @@ namespace FourSlash { private simplifyNavigationBar(items: ts.NavigationBarItem[]): any { return items.map(item => { item = ts.clone(item); - if (item.kindModifiers === "") + if (item.kindModifiers === "") { delete item.kindModifiers; + } + // We won't check this. delete item.spans; item.childItems = item.childItems.map(child => { child = ts.clone(child); - ts.Debug.assert(child.childItems.length === 0); - ts.Debug.assert(child.indent === 0); - ts.Debug.assert(child.bolded === false); - ts.Debug.assert(child.grayed === false); - delete child.childItems; - delete child.indent; - delete child.bolded; - delete child.grayed; delete child.spans; - if (child.kindModifiers === "") + ts.Debug.assert(child.childItems.length === 0); + delete child.childItems; + ts.Debug.assert(child.indent === 0); + delete child.indent; + ts.Debug.assert(child.bolded === false); + delete child.bolded; + ts.Debug.assert(child.grayed === false); + delete child.grayed; + if (child.kindModifiers === "") { delete child.kindModifiers; + } return child; }); - if (item.bolded === false) + if (item.bolded === false) { delete item.bolded; - if (item.grayed === false) + } + if (item.grayed === false) { delete item.grayed; + } return item; - }) + }); } public printNavigationItems(searchValue: string) { From 58d69cda6447b626ad269c946f2f7ec8e8aba758 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 26 May 2016 10:03:07 -0700 Subject: [PATCH 30/90] Fix localeCompare differences between node versions node 6.2.0: "a".localeCompare("A") is -1. node 0.10.45: "a".localeCompare("A") is 32. --- src/services/navigationBar.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f6f7b741a56..8db95584eb4 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -134,7 +134,7 @@ namespace ts.NavigationBar { function sortNodes(nodes: Node[]): Node[] { return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => { if (n1.name && n2.name) { - return getPropertyNameForPropertyNameNode(n1.name).localeCompare(getPropertyNameForPropertyNameNode(n2.name)); + return localeCompareFix(getPropertyNameForPropertyNameNode(n1.name), getPropertyNameForPropertyNameNode(n2.name)); } else if (n1.name) { return 1; @@ -146,6 +146,16 @@ namespace ts.NavigationBar { return n1.kind - n2.kind; } }); + + // node 0.10 treats "a" as greater than "B". + // For consistency, sort alphabetically, falling back to which is lower-case. + function localeCompareFix(a: string, b: string) { + const cmp = a.toLowerCase().localeCompare(b.toLowerCase()); + if (cmp !== 0) + return cmp; + // Return the *opposite* of the `<` operator, which works the same in node 0.10 and 6.0. + return a < b ? 1 : a > b ? -1 : 0; + } } function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void { From 81ce532cde1f4ffd88e8c507b19432712723401f Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 25 May 2016 23:05:20 -0700 Subject: [PATCH 31/90] Change how typedef tag is parsed --- src/compiler/binder.ts | 7 +- src/compiler/parser.ts | 215 ++++++++++++++++++----------------------- src/compiler/types.ts | 13 +-- 3 files changed, 103 insertions(+), 132 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 9b421ed34cb..377c5d3f465 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1702,8 +1702,9 @@ namespace ts { case SyntaxKind.PropertyDeclaration: case SyntaxKind.PropertySignature: case SyntaxKind.JSDocRecordMember: - case SyntaxKind.JSDocPropertyTag: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property | ((node).questionToken ? SymbolFlags.Optional : SymbolFlags.None), SymbolFlags.PropertyExcludes); + case SyntaxKind.JSDocPropertyTag: + return bindJSDocProperty(node); case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: return bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); @@ -2085,6 +2086,10 @@ namespace ts { : declareSymbolAndAddToSymbolTable(node, symbolFlags, symbolExcludes); } + function bindJSDocProperty(node: JSDocPropertyTag) { + return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Property, SymbolFlags.PropertyExcludes); + } + // reachability checks function shouldReportErrorOnModuleDeclaration(node: ModuleDeclaration): boolean { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 9c26fdeb179..07509d0afcc 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -406,7 +406,7 @@ namespace ts { visitNode(cbNode, (node).name) || visitNode(cbNode, (node).type); case SyntaxKind.JSDocTypeLiteral: - return visitNodes(cbNodes, (node).members); + return visitNodes(cbNodes, (node).jsDocPropertyTags); case SyntaxKind.JSDocPropertyTag: return visitNode(cbNode, (node).typeExpression) || visitNode(cbNode, (node).name); @@ -4113,9 +4113,9 @@ namespace ts { const isAsync = !!(node.flags & NodeFlags.Async); node.name = isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) : - isGenerator ? doInYieldContext(parseOptionalIdentifier) : - isAsync ? doInAwaitContext(parseOptionalIdentifier) : - parseOptionalIdentifier(); + isGenerator ? doInYieldContext(parseOptionalIdentifier) : + isAsync ? doInAwaitContext(parseOptionalIdentifier) : + parseOptionalIdentifier(); fillSignature(SyntaxKind.ColonToken, /*yieldContext*/ isGenerator, /*awaitContext*/ isAsync, /*requireCompleteParameterList*/ false, node); node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); @@ -6066,9 +6066,6 @@ namespace ts { Debug.assert(end <= content.length); let tags: NodeArray; - let currentParentJSDocTag: JSDocParentTag; - let currentParentJSDocTagEnd: number; - let result: JSDocComment; // Check for /** (JSDoc opening part) @@ -6125,10 +6122,6 @@ namespace ts { nextJSDocToken(); } - if (currentParentJSDocTag) { - finishCurrentParentTag(); - } - result = createJSDocComment(); }); @@ -6152,40 +6145,6 @@ namespace ts { } } - function finishCurrentParentTag(): void { - if (!currentParentJSDocTag) { - return; - } - - if (currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) { - const typedefTag = currentParentJSDocTag; - if (typedefTag.jsDocTypeTag) { - if (typedefTag.jsDocTypeTag.typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { - const typeTagtype = typedefTag.jsDocTypeTag.typeExpression.type; - if ((typeTagtype.name.kind !== SyntaxKind.Identifier) || - (typeTagtype.name).text !== "Object") { - typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type; - } - } - else { - typedefTag.type = typedefTag.jsDocTypeTag.typeExpression.type; - } - } - if (!typedefTag.type && typedefTag.jsDocPropertyTags && typedefTag.jsDocPropertyTags.length > 0) { - const pos = typedefTag.jsDocPropertyTags[0].pos; - const end = typedefTag.jsDocPropertyTags[typedefTag.jsDocPropertyTags.length - 1].end; - const jsdocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, pos); - jsdocTypeLiteral.members = >[]; - addRange(jsdocTypeLiteral.members, typedefTag.jsDocPropertyTags); - typedefTag.type = finishNode(jsdocTypeLiteral, end); - } - } - - addTag(finishNode(currentParentJSDocTag, currentParentJSDocTagEnd)); - currentParentJSDocTag = undefined; - currentParentJSDocTagEnd = undefined; - } - function parseTag(): void { Debug.assert(token === SyntaxKind.AtToken); const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos()); @@ -6198,34 +6157,23 @@ namespace ts { } const tag = handleTag(atToken, tagName) || handleUnknownTag(atToken, tagName); - if (!currentParentJSDocTag) { - addTag(tag); - } + addTag(tag); } function handleTag(atToken: Node, tagName: Identifier): JSDocTag { if (tagName) { switch (tagName.text) { case "param": - finishCurrentParentTag(); return handleParamTag(atToken, tagName); case "return": case "returns": - finishCurrentParentTag(); return handleReturnTag(atToken, tagName); case "template": - finishCurrentParentTag(); return handleTemplateTag(atToken, tagName); case "type": - // @typedef tag is allowed to have one @type tag, therefore seeing - // a @type tag may not indicate the end of the current parent tag. return handleTypeTag(atToken, tagName); case "typedef": - finishCurrentParentTag(); return handleTypedefTag(atToken, tagName); - case "property": - case "prop": - return handlePropertyTag(atToken, tagName); } } @@ -6251,25 +6199,6 @@ namespace ts { } } - function addToCurrentParentTag(tag: JSDocTag): void { - if (!currentParentJSDocTag) { - return; - } - switch (tag.kind) { - case SyntaxKind.JSDocPropertyTag: - if (!currentParentJSDocTag.jsDocPropertyTags) { - currentParentJSDocTag.jsDocPropertyTags = >[]; - } - currentParentJSDocTag.jsDocPropertyTags.push(tag); - break; - case SyntaxKind.JSDocTypeTag: - if (!currentParentJSDocTag.jsDocTypeTag) { - currentParentJSDocTag.jsDocTypeTag = tag; - } - break; - } - } - function tryParseTypeExpression(): JSDocTypeExpression { if (token !== SyntaxKind.OpenBraceToken) { return undefined; @@ -6345,32 +6274,14 @@ namespace ts { parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_already_specified, tagName.text); } - let result = createNode(SyntaxKind.JSDocTypeTag, atToken.pos); + const result = createNode(SyntaxKind.JSDocTypeTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; result.typeExpression = tryParseTypeExpression(); - result = finishNode(result); - - if (currentParentJSDocTag && currentParentJSDocTag.kind === SyntaxKind.JSDocTypedefTag) { - const parentTag = currentParentJSDocTag; - if (!parentTag.typeExpression && !parentTag.jsDocTypeTag) { - parentTag.jsDocTypeTag = result; - currentParentJSDocTagEnd = scanner.getStartPos(); - return result; - } - } - // If this @type tag is not part of the current parent tag, then - // it denotes the end of the current parent tag. - finishCurrentParentTag(); - return result; + return finishNode(result); } function handlePropertyTag(atToken: Node, tagName: Identifier): JSDocPropertyTag { - if (!currentParentJSDocTag) { - parseErrorAtPosition(tagName.pos, scanner.getTokenPos() - tagName.pos, Diagnostics._0_tag_cannot_be_used_independently_as_a_top_level_JSDoc_tag, tagName.text); - return undefined; - } - const typeExpression = tryParseTypeExpression(); skipWhitespace(); const name = parseJSDocIdentifierName(); @@ -6379,17 +6290,12 @@ namespace ts { return undefined; } - let result = createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); + const result = createNode(SyntaxKind.JSDocPropertyTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; result.name = name; result.typeExpression = typeExpression; - result.type = typeExpression.type; - result = finishNode(result); - - addToCurrentParentTag(result); - currentParentJSDocTagEnd = scanner.getStartPos(); - return undefined; + return finishNode(result); } function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { @@ -6413,32 +6319,99 @@ namespace ts { } } - const result = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); - result.atToken = atToken; - result.tagName = tagName; - result.name = name; - result.typeExpression = typeExpression; + const typedefTag = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); + typedefTag.atToken = atToken; + typedefTag.tagName = tagName; + typedefTag.name = name; + typedefTag.typeExpression = typeExpression; - if (typeExpression && typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { - const jsDocTypeReference = typeExpression.type; - if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { - const name = jsDocTypeReference.name; - if (name.text === "Object") { - currentParentJSDocTag = result; + if (typeExpression) { + if (typeExpression.type.kind === SyntaxKind.JSDocTypeReference) { + const jsDocTypeReference = typeExpression.type; + if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { + const name = jsDocTypeReference.name; + if (name.text === "Object") { + typedefTag.type = scanChildTags(); + } } } + if (!typedefTag.type) { + typedefTag.type = typeExpression.type; + } } - else if (!typeExpression) { - currentParentJSDocTag = result; + else { + typedefTag.type = scanChildTags(); } - if (!currentParentJSDocTag) { - result.type = result.typeExpression.type; - return finishNode(result); + return finishNode(typedefTag); + + function scanChildTags(): JSDocTypeLiteral { + const jsDocTypeLiteral = createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos()); + let resumePos = scanner.getStartPos(); + let canParseTag = true; + let seenAsterisk = false; + let parentTagTerminated = false; + + while (token !== SyntaxKind.EndOfFileToken && !parentTagTerminated) { + nextJSDocToken(); + switch (token) { + case SyntaxKind.AtToken: + if (canParseTag) { + parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral); + } + seenAsterisk = false; + break; + case SyntaxKind.NewLineTrivia: + resumePos = scanner.getStartPos() - 1; + canParseTag = true; + seenAsterisk = false; + break; + case SyntaxKind.AsteriskToken: + if (seenAsterisk) { + canParseTag = false; + } + seenAsterisk = true; + break; + case SyntaxKind.Identifier: + canParseTag = false; + case SyntaxKind.EndOfFileToken: + break; + } + } + scanner.setTextPos(resumePos); + return finishNode(jsDocTypeLiteral); + } + } + + function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean { + Debug.assert(token === SyntaxKind.AtToken); + const atToken = createNode(SyntaxKind.AtToken, scanner.getStartPos()); + atToken.end = scanner.getTextPos(); + nextJSDocToken(); + + const tagName = parseJSDocIdentifierName(); + if (!tagName) { + return false; } - currentParentJSDocTagEnd = scanner.getStartPos(); - return undefined; + switch (tagName.text) { + case "type": + if (parentTag.jsDocTypeTag) { + // already has a @type tag, terminate the parent tag now. + return false; + } + parentTag.jsDocTypeTag = handleTypeTag(atToken, tagName); + return true; + case "prop": + case "property": + if (!parentTag.jsDocPropertyTags) { + parentTag.jsDocPropertyTags = >[]; + } + const propertyTag = handlePropertyTag(atToken, tagName); + parentTag.jsDocPropertyTags.push(propertyTag); + return true; + } + return false; } function handleTemplateTag(atToken: Node, tagName: Identifier): JSDocTemplateTag { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b472aa92e62..1b9af02a8d5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1517,29 +1517,22 @@ namespace ts { } // @kind(SyntaxKind.JSDocTypedefTag) - export interface JSDocTypedefTag extends JSDocTag, Declaration, JSDocParentTag { + export interface JSDocTypedefTag extends JSDocTag, Declaration { name: Identifier; typeExpression?: JSDocTypeExpression; type: JSDocType; } - export interface JSDocParentTag extends JSDocTag { - jsDocPropertyTags?: NodeArray; - jsDocTypeTag?: JSDocTypeTag; - } - // @kind(SyntaxKind.JSDocPropertyTag) export interface JSDocPropertyTag extends JSDocTag, TypeElement { name: Identifier; typeExpression: JSDocTypeExpression; - // Add a "type" property here to make the interface compatible - // with the "VariableLikeDeclaration" definition - type: TypeNode; } // @kind(SyntaxKind.JSDocTypeLiteral) export interface JSDocTypeLiteral extends JSDocType { - members: NodeArray; + jsDocPropertyTags?: NodeArray; + jsDocTypeTag?: JSDocTypeTag; } // @kind(SyntaxKind.JSDocParameterTag) From 3b5c72c4bc033f5bf4d38931b2eed0bf0bf7e5e9 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2016 15:30:31 -0700 Subject: [PATCH 32/90] Include outer function expressions in control flow analysis --- src/compiler/binder.ts | 24 ++++++++++++++++- src/compiler/checker.ts | 57 +++++++++++++++++++++++++++++++++-------- src/compiler/types.ts | 7 +++++ 3 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index a7cb0d12758..2476709b501 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -472,6 +472,9 @@ namespace ts { hasExplicitReturn = false; currentFlow = { flags: FlowFlags.Start }; + if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { + (currentFlow).container = node; + } currentBreakTarget = undefined; currentContinueTarget = undefined; activeLabels = undefined; @@ -589,6 +592,9 @@ namespace ts { case SyntaxKind.VariableDeclaration: bindVariableDeclarationFlow(node); break; + case SyntaxKind.CallExpression: + bindCallExpressionFlow(node); + break; default: forEachChild(node, bind); break; @@ -1098,6 +1104,20 @@ namespace ts { } } + function bindCallExpressionFlow(node: CallExpression) { + forEachChild(node, bind); + // If the target of the call expression is a function expression or arrow function we have + // an immediately invoked function expression (IIFE). Initialize the flowNode property to + // the current control flow (which includes evaluation of the IIFE arguments). + let expr: Expression = node.expression; + while (expr.kind === SyntaxKind.ParenthesizedExpression) { + expr = (expr).expression; + } + if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { + node.flowNode = currentFlow; + } + } + function getContainerFlags(node: Node): ContainerFlags { switch (node.kind) { case SyntaxKind.ClassExpression: @@ -2054,7 +2074,9 @@ namespace ts { hasAsyncFunctions = true; } } - + if (currentFlow) { + node.flowNode = currentFlow; + } checkStrictModeFunctionName(node); const bindingName = (node).name ? (node).name.text : "__function"; return bindAnonymousDeclaration(node, SymbolFlags.Function, bindingName); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5a7c6fa29e5..c14a7e26add 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7648,7 +7648,7 @@ namespace ts { getInitialTypeOfBindingElement(node); } - function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean) { + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; @@ -7694,15 +7694,30 @@ namespace ts { getTypeAtFlowBranchLabel(flow) : getTypeAtFlowLoopLabel(flow); } - else if (flow.flags & FlowFlags.Unreachable) { + else if (flow.flags & FlowFlags.Start) { + let container = (flow).container; + if (container) { + // If container is an IIFE continue with the control flow associated with the + // call expression node. + let iife = getImmediatelyInvokedFunctionExpression(container); + if (iife && iife.flowNode) { + flow = iife.flowNode; + continue; + } + // Check if we should continue with the control flow of the containing function. + if (includeOuterFunctions && container.flowNode) { + flow = container.flowNode; + continue; + } + } + // At the top of the flow we have the initial type. + type = initialType; + } + else { // Unreachable code errors are reported in the binding phase. Here we // simply return the declared type to reduce follow-on errors. type = declaredType; } - else { - // At the top of the flow we have the initial type. - type = initialType; - } if (flow.flags & FlowFlags.Shared) { // Record visited node and the associated type in the cache. visitedFlowNodes[visitedFlowCount] = flow; @@ -8071,6 +8086,27 @@ namespace ts { return expression; } + function getControlFlowContainer(node: Identifier, declarationContainer: Node, skipFunctionExpressions: boolean) { + let container = getContainingFunctionOrModule(node); + while (container !== declarationContainer && + (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) && + (skipFunctionExpressions || getImmediatelyInvokedFunctionExpression(container))) { + container = getContainingFunctionOrModule(container); + } + return container; + } + + function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) { + const declarationContainer = getContainingFunctionOrModule(declaration); + let container = getContainingFunctionOrModule(reference); + while (container !== declarationContainer && + (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) && + (includeOuterFunctions || getImmediatelyInvokedFunctionExpression(container))) { + container = getContainingFunctionOrModule(container); + } + return container === declarationContainer; + } + function checkIdentifier(node: Identifier): Type { const symbol = getResolvedSymbol(node); @@ -8127,10 +8163,11 @@ namespace ts { return type; } const declaration = localOrExportSymbol.valueDeclaration; + const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol); const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration || getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - const flowType = getFlowTypeOfReference(node, type, assumeInitialized); + !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); + const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors @@ -8379,7 +8416,7 @@ namespace ts { if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType; - return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true); + return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true); } if (isInJavaScriptFile(node)) { @@ -9991,7 +10028,7 @@ namespace ts { return propType; } } - return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true); + return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false); } function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7613c489920..762d710d504 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1537,6 +1537,13 @@ namespace ts { id?: number; // Node id used by flow type cache in checker } + // FlowStart represents the start of a control flow. For a function expression or arrow + // function, the container property references the function (which in turn has a flowNode + // property for the containing control flow). + export interface FlowStart extends FlowNode { + container?: FunctionExpression | ArrowFunction; + } + // FlowLabel represents a junction with multiple possible preceding control flows. export interface FlowLabel extends FlowNode { antecedents: FlowNode[]; From d20664aa69b54cb05b427fb357c72a384100d131 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2016 16:26:30 -0700 Subject: [PATCH 33/90] Fix test --- .../typeGuardsInFunctionAndModuleBlock.js | 4 +-- ...typeGuardsInFunctionAndModuleBlock.symbols | 18 +++++----- .../typeGuardsInFunctionAndModuleBlock.types | 34 +++++++++---------- .../typeGuardsInFunctionAndModuleBlock.ts | 2 +- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js index 91443a9ae78..8ec45de814e 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.js @@ -41,7 +41,7 @@ function foo4(x: number | string | boolean) { : x.toString(); // number })(x); // x here is narrowed to number | boolean } -// Type guards affect nested function expressions and nested function declarations +// Type guards do not affect nested function declarations function foo5(x: number | string | boolean) { if (typeof x === "string") { var y = x; // string; @@ -121,7 +121,7 @@ function foo4(x) { : x.toString(); // number })(x); // x here is narrowed to number | boolean } -// Type guards affect nested function expressions and nested function declarations +// Type guards do not affect nested function declarations function foo5(x) { if (typeof x === "string") { var y = x; // string; diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols index bf21641624e..56eeb22bf9f 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.symbols @@ -27,9 +27,9 @@ function foo(x: number | string | boolean) { >toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) : x.toString(); // number ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 2, 13)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) } (); } @@ -60,9 +60,9 @@ function foo2(x: number | string | boolean) { >toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) : x.toString(); // number ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 12, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) } (x); // x here is narrowed to number | boolean >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 12, 14)) @@ -91,9 +91,9 @@ function foo3(x: number | string | boolean) { >toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) : x.toString(); // number ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 22, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) })(); } @@ -123,14 +123,14 @@ function foo4(x: number | string | boolean) { >toString : Symbol(Object.toString, Decl(lib.d.ts, --, --)) : x.toString(); // number ->x.toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>x.toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14)) ->toString : Symbol(toString, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>toString : Symbol(Number.toString, Decl(lib.d.ts, --, --)) })(x); // x here is narrowed to number | boolean >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 32, 14)) } -// Type guards affect nested function expressions and nested function declarations +// Type guards do not affect nested function declarations function foo5(x: number | string | boolean) { >foo5 : Symbol(foo5, Decl(typeGuardsInFunctionAndModuleBlock.ts, 41, 1)) >x : Symbol(x, Decl(typeGuardsInFunctionAndModuleBlock.ts, 43, 14)) diff --git a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types index 67d1816cfc3..cc9553b9767 100644 --- a/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types +++ b/tests/baselines/reference/typeGuardsInFunctionAndModuleBlock.types @@ -21,14 +21,14 @@ function foo(x: number | string | boolean) { >f : () => string var b = x; // number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string >typeof x === "boolean" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"boolean" : string ? x.toString() // boolean @@ -40,7 +40,7 @@ function foo(x: number | string | boolean) { : x.toString(); // number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string +>x : number >toString : (radix?: number) => string } (); @@ -66,14 +66,14 @@ function foo2(x: number | string | boolean) { >a : number | boolean var b = x; // new scope - number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string >typeof x === "boolean" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"boolean" : string ? x.toString() // boolean @@ -85,7 +85,7 @@ function foo2(x: number | string | boolean) { : x.toString(); // number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string +>x : number >toString : (radix?: number) => string } (x); // x here is narrowed to number | boolean @@ -111,14 +111,14 @@ function foo3(x: number | string | boolean) { >() => { var b = x; // new scope - number | boolean return typeof x === "boolean" ? x.toString() // boolean : x.toString(); // number } : () => string var b = x; // new scope - number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string >typeof x === "boolean" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"boolean" : string ? x.toString() // boolean @@ -130,7 +130,7 @@ function foo3(x: number | string | boolean) { : x.toString(); // number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string +>x : number >toString : (radix?: number) => string })(); @@ -156,14 +156,14 @@ function foo4(x: number | string | boolean) { >a : number | boolean var b = x; // new scope - number | boolean ->b : number | string | boolean ->x : number | string | boolean +>b : number | boolean +>x : number | boolean return typeof x === "boolean" >typeof x === "boolean" ? x.toString() // boolean : x.toString() : string >typeof x === "boolean" : boolean >typeof x : string ->x : number | string | boolean +>x : number | boolean >"boolean" : string ? x.toString() // boolean @@ -175,13 +175,13 @@ function foo4(x: number | string | boolean) { : x.toString(); // number >x.toString() : string >x.toString : (radix?: number) => string ->x : number | string +>x : number >toString : (radix?: number) => string })(x); // x here is narrowed to number | boolean >x : number | boolean } -// Type guards affect nested function expressions and nested function declarations +// Type guards do not affect nested function declarations function foo5(x: number | string | boolean) { >foo5 : (x: number | string | boolean) => void >x : number | string | boolean diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts index 5dbc925d04b..f1ba854394b 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsInFunctionAndModuleBlock.ts @@ -40,7 +40,7 @@ function foo4(x: number | string | boolean) { : x.toString(); // number })(x); // x here is narrowed to number | boolean } -// Type guards affect nested function expressions and nested function declarations +// Type guards do not affect nested function declarations function foo5(x: number | string | boolean) { if (typeof x === "string") { var y = x; // string; From e5e21f9b5e34f1c999289a9ba7cbd9adbde67290 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2016 16:26:40 -0700 Subject: [PATCH 34/90] Add new tests --- .../constLocalsInFunctionExpressions.js | 73 +++++++++ .../constLocalsInFunctionExpressions.symbols | 95 +++++++++++ .../constLocalsInFunctionExpressions.types | 121 ++++++++++++++ tests/baselines/reference/controlFlowIIFE.js | 87 ++++++++++ .../reference/controlFlowIIFE.symbols | 108 +++++++++++++ .../baselines/reference/controlFlowIIFE.types | 149 ++++++++++++++++++ .../constLocalsInFunctionExpressions.ts | 38 +++++ .../controlFlow/controlFlowIIFE.ts | 47 ++++++ 8 files changed, 718 insertions(+) create mode 100644 tests/baselines/reference/constLocalsInFunctionExpressions.js create mode 100644 tests/baselines/reference/constLocalsInFunctionExpressions.symbols create mode 100644 tests/baselines/reference/constLocalsInFunctionExpressions.types create mode 100644 tests/baselines/reference/controlFlowIIFE.js create mode 100644 tests/baselines/reference/controlFlowIIFE.symbols create mode 100644 tests/baselines/reference/controlFlowIIFE.types create mode 100644 tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts create mode 100644 tests/cases/conformance/controlFlow/controlFlowIIFE.ts diff --git a/tests/baselines/reference/constLocalsInFunctionExpressions.js b/tests/baselines/reference/constLocalsInFunctionExpressions.js new file mode 100644 index 00000000000..473de3b1a6c --- /dev/null +++ b/tests/baselines/reference/constLocalsInFunctionExpressions.js @@ -0,0 +1,73 @@ +//// [constLocalsInFunctionExpressions.ts] +declare function getStringOrNumber(): string | number; + +function f1() { + const x = getStringOrNumber(); + if (typeof x === "string") { + const f = () => x.length; + } +} + +function f2() { + const x = getStringOrNumber(); + if (typeof x !== "string") { + return; + } + const f = () => x.length; +} + +function f3() { + const x = getStringOrNumber(); + if (typeof x === "string") { + const f = function() { return x.length; }; + } +} + +function f4() { + const x = getStringOrNumber(); + if (typeof x !== "string") { + return; + } + const f = function() { return x.length; }; +} + +function f5() { + const x = getStringOrNumber(); + if (typeof x === "string") { + const f = () => () => x.length; + } +} + +//// [constLocalsInFunctionExpressions.js] +function f1() { + var x = getStringOrNumber(); + if (typeof x === "string") { + var f = function () { return x.length; }; + } +} +function f2() { + var x = getStringOrNumber(); + if (typeof x !== "string") { + return; + } + var f = function () { return x.length; }; +} +function f3() { + var x = getStringOrNumber(); + if (typeof x === "string") { + var f = function () { return x.length; }; + } +} +function f4() { + var x = getStringOrNumber(); + if (typeof x !== "string") { + return; + } + var f = function () { return x.length; }; +} +function f5() { + var x = getStringOrNumber(); + if (typeof x === "string") { + var f = function () { return function () { return x.length; }; }; + } +} diff --git a/tests/baselines/reference/constLocalsInFunctionExpressions.symbols b/tests/baselines/reference/constLocalsInFunctionExpressions.symbols new file mode 100644 index 00000000000..d9465a118aa --- /dev/null +++ b/tests/baselines/reference/constLocalsInFunctionExpressions.symbols @@ -0,0 +1,95 @@ +=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts === +declare function getStringOrNumber(): string | number; +>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0)) + +function f1() { +>f1 : Symbol(f1, Decl(constLocalsInFunctionExpressions.ts, 0, 54)) + + const x = getStringOrNumber(); +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9)) + + const f = () => x.length; +>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 5, 13)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 3, 9)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } +} + +function f2() { +>f2 : Symbol(f2, Decl(constLocalsInFunctionExpressions.ts, 7, 1)) + + const x = getStringOrNumber(); +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0)) + + if (typeof x !== "string") { +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9)) + + return; + } + const f = () => x.length; +>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 14, 9)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 10, 9)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +function f3() { +>f3 : Symbol(f3, Decl(constLocalsInFunctionExpressions.ts, 15, 1)) + + const x = getStringOrNumber(); +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9)) + + const f = function() { return x.length; }; +>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 20, 13)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 18, 9)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } +} + +function f4() { +>f4 : Symbol(f4, Decl(constLocalsInFunctionExpressions.ts, 22, 1)) + + const x = getStringOrNumber(); +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0)) + + if (typeof x !== "string") { +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9)) + + return; + } + const f = function() { return x.length; }; +>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 29, 9)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 25, 9)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} + +function f5() { +>f5 : Symbol(f5, Decl(constLocalsInFunctionExpressions.ts, 30, 1)) + + const x = getStringOrNumber(); +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(constLocalsInFunctionExpressions.ts, 0, 0)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9)) + + const f = () => () => x.length; +>f : Symbol(f, Decl(constLocalsInFunctionExpressions.ts, 35, 13)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(constLocalsInFunctionExpressions.ts, 33, 9)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + } +} diff --git a/tests/baselines/reference/constLocalsInFunctionExpressions.types b/tests/baselines/reference/constLocalsInFunctionExpressions.types new file mode 100644 index 00000000000..e9f0086f6b1 --- /dev/null +++ b/tests/baselines/reference/constLocalsInFunctionExpressions.types @@ -0,0 +1,121 @@ +=== tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts === +declare function getStringOrNumber(): string | number; +>getStringOrNumber : () => string | number + +function f1() { +>f1 : () => void + + const x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + const f = () => x.length; +>f : () => number +>() => x.length : () => number +>x.length : number +>x : string +>length : number + } +} + +function f2() { +>f2 : () => void + + const x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x !== "string") { +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + return; + } + const f = () => x.length; +>f : () => number +>() => x.length : () => number +>x.length : number +>x : string +>length : number +} + +function f3() { +>f3 : () => void + + const x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + const f = function() { return x.length; }; +>f : () => number +>function() { return x.length; } : () => number +>x.length : number +>x : string +>length : number + } +} + +function f4() { +>f4 : () => void + + const x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x !== "string") { +>typeof x !== "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + return; + } + const f = function() { return x.length; }; +>f : () => number +>function() { return x.length; } : () => number +>x.length : number +>x : string +>length : number +} + +function f5() { +>f5 : () => void + + const x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + const f = () => () => x.length; +>f : () => () => number +>() => () => x.length : () => () => number +>() => x.length : () => number +>x.length : number +>x : string +>length : number + } +} diff --git a/tests/baselines/reference/controlFlowIIFE.js b/tests/baselines/reference/controlFlowIIFE.js new file mode 100644 index 00000000000..6454d1117db --- /dev/null +++ b/tests/baselines/reference/controlFlowIIFE.js @@ -0,0 +1,87 @@ +//// [controlFlowIIFE.ts] + +declare function getStringOrNumber(): string | number; + +function f1() { + let x = getStringOrNumber(); + if (typeof x === "string") { + let n = function() { + return x.length; + }(); + } +} + +function f2() { + let x = getStringOrNumber(); + if (typeof x === "string") { + let n = (function() { + return x.length; + })(); + } +} + +function f3() { + let x = getStringOrNumber(); + let y: number; + if (typeof x === "string") { + let n = (z => x.length + y + z)(y = 1); + } +} + +// Repros from #8381 + +let maybeNumber: number | undefined; +(function () { + maybeNumber = 1; +})(); +if (maybeNumber !== undefined) { + maybeNumber++; +} + +let test: string | undefined; +if (!test) { + throw new Error('Test is not defined'); +} +(() => { + test.slice(1); // No error +})(); + +//// [controlFlowIIFE.js] +function f1() { + var x = getStringOrNumber(); + if (typeof x === "string") { + var n = function () { + return x.length; + }(); + } +} +function f2() { + var x = getStringOrNumber(); + if (typeof x === "string") { + var n = (function () { + return x.length; + })(); + } +} +function f3() { + var x = getStringOrNumber(); + var y; + if (typeof x === "string") { + var n = (function (z) { return x.length + y + z; })(y = 1); + } +} +// Repros from #8381 +var maybeNumber; +(function () { + maybeNumber = 1; +})(); +if (maybeNumber !== undefined) { + maybeNumber++; +} +var test; +if (!test) { + throw new Error('Test is not defined'); +} +(function () { + test.slice(1); // No error +})(); diff --git a/tests/baselines/reference/controlFlowIIFE.symbols b/tests/baselines/reference/controlFlowIIFE.symbols new file mode 100644 index 00000000000..2a03bb3ff2b --- /dev/null +++ b/tests/baselines/reference/controlFlowIIFE.symbols @@ -0,0 +1,108 @@ +=== tests/cases/conformance/controlFlow/controlFlowIIFE.ts === + +declare function getStringOrNumber(): string | number; +>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0)) + +function f1() { +>f1 : Symbol(f1, Decl(controlFlowIIFE.ts, 1, 54)) + + let x = getStringOrNumber(); +>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7)) + + let n = function() { +>n : Symbol(n, Decl(controlFlowIIFE.ts, 6, 11)) + + return x.length; +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowIIFE.ts, 4, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + + }(); + } +} + +function f2() { +>f2 : Symbol(f2, Decl(controlFlowIIFE.ts, 10, 1)) + + let x = getStringOrNumber(); +>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7)) + + let n = (function() { +>n : Symbol(n, Decl(controlFlowIIFE.ts, 15, 11)) + + return x.length; +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowIIFE.ts, 13, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + + })(); + } +} + +function f3() { +>f3 : Symbol(f3, Decl(controlFlowIIFE.ts, 19, 1)) + + let x = getStringOrNumber(); +>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7)) +>getStringOrNumber : Symbol(getStringOrNumber, Decl(controlFlowIIFE.ts, 0, 0)) + + let y: number; +>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7)) + + if (typeof x === "string") { +>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7)) + + let n = (z => x.length + y + z)(y = 1); +>n : Symbol(n, Decl(controlFlowIIFE.ts, 25, 11)) +>z : Symbol(z, Decl(controlFlowIIFE.ts, 25, 17)) +>x.length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>x : Symbol(x, Decl(controlFlowIIFE.ts, 22, 7)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7)) +>z : Symbol(z, Decl(controlFlowIIFE.ts, 25, 17)) +>y : Symbol(y, Decl(controlFlowIIFE.ts, 23, 7)) + } +} + +// Repros from #8381 + +let maybeNumber: number | undefined; +>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) + +(function () { + maybeNumber = 1; +>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) + +})(); +if (maybeNumber !== undefined) { +>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) +>undefined : Symbol(undefined) + + maybeNumber++; +>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) +} + +let test: string | undefined; +>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) + +if (!test) { +>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) + + throw new Error('Test is not defined'); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +} +(() => { + test.slice(1); // No error +>test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --)) +>test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) +>slice : Symbol(String.slice, Decl(lib.d.ts, --, --)) + +})(); diff --git a/tests/baselines/reference/controlFlowIIFE.types b/tests/baselines/reference/controlFlowIIFE.types new file mode 100644 index 00000000000..0a6ad5da02f --- /dev/null +++ b/tests/baselines/reference/controlFlowIIFE.types @@ -0,0 +1,149 @@ +=== tests/cases/conformance/controlFlow/controlFlowIIFE.ts === + +declare function getStringOrNumber(): string | number; +>getStringOrNumber : () => string | number + +function f1() { +>f1 : () => void + + let x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + let n = function() { +>n : number +>function() { return x.length; }() : number +>function() { return x.length; } : () => number + + return x.length; +>x.length : number +>x : string +>length : number + + }(); + } +} + +function f2() { +>f2 : () => void + + let x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + let n = (function() { +>n : number +>(function() { return x.length; })() : number +>(function() { return x.length; }) : () => number +>function() { return x.length; } : () => number + + return x.length; +>x.length : number +>x : string +>length : number + + })(); + } +} + +function f3() { +>f3 : () => void + + let x = getStringOrNumber(); +>x : string | number +>getStringOrNumber() : string | number +>getStringOrNumber : () => string | number + + let y: number; +>y : number + + if (typeof x === "string") { +>typeof x === "string" : boolean +>typeof x : string +>x : string | number +>"string" : string + + let n = (z => x.length + y + z)(y = 1); +>n : number +>(z => x.length + y + z)(y = 1) : number +>(z => x.length + y + z) : (z: number) => number +>z => x.length + y + z : (z: number) => number +>z : number +>x.length + y + z : number +>x.length + y : number +>x.length : number +>x : string +>length : number +>y : number +>z : number +>y = 1 : number +>y : number +>1 : number + } +} + +// Repros from #8381 + +let maybeNumber: number | undefined; +>maybeNumber : number | undefined + +(function () { +>(function () { maybeNumber = 1;})() : void +>(function () { maybeNumber = 1;}) : () => void +>function () { maybeNumber = 1;} : () => void + + maybeNumber = 1; +>maybeNumber = 1 : number +>maybeNumber : number | undefined +>1 : number + +})(); +if (maybeNumber !== undefined) { +>maybeNumber !== undefined : boolean +>maybeNumber : number | undefined +>undefined : undefined + + maybeNumber++; +>maybeNumber++ : number +>maybeNumber : number +} + +let test: string | undefined; +>test : string | undefined + +if (!test) { +>!test : boolean +>test : string | undefined + + throw new Error('Test is not defined'); +>new Error('Test is not defined') : Error +>Error : ErrorConstructor +>'Test is not defined' : string +} +(() => { +>(() => { test.slice(1); // No error})() : void +>(() => { test.slice(1); // No error}) : () => void +>() => { test.slice(1); // No error} : () => void + + test.slice(1); // No error +>test.slice(1) : string +>test.slice : (start?: number | undefined, end?: number | undefined) => string +>test : string +>slice : (start?: number | undefined, end?: number | undefined) => string +>1 : number + +})(); diff --git a/tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts b/tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts new file mode 100644 index 00000000000..69b19fcb307 --- /dev/null +++ b/tests/cases/conformance/controlFlow/constLocalsInFunctionExpressions.ts @@ -0,0 +1,38 @@ +declare function getStringOrNumber(): string | number; + +function f1() { + const x = getStringOrNumber(); + if (typeof x === "string") { + const f = () => x.length; + } +} + +function f2() { + const x = getStringOrNumber(); + if (typeof x !== "string") { + return; + } + const f = () => x.length; +} + +function f3() { + const x = getStringOrNumber(); + if (typeof x === "string") { + const f = function() { return x.length; }; + } +} + +function f4() { + const x = getStringOrNumber(); + if (typeof x !== "string") { + return; + } + const f = function() { return x.length; }; +} + +function f5() { + const x = getStringOrNumber(); + if (typeof x === "string") { + const f = () => () => x.length; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/controlFlow/controlFlowIIFE.ts b/tests/cases/conformance/controlFlow/controlFlowIIFE.ts new file mode 100644 index 00000000000..e703fb2fdff --- /dev/null +++ b/tests/cases/conformance/controlFlow/controlFlowIIFE.ts @@ -0,0 +1,47 @@ +// @strictNullChecks: true + +declare function getStringOrNumber(): string | number; + +function f1() { + let x = getStringOrNumber(); + if (typeof x === "string") { + let n = function() { + return x.length; + }(); + } +} + +function f2() { + let x = getStringOrNumber(); + if (typeof x === "string") { + let n = (function() { + return x.length; + })(); + } +} + +function f3() { + let x = getStringOrNumber(); + let y: number; + if (typeof x === "string") { + let n = (z => x.length + y + z)(y = 1); + } +} + +// Repros from #8381 + +let maybeNumber: number | undefined; +(function () { + maybeNumber = 1; +})(); +if (maybeNumber !== undefined) { + maybeNumber++; +} + +let test: string | undefined; +if (!test) { + throw new Error('Test is not defined'); +} +(() => { + test.slice(1); // No error +})(); \ No newline at end of file From 1647d20d90e0cc68a2714abeb7a1e81bb32c2b17 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 26 May 2016 16:50:36 -0700 Subject: [PATCH 35/90] Fix linting errors --- src/compiler/checker.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c14a7e26add..58aaae9f72b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7695,11 +7695,11 @@ namespace ts { getTypeAtFlowLoopLabel(flow); } else if (flow.flags & FlowFlags.Start) { - let container = (flow).container; + const container = (flow).container; if (container) { // If container is an IIFE continue with the control flow associated with the // call expression node. - let iife = getImmediatelyInvokedFunctionExpression(container); + const iife = getImmediatelyInvokedFunctionExpression(container); if (iife && iife.flowNode) { flow = iife.flowNode; continue; @@ -8086,16 +8086,6 @@ namespace ts { return expression; } - function getControlFlowContainer(node: Identifier, declarationContainer: Node, skipFunctionExpressions: boolean) { - let container = getContainingFunctionOrModule(node); - while (container !== declarationContainer && - (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) && - (skipFunctionExpressions || getImmediatelyInvokedFunctionExpression(container))) { - container = getContainingFunctionOrModule(container); - } - return container; - } - function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) { const declarationContainer = getContainingFunctionOrModule(declaration); let container = getContainingFunctionOrModule(reference); From b2664e7f842fe838e4a96564651b0fab723207ee Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 27 May 2016 06:12:10 -0700 Subject: [PATCH 36/90] Use a JSON.stringify replacer function instead of changing the value beforehand --- src/harness/fourslash.ts | 52 ++++++------------- .../fourslash/deleteClassWithEnumPresent.ts | 3 +- .../cases/fourslash/getNavigationBarItems.ts | 3 +- tests/cases/fourslash/navbar_const.ts | 3 +- .../navbar_contains-no-duplicates.ts | 3 +- tests/cases/fourslash/navbar_exportDefault.ts | 16 ++---- tests/cases/fourslash/navbar_let.ts | 3 +- .../navigationBarItemsBindingPatterns.ts | 3 +- ...ionBarItemsBindingPatternsInConstructor.ts | 3 +- .../navigationBarItemsEmptyConstructors.ts | 3 +- .../fourslash/navigationBarItemsExports.ts | 3 +- .../fourslash/navigationBarItemsFunctions.ts | 4 +- .../navigationBarItemsFunctionsBroken.ts | 4 +- .../navigationBarItemsFunctionsBroken2.ts | 4 +- .../fourslash/navigationBarItemsImports.ts | 3 +- ...ionBarItemsInsideMethodsAndConstructors.ts | 6 +-- .../fourslash/navigationBarItemsItems.ts | 3 +- .../fourslash/navigationBarItemsItems2.ts | 5 +- ...rItemsItemsContainsNoAnonymousFunctions.ts | 12 ++--- .../navigationBarItemsItemsExternalModules.ts | 3 +- ...navigationBarItemsItemsExternalModules2.ts | 3 +- ...navigationBarItemsItemsExternalModules3.ts | 3 +- .../navigationBarItemsItemsModuleVariables.ts | 6 +-- .../navigationBarItemsMissingName1.ts | 3 +- .../navigationBarItemsMissingName2.ts | 4 +- .../fourslash/navigationBarItemsModules.ts | 5 +- ...ationBarItemsMultilineStringIdentifiers.ts | 6 +-- ...BarItemsPropertiesDefinedInConstructors.ts | 3 +- .../fourslash/navigationBarItemsSymbols1.ts | 3 +- .../fourslash/navigationBarItemsSymbols2.ts | 3 +- .../fourslash/navigationBarItemsSymbols3.ts | 4 +- .../fourslash/navigationBarItemsTypeAlias.ts | 4 +- tests/cases/fourslash/server/navbar01.ts | 15 ++---- .../shims-pp/getNavigationBarItems.ts | 3 +- .../fourslash/shims/getNavigationBarItems.ts | 3 +- 35 files changed, 58 insertions(+), 149 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 53aaa8c7d6e..4a756ab3b1e 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1960,46 +1960,24 @@ namespace FourSlash { } public verifyNavigationBar(json: any) { - let items = this.languageService.getNavigationBarItems(this.activeFile.fileName); - items = this.simplifyNavigationBar(items); - if (JSON.stringify(items) !== JSON.stringify(json)) { - this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, undefined, 2)}`); + const items = this.languageService.getNavigationBarItems(this.activeFile.fileName); + if (JSON.stringify(items, replacer) !== JSON.stringify(json)) { + this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, replacer, 2)}`); } - } - // Remove any properties that tend to all have the same value so that test data is easier to read. - private simplifyNavigationBar(items: ts.NavigationBarItem[]): any { - return items.map(item => { - item = ts.clone(item); - if (item.kindModifiers === "") { - delete item.kindModifiers; + // Make the data easier to read. + function replacer(key: string, value: any) { + switch (key) { + case "spans": + // We won't ever check this. + return undefined; + case "childItems": + return value.length === 0 ? undefined : value; + default: + // Omit falsy values, those are presumed to be the default. + return value || undefined; } - // We won't check this. - delete item.spans; - item.childItems = item.childItems.map(child => { - child = ts.clone(child); - delete child.spans; - ts.Debug.assert(child.childItems.length === 0); - delete child.childItems; - ts.Debug.assert(child.indent === 0); - delete child.indent; - ts.Debug.assert(child.bolded === false); - delete child.bolded; - ts.Debug.assert(child.grayed === false); - delete child.grayed; - if (child.kindModifiers === "") { - delete child.kindModifiers; - } - return child; - }); - if (item.bolded === false) { - delete item.bolded; - } - if (item.grayed === false) { - delete item.grayed; - } - return item; - }); + } } public printNavigationItems(searchValue: string) { diff --git a/tests/cases/fourslash/deleteClassWithEnumPresent.ts b/tests/cases/fourslash/deleteClassWithEnumPresent.ts index d8d4478afe7..17d2adae0b1 100644 --- a/tests/cases/fourslash/deleteClassWithEnumPresent.ts +++ b/tests/cases/fourslash/deleteClassWithEnumPresent.ts @@ -14,8 +14,7 @@ verify.navigationBar([ "text": "Foo", "kind": "enum" } - ], - "indent": 0 + ] }, { "text": "Foo", diff --git a/tests/cases/fourslash/getNavigationBarItems.ts b/tests/cases/fourslash/getNavigationBarItems.ts index a2b0492514a..c3b519fe114 100644 --- a/tests/cases/fourslash/getNavigationBarItems.ts +++ b/tests/cases/fourslash/getNavigationBarItems.ts @@ -14,8 +14,7 @@ verify.navigationBar([ "text": "C", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "C", diff --git a/tests/cases/fourslash/navbar_const.ts b/tests/cases/fourslash/navbar_const.ts index 02a10f46a74..6ec594a2873 100644 --- a/tests/cases/fourslash/navbar_const.ts +++ b/tests/cases/fourslash/navbar_const.ts @@ -11,7 +11,6 @@ verify.navigationBar([ "text": "c", "kind": "const" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/navbar_contains-no-duplicates.ts b/tests/cases/fourslash/navbar_contains-no-duplicates.ts index 333130ff795..e259a7bef90 100644 --- a/tests/cases/fourslash/navbar_contains-no-duplicates.ts +++ b/tests/cases/fourslash/navbar_contains-no-duplicates.ts @@ -45,8 +45,7 @@ verify.navigationBar([ "kind": "module", "kindModifiers": "declare" } - ], - "indent": 0 + ] }, { "text": "ABC", diff --git a/tests/cases/fourslash/navbar_exportDefault.ts b/tests/cases/fourslash/navbar_exportDefault.ts index 7c666589f12..dc99cff7d64 100644 --- a/tests/cases/fourslash/navbar_exportDefault.ts +++ b/tests/cases/fourslash/navbar_exportDefault.ts @@ -16,15 +16,12 @@ goTo.file("a.ts"); verify.navigationBar([ { "text": "\"a\"", - "kind": "module", - "childItems": [], - "indent": 0 + "kind": "module" }, { "text": "default", "kind": "class", "kindModifiers": "export", - "childItems": [], "indent": 1 } ]); @@ -40,14 +37,12 @@ verify.navigationBar([ "kind": "class", "kindModifiers": "export" } - ], - "indent": 0 + ] }, { "text": "C", "kind": "class", "kindModifiers": "export", - "childItems": [], "indent": 1 } ]); @@ -57,14 +52,11 @@ verify.navigationBar([ { "text": "\"c\"", "kind": "module", - "childItems": [], - "indent": 0 }, { "text": "default", "kind": "function", "kindModifiers": "export", - "childItems": [], "indent": 1 } ]); @@ -80,14 +72,12 @@ verify.navigationBar([ "kind": "function", "kindModifiers": "export" } - ], - "indent": 0 + ] }, { "text": "Func", "kind": "function", "kindModifiers": "export", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navbar_let.ts b/tests/cases/fourslash/navbar_let.ts index dc9d9c07857..7ccd61cdfd6 100644 --- a/tests/cases/fourslash/navbar_let.ts +++ b/tests/cases/fourslash/navbar_let.ts @@ -11,7 +11,6 @@ verify.navigationBar([ "text": "c", "kind": "let" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts b/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts index 007a9c9b804..849f918f473 100644 --- a/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts +++ b/tests/cases/fourslash/navigationBarItemsBindingPatterns.ts @@ -55,7 +55,6 @@ verify.navigationBar([ "text": "g", "kind": "var" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts b/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts index 41751f5c74b..0ae3e692aa1 100644 --- a/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts +++ b/tests/cases/fourslash/navigationBarItemsBindingPatternsInConstructor.ts @@ -24,8 +24,7 @@ verify.navigationBar([ "text": "B", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "A", diff --git a/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts b/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts index 27c33f6afb2..fd534f4c6db 100644 --- a/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsEmptyConstructors.ts @@ -14,8 +14,7 @@ verify.navigationBar([ "text": "Test", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "Test", diff --git a/tests/cases/fourslash/navigationBarItemsExports.ts b/tests/cases/fourslash/navigationBarItemsExports.ts index 79422f96274..3ec9b0a7138 100644 --- a/tests/cases/fourslash/navigationBarItemsExports.ts +++ b/tests/cases/fourslash/navigationBarItemsExports.ts @@ -27,7 +27,6 @@ verify.navigationBar([ "kind": "alias", "kindModifiers": "export" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctions.ts b/tests/cases/fourslash/navigationBarItemsFunctions.ts index e2260f2ff2b..91546462a25 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctions.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctions.ts @@ -27,13 +27,11 @@ verify.navigationBar([ "text": "foo", "kind": "function" } - ], - "indent": 0 + ] }, { "text": "baz", "kind": "function", - "childItems": [], "indent": 1 }, { diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts index dc84b50ff30..96dcbb944ae 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken.ts @@ -13,13 +13,11 @@ verify.navigationBar([ "text": "f", "kind": "function" } - ], - "indent": 0 + ] }, { "text": "f", "kind": "function", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts index b4612efcac0..127bde8c9e7 100644 --- a/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts +++ b/tests/cases/fourslash/navigationBarItemsFunctionsBroken2.ts @@ -14,13 +14,11 @@ verify.navigationBar([ "text": "f", "kind": "function" } - ], - "indent": 0 + ] }, { "text": "f", "kind": "function", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsImports.ts b/tests/cases/fourslash/navigationBarItemsImports.ts index d6526a7a68c..ed398ec7f1d 100644 --- a/tests/cases/fourslash/navigationBarItemsImports.ts +++ b/tests/cases/fourslash/navigationBarItemsImports.ts @@ -51,7 +51,6 @@ verify.navigationBar([ "text": "ns", "kind": "alias" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts index 7d2fb4ed127..28ec25c6f1d 100644 --- a/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsInsideMethodsAndConstructors.ts @@ -27,8 +27,7 @@ verify.navigationBar([ "text": "Class", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "Class", @@ -82,13 +81,11 @@ verify.navigationBar([ { "text": "LocalFunctionInConstructor", "kind": "function", - "childItems": [], "indent": 2 }, { "text": "LocalInterfaceInConstrcutor", "kind": "interface", - "childItems": [], "indent": 2 }, { @@ -135,7 +132,6 @@ verify.navigationBar([ { "text": "LocalInterfaceInMethod", "kind": "interface", - "childItems": [], "indent": 2 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsItems.ts b/tests/cases/fourslash/navigationBarItemsItems.ts index 013d28cd402..87da8581734 100644 --- a/tests/cases/fourslash/navigationBarItemsItems.ts +++ b/tests/cases/fourslash/navigationBarItemsItems.ts @@ -60,8 +60,7 @@ verify.navigationBar([ "text": "Shapes", "kind": "module" } - ], - "indent": 0 + ] }, { "text": "IPoint", diff --git a/tests/cases/fourslash/navigationBarItemsItems2.ts b/tests/cases/fourslash/navigationBarItemsItems2.ts index 27c32531d6b..762531d8778 100644 --- a/tests/cases/fourslash/navigationBarItemsItems2.ts +++ b/tests/cases/fourslash/navigationBarItemsItems2.ts @@ -16,20 +16,17 @@ verify.navigationBar([ "text": "A", "kind": "module" } - ], - "indent": 0 + ] }, { "text": "default", "kind": "class", "kindModifiers": "export", - "childItems": [], "indent": 1 }, { "text": "A", "kind": "module", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts b/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts index 34e4a1c0c89..276e19624c9 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsContainsNoAnonymousFunctions.ts @@ -33,9 +33,7 @@ goTo.marker("file1"); verify.navigationBar([ { "text": "", - "kind": "module", - "childItems": [], - "indent": 0 + "kind": "module" } ]); @@ -49,8 +47,7 @@ verify.navigationBar([ "text": "x", "kind": "var" } - ], - "indent": 0 + ] } ]); @@ -68,19 +65,16 @@ verify.navigationBar([ "text": "foo", "kind": "function" } - ], - "indent": 0 + ] }, { "text": "bar", "kind": "function", - "childItems": [], "indent": 1 }, { "text": "foo", "kind": "function", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts index f2df772fb19..04091185dd0 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules.ts @@ -14,8 +14,7 @@ verify.navigationBar([ "kind": "class", "kindModifiers": "export" } - ], - "indent": 0 + ] }, { "text": "Bar", diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts index 54d4759afe6..4939091d944 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules2.ts @@ -21,8 +21,7 @@ verify.navigationBar([ "kind": "var", "kindModifiers": "export" } - ], - "indent": 0 + ] }, { "text": "Bar", diff --git a/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts b/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts index 2db04c980d2..5ee760fabc6 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsExternalModules3.ts @@ -21,8 +21,7 @@ verify.navigationBar([ "kind": "var", "kindModifiers": "export" } - ], - "indent": 0 + ] }, { "text": "Bar", diff --git a/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts b/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts index ef4022ce046..6b85d481612 100644 --- a/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts +++ b/tests/cases/fourslash/navigationBarItemsItemsModuleVariables.ts @@ -29,8 +29,7 @@ verify.navigationBar([ "text": "Module1", "kind": "module" } - ], - "indent": 0 + ] }, { "text": "Module1", @@ -56,8 +55,7 @@ verify.navigationBar([ "text": "Module1.SubModule", "kind": "module" } - ], - "indent": 0 + ] }, { "text": "Module1.SubModule", diff --git a/tests/cases/fourslash/navigationBarItemsMissingName1.ts b/tests/cases/fourslash/navigationBarItemsMissingName1.ts index 4738cc45063..af0e89683bc 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName1.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName1.ts @@ -12,8 +12,7 @@ verify.navigationBar([ "text": "C", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "C", diff --git a/tests/cases/fourslash/navigationBarItemsMissingName2.ts b/tests/cases/fourslash/navigationBarItemsMissingName2.ts index f4a37cf1154..2eda3b07855 100644 --- a/tests/cases/fourslash/navigationBarItemsMissingName2.ts +++ b/tests/cases/fourslash/navigationBarItemsMissingName2.ts @@ -9,9 +9,7 @@ verify.navigationBar([ { "text": "", - "kind": "module", - "childItems": [], - "indent": 0 + "kind": "module" }, { "text": "default", diff --git a/tests/cases/fourslash/navigationBarItemsModules.ts b/tests/cases/fourslash/navigationBarItemsModules.ts index c3b8aef08eb..979f22084e5 100644 --- a/tests/cases/fourslash/navigationBarItemsModules.ts +++ b/tests/cases/fourslash/navigationBarItemsModules.ts @@ -54,8 +54,7 @@ verify.navigationBar([ "kind": "module", "kindModifiers": "declare" } - ], - "indent": 0 + ] }, { "text": "A.B.C", @@ -124,14 +123,12 @@ verify.navigationBar([ "text": "\"X.Y.Z\"", "kind": "module", "kindModifiers": "declare", - "childItems": [], "indent": 1 }, { "text": "'X2.Y2.Z2'", "kind": "module", "kindModifiers": "declare", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts index c2e43f5f7b7..2ee4c93a2f1 100644 --- a/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts +++ b/tests/cases/fourslash/navigationBarItemsMultilineStringIdentifiers.ts @@ -52,8 +52,7 @@ verify.navigationBar([ "kind": "module", "kindModifiers": "declare" } - ], - "indent": 0 + ] }, { "text": "Bar", @@ -89,21 +88,18 @@ verify.navigationBar([ "text": "\"Multiline\\r\\nMadness\"", "kind": "module", "kindModifiers": "declare", - "childItems": [], "indent": 1 }, { "text": "\"Multiline\\\nMadness\"", "kind": "module", "kindModifiers": "declare", - "childItems": [], "indent": 1 }, { "text": "\"MultilineMadness\"", "kind": "module", "kindModifiers": "declare", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts index d99d3bce9eb..9e5a48370c1 100644 --- a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts @@ -15,8 +15,7 @@ verify.navigationBar([ "text": "List", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "List", diff --git a/tests/cases/fourslash/navigationBarItemsSymbols1.ts b/tests/cases/fourslash/navigationBarItemsSymbols1.ts index 8c565447c19..53f4c7af445 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols1.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols1.ts @@ -15,8 +15,7 @@ verify.navigationBar([ "text": "C", "kind": "class" } - ], - "indent": 0 + ] }, { "text": "C", diff --git a/tests/cases/fourslash/navigationBarItemsSymbols2.ts b/tests/cases/fourslash/navigationBarItemsSymbols2.ts index d747b5dadaa..2674047f9c0 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols2.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols2.ts @@ -14,8 +14,7 @@ verify.navigationBar([ "text": "I", "kind": "interface" } - ], - "indent": 0 + ] }, { "text": "I", diff --git a/tests/cases/fourslash/navigationBarItemsSymbols3.ts b/tests/cases/fourslash/navigationBarItemsSymbols3.ts index 29ff9f277a3..9564c496dd8 100644 --- a/tests/cases/fourslash/navigationBarItemsSymbols3.ts +++ b/tests/cases/fourslash/navigationBarItemsSymbols3.ts @@ -14,13 +14,11 @@ verify.navigationBar([ "text": "E", "kind": "enum" } - ], - "indent": 0 + ] }, { "text": "E", "kind": "enum", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/navigationBarItemsTypeAlias.ts b/tests/cases/fourslash/navigationBarItemsTypeAlias.ts index 4973e84b2ed..8ff9854ac16 100644 --- a/tests/cases/fourslash/navigationBarItemsTypeAlias.ts +++ b/tests/cases/fourslash/navigationBarItemsTypeAlias.ts @@ -11,13 +11,11 @@ verify.navigationBar([ "text": "T", "kind": "type" } - ], - "indent": 0 + ] }, { "text": "T", "kind": "type", - "childItems": [], "indent": 1 } ]); diff --git a/tests/cases/fourslash/server/navbar01.ts b/tests/cases/fourslash/server/navbar01.ts index ed8ed2d8563..7f2229fcb81 100644 --- a/tests/cases/fourslash/server/navbar01.ts +++ b/tests/cases/fourslash/server/navbar01.ts @@ -59,8 +59,7 @@ verify.navigationBar([ "text": "Shapes", "kind": "module" } - ], - "indent": 0 + ] }, { "text": "IPoint", @@ -86,8 +85,7 @@ verify.navigationBar([ "text": "prop", "kind": "property" } - ], - "indent": 0 + ] }, { "text": "Shapes", @@ -102,8 +100,7 @@ verify.navigationBar([ "text": "Values", "kind": "enum" } - ], - "indent": 0 + ] }, { "text": "Point", @@ -146,8 +143,7 @@ verify.navigationBar([ "kind": "property", "kindModifiers": "public" } - ], - "indent": 0 + ] }, { "text": "Values", @@ -165,7 +161,6 @@ verify.navigationBar([ "text": "value3", "kind": "property" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts b/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts index fe852e03a28..b06d782d8bf 100644 --- a/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts +++ b/tests/cases/fourslash/shims-pp/getNavigationBarItems.ts @@ -11,7 +11,6 @@ verify.navigationBar([ "text": "c", "kind": "const" } - ], - "indent": 0 + ] } ]); diff --git a/tests/cases/fourslash/shims/getNavigationBarItems.ts b/tests/cases/fourslash/shims/getNavigationBarItems.ts index fe852e03a28..b06d782d8bf 100644 --- a/tests/cases/fourslash/shims/getNavigationBarItems.ts +++ b/tests/cases/fourslash/shims/getNavigationBarItems.ts @@ -11,7 +11,6 @@ verify.navigationBar([ "text": "c", "kind": "const" } - ], - "indent": 0 + ] } ]); From eab25119898cde36f1e7c1bc8e35a1aa52696b5c Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 27 May 2016 09:51:30 -0700 Subject: [PATCH 37/90] update signature of finishWorker --- Jakefile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jakefile.js b/Jakefile.js index f0415635956..474303a9722 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -759,7 +759,7 @@ function runConsoleTests(defaultReporter, runInParallel) { exec(workerCmd, finishWorker, finishWorker) }); - function finishWorker(errorStatus) { + function finishWorker(e, errorStatus) { counter--; if (firstErrorStatus === undefined && errorStatus !== undefined) { firstErrorStatus = errorStatus; From f3d1b46dcba63bf70900c9ca607bb1aabf0047dd Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 27 May 2016 10:40:28 -0700 Subject: [PATCH 38/90] Add tests for private and readonly parameter properties in navbar --- .../navigationBarItemsPropertiesDefinedInConstructors.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts index 9e5a48370c1..0ea7a0745c2 100644 --- a/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts +++ b/tests/cases/fourslash/navigationBarItemsPropertiesDefinedInConstructors.ts @@ -1,7 +1,7 @@ /// ////class List { -//// constructor(public a: boolean, public b: T, c: number) { +//// constructor(public a: boolean, private b: T, readonly c: string, d: number) { //// var local = 0; //// } ////} @@ -33,7 +33,11 @@ verify.navigationBar([ { "text": "b", "kind": "property", - "kindModifiers": "public" + "kindModifiers": "private" + }, + { + "text": "c", + "kind": "property" } ], "indent": 1 From 92938cd8df88dd89b2cd0791084e0b7fb871a4c9 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 27 May 2016 15:38:59 -0700 Subject: [PATCH 39/90] check that default clause is non-empty in reachability checks --- src/compiler/binder.ts | 4 ++-- .../reachabilityCheckWithEmptyDefault.js | 18 +++++++++++++++ .../reachabilityCheckWithEmptyDefault.symbols | 18 +++++++++++++++ .../reachabilityCheckWithEmptyDefault.types | 22 +++++++++++++++++++ .../reachabilityCheckWithEmptyDefault.ts | 8 +++++++ 5 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/reachabilityCheckWithEmptyDefault.js create mode 100644 tests/baselines/reference/reachabilityCheckWithEmptyDefault.symbols create mode 100644 tests/baselines/reference/reachabilityCheckWithEmptyDefault.types create mode 100644 tests/cases/compiler/reachabilityCheckWithEmptyDefault.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 6f0f1ac7863..fef23770475 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -912,8 +912,8 @@ namespace ts { preSwitchCaseFlow = currentFlow; bind(node.caseBlock); addAntecedent(postSwitchLabel, currentFlow); - const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause); - if (!hasDefault) { + const hasNonEmptyDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause && c.statements.length); + if (!hasNonEmptyDefault) { addAntecedent(postSwitchLabel, preSwitchCaseFlow); } currentBreakTarget = saveBreakTarget; diff --git a/tests/baselines/reference/reachabilityCheckWithEmptyDefault.js b/tests/baselines/reference/reachabilityCheckWithEmptyDefault.js new file mode 100644 index 00000000000..0096d2d2f9a --- /dev/null +++ b/tests/baselines/reference/reachabilityCheckWithEmptyDefault.js @@ -0,0 +1,18 @@ +//// [reachabilityCheckWithEmptyDefault.ts] +declare function print(s: string): void; +function foo(x: any) { + switch(x) { + case 1: return; + default: + } + print('1'); +} + +//// [reachabilityCheckWithEmptyDefault.js] +function foo(x) { + switch (x) { + case 1: return; + default: + } + print('1'); +} diff --git a/tests/baselines/reference/reachabilityCheckWithEmptyDefault.symbols b/tests/baselines/reference/reachabilityCheckWithEmptyDefault.symbols new file mode 100644 index 00000000000..d3123c0e8ba --- /dev/null +++ b/tests/baselines/reference/reachabilityCheckWithEmptyDefault.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/reachabilityCheckWithEmptyDefault.ts === +declare function print(s: string): void; +>print : Symbol(print, Decl(reachabilityCheckWithEmptyDefault.ts, 0, 0)) +>s : Symbol(s, Decl(reachabilityCheckWithEmptyDefault.ts, 0, 23)) + +function foo(x: any) { +>foo : Symbol(foo, Decl(reachabilityCheckWithEmptyDefault.ts, 0, 40)) +>x : Symbol(x, Decl(reachabilityCheckWithEmptyDefault.ts, 1, 13)) + + switch(x) { +>x : Symbol(x, Decl(reachabilityCheckWithEmptyDefault.ts, 1, 13)) + + case 1: return; + default: + } + print('1'); +>print : Symbol(print, Decl(reachabilityCheckWithEmptyDefault.ts, 0, 0)) +} diff --git a/tests/baselines/reference/reachabilityCheckWithEmptyDefault.types b/tests/baselines/reference/reachabilityCheckWithEmptyDefault.types new file mode 100644 index 00000000000..3d01b876473 --- /dev/null +++ b/tests/baselines/reference/reachabilityCheckWithEmptyDefault.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/reachabilityCheckWithEmptyDefault.ts === +declare function print(s: string): void; +>print : (s: string) => void +>s : string + +function foo(x: any) { +>foo : (x: any) => void +>x : any + + switch(x) { +>x : any + + case 1: return; +>1 : number + + default: + } + print('1'); +>print('1') : void +>print : (s: string) => void +>'1' : string +} diff --git a/tests/cases/compiler/reachabilityCheckWithEmptyDefault.ts b/tests/cases/compiler/reachabilityCheckWithEmptyDefault.ts new file mode 100644 index 00000000000..6429528a0da --- /dev/null +++ b/tests/cases/compiler/reachabilityCheckWithEmptyDefault.ts @@ -0,0 +1,8 @@ +declare function print(s: string): void; +function foo(x: any) { + switch(x) { + case 1: return; + default: + } + print('1'); +} \ No newline at end of file From e8ecf0e615ca552b7355740cd91cae6f8ed7b74e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 28 May 2016 06:43:28 -0700 Subject: [PATCH 40/90] Track return statements in IIFE using a flow label --- src/compiler/binder.ts | 83 +++++++++++++++++++++++++-------------- src/compiler/checker.ts | 63 ++++++++++------------------- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 14 +++++++ 4 files changed, 89 insertions(+), 72 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 2476709b501..93417493fa7 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -104,14 +104,15 @@ namespace ts { let seenThisKeyword: boolean; // state used by reachability checks - let hasExplicitReturn: boolean; let currentFlow: FlowNode; let currentBreakTarget: FlowLabel; let currentContinueTarget: FlowLabel; + let currentReturnTarget: FlowLabel; let currentTrueTarget: FlowLabel; let currentFalseTarget: FlowLabel; let preSwitchCaseFlow: FlowNode; let activeLabels: ActiveLabel[]; + let hasExplicitReturn: boolean; // state used for emit helpers let hasClassExtends: boolean; @@ -156,13 +157,14 @@ namespace ts { blockScopeContainer = undefined; lastContainer = undefined; seenThisKeyword = false; - hasExplicitReturn = false; currentFlow = undefined; currentBreakTarget = undefined; currentContinueTarget = undefined; + currentReturnTarget = undefined; currentTrueTarget = undefined; currentFalseTarget = undefined; activeLabels = undefined; + hasExplicitReturn = false; hasClassExtends = false; hasAsyncFunctions = false; hasDecorators = false; @@ -443,20 +445,20 @@ namespace ts { blockScopeContainer.locals = undefined; } - let savedHasExplicitReturn: boolean; - let savedCurrentFlow: FlowNode; - let savedBreakTarget: FlowLabel; - let savedContinueTarget: FlowLabel; - let savedActiveLabels: ActiveLabel[]; + let saveCurrentFlow: FlowNode; + let saveBreakTarget: FlowLabel; + let saveContinueTarget: FlowLabel; + let saveReturnTarget: FlowLabel; + let saveActiveLabels: ActiveLabel[]; + let saveHasExplicitReturn: boolean; + let isIIFE: boolean; const kind = node.kind; let flags = node.flags; - // reset all reachability check related flags on node (for incremental scenarios) - flags &= ~NodeFlags.ReachabilityCheckFlags; - - // reset all emit helper flags on node (for incremental scenarios) - flags &= ~NodeFlags.EmitHelperFlags; + // Reset all reachability check related flags on node (for incremental scenarios) + // Reset all emit helper flags on node (for incremental scenarios) + flags &= ~NodeFlags.ReachabilityAndEmitFlags; if (kind === SyntaxKind.InterfaceDeclaration) { seenThisKeyword = false; @@ -464,23 +466,30 @@ namespace ts { const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind); if (saveState) { - savedHasExplicitReturn = hasExplicitReturn; - savedCurrentFlow = currentFlow; - savedBreakTarget = currentBreakTarget; - savedContinueTarget = currentContinueTarget; - savedActiveLabels = activeLabels; - - hasExplicitReturn = false; - currentFlow = { flags: FlowFlags.Start }; - if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { - (currentFlow).container = node; + saveCurrentFlow = currentFlow; + saveBreakTarget = currentBreakTarget; + saveContinueTarget = currentContinueTarget; + saveReturnTarget = currentReturnTarget; + saveActiveLabels = activeLabels; + saveHasExplicitReturn = hasExplicitReturn; + isIIFE = (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) && !!getImmediatelyInvokedFunctionExpression(node); + if (isIIFE) { + currentReturnTarget = createBranchLabel(); + } + else { + currentFlow = { flags: FlowFlags.Start }; + if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { + (currentFlow).container = node; + } + currentReturnTarget = undefined; } currentBreakTarget = undefined; currentContinueTarget = undefined; activeLabels = undefined; + hasExplicitReturn = false; } - if (isInJavaScriptFile(node) && node.jsDocComment) { + if (node.flags & NodeFlags.JavaScriptFile && node.jsDocComment) { bind(node.jsDocComment); } @@ -518,11 +527,18 @@ namespace ts { node.flags = flags; if (saveState) { - hasExplicitReturn = savedHasExplicitReturn; - currentFlow = savedCurrentFlow; - currentBreakTarget = savedBreakTarget; - currentContinueTarget = savedContinueTarget; - activeLabels = savedActiveLabels; + if (isIIFE) { + addAntecedent(currentReturnTarget, currentFlow); + currentFlow = finishFlowLabel(currentReturnTarget); + } + else { + currentFlow = saveCurrentFlow; + } + currentBreakTarget = saveBreakTarget; + currentContinueTarget = saveContinueTarget; + currentReturnTarget = saveReturnTarget; + activeLabels = saveActiveLabels; + hasExplicitReturn = saveHasExplicitReturn; } container = saveContainer; @@ -854,6 +870,9 @@ namespace ts { bind(node.expression); if (node.kind === SyntaxKind.ReturnStatement) { hasExplicitReturn = true; + if (currentReturnTarget) { + addAntecedent(currentReturnTarget, currentFlow); + } } currentFlow = unreachableFlow; } @@ -1105,7 +1124,6 @@ namespace ts { } function bindCallExpressionFlow(node: CallExpression) { - forEachChild(node, bind); // If the target of the call expression is a function expression or arrow function we have // an immediately invoked function expression (IIFE). Initialize the flowNode property to // the current control flow (which includes evaluation of the IIFE arguments). @@ -1114,7 +1132,12 @@ namespace ts { expr = (expr).expression; } if (expr.kind === SyntaxKind.FunctionExpression || expr.kind === SyntaxKind.ArrowFunction) { - node.flowNode = currentFlow; + forEach(node.typeArguments, bind); + forEach(node.arguments, bind); + bind(node.expression); + } + else { + forEachChild(node, bind); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 58aaae9f72b..037ab78413a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7695,20 +7695,11 @@ namespace ts { getTypeAtFlowLoopLabel(flow); } else if (flow.flags & FlowFlags.Start) { + // Check if we should continue with the control flow of the containing function. const container = (flow).container; - if (container) { - // If container is an IIFE continue with the control flow associated with the - // call expression node. - const iife = getImmediatelyInvokedFunctionExpression(container); - if (iife && iife.flowNode) { - flow = iife.flowNode; - continue; - } - // Check if we should continue with the control flow of the containing function. - if (includeOuterFunctions && container.flowNode) { - flow = container.flowNode; - continue; - } + if (container && includeOuterFunctions) { + flow = container.flowNode; + continue; } // At the top of the flow we have the initial type. type = initialType; @@ -8652,23 +8643,25 @@ namespace ts { function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - const iife = getImmediatelyInvokedFunctionExpression(func); - if (iife) { - const indexOfParameter = indexOf(func.parameters, parameter); - if (iife.arguments && indexOfParameter < iife.arguments.length) { - if (parameter.dotDotDotToken) { - const restTypes: Type[] = []; - for (let i = indexOfParameter; i < iife.arguments.length; i++) { - restTypes.push(getTypeOfExpression(iife.arguments[i])); + if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) { + const iife = getImmediatelyInvokedFunctionExpression(func); + if (iife) { + const indexOfParameter = indexOf(func.parameters, parameter); + if (iife.arguments && indexOfParameter < iife.arguments.length) { + if (parameter.dotDotDotToken) { + const restTypes: Type[] = []; + for (let i = indexOfParameter; i < iife.arguments.length; i++) { + restTypes.push(getTypeOfExpression(iife.arguments[i])); + } + return createArrayType(getUnionType(restTypes)); } - return createArrayType(getUnionType(restTypes)); + const links = getNodeLinks(iife); + const cached = links.resolvedSignature; + links.resolvedSignature = anySignature; + const type = checkExpression(iife.arguments[indexOfParameter]); + links.resolvedSignature = cached; + return type; } - const links = getNodeLinks(iife); - const cached = links.resolvedSignature; - links.resolvedSignature = anySignature; - const type = checkExpression(iife.arguments[indexOfParameter]); - links.resolvedSignature = cached; - return type; } } const contextualSignature = getContextualSignature(func); @@ -8691,20 +8684,6 @@ namespace ts { return undefined; } - function getImmediatelyInvokedFunctionExpression(func: FunctionExpression | MethodDeclaration) { - if (isFunctionExpressionOrArrowFunction(func)) { - let prev: Node = func; - let parent: Node = func.parent; - while (parent.kind === SyntaxKind.ParenthesizedExpression) { - prev = parent; - parent = parent.parent; - } - if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) { - return parent as CallExpression; - } - } - } - // In a variable, parameter or property declaration with a type annotation, // the contextual type of an initializer expression is the type of the variable, parameter or property. // Otherwise, in a parameter declaration of a contextually typed function expression, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 762d710d504..4e55956fa7c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -416,6 +416,7 @@ namespace ts { ReachabilityCheckFlags = HasImplicitReturn | HasExplicitReturn, EmitHelperFlags = HasClassExtends | HasDecorators | HasParamDecorators | HasAsyncFunctions, + ReachabilityAndEmitFlags = ReachabilityCheckFlags | EmitHelperFlags, // Parsing context flags ContextFlags = DisallowInContext | YieldContext | DecoratorContext | AwaitContext | JavaScriptFile, diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7969389d788..500fa722dda 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -987,6 +987,20 @@ namespace ts { } } + export function getImmediatelyInvokedFunctionExpression(func: Node): CallExpression { + if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) { + let prev = func; + let parent = func.parent; + while (parent.kind === SyntaxKind.ParenthesizedExpression) { + prev = parent; + parent = parent.parent; + } + if (parent.kind === SyntaxKind.CallExpression && (parent as CallExpression).expression === prev) { + return parent as CallExpression; + } + } + } + /** * Determines whether a node is a property or element access expression for super. */ From 4d730a5c6a6cda260f5e382fa2c4b8cf92cb578c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 28 May 2016 06:45:10 -0700 Subject: [PATCH 41/90] Update test --- tests/baselines/reference/controlFlowIIFE.js | 2 ++ tests/baselines/reference/controlFlowIIFE.symbols | 9 ++++++--- tests/baselines/reference/controlFlowIIFE.types | 6 +++++- tests/cases/conformance/controlFlow/controlFlowIIFE.ts | 1 + 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/controlFlowIIFE.js b/tests/baselines/reference/controlFlowIIFE.js index 6454d1117db..5d295b57315 100644 --- a/tests/baselines/reference/controlFlowIIFE.js +++ b/tests/baselines/reference/controlFlowIIFE.js @@ -34,6 +34,7 @@ let maybeNumber: number | undefined; (function () { maybeNumber = 1; })(); +maybeNumber++; if (maybeNumber !== undefined) { maybeNumber++; } @@ -75,6 +76,7 @@ var maybeNumber; (function () { maybeNumber = 1; })(); +maybeNumber++; if (maybeNumber !== undefined) { maybeNumber++; } diff --git a/tests/baselines/reference/controlFlowIIFE.symbols b/tests/baselines/reference/controlFlowIIFE.symbols index 2a03bb3ff2b..b1b2b741158 100644 --- a/tests/baselines/reference/controlFlowIIFE.symbols +++ b/tests/baselines/reference/controlFlowIIFE.symbols @@ -82,6 +82,9 @@ let maybeNumber: number | undefined; >maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) })(); +maybeNumber++; +>maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) + if (maybeNumber !== undefined) { >maybeNumber : Symbol(maybeNumber, Decl(controlFlowIIFE.ts, 31, 3)) >undefined : Symbol(undefined) @@ -91,10 +94,10 @@ if (maybeNumber !== undefined) { } let test: string | undefined; ->test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) +>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3)) if (!test) { ->test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) +>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3)) throw new Error('Test is not defined'); >Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) @@ -102,7 +105,7 @@ if (!test) { (() => { test.slice(1); // No error >test.slice : Symbol(String.slice, Decl(lib.d.ts, --, --)) ->test : Symbol(test, Decl(controlFlowIIFE.ts, 39, 3)) +>test : Symbol(test, Decl(controlFlowIIFE.ts, 40, 3)) >slice : Symbol(String.slice, Decl(lib.d.ts, --, --)) })(); diff --git a/tests/baselines/reference/controlFlowIIFE.types b/tests/baselines/reference/controlFlowIIFE.types index 0a6ad5da02f..c041de04bc7 100644 --- a/tests/baselines/reference/controlFlowIIFE.types +++ b/tests/baselines/reference/controlFlowIIFE.types @@ -112,9 +112,13 @@ let maybeNumber: number | undefined; >1 : number })(); +maybeNumber++; +>maybeNumber++ : number +>maybeNumber : number + if (maybeNumber !== undefined) { >maybeNumber !== undefined : boolean ->maybeNumber : number | undefined +>maybeNumber : number >undefined : undefined maybeNumber++; diff --git a/tests/cases/conformance/controlFlow/controlFlowIIFE.ts b/tests/cases/conformance/controlFlow/controlFlowIIFE.ts index e703fb2fdff..c72f038c1ec 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIIFE.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIIFE.ts @@ -34,6 +34,7 @@ let maybeNumber: number | undefined; (function () { maybeNumber = 1; })(); +maybeNumber++; if (maybeNumber !== undefined) { maybeNumber++; } From 66e9f7dc214dc13c936e3cec2a27d3fef56777be Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 28 May 2016 16:00:31 -0700 Subject: [PATCH 42/90] Optimize critical code paths in binder --- src/compiler/binder.ts | 213 +++++++++++++++++------------------------ 1 file changed, 87 insertions(+), 126 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 93417493fa7..4f24458f08d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -77,12 +77,14 @@ namespace ts { // Blocks (when not parented by functions), Catch clauses, For/For-in/For-of statements... IsBlockScopedContainer = 1 << 1, - HasLocals = 1 << 2, + // The current node is the container of a control flow path. The current control flow should + // be saved and restored, and a new control flow initialized within the container. + IsControlFlowContainer = 1 << 2, - // If the current node is a container that also container that also contains locals. Examples: - // - // Functions, Methods, Modules, Source-files. - IsContainerWithLocals = IsContainer | HasLocals + IsFunctionLike = 1 << 3, + IsFunctionExpression = 1 << 4, + HasLocals = 1 << 5, + IsInterface = 1 << 6, } const binder = createBinder(); @@ -103,7 +105,7 @@ namespace ts { let lastContainer: Node; let seenThisKeyword: boolean; - // state used by reachability checks + // state used by control flow analysis let currentFlow: FlowNode; let currentBreakTarget: FlowLabel; let currentContinueTarget: FlowLabel; @@ -115,11 +117,7 @@ namespace ts { let hasExplicitReturn: boolean; // state used for emit helpers - let hasClassExtends: boolean; - let hasAsyncFunctions: boolean; - let hasDecorators: boolean; - let hasParameterDecorators: boolean; - let hasJsxSpreadAttribute: boolean; + let emitFlags: NodeFlags; // If this file is an external module, then it is automatically in strict-mode according to // ES6. If it is not an external module, then we'll determine if it is in strict mode or @@ -165,11 +163,7 @@ namespace ts { currentFalseTarget = undefined; activeLabels = undefined; hasExplicitReturn = false; - hasClassExtends = false; - hasAsyncFunctions = false; - hasDecorators = false; - hasParameterDecorators = false; - hasJsxSpreadAttribute = false; + emitFlags = NodeFlags.None; } return bindSourceFile; @@ -402,17 +396,12 @@ namespace ts { // All container nodes are kept on a linked list in declaration order. This list is used by // the getLocalNameOfContainer function in the type checker to validate that the local name // used for a container is unique. - function bindChildren(node: Node) { + function bindContainer(node: Node, containerFlags: ContainerFlags) { // Before we recurse into a node's children, we first save the existing parent, container // and block-container. Then after we pop out of processing the children, we restore // these saved values. - const saveParent = parent; const saveContainer = container; const savedBlockScopeContainer = blockScopeContainer; - - // This node will now be set as the parent of all of its children as we recurse into them. - parent = node; - // Depending on what kind of node this is, we may have to adjust the current container // and block-container. If the current node is a container, then it is automatically // considered the current block-container as well. Also, for containers that we know @@ -430,55 +419,33 @@ namespace ts { // reusing a node from a previous compilation, that node may have had 'locals' created // for it. We must clear this so we don't accidentally move any stale data forward from // a previous compilation. - const containerFlags = getContainerFlags(node); if (containerFlags & ContainerFlags.IsContainer) { container = blockScopeContainer = node; - if (containerFlags & ContainerFlags.HasLocals) { container.locals = {}; } - addToContainerChain(container); } else if (containerFlags & ContainerFlags.IsBlockScopedContainer) { blockScopeContainer = node; blockScopeContainer.locals = undefined; } - - let saveCurrentFlow: FlowNode; - let saveBreakTarget: FlowLabel; - let saveContinueTarget: FlowLabel; - let saveReturnTarget: FlowLabel; - let saveActiveLabels: ActiveLabel[]; - let saveHasExplicitReturn: boolean; - let isIIFE: boolean; - - const kind = node.kind; - let flags = node.flags; - - // Reset all reachability check related flags on node (for incremental scenarios) - // Reset all emit helper flags on node (for incremental scenarios) - flags &= ~NodeFlags.ReachabilityAndEmitFlags; - - if (kind === SyntaxKind.InterfaceDeclaration) { - seenThisKeyword = false; - } - - const saveState = kind === SyntaxKind.SourceFile || kind === SyntaxKind.ModuleBlock || isFunctionLikeKind(kind); - if (saveState) { - saveCurrentFlow = currentFlow; - saveBreakTarget = currentBreakTarget; - saveContinueTarget = currentContinueTarget; - saveReturnTarget = currentReturnTarget; - saveActiveLabels = activeLabels; - saveHasExplicitReturn = hasExplicitReturn; - isIIFE = (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) && !!getImmediatelyInvokedFunctionExpression(node); + if (containerFlags & ContainerFlags.IsControlFlowContainer) { + const saveCurrentFlow = currentFlow; + const saveBreakTarget = currentBreakTarget; + const saveContinueTarget = currentContinueTarget; + const saveReturnTarget = currentReturnTarget; + const saveActiveLabels = activeLabels; + const saveHasExplicitReturn = hasExplicitReturn; + const isIIFE = containerFlags & ContainerFlags.IsFunctionExpression && !!getImmediatelyInvokedFunctionExpression(node); + // An IIFE is considered part of the containing control flow. Return statements behave + // similarly to break statements that exit to a label just past the statement body. if (isIIFE) { currentReturnTarget = createBranchLabel(); } else { currentFlow = { flags: FlowFlags.Start }; - if (kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction) { + if (containerFlags & ContainerFlags.IsFunctionExpression) { (currentFlow).container = node; } currentReturnTarget = undefined; @@ -487,46 +454,17 @@ namespace ts { currentContinueTarget = undefined; activeLabels = undefined; hasExplicitReturn = false; - } - - if (node.flags & NodeFlags.JavaScriptFile && node.jsDocComment) { - bind(node.jsDocComment); - } - - bindReachableStatement(node); - - if (!(currentFlow.flags & FlowFlags.Unreachable) && isFunctionLikeKind(kind) && nodeIsPresent((node).body)) { - flags |= NodeFlags.HasImplicitReturn; - if (hasExplicitReturn) { - flags |= NodeFlags.HasExplicitReturn; + bindChildren(node); + // Reset all reachability check related flags on node (for incremental scenarios) + // Reset all emit helper flags on node (for incremental scenarios) + node.flags &= ~NodeFlags.ReachabilityAndEmitFlags; + if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((node).body)) { + node.flags |= NodeFlags.HasImplicitReturn; + if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn; } - } - - if (kind === SyntaxKind.InterfaceDeclaration) { - flags = seenThisKeyword ? flags | NodeFlags.ContainsThis : flags & ~NodeFlags.ContainsThis; - } - - if (kind === SyntaxKind.SourceFile) { - if (hasClassExtends) { - flags |= NodeFlags.HasClassExtends; + if (node.kind === SyntaxKind.SourceFile) { + node.flags |= emitFlags; } - if (hasDecorators) { - flags |= NodeFlags.HasDecorators; - } - if (hasParameterDecorators) { - flags |= NodeFlags.HasParamDecorators; - } - if (hasAsyncFunctions) { - flags |= NodeFlags.HasAsyncFunctions; - } - if (hasJsxSpreadAttribute) { - flags |= NodeFlags.HasJsxSpreadAttribute; - } - } - - node.flags = flags; - - if (saveState) { if (isIIFE) { addAntecedent(currentReturnTarget, currentFlow); currentFlow = finishFlowLabel(currentReturnTarget); @@ -540,22 +478,26 @@ namespace ts { activeLabels = saveActiveLabels; hasExplicitReturn = saveHasExplicitReturn; } - + else if (containerFlags & ContainerFlags.IsInterface) { + seenThisKeyword = false; + bindChildren(node); + node.flags = seenThisKeyword ? node.flags | NodeFlags.ContainsThis : node.flags & ~NodeFlags.ContainsThis; + } + else { + bindChildren(node); + } container = saveContainer; - parent = saveParent; blockScopeContainer = savedBlockScopeContainer; } - /** - * Returns true if node and its subnodes were successfully traversed. - * Returning false means that node was not examined and caller needs to dive into the node himself. - */ - function bindReachableStatement(node: Node): void { + function bindChildren(node: Node): void { + if (node.flags & NodeFlags.JavaScriptFile && node.jsDocComment) { + bind(node.jsDocComment); + } if (checkUnreachable(node)) { forEachChild(node, bind); return; } - switch (node.kind) { case SyntaxKind.WhileStatement: bindWhileStatement(node); @@ -1145,31 +1087,42 @@ namespace ts { switch (node.kind) { case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.TypeLiteral: case SyntaxKind.JSDocRecordType: return ContainerFlags.IsContainer; + case SyntaxKind.InterfaceDeclaration: + return ContainerFlags.IsContainer | ContainerFlags.IsInterface; + + case SyntaxKind.JSDocFunctionType: + case SyntaxKind.ModuleDeclaration: + case SyntaxKind.TypeAliasDeclaration: + return ContainerFlags.IsContainer | ContainerFlags.HasLocals; + + case SyntaxKind.SourceFile: + return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals; + + case SyntaxKind.Constructor: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + case SyntaxKind.GetAccessor: + case SyntaxKind.SetAccessor: case SyntaxKind.CallSignature: case SyntaxKind.ConstructSignature: case SyntaxKind.IndexSignature: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.Constructor: - case SyntaxKind.GetAccessor: - case SyntaxKind.SetAccessor: case SyntaxKind.FunctionType: - case SyntaxKind.JSDocFunctionType: case SyntaxKind.ConstructorType: + return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike; + case SyntaxKind.FunctionExpression: case SyntaxKind.ArrowFunction: - case SyntaxKind.ModuleDeclaration: - case SyntaxKind.SourceFile: - case SyntaxKind.TypeAliasDeclaration: - return ContainerFlags.IsContainerWithLocals; + return ContainerFlags.IsContainer | ContainerFlags.IsControlFlowContainer | ContainerFlags.HasLocals | ContainerFlags.IsFunctionLike | ContainerFlags.IsFunctionExpression; + + case SyntaxKind.ModuleBlock: + return ContainerFlags.IsControlFlowContainer; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: @@ -1606,8 +1559,8 @@ namespace ts { node.parent = parent; - const savedInStrictMode = inStrictMode; - if (!savedInStrictMode) { + const saveInStrictMode = inStrictMode; + if (!saveInStrictMode) { updateStrictMode(node); } @@ -1627,9 +1580,18 @@ namespace ts { // symbols we do specialized work when we recurse. For example, we'll keep track of // the current 'container' node when it changes. This helps us know which symbol table // a local should go into for example. - bindChildren(node); + const saveParent = parent; + parent = node; + const containerFlags = getContainerFlags(node); + if (containerFlags === ContainerFlags.None) { + bindChildren(node); + } + else { + bindContainer(node, containerFlags); + } + parent = saveParent; - inStrictMode = savedInStrictMode; + inStrictMode = saveInStrictMode; } function updateStrictMode(node: Node) { @@ -1746,7 +1708,7 @@ namespace ts { return bindPropertyOrMethodOrAccessor(node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes); case SyntaxKind.JsxSpreadAttribute: - hasJsxSpreadAttribute = true; + emitFlags |= NodeFlags.HasJsxSpreadAttribute; return; case SyntaxKind.CallSignature: @@ -1970,10 +1932,10 @@ namespace ts { function bindClassLikeDeclaration(node: ClassLikeDeclaration) { if (!isDeclarationFile(file) && !isInAmbientContext(node)) { if (getClassExtendsHeritageClauseElement(node) !== undefined) { - hasClassExtends = true; + emitFlags |= NodeFlags.HasClassExtends; } if (nodeIsDecorated(node)) { - hasDecorators = true; + emitFlags |= NodeFlags.HasDecorators; } } @@ -2049,8 +2011,7 @@ namespace ts { if (!isDeclarationFile(file) && !isInAmbientContext(node) && nodeIsDecorated(node)) { - hasDecorators = true; - hasParameterDecorators = true; + emitFlags |= (NodeFlags.HasDecorators | NodeFlags.HasParamDecorators); } if (inStrictMode) { @@ -2077,7 +2038,7 @@ namespace ts { function bindFunctionDeclaration(node: FunctionDeclaration) { if (!isDeclarationFile(file) && !isInAmbientContext(node)) { if (isAsyncFunctionLike(node)) { - hasAsyncFunctions = true; + emitFlags |= NodeFlags.HasAsyncFunctions; } } @@ -2094,7 +2055,7 @@ namespace ts { function bindFunctionExpression(node: FunctionExpression) { if (!isDeclarationFile(file) && !isInAmbientContext(node)) { if (isAsyncFunctionLike(node)) { - hasAsyncFunctions = true; + emitFlags |= NodeFlags.HasAsyncFunctions; } } if (currentFlow) { @@ -2108,10 +2069,10 @@ namespace ts { function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) { if (!isDeclarationFile(file) && !isInAmbientContext(node)) { if (isAsyncFunctionLike(node)) { - hasAsyncFunctions = true; + emitFlags |= NodeFlags.HasAsyncFunctions; } if (nodeIsDecorated(node)) { - hasDecorators = true; + emitFlags |= NodeFlags.HasDecorators; } } From 5b4dffc2d72f9c1b99fdf6fa3d2f090fc735066c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 29 May 2016 16:04:34 -0700 Subject: [PATCH 43/90] More critical path optimization in binder --- src/compiler/binder.ts | 86 ++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4f24458f08d..af304e5f0a2 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1556,15 +1556,9 @@ namespace ts { if (!node) { return; } - node.parent = parent; - const saveInStrictMode = inStrictMode; - if (!saveInStrictMode) { - updateStrictMode(node); - } - - // First we bind declaration nodes to a symbol if possible. We'll both create a symbol + // First we bind declaration nodes to a symbol if possible. We'll both create a symbol // and then potentially add the symbol to an appropriate symbol table. Possible // destination symbol tables are: // @@ -1572,56 +1566,40 @@ namespace ts { // 2) The 'members' table of the current container's symbol. // 3) The 'locals' table of the current container. // - // However, not all symbols will end up in any of these tables. 'Anonymous' symbols + // However, not all symbols will end up in any of these tables. 'Anonymous' symbols // (like TypeLiterals for example) will not be put in any table. bindWorker(node); - - // Then we recurse into the children of the node to bind them as well. For certain - // symbols we do specialized work when we recurse. For example, we'll keep track of - // the current 'container' node when it changes. This helps us know which symbol table - // a local should go into for example. - const saveParent = parent; - parent = node; - const containerFlags = getContainerFlags(node); - if (containerFlags === ContainerFlags.None) { - bindChildren(node); + // Then we recurse into the children of the node to bind them as well. For certain + // symbols we do specialized work when we recurse. For example, we'll keep track of + // the current 'container' node when it changes. This helps us know which symbol table + // a local should go into for example. Since terminal nodes are known not to have + // children, as an optimization we don't process those. + if (node.kind > SyntaxKind.LastToken) { + const saveParent = parent; + parent = node; + const containerFlags = getContainerFlags(node); + if (containerFlags === ContainerFlags.None) { + bindChildren(node); + } + else { + bindContainer(node, containerFlags); + } + parent = saveParent; } - else { - bindContainer(node, containerFlags); - } - parent = saveParent; - inStrictMode = saveInStrictMode; } - function updateStrictMode(node: Node) { - switch (node.kind) { - case SyntaxKind.SourceFile: - case SyntaxKind.ModuleBlock: - updateStrictModeStatementList((node).statements); - return; - case SyntaxKind.Block: - if (isFunctionLike(node.parent)) { - updateStrictModeStatementList((node).statements); - } - return; - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - // All classes are automatically in strict mode in ES6. - inStrictMode = true; - return; - } - } - function updateStrictModeStatementList(statements: NodeArray) { - for (const statement of statements) { - if (!isPrologueDirective(statement)) { - return; - } + if (!inStrictMode) { + for (const statement of statements) { + if (!isPrologueDirective(statement)) { + return; + } - if (isUseStrictPrologueDirective(statement)) { - inStrictMode = true; - return; + if (isUseStrictPrologueDirective(statement)) { + inStrictMode = true; + return; + } } } } @@ -1753,6 +1731,8 @@ namespace ts { // Members of classes, interfaces, and modules case SyntaxKind.ClassExpression: case SyntaxKind.ClassDeclaration: + // All classes are automatically in strict mode in ES6. + inStrictMode = true; return bindClassLikeDeclaration(node); case SyntaxKind.InterfaceDeclaration: return bindBlockScopedDeclaration(node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); @@ -1778,7 +1758,15 @@ namespace ts { case SyntaxKind.ExportAssignment: return bindExportAssignment(node); case SyntaxKind.SourceFile: + updateStrictModeStatementList((node).statements); return bindSourceFileIfExternalModule(); + case SyntaxKind.Block: + if (!isFunctionLike(node.parent)) { + return; + } + // Fall through + case SyntaxKind.ModuleBlock: + return updateStrictModeStatementList((node).statements); } } From 18ee4b0a1ee1e326b5bbcdc0104466ac9c3d13d2 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Mon, 30 May 2016 22:11:43 -0700 Subject: [PATCH 44/90] cr feedback --- src/compiler/binder.ts | 12 ++++++++++++ src/compiler/checker.ts | 16 +++++++++++++--- src/compiler/parser.ts | 29 ++++++----------------------- src/compiler/types.ts | 8 +++++--- src/services/navigationBar.ts | 6 ++++++ src/services/services.ts | 11 +---------- 6 files changed, 43 insertions(+), 39 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 756be48ed94..3d3a86f20fd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -267,6 +267,18 @@ namespace ts { let functionType = node.parent; let index = indexOf(functionType.parameters, node); return "p" + index; + case SyntaxKind.JSDocTypedefTag: + const parentNode = node.parent && node.parent.parent; + let nameFromParentNode: string; + if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { + if ((parentNode).declarationList.declarations.length > 0) { + const nameIdentifier = (parentNode).declarationList.declarations[0].name; + if (nameIdentifier.kind === SyntaxKind.Identifier) { + nameFromParentNode = (nameIdentifier).text; + } + } + } + return nameFromParentNode; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 42af34c0659..e8b23822593 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3586,11 +3586,21 @@ namespace ts { return unknownType; } + let type: Type; let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); - if (!declaration) { - declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); + if (declaration) { + if (declaration.jsDocTypeLiteral) { + type = getTypeFromTypeNode(declaration.jsDocTypeLiteral); + } + else { + type = getTypeFromTypeNode(declaration.typeExpression.type); + } } - let type = getTypeFromTypeNode(declaration.type); + else { + declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); + type = getTypeFromTypeNode(declaration.type); + } + if (popTypeResolution()) { links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); if (links.typeParameters) { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 07509d0afcc..c61b4b836ea 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -404,7 +404,7 @@ namespace ts { case SyntaxKind.JSDocTypedefTag: return visitNode(cbNode, (node).typeExpression) || visitNode(cbNode, (node).name) || - visitNode(cbNode, (node).type); + visitNode(cbNode, (node).jsDocTypeLiteral); case SyntaxKind.JSDocTypeLiteral: return visitNodes(cbNodes, (node).jsDocPropertyTags); case SyntaxKind.JSDocPropertyTag: @@ -6301,28 +6301,11 @@ namespace ts { function handleTypedefTag(atToken: Node, tagName: Identifier): JSDocTypedefTag { const typeExpression = tryParseTypeExpression(); skipWhitespace(); - let name = parseJSDocIdentifierName(); - if (!name) { - let foundNameFromParentNode = false; - if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { - if ((parentNode).declarationList.declarations.length > 0) { - const nameFromParentNode = (parentNode).declarationList.declarations[0].name; - if (nameFromParentNode.kind === SyntaxKind.Identifier) { - foundNameFromParentNode = true; - name = nameFromParentNode; - } - } - } - if (!foundNameFromParentNode) { - parseErrorAtPosition(scanner.getStartPos(), 0, Diagnostics.Identifier_expected); - return undefined; - } - } const typedefTag = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos); typedefTag.atToken = atToken; typedefTag.tagName = tagName; - typedefTag.name = name; + typedefTag.name = parseJSDocIdentifierName(); typedefTag.typeExpression = typeExpression; if (typeExpression) { @@ -6331,16 +6314,16 @@ namespace ts { if (jsDocTypeReference.name.kind === SyntaxKind.Identifier) { const name = jsDocTypeReference.name; if (name.text === "Object") { - typedefTag.type = scanChildTags(); + typedefTag.jsDocTypeLiteral = scanChildTags(); } } } - if (!typedefTag.type) { - typedefTag.type = typeExpression.type; + if (!typedefTag.jsDocTypeLiteral) { + typedefTag.jsDocTypeLiteral = typeExpression.type; } } else { - typedefTag.type = scanChildTags(); + typedefTag.jsDocTypeLiteral = scanChildTags(); } return finishNode(typedefTag); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1b9af02a8d5..f23b52b35d7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -376,7 +376,9 @@ namespace ts { LastBinaryOperator = CaretEqualsToken, FirstNode = QualifiedName, FirstJSDocNode = JSDocTypeExpression, - LastJSDocNode = JSDocTypeLiteral + LastJSDocNode = JSDocTypeLiteral, + FirstJSDocTagNode = JSDocComment, + LastJSDocTagNode = JSDocTypeLiteral } export const enum NodeFlags { @@ -1518,9 +1520,9 @@ namespace ts { // @kind(SyntaxKind.JSDocTypedefTag) export interface JSDocTypedefTag extends JSDocTag, Declaration { - name: Identifier; + name?: Identifier; typeExpression?: JSDocTypeExpression; - type: JSDocType; + jsDocTypeLiteral?: JSDocTypeLiteral; } // @kind(SyntaxKind.JSDocPropertyTag) diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 0cb431de668..5515356a259 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -653,6 +653,12 @@ namespace ts.NavigationBar { topItem.childItems.push(newItem); } + if (node.jsDocComments && node.jsDocComments.length > 0) { + for (const jsDocComment of node.jsDocComments) { + visitNode(jsDocComment); + } + } + // Add a level if traversing into a container if (newItem && (isFunctionLike(node) || isClassLike(node))) { const lastTop = topItem; diff --git a/src/services/services.ts b/src/services/services.ts index 8fef753f2e8..90740b257c6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -276,16 +276,7 @@ namespace ts { scanner.setText((sourceFile || this.getSourceFile()).text); children = []; let pos = this.pos; - const useJSDocScanner = - this.kind === SyntaxKind.JSDocComment || - this.kind === SyntaxKind.JSDocParameterTag || - this.kind === SyntaxKind.JSDocTag || - this.kind === SyntaxKind.JSDocParameterTag || - this.kind === SyntaxKind.JSDocReturnTag || - this.kind === SyntaxKind.JSDocTypeTag || - this.kind === SyntaxKind.JSDocTemplateTag || - this.kind === SyntaxKind.JSDocTypedefTag || - this.kind === SyntaxKind.JSDocPropertyTag; + const useJSDocScanner = this.kind >= SyntaxKind.FirstJSDocTagNode && this.kind <= SyntaxKind.LastJSDocTagNode; const processNode = (node: Node) => { if (pos < node.pos) { pos = this.addSyntheticNodes(children, pos, node.pos, useJSDocScanner); From 59b188d4cae7c9d0c4d39a6ec839506c1170a519 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 31 May 2016 02:05:26 -0700 Subject: [PATCH 45/90] Add navigationTo test for jsdoc typedef --- src/compiler/parser.ts | 7 ++++++ src/harness/harness.ts | 5 +++- src/services/navigationBar.ts | 23 ++++++++++++++++++- .../server/jsdocTypedefTagNavigateTo.ts | 16 +++++++++++++ tests/cases/unittests/jsDocParsing.ts | 3 ++- 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index c61b4b836ea..20f3d927e71 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -672,6 +672,13 @@ namespace ts { const saveParent = parent; parent = n; forEachChild(n, visitNode); + if (n.jsDocComments) { + for (const jsDocComment of n.jsDocComments) { + jsDocComment.parent = n; + parent = jsDocComment; + forEachChild(jsDocComment, visitNode); + } + } parent = saveParent; } } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index e1c90cee80c..5fe5b84ac91 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -223,7 +223,10 @@ namespace Utils { // For some markers in SyntaxKind, we should print its original syntax name instead of // the marker name in tests. - if (k === (ts).SyntaxKind.FirstJSDocNode || k === (ts).SyntaxKind.LastJSDocNode) { + if (k === (ts).SyntaxKind.FirstJSDocNode || + k === (ts).SyntaxKind.LastJSDocNode || + k === (ts).SyntaxKind.FirstJSDocTagNode || + k === (ts).SyntaxKind.LastJSDocTagNode) { for (const kindName in (ts).SyntaxKind) { if ((ts).SyntaxKind[kindName] === k) { return kindName; diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 5515356a259..3adc5563d71 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -738,6 +738,27 @@ namespace ts.NavigationBar { } const declName = declarationNameToString(decl.name); return getNavBarItem(declName, ScriptElementKind.constElement, [getNodeSpan(node)]); + case SyntaxKind.JSDocTypedefTag: + if ((node).name) { + return getNavBarItem( + (node).name.text, + ScriptElementKind.typeElement, + [getNodeSpan(node)]); + } + else { + const parentNode = node.parent && node.parent.parent; + if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) { + if ((parentNode).declarationList.declarations.length > 0) { + const nameIdentifier = (parentNode).declarationList.declarations[0].name; + if (nameIdentifier.kind === SyntaxKind.Identifier) { + return getNavBarItem( + (nameIdentifier).text, + ScriptElementKind.typeElement, + [getNodeSpan(node)]); + } + } + } + } default: return undefined; } @@ -808,7 +829,7 @@ namespace ts.NavigationBar { } function getNodeSpan(node: Node) { - return node.kind === SyntaxKind.SourceFile + return node.kind === SyntaxKind.SourceFile ? createTextSpanFromBounds(node.getFullStart(), node.getEnd()) : createTextSpanFromBounds(node.getStart(), node.getEnd()); } diff --git a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts new file mode 100644 index 00000000000..1d1c1ac27ca --- /dev/null +++ b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts @@ -0,0 +1,16 @@ +/// + +// @allowNonTsExtensions: true +// @Filename: jsDocTypedef_form2.js +//// +//// /** @typedef {(string | number)} NumberLike */ +//// /** @typedef {(string | number | string[])} */ +//// var NumberLike2; +//// +//// /** @type {/*1*/NumberLike} */ +//// var numberLike; + +verify.navigationBarContains("NumberLike", "type"); +verify.navigationBarContains("NumberLike2", "type"); +verify.navigationBarContains("NumberLike2", "var"); +verify.navigationBarContains("numberLike", "var"); \ No newline at end of file diff --git a/tests/cases/unittests/jsDocParsing.ts b/tests/cases/unittests/jsDocParsing.ts index 8afad08909c..fb75b4d4940 100644 --- a/tests/cases/unittests/jsDocParsing.ts +++ b/tests/cases/unittests/jsDocParsing.ts @@ -1004,7 +1004,8 @@ namespace ts { if (result !== expected) { // Turn on a human-readable diff if (typeof require !== "undefined") { - require("chai").config.showDiff = true; + const chai = require("chai"); + chai.config.showDiff = true; chai.expect(JSON.parse(result)).equal(JSON.parse(expected)); } else { From e4ca76898f6ae52496c6c84ddb24c9199c77b702 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 31 May 2016 10:17:45 -0700 Subject: [PATCH 46/90] Remove redundant parameter to `writeTestConfigFile` It allowed a new parameter to silently succeed, causing runtests-browser to run zero files. --- Jakefile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 474303a9722..2f0ce4aa399 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -680,7 +680,7 @@ function cleanTestDirs() { } // used to pass data from jake command line directly to run.js -function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, testConfigFile) { +function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount) { var testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder }); console.log('Running tests with config: ' + testConfigContents); fs.writeFileSync('test.config', testConfigContents); @@ -716,7 +716,7 @@ function runConsoleTests(defaultReporter, runInParallel) { } if (tests || light || taskConfigsFolder) { - writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, testConfigFile); + writeTestConfigFile(tests, light, taskConfigsFolder, workerCount); } if (tests && tests.toLocaleLowerCase() === "rwc") { @@ -850,7 +850,7 @@ task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFi fs.unlinkSync(testConfigFile); } if(tests || light) { - writeTestConfigFile(tests, light, testConfigFile); + writeTestConfigFile(tests, light); } tests = tests ? tests : ''; From e93f9df95506a8cf3daa99b18a05331960c9f726 Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Tue, 31 May 2016 10:48:25 -0700 Subject: [PATCH 47/90] Fix broken test --- .../server/jsdocTypedefTagNavigateTo.ts | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts index 1d1c1ac27ca..77cd75aa44c 100644 --- a/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts +++ b/tests/cases/fourslash/server/jsdocTypedefTagNavigateTo.ts @@ -10,7 +10,21 @@ //// /** @type {/*1*/NumberLike} */ //// var numberLike; -verify.navigationBarContains("NumberLike", "type"); -verify.navigationBarContains("NumberLike2", "type"); -verify.navigationBarContains("NumberLike2", "var"); -verify.navigationBarContains("numberLike", "var"); \ No newline at end of file +verify.navigationBar([ + { + "text": "NumberLike", + "kind": "type" + }, + { + "text": "NumberLike2", + "kind": "type" + }, + { + "text": "NumberLike2", + "kind": "var" + }, + { + "text": "numberLike", + "kind": "var" + } +]); \ No newline at end of file From 35b8b42b554d2b98230320b727b452d0abe9aaae Mon Sep 17 00:00:00 2001 From: Yui Date: Tue, 31 May 2016 11:16:56 -0700 Subject: [PATCH 48/90] Only check if the extensions match (#8870) --- src/harness/loggedIO.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/harness/loggedIO.ts b/src/harness/loggedIO.ts index 1c399b30a1d..3535decf121 100644 --- a/src/harness/loggedIO.ts +++ b/src/harness/loggedIO.ts @@ -226,13 +226,7 @@ namespace Playback { (path, extension, exclude) => findResultByPath(wrapper, replayLog.directoriesRead.filter( d => { - if (d.extension === extension) { - if (d.exclude) { - return ts.arrayIsEqualTo(d.exclude, exclude); - } - return true; - } - return false; + return d.extension === extension; } ), path)); From 3433a7800a01e045486e48bc06fffc551295086f Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 31 May 2016 12:35:12 -0700 Subject: [PATCH 49/90] Fix formatOnEnter for double newlines --- src/harness/fourslash.ts | 10 ++++++++++ src/services/formatting/formatting.ts | 6 ++++++ tests/cases/fourslash/formatOnEnter.ts | 17 +++++++++++++++++ tests/cases/fourslash/fourslash.ts | 1 + 4 files changed, 34 insertions(+) create mode 100644 tests/cases/fourslash/formatOnEnter.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 4a756ab3b1e..bd556c659c7 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1486,6 +1486,12 @@ namespace FourSlash { this.fixCaretPosition(); } + public formatOnType(pos: number, key: string) { + const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, pos, key, this.formatCodeOptions); + this.currentCaretPosition += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); + this.fixCaretPosition(); + } + private updateMarkersForEdit(fileName: string, minChar: number, limChar: number, text: string) { for (let i = 0; i < this.testData.markers.length; i++) { const marker = this.testData.markers[i]; @@ -3223,6 +3229,10 @@ namespace FourSlashInterface { this.state.formatSelection(this.state.getMarkerByName(startMarker).position, this.state.getMarkerByName(endMarker).position); } + public onType(posMarker: string, key: string) { + this.state.formatOnType(this.state.getMarkerByName(posMarker).position, key); + } + public setOption(name: string, value: number): void; public setOption(name: string, value: string): void; public setOption(name: string, value: boolean): void; diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 2f5f10752f4..7127eb98006 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -81,6 +81,12 @@ namespace ts.formatting { while (isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { endOfFormatSpan--; } + // if the character at the end of the span is a line break, we shouldn't include it, because it indicates we don't want to + // touch the current line at all. Also, on some OSes the line break consists of two characters (\r\n), we should test if the + // previous character before the end of format span is line break character as well. + while (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { + endOfFormatSpan--; + } const span = { // get start position for the previous line pos: getStartPositionOfLine(line - 1, sourceFile), diff --git a/tests/cases/fourslash/formatOnEnter.ts b/tests/cases/fourslash/formatOnEnter.ts new file mode 100644 index 00000000000..f505cf6cec7 --- /dev/null +++ b/tests/cases/fourslash/formatOnEnter.ts @@ -0,0 +1,17 @@ +/// + +/////*3*/function listAPIFiles (path : string): string[] { +//// /*1*/ +//// /*2*/ +////} + +goTo.marker("1"); +format.onType("1", "\n"); +verify.currentLineContentIs(" "); + +goTo.marker("2"); +format.onType("2", "\n"); +verify.currentLineContentIs(" "); + +goTo.marker("3"); +verify.currentLineContentIs("function listAPIFiles(path: string): string[] {"); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 8a0d2d36364..71e1c3cc5d7 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -246,6 +246,7 @@ declare namespace FourSlashInterface { copyFormatOptions(): FormatCodeOptions; setFormatOptions(options: FormatCodeOptions): any; selection(startMarker: string, endMarker: string): void; + onType(posMarker: string, key: string): void; setOption(name: string, value: number): any; setOption(name: string, value: string): any; setOption(name: string, value: boolean): any; From 58fdd011df74f226f1909a2155896e43fc5c2042 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 31 May 2016 14:08:48 -0700 Subject: [PATCH 50/90] avoid eating all preceding empty lines --- src/services/formatting/formatting.ts | 2 +- tests/cases/fourslash/{ => server}/formatOnEnter.ts | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/cases/fourslash/{ => server}/formatOnEnter.ts (100%) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 7127eb98006..ef8fddcfb3a 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -84,7 +84,7 @@ namespace ts.formatting { // if the character at the end of the span is a line break, we shouldn't include it, because it indicates we don't want to // touch the current line at all. Also, on some OSes the line break consists of two characters (\r\n), we should test if the // previous character before the end of format span is line break character as well. - while (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { + if (isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { endOfFormatSpan--; } const span = { diff --git a/tests/cases/fourslash/formatOnEnter.ts b/tests/cases/fourslash/server/formatOnEnter.ts similarity index 100% rename from tests/cases/fourslash/formatOnEnter.ts rename to tests/cases/fourslash/server/formatOnEnter.ts From 41446fe4c27820e5668e6a37ba7a2934d6dfa7ad Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 31 May 2016 14:33:00 -0700 Subject: [PATCH 51/90] Address CR feedback --- src/compiler/checker.ts | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 037ab78413a..caaabdc96cd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8643,25 +8643,23 @@ namespace ts { function getContextuallyTypedParameterType(parameter: ParameterDeclaration): Type { const func = parameter.parent; if (isContextSensitiveFunctionOrObjectLiteralMethod(func)) { - if (func.kind === SyntaxKind.FunctionExpression || func.kind === SyntaxKind.ArrowFunction) { - const iife = getImmediatelyInvokedFunctionExpression(func); - if (iife) { - const indexOfParameter = indexOf(func.parameters, parameter); - if (iife.arguments && indexOfParameter < iife.arguments.length) { - if (parameter.dotDotDotToken) { - const restTypes: Type[] = []; - for (let i = indexOfParameter; i < iife.arguments.length; i++) { - restTypes.push(getTypeOfExpression(iife.arguments[i])); - } - return createArrayType(getUnionType(restTypes)); + const iife = getImmediatelyInvokedFunctionExpression(func); + if (iife) { + const indexOfParameter = indexOf(func.parameters, parameter); + if (iife.arguments && indexOfParameter < iife.arguments.length) { + if (parameter.dotDotDotToken) { + const restTypes: Type[] = []; + for (let i = indexOfParameter; i < iife.arguments.length; i++) { + restTypes.push(getTypeOfExpression(iife.arguments[i])); } - const links = getNodeLinks(iife); - const cached = links.resolvedSignature; - links.resolvedSignature = anySignature; - const type = checkExpression(iife.arguments[indexOfParameter]); - links.resolvedSignature = cached; - return type; + return createArrayType(getUnionType(restTypes)); } + const links = getNodeLinks(iife); + const cached = links.resolvedSignature; + links.resolvedSignature = anySignature; + const type = checkExpression(iife.arguments[indexOfParameter]); + links.resolvedSignature = cached; + return type; } } const contextualSignature = getContextualSignature(func); From eb0f035c78219ed920052094351326c5b35e1fe3 Mon Sep 17 00:00:00 2001 From: zhengbli Date: Tue, 31 May 2016 16:08:12 -0700 Subject: [PATCH 52/90] Remove unused parameter --- src/compiler/parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 20f3d927e71..6f042627c6b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2725,7 +2725,7 @@ namespace ts { // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] // Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression". - // And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression". + // And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression". // // If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done @@ -6050,7 +6050,7 @@ namespace ts { const saveParseDiagnosticsLength = parseDiagnostics.length; const saveParseErrorBeforeNextFinishedNode = parseErrorBeforeNextFinishedNode; - const comment = parseJSDocCommentWorker(start, length, parent); + const comment = parseJSDocCommentWorker(start, length); if (comment) { comment.parent = parent; } @@ -6062,7 +6062,7 @@ namespace ts { return comment; } - export function parseJSDocCommentWorker(start: number, length: number, parentNode?: Node): JSDocComment { + export function parseJSDocCommentWorker(start: number, length: number): JSDocComment { const content = sourceText; start = start || 0; const end = length === undefined ? content.length : start + length; From 9a620bf616e1f6fac6d035ce43ad690455fa84a6 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 1 Jun 2016 11:39:22 -0700 Subject: [PATCH 53/90] Actually merge from master --- src/compiler/checker.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 52e70941408..ef11dc904ea 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8156,23 +8156,12 @@ namespace ts { return type; } const declaration = localOrExportSymbol.valueDeclaration; -<<<<<<< HEAD - const defaultsToDeclaredType = !strictNullChecks || type.flags & TypeFlags.Any || !declaration || - declaration.kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - getContainingFunctionOrModule(declaration) !== getContainingFunctionOrModule(node); - if (defaultsToDeclaredType && type.flags & TypeFlags.NotNarrowable) { - return type; - } - const flowType = getFlowTypeOfReference(node, type, defaultsToDeclaredType ? type : undefinedType); - if (strictNullChecks && !(type.flags & TypeFlags.Any) && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { -======= const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol); const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration || getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { ->>>>>>> master error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; From 0e96c5eaf134c5317febf417044a638b4ce6d70e Mon Sep 17 00:00:00 2001 From: zhengbli Date: Wed, 1 Jun 2016 22:57:25 -0700 Subject: [PATCH 54/90] Run fixupParentReferences when parsing isolated jsDocComment --- src/compiler/parser.ts | 21 +++++++++++++++++- ...tacticClassificationForJSDocTemplateTag.ts | 22 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 6f042627c6b..13f16f3848a 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -440,7 +440,26 @@ namespace ts { /* @internal */ export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) { - return Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length); + const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length); + if (result.jsDocComment) { + // because the jsDocComment was parsed out of the source file, it might + // not be covered by the fixupParentReferences. + let parentNode: Node = result.jsDocComment; + forEachChild(result.jsDocComment, visitNode); + + function visitNode(n: Node): void { + if (n.parent !== parentNode) { + n.parent = parentNode; + + const saveParent = parentNode; + parentNode = n; + forEachChild(n, visitNode); + parentNode = saveParent; + } + } + } + + return result; } /* @internal */ diff --git a/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts b/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts new file mode 100644 index 00000000000..c3368207d2c --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationForJSDocTemplateTag.ts @@ -0,0 +1,22 @@ +/// + +/////** @template T */ +////function ident: T { +////} + +var c = classification; +verify.syntacticClassificationsAre( + c.comment("/** "), + c.punctuation("@"), + c.docCommentTagName("template"), + c.typeParameterName("T"), + c.comment(" */"), + c.keyword("function"), + c.identifier("ident"), + c.punctuation("<"), + c.typeParameterName("T"), + c.punctuation(">"), + c.punctuation(":"), + c.identifier("T"), + c.punctuation("{"), + c.punctuation("}")); From 92177bee913eb7d27b256d421f590290963e4c11 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 2 Jun 2016 00:03:10 -0700 Subject: [PATCH 55/90] initial revision of unit test support for project system in tsserver --- Jakefile.js | 3 +- src/server/editorServices.ts | 2 +- .../cases/unittests/tsserverProjectSystem.ts | 294 ++++++++++++++++++ 3 files changed, 297 insertions(+), 2 deletions(-) create mode 100644 tests/cases/unittests/tsserverProjectSystem.ts diff --git a/Jakefile.js b/Jakefile.js index 2f0ce4aa399..c4f56c559f3 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -153,7 +153,8 @@ var harnessSources = harnessCoreSources.concat([ "tsconfigParsing.ts", "commandLineParsing.ts", "convertCompilerOptionsFromJson.ts", - "convertTypingOptionsFromJson.ts" + "convertTypingOptionsFromJson.ts", + "tsserverProjectSystem.ts" ].map(function (f) { return path.join(unittestsDirectory, f); })).concat([ diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 5502f74408e..8eae43b503d 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1138,7 +1138,7 @@ namespace ts.server { else { this.log("No config files found."); } - return {}; + return configFileName ? { configFileName } : {}; } /** diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts new file mode 100644 index 00000000000..c69821ced5f --- /dev/null +++ b/tests/cases/unittests/tsserverProjectSystem.ts @@ -0,0 +1,294 @@ +/// + +namespace ts { + function notImplemented(): any { + throw new Error("Not yet implemented"); + } + + const nullLogger: server.Logger = { + close: () => void 0, + isVerbose: () => void 0, + loggingEnabled: () => false, + perftrc: () => void 0, + info: () => void 0, + startGroup: () => void 0, + endGroup: () => void 0, + msg: () => void 0 + }; + + const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO); + + function getExecutingFilePathFromLibFile(libFile: FileOrFolder): string { + return combinePaths(getDirectoryPath(libFile.path), "tsc.js"); + } + + interface FileOrFolder { + path: string; + content?: string; + } + + interface FSEntry { + path: Path; + fullPath: string; + } + + interface File extends FSEntry { + content: string; + } + + interface Folder extends FSEntry { + entries: FSEntry[]; + } + + function isFolder(s: FSEntry): s is Folder { + return isArray((s).entries); + } + + function isFile(s: FSEntry): s is File { + return typeof (s).content === "string"; + } + + function addFolder(fullPath: string, toPath: (s: string) => Path, fs: FileMap): Folder { + const path = toPath(fullPath); + if (fs.contains(path)) { + Debug.assert(isFolder(fs.get(path))); + return (fs.get(path)); + } + + const entry: Folder = { path, entries: [], fullPath }; + fs.set(path, entry); + + const baseFullPath = getDirectoryPath(fullPath); + if (fullPath !== baseFullPath) { + addFolder(baseFullPath, toPath, fs).entries.push(entry); + } + + return entry; + } + + function sizeOfMap(map: Map): number { + let n = 0; + for (const name in map) { + if (hasProperty(map, name)) { + n++; + } + } + return n; + } + + function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { + assert.equal(sizeOfMap(map), expectedKeys.length, `${caption}: incorrect size of map`); + for (const name of expectedKeys) { + assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getKeys(map)}`); + } + } + + function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) { + assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`); + for (const f of expectedFileNames) { + assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`); + } + } + + function readDirectory(folder: FSEntry, ext: string, excludes: Path[], result: string[]): void { + if (!folder || !isFolder(folder) || contains(excludes, folder.path)) { + return; + } + for (const entry of folder.entries) { + if (contains(excludes, entry.path)) { + continue; + } + if (isFolder(entry)) { + readDirectory(entry, ext, excludes, result); + } + else if (fileExtensionIs(entry.path, ext)) { + result.push(entry.fullPath); + } + } + } + + class TestServerHost implements server.ServerHost { + args: string[] = []; + newLine: "\n"; + + private fs: ts.FileMap; + private getCanonicalFileName: (s: string) => string; + private toPath: (f: string) => Path; + readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; + readonly watchedFiles: Map = {}; + + constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) { + this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + this.toPath = s => toPath(s, currentDirectory, this.getCanonicalFileName); + + this.reloadFS(fileOrFolderList); + } + + reloadFS(filesOrFolders: FileOrFolder[]) { + this.fs = createFileMap(); + for (const fileOrFolder of filesOrFolders) { + const path = this.toPath(fileOrFolder.path); + const fullPath = getNormalizedAbsolutePath(fileOrFolder.path, this.currentDirectory); + if (typeof fileOrFolder.content === "string") { + const entry = { path, content: fileOrFolder.content, fullPath }; + this.fs.set(path, entry); + addFolder(getDirectoryPath(fullPath), this.toPath, this.fs).entries.push(entry); + } + else { + addFolder(fullPath, this.toPath, this.fs); + } + } + } + + fileExists(s: string) { + const path = this.toPath(s); + return this.fs.contains(path) && isFile(this.fs.get(path)); + }; + + directoryExists(s: string) { + const path = this.toPath(s); + return this.fs.contains(path) && isFolder(this.fs.get(path)); + } + + getDirectories(s: string) { + const path = this.toPath(s); + if (!this.fs.contains(path)) { + return []; + } + else { + const entry = this.fs.get(path); + return isFolder(entry) ? map(entry.entries, x => getBaseFileName(x.fullPath)) : []; + } + } + + readDirectory(path: string, ext: string, excludes: string[]): string[] { + const result: string[] = []; + readDirectory(this.fs.get(this.toPath(path)), ext, map(excludes, e => toPath(e, path, this.getCanonicalFileName)), result); + return result; + } + + watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { + const path = this.toPath(directoryName); + const callbacks = lookUp(this.watchedDirectories, path) || (this.watchedDirectories[path] = []); + callbacks.push({ cb: callback, recursive }); + return { + referenceCount: 0, + directoryName, + close: () => { + for (let i = 0; i < callbacks.length; i++) { + if (callbacks[i].cb === callback) { + callbacks.splice(i, 1); + break; + } + } + if (!callbacks.length) { + delete this.watchedDirectories[path]; + } + } + }; + } + + watchFile(fileName: string, callback: FileWatcherCallback) { + const path = this.toPath(fileName); + const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []); + callbacks.push(callback); + return { + close: () => { + const i = callbacks.indexOf(callback); + callbacks.splice(i, 1); + if (!callbacks.length) { + delete this.watchedFiles[path]; + } + } + }; + } + + // TOOD: record and invoke callbacks to simulate timer events + readonly setTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]): any => void 0; + readonly clearTimeout = (timeoutId: any): void => void 0; + readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content; + readonly resolvePath = (s: string) => s; + readonly getExecutingFilePath = () => this.executingFilePath; + readonly getCurrentDirectory = () => this.currentDirectory; + readonly writeFile = (path: string, content: string) => notImplemented(); + readonly write = (s: string) => notImplemented(); + readonly createDirectory = (s: string) => notImplemented(); + readonly exit = () => notImplemented(); + } + + describe("tsserver project system:", () => { + it("create inferred project", () => { + const appFile: FileOrFolder = { + path: "/a/b/c/app.ts", + content: ` + import {f} from "./module" + console.log(f) + ` + }; + const libFile: FileOrFolder = { + path: "/a/lib/lib.d.ts", + content: libFileContent + }; + const moduleFile: FileOrFolder = { + path: "/a/b/c/module.d.ts", + content: `export let x: number` + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [appFile, moduleFile, libFile]); + const projectService = new server.ProjectService(host, nullLogger); + const { configFileName } = projectService.openClientFile(appFile.path); + + assert(!configFileName, `should not find config, got: '${configFileName}`); + assert.equal(projectService.inferredProjects.length, 1, "expected one inferred project"); + assert.equal(projectService.configuredProjects.length, 0, "expected no configured project"); + + const project = projectService.inferredProjects[0]; + + checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]); + checkMapKeys("watchedDirectories", host.watchedDirectories, ["/a/b/c", "/a/b", "/a"]); + }); + + it("create configured project without file list", () => { + const configFile: FileOrFolder = { + path: "/a/b/tsconfig.json", + content: ` + { + "compilerOptions": {}, + "exclude": [ + "e" + ] + }` + }; + const libFile: FileOrFolder = { + path: "/a/lib/lib.d.ts", + content: libFileContent + }; + const file1: FileOrFolder = { + path: "/a/b/c/f1.ts", + content: "let x = 1" + }; + const file2: FileOrFolder = { + path: "/a/b/d/f2.ts", + content: "let y = 1" + }; + const file3: FileOrFolder = { + path: "/a/b/e/f3.ts", + content: "let z = 1" + }; + const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [ configFile, libFile, file1, file2, file3 ]); + const projectService = new server.ProjectService(host, nullLogger); + const { configFileName, configFileErrors } = projectService.openClientFile(file1.path); + + assert(configFileName, "should find config file"); + assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`); + assert.equal(projectService.inferredProjects.length, 0, "expected no inferred project"); + assert.equal(projectService.configuredProjects.length, 1, "expected one configured project"); + + const project = projectService.configuredProjects[0]; + checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), [file1.path, libFile.path, file2.path]); + checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), [file1.path, file2.path]); + + checkMapKeys("watchedFiles", host.watchedFiles, [configFile.path, file2.path, libFile.path]); // watching all files except one that was open + checkMapKeys("watchedDirectories", host.watchedDirectories, [getDirectoryPath(configFile.path)]); + }); + }); +} \ No newline at end of file From 251723826962cc3fa3cc2d766c17dc197099cce3 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:32:14 -0700 Subject: [PATCH 56/90] Add non-widening forms of null and undefined --- src/compiler/checker.ts | 37 ++++++++++++++++++++----------------- src/compiler/types.ts | 6 +++--- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 06ef2463402..6166234dcf4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -110,16 +110,17 @@ namespace ts { const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__"); - const nullableWideningFlags = strictNullChecks ? 0 : TypeFlags.ContainsUndefinedOrNull; const anyType = createIntrinsicType(TypeFlags.Any, "any"); const stringType = createIntrinsicType(TypeFlags.String, "string"); const numberType = createIntrinsicType(TypeFlags.Number, "number"); const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean"); const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol"); const voidType = createIntrinsicType(TypeFlags.Void, "void"); - const undefinedType = createIntrinsicType(TypeFlags.Undefined | nullableWideningFlags, "undefined"); - const nullType = createIntrinsicType(TypeFlags.Null | nullableWideningFlags, "null"); - const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsUndefinedOrNull, "undefined"); + const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined"); + const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined"); + const nullType = createIntrinsicType(TypeFlags.Null, "null"); + const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null"); + const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined"); const unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); const neverType = createIntrinsicType(TypeFlags.Never, "never"); @@ -3405,7 +3406,7 @@ namespace ts { error(type.symbol.valueDeclaration, Diagnostics._0_is_referenced_directly_or_indirectly_in_its_own_base_expression, symbolToString(type.symbol)); return type.resolvedBaseConstructorType = unknownType; } - if (baseConstructorType !== unknownType && baseConstructorType !== nullType && !isConstructorType(baseConstructorType)) { + if (baseConstructorType !== unknownType && baseConstructorType !== nullWideningType && !isConstructorType(baseConstructorType)) { error(baseTypeNode.expression, Diagnostics.Type_0_is_not_a_constructor_function_type, typeToString(baseConstructorType)); return type.resolvedBaseConstructorType = unknownType; } @@ -5011,6 +5012,7 @@ namespace ts { containsAny?: boolean; containsUndefined?: boolean; containsNull?: boolean; + containsNonWideningType?: boolean; } function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) { @@ -5021,6 +5023,7 @@ namespace ts { if (type.flags & TypeFlags.Any) typeSet.containsAny = true; if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true; if (type.flags & TypeFlags.Null) typeSet.containsNull = true; + if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true; } else if (type !== neverType && !contains(typeSet, type)) { typeSet.push(type); @@ -5081,8 +5084,8 @@ namespace ts { removeSubtypes(typeSet); } if (typeSet.length === 0) { - return typeSet.containsNull ? nullType : - typeSet.containsUndefined ? undefinedType : + return typeSet.containsNull ? typeSet.containsNonWideningType ? nullType : nullWideningType : + typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType : neverType; } else if (typeSet.length === 1) { @@ -5882,7 +5885,7 @@ namespace ts { if (!(target.flags & TypeFlags.Never)) { if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return Ternary.True; if (source.flags & TypeFlags.Undefined) { - if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void) || source === emptyArrayElementType) return Ternary.True; + if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void)) return Ternary.True; } if (source.flags & TypeFlags.Null) { if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True; @@ -6972,7 +6975,7 @@ namespace ts { if (type.flags & TypeFlags.ObjectLiteral) { for (const p of getPropertiesOfObjectType(type)) { const t = getTypeOfSymbol(p); - if (t.flags & TypeFlags.ContainsUndefinedOrNull) { + if (t.flags & TypeFlags.ContainsWideningType) { if (!reportWideningErrorsInType(t)) { error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, p.name, typeToString(getWidenedType(t))); } @@ -7019,7 +7022,7 @@ namespace ts { } function reportErrorsFromWidening(declaration: Declaration, type: Type) { - if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsUndefinedOrNull) { + if (produceDiagnostics && compilerOptions.noImplicitAny && type.flags & TypeFlags.ContainsWideningType) { // Report implicit any error within type if possible, otherwise report error on declaration if (!reportWideningErrorsInType(type)) { reportImplicitAnyError(declaration, type); @@ -8311,7 +8314,7 @@ namespace ts { const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType); - return baseConstructorType === nullType; + return baseConstructorType === nullWideningType; } function checkThisExpression(node: Node): Type { @@ -9202,7 +9205,7 @@ namespace ts { } } } - return createArrayType(elementTypes.length ? getUnionType(elementTypes) : emptyArrayElementType); + return createArrayType(elementTypes.length ? getUnionType(elementTypes) : strictNullChecks ? neverType : undefinedWideningType); } function isNumericName(name: DeclarationName): boolean { @@ -12002,7 +12005,7 @@ namespace ts { function checkVoidExpression(node: VoidExpression): Type { checkExpression(node.expression); - return undefinedType; + return undefinedWideningType; } function checkAwaitExpression(node: AwaitExpression): Type { @@ -12409,7 +12412,7 @@ namespace ts { case SyntaxKind.InKeyword: return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: - return addNullableKind(rightType, getNullableKind(leftType)); + return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType; case SyntaxKind.BarBarToken: return getUnionType([getNonNullableType(leftType), rightType]); case SyntaxKind.EqualsToken: @@ -12676,7 +12679,7 @@ namespace ts { case SyntaxKind.SuperKeyword: return checkSuperExpression(node); case SyntaxKind.NullKeyword: - return nullType; + return nullWideningType; case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: return booleanType; @@ -12734,7 +12737,7 @@ namespace ts { case SyntaxKind.SpreadElementExpression: return checkSpreadElementExpression(node, contextualMapper); case SyntaxKind.OmittedExpression: - return undefinedType; + return undefinedWideningType; case SyntaxKind.YieldExpression: return checkYieldExpression(node); case SyntaxKind.JsxExpression: @@ -17648,7 +17651,7 @@ namespace ts { // Setup global builtins addToSymbolTable(globals, builtinGlobals, Diagnostics.Declaration_name_conflicts_with_built_in_global_identifier_0); - getSymbolLinks(undefinedSymbol).type = undefinedType; + getSymbolLinks(undefinedSymbol).type = undefinedWideningType; getSymbolLinks(argumentsSymbol).type = getGlobalType("IArguments"); getSymbolLinks(unknownSymbol).type = unknownType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c7e14a10031..4c0e7a036db 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2197,7 +2197,7 @@ namespace ts { /* @internal */ FreshObjectLiteral = 0x00100000, // Fresh object literal type /* @internal */ - ContainsUndefinedOrNull = 0x00200000, // Type is or contains undefined or null type + ContainsWideningType = 0x00200000, // Type is or contains undefined or null widening type /* @internal */ ContainsObjectLiteral = 0x00400000, // Type is or contains object literal type /* @internal */ @@ -2220,9 +2220,9 @@ namespace ts { StructuredType = ObjectType | Union | Intersection, Narrowable = Any | ObjectType | Union | TypeParameter, /* @internal */ - RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral, + RequiresWidening = ContainsWideningType | ContainsObjectLiteral, /* @internal */ - PropagatingFlags = ContainsUndefinedOrNull | ContainsObjectLiteral | ContainsAnyFunctionType + PropagatingFlags = ContainsWideningType | ContainsObjectLiteral | ContainsAnyFunctionType } export type DestructuringPattern = BindingPattern | ObjectLiteralExpression | ArrayLiteralExpression; From 5f3f2d302fbbb5fd95c81534944553221914a342 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:47:37 -0700 Subject: [PATCH 57/90] Create separate control flows for property declarations with initializers --- src/compiler/binder.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5fdfc7f02be..8c48d984b78 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1142,6 +1142,8 @@ namespace ts { case SyntaxKind.ModuleBlock: return ContainerFlags.IsControlFlowContainer; + case SyntaxKind.PropertyDeclaration: + return (node).initializer ? ContainerFlags.IsControlFlowContainer : 0; case SyntaxKind.CatchClause: case SyntaxKind.ForStatement: From 706683d51b90ef8b67b52db0e21182c0eaba816d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 06:54:27 -0700 Subject: [PATCH 58/90] Add regression test --- .../controlFlowPropertyDeclarations.js | 291 +++++++++++++ .../controlFlowPropertyDeclarations.symbols | 288 +++++++++++++ .../controlFlowPropertyDeclarations.types | 383 ++++++++++++++++++ .../controlFlowPropertyDeclarations.ts | 148 +++++++ 4 files changed, 1110 insertions(+) create mode 100644 tests/baselines/reference/controlFlowPropertyDeclarations.js create mode 100644 tests/baselines/reference/controlFlowPropertyDeclarations.symbols create mode 100644 tests/baselines/reference/controlFlowPropertyDeclarations.types create mode 100644 tests/cases/compiler/controlFlowPropertyDeclarations.ts diff --git a/tests/baselines/reference/controlFlowPropertyDeclarations.js b/tests/baselines/reference/controlFlowPropertyDeclarations.js new file mode 100644 index 00000000000..748f63ea858 --- /dev/null +++ b/tests/baselines/reference/controlFlowPropertyDeclarations.js @@ -0,0 +1,291 @@ +//// [controlFlowPropertyDeclarations.ts] +// Repro from ##8913 + +declare var require:any; + +var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig'); + +// Populate property map with ReactJS's attribute and property mappings +// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr +for (var propname in HTMLDOMPropertyConfig.Properties) { + if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) { + continue; + } + + var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase(); +} + +/** + * Repeats a string a certain number of times. + * Also: the future is bright and consists of native string repetition: + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat + * + * @param {string} string String to repeat + * @param {number} times Number of times to repeat string. Integer. + * @see http://jsperf.com/string-repeater/2 + */ +function repeatString(string, times) { + if (times === 1) { + return string; + } + if (times < 0) { throw new Error(); } + var repeated = ''; + while (times) { + if (times & 1) { + repeated += string; + } + if (times >>= 1) { + string += string; + } + } + return repeated; +} + +/** + * Determine if the string ends with the specified substring. + * + * @param {string} haystack String to search in + * @param {string} needle String to search for + * @return {boolean} + */ +function endsWith(haystack, needle) { + return haystack.slice(-needle.length) === needle; +} + +/** + * Trim the specified substring off the string. If the string does not end + * with the specified substring, this is a no-op. + * + * @param {string} haystack String to search in + * @param {string} needle String to search for + * @return {string} + */ +function trimEnd(haystack, needle) { + return endsWith(haystack, needle) + ? haystack.slice(0, -needle.length) + : haystack; +} + +/** + * Convert a hyphenated string to camelCase. + */ +function hyphenToCamelCase(string) { + return string.replace(/-(.)/g, function(match, chr) { + return chr.toUpperCase(); + }); +} + +/** + * Determines if the specified string consists entirely of whitespace. + */ +function isEmpty(string) { + return !/[^\s]/.test(string); +} + +/** + * Determines if the CSS value can be converted from a + * 'px' suffixed string to a numeric value + * + * @param {string} value CSS property value + * @return {boolean} + */ +function isConvertiblePixelValue(value) { + return /^\d+px$/.test(value); +} + +export class HTMLtoJSX { + private output: string; + private level: number; + private _inPreTag: boolean; + + + /** + * Handles processing of the specified text node + * + * @param {TextNode} node + */ + _visitText = (node) => { + var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase(); + if (parentTag === 'textarea' || parentTag === 'style') { + // Ignore text content of textareas and styles, as it will have already been moved + // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively. + return; + } + + var text = '' + + if (this._inPreTag) { + // If this text is contained within a
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+        .replace(/\r/g, '')
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+          return '{' + JSON.stringify(whitespace) + '}';
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+      }
+    }
+    this.output += text;
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+  styles = {};
+  toJSXString = () => {
+    for (var key in this.styles) {
+      if (!this.styles.hasOwnProperty(key)) {
+      }
+    }
+  }
+}
+
+//// [controlFlowPropertyDeclarations.js]
+// Repro from ##8913
+"use strict";
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+    if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+        continue;
+    }
+    var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+}
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+    if (times === 1) {
+        return string;
+    }
+    if (times < 0) {
+        throw new Error();
+    }
+    var repeated = '';
+    while (times) {
+        if (times & 1) {
+            repeated += string;
+        }
+        if (times >>= 1) {
+            string += string;
+        }
+    }
+    return repeated;
+}
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+    return haystack.slice(-needle.length) === needle;
+}
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+    return endsWith(haystack, needle)
+        ? haystack.slice(0, -needle.length)
+        : haystack;
+}
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+    return string.replace(/-(.)/g, function (match, chr) {
+        return chr.toUpperCase();
+    });
+}
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+    return !/[^\s]/.test(string);
+}
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+    return /^\d+px$/.test(value);
+}
+var HTMLtoJSX = (function () {
+    function HTMLtoJSX() {
+        var _this = this;
+        /**
+         * Handles processing of the specified text node
+         *
+         * @param {TextNode} node
+         */
+        this._visitText = function (node) {
+            var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+            if (parentTag === 'textarea' || parentTag === 'style') {
+                // Ignore text content of textareas and styles, as it will have already been moved
+                // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+                return;
+            }
+            var text = '';
+            if (_this._inPreTag) {
+                // If this text is contained within a 
, we need to ensure the JSX
+                // whitespace coalescing rules don't eat the whitespace. This means
+                // wrapping newlines and sequences of two or more spaces in variables.
+                text = text
+                    .replace(/\r/g, '')
+                    .replace(/( {2,}|\n|\t|\{|\})/g, function (whitespace) {
+                    return '{' + JSON.stringify(whitespace) + '}';
+                });
+            }
+            else {
+                // If there's a newline in the text, adjust the indent level
+                if (text.indexOf('\n') > -1) {
+                }
+            }
+            _this.output += text;
+        };
+    }
+    return HTMLtoJSX;
+}());
+exports.HTMLtoJSX = HTMLtoJSX;
+;
+/**
+ * Handles parsing of inline styles
+ */
+var StyleParser = (function () {
+    function StyleParser() {
+        var _this = this;
+        this.styles = {};
+        this.toJSXString = function () {
+            for (var key in _this.styles) {
+                if (!_this.styles.hasOwnProperty(key)) {
+                }
+            }
+        };
+    }
+    return StyleParser;
+}());
+exports.StyleParser = StyleParser;
diff --git a/tests/baselines/reference/controlFlowPropertyDeclarations.symbols b/tests/baselines/reference/controlFlowPropertyDeclarations.symbols
new file mode 100644
index 00000000000..8e87d71ae33
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyDeclarations.symbols
@@ -0,0 +1,288 @@
+=== tests/cases/compiler/controlFlowPropertyDeclarations.ts ===
+// Repro from ##8913
+
+declare var require:any;
+>require : Symbol(require, Decl(controlFlowPropertyDeclarations.ts, 2, 11))
+
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+>require : Symbol(require, Decl(controlFlowPropertyDeclarations.ts, 2, 11))
+
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+
+  if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+
+    continue;
+  }
+
+  var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+>mapFrom : Symbol(mapFrom, Decl(controlFlowPropertyDeclarations.ts, 13, 5))
+>HTMLDOMPropertyConfig : Symbol(HTMLDOMPropertyConfig, Decl(controlFlowPropertyDeclarations.ts, 4, 3))
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+>propname.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
+>propname : Symbol(propname, Decl(controlFlowPropertyDeclarations.ts, 8, 8))
+>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
+}
+
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+>repeatString : Symbol(repeatString, Decl(controlFlowPropertyDeclarations.ts, 14, 1))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+  if (times === 1) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+    return string;
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+  }
+  if (times < 0) { throw new Error(); }
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+
+  var repeated = '';
+>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
+
+  while (times) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+    if (times & 1) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+      repeated += string;
+>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+    }
+    if (times >>= 1) {
+>times : Symbol(times, Decl(controlFlowPropertyDeclarations.ts, 25, 29))
+
+      string += string;
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 25, 22))
+    }
+  }
+  return repeated;
+>repeated : Symbol(repeated, Decl(controlFlowPropertyDeclarations.ts, 30, 5))
+}
+
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+>endsWith : Symbol(endsWith, Decl(controlFlowPropertyDeclarations.ts, 40, 1))
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 49, 18))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
+
+  return haystack.slice(-needle.length) === needle;
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 49, 18))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 49, 27))
+}
+
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+>trimEnd : Symbol(trimEnd, Decl(controlFlowPropertyDeclarations.ts, 51, 1))
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
+
+  return endsWith(haystack, needle)
+>endsWith : Symbol(endsWith, Decl(controlFlowPropertyDeclarations.ts, 40, 1))
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
+
+    ? haystack.slice(0, -needle.length)
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+>needle : Symbol(needle, Decl(controlFlowPropertyDeclarations.ts, 61, 26))
+
+    : haystack;
+>haystack : Symbol(haystack, Decl(controlFlowPropertyDeclarations.ts, 61, 17))
+}
+
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+>hyphenToCamelCase : Symbol(hyphenToCamelCase, Decl(controlFlowPropertyDeclarations.ts, 65, 1))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 70, 27))
+
+  return string.replace(/-(.)/g, function(match, chr) {
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 70, 27))
+>match : Symbol(match, Decl(controlFlowPropertyDeclarations.ts, 71, 42))
+>chr : Symbol(chr, Decl(controlFlowPropertyDeclarations.ts, 71, 48))
+
+    return chr.toUpperCase();
+>chr : Symbol(chr, Decl(controlFlowPropertyDeclarations.ts, 71, 48))
+
+  });
+}
+
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+>isEmpty : Symbol(isEmpty, Decl(controlFlowPropertyDeclarations.ts, 74, 1))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 79, 17))
+
+   return !/[^\s]/.test(string);
+>/[^\s]/.test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>string : Symbol(string, Decl(controlFlowPropertyDeclarations.ts, 79, 17))
+}
+
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+>isConvertiblePixelValue : Symbol(isConvertiblePixelValue, Decl(controlFlowPropertyDeclarations.ts, 81, 1))
+>value : Symbol(value, Decl(controlFlowPropertyDeclarations.ts, 90, 33))
+
+  return /^\d+px$/.test(value);
+>/^\d+px$/.test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>test : Symbol(RegExp.test, Decl(lib.d.ts, --, --))
+>value : Symbol(value, Decl(controlFlowPropertyDeclarations.ts, 90, 33))
+}
+
+export class HTMLtoJSX {
+>HTMLtoJSX : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
+
+    private output: string;
+>output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
+
+    private level: number;
+>level : Symbol(HTMLtoJSX.level, Decl(controlFlowPropertyDeclarations.ts, 95, 27))
+
+    private _inPreTag: boolean;
+>_inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
+
+
+  /**
+   * Handles processing of the specified text node
+   *
+   * @param {TextNode} node
+   */
+  _visitText = (node) => {
+>_visitText : Symbol(HTMLtoJSX._visitText, Decl(controlFlowPropertyDeclarations.ts, 97, 31))
+>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
+
+    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
+>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
+>node : Symbol(node, Decl(controlFlowPropertyDeclarations.ts, 105, 16))
+
+    if (parentTag === 'textarea' || parentTag === 'style') {
+>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
+>parentTag : Symbol(parentTag, Decl(controlFlowPropertyDeclarations.ts, 106, 7))
+
+      // Ignore text content of textareas and styles, as it will have already been moved
+      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+      return;
+    }
+
+    var text = ''
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+
+    if (this._inPreTag) {
+>this._inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
+>this : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
+>_inPreTag : Symbol(HTMLtoJSX._inPreTag, Decl(controlFlowPropertyDeclarations.ts, 96, 26))
+
+      // If this text is contained within a 
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+>text        .replace(/\r/g, '')        .replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>text        .replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+
+        .replace(/\r/g, '')
+>replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+>replace : Symbol(String.replace, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))
+
+          return '{' + JSON.stringify(whitespace) + '}';
+>JSON.stringify : Symbol(JSON.stringify, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>JSON : Symbol(JSON, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>stringify : Symbol(JSON.stringify, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
+>whitespace : Symbol(whitespace, Decl(controlFlowPropertyDeclarations.ts, 121, 50))
+
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+>text.indexOf : Symbol(String.indexOf, Decl(lib.d.ts, --, --))
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+>indexOf : Symbol(String.indexOf, Decl(lib.d.ts, --, --))
+      }
+    }
+    this.output += text;
+>this.output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
+>this : Symbol(HTMLtoJSX, Decl(controlFlowPropertyDeclarations.ts, 92, 1))
+>output : Symbol(HTMLtoJSX.output, Decl(controlFlowPropertyDeclarations.ts, 94, 24))
+>text : Symbol(text, Decl(controlFlowPropertyDeclarations.ts, 113, 7))
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+>StyleParser : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
+
+  styles = {};
+>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+
+  toJSXString = () => {
+>toJSXString : Symbol(StyleParser.toJSXString, Decl(controlFlowPropertyDeclarations.ts, 140, 14))
+
+    for (var key in this.styles) {
+>key : Symbol(key, Decl(controlFlowPropertyDeclarations.ts, 142, 12))
+>this.styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+>this : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
+>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+
+      if (!this.styles.hasOwnProperty(key)) {
+>this.styles.hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
+>this.styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+>this : Symbol(StyleParser, Decl(controlFlowPropertyDeclarations.ts, 134, 2))
+>styles : Symbol(StyleParser.styles, Decl(controlFlowPropertyDeclarations.ts, 139, 26))
+>hasOwnProperty : Symbol(Object.hasOwnProperty, Decl(lib.d.ts, --, --))
+>key : Symbol(key, Decl(controlFlowPropertyDeclarations.ts, 142, 12))
+      }
+    }
+  }
+}
diff --git a/tests/baselines/reference/controlFlowPropertyDeclarations.types b/tests/baselines/reference/controlFlowPropertyDeclarations.types
new file mode 100644
index 00000000000..81c86e8625d
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyDeclarations.types
@@ -0,0 +1,383 @@
+=== tests/cases/compiler/controlFlowPropertyDeclarations.ts ===
+// Repro from ##8913
+
+declare var require:any;
+>require : any
+
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+>HTMLDOMPropertyConfig : any
+>require('react/lib/HTMLDOMPropertyConfig') : any
+>require : any
+>'react/lib/HTMLDOMPropertyConfig' : string
+
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+>propname : string
+>HTMLDOMPropertyConfig.Properties : any
+>HTMLDOMPropertyConfig : any
+>Properties : any
+
+  if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+>!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname) : boolean
+>HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname) : any
+>HTMLDOMPropertyConfig.Properties.hasOwnProperty : any
+>HTMLDOMPropertyConfig.Properties : any
+>HTMLDOMPropertyConfig : any
+>Properties : any
+>hasOwnProperty : any
+>propname : string
+
+    continue;
+  }
+
+  var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+>mapFrom : any
+>HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase() : any
+>HTMLDOMPropertyConfig.DOMAttributeNames[propname] : any
+>HTMLDOMPropertyConfig.DOMAttributeNames : any
+>HTMLDOMPropertyConfig : any
+>DOMAttributeNames : any
+>propname : string
+>propname.toLowerCase() : string
+>propname.toLowerCase : () => string
+>propname : string
+>toLowerCase : () => string
+}
+
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+>repeatString : (string: any, times: any) => any
+>string : any
+>times : any
+
+  if (times === 1) {
+>times === 1 : boolean
+>times : any
+>1 : number
+
+    return string;
+>string : any
+  }
+  if (times < 0) { throw new Error(); }
+>times < 0 : boolean
+>times : any
+>0 : number
+>new Error() : Error
+>Error : ErrorConstructor
+
+  var repeated = '';
+>repeated : string
+>'' : string
+
+  while (times) {
+>times : any
+
+    if (times & 1) {
+>times & 1 : number
+>times : any
+>1 : number
+
+      repeated += string;
+>repeated += string : string
+>repeated : string
+>string : any
+    }
+    if (times >>= 1) {
+>times >>= 1 : number
+>times : any
+>1 : number
+
+      string += string;
+>string += string : any
+>string : any
+>string : any
+    }
+  }
+  return repeated;
+>repeated : string
+}
+
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+>endsWith : (haystack: any, needle: any) => boolean
+>haystack : any
+>needle : any
+
+  return haystack.slice(-needle.length) === needle;
+>haystack.slice(-needle.length) === needle : boolean
+>haystack.slice(-needle.length) : any
+>haystack.slice : any
+>haystack : any
+>slice : any
+>-needle.length : number
+>needle.length : any
+>needle : any
+>length : any
+>needle : any
+}
+
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+>trimEnd : (haystack: any, needle: any) => any
+>haystack : any
+>needle : any
+
+  return endsWith(haystack, needle)
+>endsWith(haystack, needle)    ? haystack.slice(0, -needle.length)    : haystack : any
+>endsWith(haystack, needle) : boolean
+>endsWith : (haystack: any, needle: any) => boolean
+>haystack : any
+>needle : any
+
+    ? haystack.slice(0, -needle.length)
+>haystack.slice(0, -needle.length) : any
+>haystack.slice : any
+>haystack : any
+>slice : any
+>0 : number
+>-needle.length : number
+>needle.length : any
+>needle : any
+>length : any
+
+    : haystack;
+>haystack : any
+}
+
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+>hyphenToCamelCase : (string: any) => any
+>string : any
+
+  return string.replace(/-(.)/g, function(match, chr) {
+>string.replace(/-(.)/g, function(match, chr) {    return chr.toUpperCase();  }) : any
+>string.replace : any
+>string : any
+>replace : any
+>/-(.)/g : RegExp
+>function(match, chr) {    return chr.toUpperCase();  } : (match: any, chr: any) => any
+>match : any
+>chr : any
+
+    return chr.toUpperCase();
+>chr.toUpperCase() : any
+>chr.toUpperCase : any
+>chr : any
+>toUpperCase : any
+
+  });
+}
+
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+>isEmpty : (string: any) => boolean
+>string : any
+
+   return !/[^\s]/.test(string);
+>!/[^\s]/.test(string) : boolean
+>/[^\s]/.test(string) : boolean
+>/[^\s]/.test : (string: string) => boolean
+>/[^\s]/ : RegExp
+>test : (string: string) => boolean
+>string : any
+}
+
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+>isConvertiblePixelValue : (value: any) => boolean
+>value : any
+
+  return /^\d+px$/.test(value);
+>/^\d+px$/.test(value) : boolean
+>/^\d+px$/.test : (string: string) => boolean
+>/^\d+px$/ : RegExp
+>test : (string: string) => boolean
+>value : any
+}
+
+export class HTMLtoJSX {
+>HTMLtoJSX : HTMLtoJSX
+
+    private output: string;
+>output : string
+
+    private level: number;
+>level : number
+
+    private _inPreTag: boolean;
+>_inPreTag : boolean
+
+
+  /**
+   * Handles processing of the specified text node
+   *
+   * @param {TextNode} node
+   */
+  _visitText = (node) => {
+>_visitText : (node: any) => void
+>(node) => {    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();    if (parentTag === 'textarea' || parentTag === 'style') {      // Ignore text content of textareas and styles, as it will have already been moved      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.      return;    }    var text = ''    if (this._inPreTag) {      // If this text is contained within a 
, we need to ensure the JSX      // whitespace coalescing rules don't eat the whitespace. This means      // wrapping newlines and sequences of two or more spaces in variables.      text = text        .replace(/\r/g, '')        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        });    } else {      // If there's a newline in the text, adjust the indent level      if (text.indexOf('\n') > -1) {      }    }    this.output += text;  } : (node: any) => void
+>node : any
+
+    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+>parentTag : any
+>node.parentNode && node.parentNode.tagName.toLowerCase() : any
+>node.parentNode : any
+>node : any
+>parentNode : any
+>node.parentNode.tagName.toLowerCase() : any
+>node.parentNode.tagName.toLowerCase : any
+>node.parentNode.tagName : any
+>node.parentNode : any
+>node : any
+>parentNode : any
+>tagName : any
+>toLowerCase : any
+
+    if (parentTag === 'textarea' || parentTag === 'style') {
+>parentTag === 'textarea' || parentTag === 'style' : boolean
+>parentTag === 'textarea' : boolean
+>parentTag : any
+>'textarea' : string
+>parentTag === 'style' : boolean
+>parentTag : any
+>'style' : string
+
+      // Ignore text content of textareas and styles, as it will have already been moved
+      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+      return;
+    }
+
+    var text = ''
+>text : string
+>'' : string
+
+    if (this._inPreTag) {
+>this._inPreTag : boolean
+>this : this
+>_inPreTag : boolean
+
+      // If this text is contained within a 
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+>text = text        .replace(/\r/g, '')        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        }) : string
+>text : string
+>text        .replace(/\r/g, '')        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        }) : string
+>text        .replace(/\r/g, '')        .replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>text        .replace(/\r/g, '') : string
+>text        .replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>text : string
+
+        .replace(/\r/g, '')
+>replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>/\r/g : RegExp
+>'' : string
+
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+>replace : { (searchValue: string, replaceValue: string): string; (searchValue: string, replacer: (substring: string, ...args: any[]) => string): string; (searchValue: RegExp, replaceValue: string): string; (searchValue: RegExp, replacer: (substring: string, ...args: any[]) => string): string; }
+>/( {2,}|\n|\t|\{|\})/g : RegExp
+>function(whitespace) {          return '{' + JSON.stringify(whitespace) + '}';        } : (whitespace: string) => string
+>whitespace : string
+
+          return '{' + JSON.stringify(whitespace) + '}';
+>'{' + JSON.stringify(whitespace) + '}' : string
+>'{' + JSON.stringify(whitespace) : string
+>'{' : string
+>JSON.stringify(whitespace) : string
+>JSON.stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[], space?: string | number): string; }
+>JSON : JSON
+>stringify : { (value: any, replacer?: (key: string, value: any) => any, space?: string | number): string; (value: any, replacer?: (number | string)[], space?: string | number): string; }
+>whitespace : string
+>'}' : string
+
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+>text.indexOf('\n') > -1 : boolean
+>text.indexOf('\n') : number
+>text.indexOf : (searchString: string, position?: number) => number
+>text : string
+>indexOf : (searchString: string, position?: number) => number
+>'\n' : string
+>-1 : number
+>1 : number
+      }
+    }
+    this.output += text;
+>this.output += text : string
+>this.output : string
+>this : this
+>output : string
+>text : string
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+>StyleParser : StyleParser
+
+  styles = {};
+>styles : {}
+>{} : {}
+
+  toJSXString = () => {
+>toJSXString : () => void
+>() => {    for (var key in this.styles) {      if (!this.styles.hasOwnProperty(key)) {      }    }  } : () => void
+
+    for (var key in this.styles) {
+>key : string
+>this.styles : {}
+>this : this
+>styles : {}
+
+      if (!this.styles.hasOwnProperty(key)) {
+>!this.styles.hasOwnProperty(key) : boolean
+>this.styles.hasOwnProperty(key) : boolean
+>this.styles.hasOwnProperty : (v: string) => boolean
+>this.styles : {}
+>this : this
+>styles : {}
+>hasOwnProperty : (v: string) => boolean
+>key : string
+      }
+    }
+  }
+}
diff --git a/tests/cases/compiler/controlFlowPropertyDeclarations.ts b/tests/cases/compiler/controlFlowPropertyDeclarations.ts
new file mode 100644
index 00000000000..5a5e9fb96bf
--- /dev/null
+++ b/tests/cases/compiler/controlFlowPropertyDeclarations.ts
@@ -0,0 +1,148 @@
+// Repro from ##8913
+
+declare var require:any;
+
+var HTMLDOMPropertyConfig = require('react/lib/HTMLDOMPropertyConfig');
+
+// Populate property map with ReactJS's attribute and property mappings
+// TODO handle/use .Properties value eg: MUST_USE_PROPERTY is not HTML attr
+for (var propname in HTMLDOMPropertyConfig.Properties) {
+  if (!HTMLDOMPropertyConfig.Properties.hasOwnProperty(propname)) {
+    continue;
+  }
+
+  var mapFrom = HTMLDOMPropertyConfig.DOMAttributeNames[propname] || propname.toLowerCase();
+}
+
+/**
+ * Repeats a string a certain number of times.
+ * Also: the future is bright and consists of native string repetition:
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
+ *
+ * @param {string} string  String to repeat
+ * @param {number} times   Number of times to repeat string. Integer.
+ * @see http://jsperf.com/string-repeater/2
+ */
+function repeatString(string, times) {
+  if (times === 1) {
+    return string;
+  }
+  if (times < 0) { throw new Error(); }
+  var repeated = '';
+  while (times) {
+    if (times & 1) {
+      repeated += string;
+    }
+    if (times >>= 1) {
+      string += string;
+    }
+  }
+  return repeated;
+}
+
+/**
+ * Determine if the string ends with the specified substring.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {boolean}
+ */
+function endsWith(haystack, needle) {
+  return haystack.slice(-needle.length) === needle;
+}
+
+/**
+ * Trim the specified substring off the string. If the string does not end
+ * with the specified substring, this is a no-op.
+ *
+ * @param {string} haystack String to search in
+ * @param {string} needle   String to search for
+ * @return {string}
+ */
+function trimEnd(haystack, needle) {
+  return endsWith(haystack, needle)
+    ? haystack.slice(0, -needle.length)
+    : haystack;
+}
+
+/**
+ * Convert a hyphenated string to camelCase.
+ */
+function hyphenToCamelCase(string) {
+  return string.replace(/-(.)/g, function(match, chr) {
+    return chr.toUpperCase();
+  });
+}
+
+/**
+ * Determines if the specified string consists entirely of whitespace.
+ */
+function isEmpty(string) {
+   return !/[^\s]/.test(string);
+}
+
+/**
+ * Determines if the CSS value can be converted from a
+ * 'px' suffixed string to a numeric value
+ *
+ * @param {string} value CSS property value
+ * @return {boolean}
+ */
+function isConvertiblePixelValue(value) {
+  return /^\d+px$/.test(value);
+}
+
+export class HTMLtoJSX {
+    private output: string;
+    private level: number;
+    private _inPreTag: boolean;
+
+
+  /**
+   * Handles processing of the specified text node
+   *
+   * @param {TextNode} node
+   */
+  _visitText = (node) => {
+    var parentTag = node.parentNode && node.parentNode.tagName.toLowerCase();
+    if (parentTag === 'textarea' || parentTag === 'style') {
+      // Ignore text content of textareas and styles, as it will have already been moved
+      // to a "defaultValue" attribute and "dangerouslySetInnerHTML" attribute respectively.
+      return;
+    }
+
+    var text = ''
+
+    if (this._inPreTag) {
+      // If this text is contained within a 
, we need to ensure the JSX
+      // whitespace coalescing rules don't eat the whitespace. This means
+      // wrapping newlines and sequences of two or more spaces in variables.
+      text = text
+        .replace(/\r/g, '')
+        .replace(/( {2,}|\n|\t|\{|\})/g, function(whitespace) {
+          return '{' + JSON.stringify(whitespace) + '}';
+        });
+    } else {
+      // If there's a newline in the text, adjust the indent level
+      if (text.indexOf('\n') > -1) {
+      }
+    }
+    this.output += text;
+  }
+
+
+
+};
+
+/**
+ * Handles parsing of inline styles
+ */
+export class StyleParser {
+  styles = {};
+  toJSXString = () => {
+    for (var key in this.styles) {
+      if (!this.styles.hasOwnProperty(key)) {
+      }
+    }
+  }
+}
\ No newline at end of file

From 4281bf5752b58ac466723e2339e49aad0f3ac298 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Thu, 2 Jun 2016 07:35:58 -0700
Subject: [PATCH 59/90] Allow trailing commas in function parameter and
 argument lists

---
 src/compiler/checker.ts                       | 20 +++++++------------
 .../reference/ArrowFunction2.errors.txt       |  3 ---
 ...serErrorRecovery_ParameterList3.errors.txt |  8 --------
 .../parserErrorRecovery_ParameterList3.js     |  7 -------
 .../parserParameterList12.errors.txt          |  8 --------
 .../reference/parserParameterList12.symbols   |  5 +++++
 .../reference/parserParameterList12.types     |  5 +++++
 ...gCommasInFunctionParametersAndArguments.js |  9 +++++++++
 ...asInFunctionParametersAndArguments.symbols |  8 ++++++++
 ...mmasInFunctionParametersAndArguments.types | 10 ++++++++++
 ...gCommasInFunctionParametersAndArguments.ts |  3 +++
 .../parserErrorRecovery_ParameterList3.ts     |  2 --
 .../fourslash/trailingCommaSignatureHelp.ts   | 15 ++++++++++++++
 13 files changed, 62 insertions(+), 41 deletions(-)
 delete mode 100644 tests/baselines/reference/parserErrorRecovery_ParameterList3.errors.txt
 delete mode 100644 tests/baselines/reference/parserErrorRecovery_ParameterList3.js
 delete mode 100644 tests/baselines/reference/parserParameterList12.errors.txt
 create mode 100644 tests/baselines/reference/parserParameterList12.symbols
 create mode 100644 tests/baselines/reference/parserParameterList12.types
 create mode 100644 tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
 create mode 100644 tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
 create mode 100644 tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
 create mode 100644 tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
 delete mode 100644 tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList3.ts
 create mode 100644 tests/cases/fourslash/trailingCommaSignatureHelp.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 06ef2463402..402d6455387 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -10322,7 +10322,7 @@ namespace ts {
         }
 
         function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
-            let adjustedArgCount: number;            // Apparent number of arguments we will have in this call
+            let argCount: number;            // Apparent number of arguments we will have in this call
             let typeArguments: NodeArray;  // Type arguments (undefined if none)
             let callIsIncomplete: boolean;           // In incomplete call we want to be lenient when we have too few arguments
             let isDecorator: boolean;
@@ -10333,7 +10333,7 @@ namespace ts {
 
                 // Even if the call is incomplete, we'll have a missing expression as our last argument,
                 // so we can say the count is just the arg list length
-                adjustedArgCount = args.length;
+                argCount = args.length;
                 typeArguments = undefined;
 
                 if (tagExpression.template.kind === SyntaxKind.TemplateExpression) {
@@ -10356,7 +10356,7 @@ namespace ts {
             else if (node.kind === SyntaxKind.Decorator) {
                 isDecorator = true;
                 typeArguments = undefined;
-                adjustedArgCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
+                argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
             }
             else {
                 const callExpression = node;
@@ -10367,8 +10367,7 @@ namespace ts {
                     return signature.minArgumentCount === 0;
                 }
 
-                // For IDE scenarios we may have an incomplete call, so a trailing comma is tantamount to adding another argument.
-                adjustedArgCount = callExpression.arguments.hasTrailingComma ? args.length + 1 : args.length;
+                argCount = args.length;
 
                 // If we are missing the close paren, the call is incomplete.
                 callIsIncomplete = (callExpression).arguments.end === callExpression.end;
@@ -10392,12 +10391,12 @@ namespace ts {
             }
 
             // Too many arguments implies incorrect arity.
-            if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) {
+            if (!signature.hasRestParameter && argCount > signature.parameters.length) {
                 return false;
             }
 
             // If the call is incomplete, we should skip the lower bound check.
-            const hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount;
+            const hasEnoughArguments = argCount >= signature.minArgumentCount;
             return callIsIncomplete || hasEnoughArguments;
         }
 
@@ -18027,10 +18026,6 @@ namespace ts {
         }
 
         function checkGrammarParameterList(parameters: NodeArray) {
-            if (checkGrammarForDisallowedTrailingComma(parameters)) {
-                return true;
-            }
-
             let seenOptionalParameter = false;
             const parameterCount = parameters.length;
 
@@ -18149,8 +18144,7 @@ namespace ts {
         }
 
         function checkGrammarArguments(node: CallExpression, args: NodeArray): boolean {
-            return checkGrammarForDisallowedTrailingComma(args) ||
-                checkGrammarForOmittedArgument(node, args);
+            return checkGrammarForOmittedArgument(node, args);
         }
 
         function checkGrammarHeritageClause(node: HeritageClause): boolean {
diff --git a/tests/baselines/reference/ArrowFunction2.errors.txt b/tests/baselines/reference/ArrowFunction2.errors.txt
index 3b9ba21f15a..d18bc483acc 100644
--- a/tests/baselines/reference/ArrowFunction2.errors.txt
+++ b/tests/baselines/reference/ArrowFunction2.errors.txt
@@ -1,12 +1,9 @@
 tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts(1,13): error TS2304: Cannot find name 'b'.
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts(1,14): error TS1009: Trailing comma not allowed.
 
 
 ==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts (2 errors) ====
     var v = (a: b,) => {
                 ~
 !!! error TS2304: Cannot find name 'b'.
-                 ~
-!!! error TS1009: Trailing comma not allowed.
        
     };
\ No newline at end of file
diff --git a/tests/baselines/reference/parserErrorRecovery_ParameterList3.errors.txt b/tests/baselines/reference/parserErrorRecovery_ParameterList3.errors.txt
deleted file mode 100644
index df73dc51ce1..00000000000
--- a/tests/baselines/reference/parserErrorRecovery_ParameterList3.errors.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList3.ts(1,13): error TS1009: Trailing comma not allowed.
-
-
-==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList3.ts (1 errors) ====
-    function f(a,) {
-                ~
-!!! error TS1009: Trailing comma not allowed.
-    }
\ No newline at end of file
diff --git a/tests/baselines/reference/parserErrorRecovery_ParameterList3.js b/tests/baselines/reference/parserErrorRecovery_ParameterList3.js
deleted file mode 100644
index 4c20e676f40..00000000000
--- a/tests/baselines/reference/parserErrorRecovery_ParameterList3.js
+++ /dev/null
@@ -1,7 +0,0 @@
-//// [parserErrorRecovery_ParameterList3.ts]
-function f(a,) {
-}
-
-//// [parserErrorRecovery_ParameterList3.js]
-function f(a) {
-}
diff --git a/tests/baselines/reference/parserParameterList12.errors.txt b/tests/baselines/reference/parserParameterList12.errors.txt
deleted file mode 100644
index 686b29f63b1..00000000000
--- a/tests/baselines/reference/parserParameterList12.errors.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList12.ts(1,13): error TS1009: Trailing comma not allowed.
-
-
-==== tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList12.ts (1 errors) ====
-    function F(a,) {
-                ~
-!!! error TS1009: Trailing comma not allowed.
-    }
\ No newline at end of file
diff --git a/tests/baselines/reference/parserParameterList12.symbols b/tests/baselines/reference/parserParameterList12.symbols
new file mode 100644
index 00000000000..efaab7d1b13
--- /dev/null
+++ b/tests/baselines/reference/parserParameterList12.symbols
@@ -0,0 +1,5 @@
+=== tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList12.ts ===
+function F(a,) {
+>F : Symbol(F, Decl(parserParameterList12.ts, 0, 0))
+>a : Symbol(a, Decl(parserParameterList12.ts, 0, 11))
+}
diff --git a/tests/baselines/reference/parserParameterList12.types b/tests/baselines/reference/parserParameterList12.types
new file mode 100644
index 00000000000..7ff43cd6a15
--- /dev/null
+++ b/tests/baselines/reference/parserParameterList12.types
@@ -0,0 +1,5 @@
+=== tests/cases/conformance/parser/ecmascript5/ParameterLists/parserParameterList12.ts ===
+function F(a,) {
+>F : (a: any) => void
+>a : any
+}
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
new file mode 100644
index 00000000000..5fb4b7d638f
--- /dev/null
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
@@ -0,0 +1,9 @@
+//// [trailingCommasInFunctionParametersAndArguments.ts]
+function f1(x,) {}
+
+f1(1,);
+
+
+//// [trailingCommasInFunctionParametersAndArguments.js]
+function f1(x) { }
+f1(1);
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
new file mode 100644
index 00000000000..5eb9d75807b
--- /dev/null
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
@@ -0,0 +1,8 @@
+=== tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts ===
+function f1(x,) {}
+>f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 12))
+
+f1(1,);
+>f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
+
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
new file mode 100644
index 00000000000..4af2326076b
--- /dev/null
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
@@ -0,0 +1,10 @@
+=== tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts ===
+function f1(x,) {}
+>f1 : (x: any) => void
+>x : any
+
+f1(1,);
+>f1(1,) : void
+>f1 : (x: any) => void
+>1 : number
+
diff --git a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
new file mode 100644
index 00000000000..0492ed989e3
--- /dev/null
+++ b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
@@ -0,0 +1,3 @@
+function f1(x,) {}
+
+f1(1,);
diff --git a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList3.ts b/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList3.ts
deleted file mode 100644
index c51a52ef83e..00000000000
--- a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ParameterLists/parserErrorRecovery_ParameterList3.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-function f(a,) {
-}
\ No newline at end of file
diff --git a/tests/cases/fourslash/trailingCommaSignatureHelp.ts b/tests/cases/fourslash/trailingCommaSignatureHelp.ts
new file mode 100644
index 00000000000..4881571cd2b
--- /dev/null
+++ b/tests/cases/fourslash/trailingCommaSignatureHelp.ts
@@ -0,0 +1,15 @@
+/// 
+
+////function str(n: number): string;
+/////**
+//// * Stringifies a number with radix
+//// * @param radix The radix
+//// */
+////function str(n: number, radix: number): string;
+////function str(n: number, radix?: number): string { return ""; }
+
+edit.insert("str(1,");
+verify.currentParameterHelpArgumentNameIs("radix");
+verify.currentParameterHelpArgumentDocCommentIs("The radix");
+verify.currentSignatureHelpIs("str(n: number, radix: number): string");
+verify.currentSignatureHelpDocCommentIs("Stringifies a number with radix");

From 20bab14224bf9f7a7228f416a61807b1f7f73e4a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Thu, 2 Jun 2016 09:39:47 -0700
Subject: [PATCH 60/90] Add tests

---
 .../reference/arrayLiteralWidened.js          | 15 ++++
 .../reference/arrayLiteralWidened.symbols     | 38 +++++++--
 .../reference/arrayLiteralWidened.types       | 29 +++++++
 .../reference/initializersWidened.js          | 42 +++++++++-
 .../reference/initializersWidened.symbols     | 55 ++++++++++++-
 .../reference/initializersWidened.types       | 77 ++++++++++++++++++-
 .../logicalAndOperatorWithEveryType.types     |  2 +-
 .../reference/objectLiteralWidened.js         | 40 +++++++++-
 .../reference/objectLiteralWidened.symbols    | 53 +++++++++++--
 .../reference/objectLiteralWidened.types      | 54 ++++++++++++-
 .../reference/strictNullChecksNoWidening.js   | 31 ++++++++
 .../strictNullChecksNoWidening.symbols        | 48 ++++++++++++
 .../strictNullChecksNoWidening.types          | 67 ++++++++++++++++
 .../widenedTypes/arrayLiteralWidened.ts       |  9 +++
 .../widenedTypes/initializersWidened.ts       | 24 +++++-
 .../widenedTypes/objectLiteralWidened.ts      | 22 +++++-
 .../strictNullChecksNoWidening.ts             | 17 ++++
 17 files changed, 584 insertions(+), 39 deletions(-)
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.js
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.symbols
 create mode 100644 tests/baselines/reference/strictNullChecksNoWidening.types
 create mode 100644 tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts

diff --git a/tests/baselines/reference/arrayLiteralWidened.js b/tests/baselines/reference/arrayLiteralWidened.js
index a250c47849d..5b7346b8738 100644
--- a/tests/baselines/reference/arrayLiteralWidened.js
+++ b/tests/baselines/reference/arrayLiteralWidened.js
@@ -2,6 +2,7 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
+var a = [,,];
 
 var a = [null, null];
 var a = [undefined, undefined];
@@ -12,11 +13,20 @@ var b = [[undefined, undefined]];
 
 var c = [[[]]]; // any[][][]
 var c = [[[null]],[undefined]]
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
 
 
 //// [arrayLiteralWidened.js]
 // array literals are widened upon assignment according to their element type
 var a = []; // any[]
+var a = [, ,];
 var a = [null, null];
 var a = [undefined, undefined];
 var b = [[], [null, null]]; // any[][]
@@ -24,3 +34,8 @@ var b = [[], []];
 var b = [[undefined, undefined]];
 var c = [[[]]]; // any[][][]
 var c = [[[null]], [undefined]];
+// no widening when one or more elements are non-widening
+var x = undefined;
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
diff --git a/tests/baselines/reference/arrayLiteralWidened.symbols b/tests/baselines/reference/arrayLiteralWidened.symbols
index ba058aeded1..48137812a5d 100644
--- a/tests/baselines/reference/arrayLiteralWidened.symbols
+++ b/tests/baselines/reference/arrayLiteralWidened.symbols
@@ -2,31 +2,53 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
+
+var a = [,,];
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 
 var a = [null, null];
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 
 var a = [undefined, undefined];
->a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 4, 3), Decl(arrayLiteralWidened.ts, 5, 3))
+>a : Symbol(a, Decl(arrayLiteralWidened.ts, 2, 3), Decl(arrayLiteralWidened.ts, 3, 3), Decl(arrayLiteralWidened.ts, 5, 3), Decl(arrayLiteralWidened.ts, 6, 3))
 >undefined : Symbol(undefined)
 >undefined : Symbol(undefined)
 
 var b = [[], [null, null]]; // any[][]
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 
 var b = [[], []];
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 
 var b = [[undefined, undefined]];
->b : Symbol(b, Decl(arrayLiteralWidened.ts, 7, 3), Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3))
+>b : Symbol(b, Decl(arrayLiteralWidened.ts, 8, 3), Decl(arrayLiteralWidened.ts, 9, 3), Decl(arrayLiteralWidened.ts, 10, 3))
 >undefined : Symbol(undefined)
 >undefined : Symbol(undefined)
 
 var c = [[[]]]; // any[][][]
->c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
+>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
 
 var c = [[[null]],[undefined]]
->c : Symbol(c, Decl(arrayLiteralWidened.ts, 11, 3), Decl(arrayLiteralWidened.ts, 12, 3))
+>c : Symbol(c, Decl(arrayLiteralWidened.ts, 12, 3), Decl(arrayLiteralWidened.ts, 13, 3))
 >undefined : Symbol(undefined)
 
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+>undefined : Symbol(undefined)
+
+var d = [x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
+var d = [, x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
+var d = [undefined, x];
+>d : Symbol(d, Decl(arrayLiteralWidened.ts, 19, 3), Decl(arrayLiteralWidened.ts, 20, 3), Decl(arrayLiteralWidened.ts, 21, 3))
+>undefined : Symbol(undefined)
+>x : Symbol(x, Decl(arrayLiteralWidened.ts, 17, 3))
+
diff --git a/tests/baselines/reference/arrayLiteralWidened.types b/tests/baselines/reference/arrayLiteralWidened.types
index 83db7046eed..6237cd79a7e 100644
--- a/tests/baselines/reference/arrayLiteralWidened.types
+++ b/tests/baselines/reference/arrayLiteralWidened.types
@@ -5,6 +5,12 @@ var a = []; // any[]
 >a : any[]
 >[] : undefined[]
 
+var a = [,,];
+>a : any[]
+>[,,] : undefined[]
+> : undefined
+> : undefined
+
 var a = [null, null];
 >a : any[]
 >[null, null] : null[]
@@ -53,3 +59,26 @@ var c = [[[null]],[undefined]]
 >[undefined] : undefined[]
 >undefined : undefined
 
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+>x : undefined
+>undefined : undefined
+
+var d = [x];
+>d : undefined[]
+>[x] : undefined[]
+>x : undefined
+
+var d = [, x];
+>d : undefined[]
+>[, x] : undefined[]
+> : undefined
+>x : undefined
+
+var d = [undefined, x];
+>d : undefined[]
+>[undefined, x] : undefined[]
+>undefined : undefined
+>x : undefined
+
diff --git a/tests/baselines/reference/initializersWidened.js b/tests/baselines/reference/initializersWidened.js
index fce0750a600..3954735ee6e 100644
--- a/tests/baselines/reference/initializersWidened.js
+++ b/tests/baselines/reference/initializersWidened.js
@@ -1,10 +1,44 @@
 //// [initializersWidened.ts]
 // these are widened to any at the point of assignment
 
-var x = null;
-var y = undefined;
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+
+// these are not widened
+
+var x2: null;
+var y2: undefined;
+
+var x3: null = null;
+var y3: undefined = undefined;
+var z3: undefined = void 0;
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
 
 //// [initializersWidened.js]
 // these are widened to any at the point of assignment
-var x = null;
-var y = undefined;
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+// these are not widened
+var x2;
+var y2;
+var x3 = null;
+var y3 = undefined;
+var z3 = void 0;
+// widen only when all constituents of union are widening
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
diff --git a/tests/baselines/reference/initializersWidened.symbols b/tests/baselines/reference/initializersWidened.symbols
index 625c066a058..252a248bec8 100644
--- a/tests/baselines/reference/initializersWidened.symbols
+++ b/tests/baselines/reference/initializersWidened.symbols
@@ -1,10 +1,57 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
 // these are widened to any at the point of assignment
 
-var x = null;
->x : Symbol(x, Decl(initializersWidened.ts, 2, 3))
+var x1 = null;
+>x1 : Symbol(x1, Decl(initializersWidened.ts, 2, 3))
 
-var y = undefined;
->y : Symbol(y, Decl(initializersWidened.ts, 3, 3))
+var y1 = undefined;
+>y1 : Symbol(y1, Decl(initializersWidened.ts, 3, 3))
 >undefined : Symbol(undefined)
 
+var z1 = void 0;
+>z1 : Symbol(z1, Decl(initializersWidened.ts, 4, 3))
+
+// these are not widened
+
+var x2: null;
+>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
+
+var y2: undefined;
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
+var x3: null = null;
+>x3 : Symbol(x3, Decl(initializersWidened.ts, 11, 3))
+
+var y3: undefined = undefined;
+>y3 : Symbol(y3, Decl(initializersWidened.ts, 12, 3))
+>undefined : Symbol(undefined)
+
+var z3: undefined = void 0;
+>z3 : Symbol(z3, Decl(initializersWidened.ts, 13, 3))
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+>x4 : Symbol(x4, Decl(initializersWidened.ts, 17, 3))
+
+var y4 = undefined || undefined;
+>y4 : Symbol(y4, Decl(initializersWidened.ts, 18, 3))
+>undefined : Symbol(undefined)
+>undefined : Symbol(undefined)
+
+var z4 = void 0 || void 0;
+>z4 : Symbol(z4, Decl(initializersWidened.ts, 19, 3))
+
+var x5 = null || x2;
+>x5 : Symbol(x5, Decl(initializersWidened.ts, 21, 3))
+>x2 : Symbol(x2, Decl(initializersWidened.ts, 8, 3))
+
+var y5 = undefined || y2;
+>y5 : Symbol(y5, Decl(initializersWidened.ts, 22, 3))
+>undefined : Symbol(undefined)
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
+var z5 = void 0 || y2;
+>z5 : Symbol(z5, Decl(initializersWidened.ts, 23, 3))
+>y2 : Symbol(y2, Decl(initializersWidened.ts, 9, 3))
+
diff --git a/tests/baselines/reference/initializersWidened.types b/tests/baselines/reference/initializersWidened.types
index 15705589246..766f859029a 100644
--- a/tests/baselines/reference/initializersWidened.types
+++ b/tests/baselines/reference/initializersWidened.types
@@ -1,11 +1,80 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts ===
 // these are widened to any at the point of assignment
 
-var x = null;
->x : any
+var x1 = null;
+>x1 : any
 >null : null
 
-var y = undefined;
->y : any
+var y1 = undefined;
+>y1 : any
 >undefined : undefined
 
+var z1 = void 0;
+>z1 : any
+>void 0 : undefined
+>0 : number
+
+// these are not widened
+
+var x2: null;
+>x2 : null
+>null : null
+
+var y2: undefined;
+>y2 : undefined
+
+var x3: null = null;
+>x3 : null
+>null : null
+>null : null
+
+var y3: undefined = undefined;
+>y3 : undefined
+>undefined : undefined
+
+var z3: undefined = void 0;
+>z3 : undefined
+>void 0 : undefined
+>0 : number
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+>x4 : any
+>null || null : null
+>null : null
+>null : null
+
+var y4 = undefined || undefined;
+>y4 : any
+>undefined || undefined : undefined
+>undefined : undefined
+>undefined : undefined
+
+var z4 = void 0 || void 0;
+>z4 : any
+>void 0 || void 0 : undefined
+>void 0 : undefined
+>0 : number
+>void 0 : undefined
+>0 : number
+
+var x5 = null || x2;
+>x5 : null
+>null || x2 : null
+>null : null
+>x2 : null
+
+var y5 = undefined || y2;
+>y5 : undefined
+>undefined || y2 : undefined
+>undefined : undefined
+>y2 : undefined
+
+var z5 = void 0 || y2;
+>z5 : undefined
+>void 0 || y2 : undefined
+>void 0 : undefined
+>0 : number
+>y2 : undefined
+
diff --git a/tests/baselines/reference/logicalAndOperatorWithEveryType.types b/tests/baselines/reference/logicalAndOperatorWithEveryType.types
index b2628eed921..bd913e94da5 100644
--- a/tests/baselines/reference/logicalAndOperatorWithEveryType.types
+++ b/tests/baselines/reference/logicalAndOperatorWithEveryType.types
@@ -623,7 +623,7 @@ var rj8 = a8 && undefined;
 
 var rj9 = null && undefined;
 >rj9 : any
->null && undefined : null
+>null && undefined : undefined
 >null : null
 >undefined : undefined
 
diff --git a/tests/baselines/reference/objectLiteralWidened.js b/tests/baselines/reference/objectLiteralWidened.js
index 4de228cb14a..98d79133540 100644
--- a/tests/baselines/reference/objectLiteralWidened.js
+++ b/tests/baselines/reference/objectLiteralWidened.js
@@ -1,29 +1,61 @@
 //// [objectLiteralWidened.ts]
 // object literal properties are widened to any
 
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 }
 
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
+}
+
+// these are not widened
+
+var u: undefined = undefined;
+var n: null = null;
+
+var x2 = {
+    foo: n,
+    bar: u
+}
+
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
 }
 
 //// [objectLiteralWidened.js]
 // object literal properties are widened to any
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 };
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
 };
+// these are not widened
+var u = undefined;
+var n = null;
+var x2 = {
+    foo: n,
+    bar: u
+};
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
+};
diff --git a/tests/baselines/reference/objectLiteralWidened.symbols b/tests/baselines/reference/objectLiteralWidened.symbols
index 0bf077cd9d8..4b2a1b4a001 100644
--- a/tests/baselines/reference/objectLiteralWidened.symbols
+++ b/tests/baselines/reference/objectLiteralWidened.symbols
@@ -1,22 +1,22 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
 // object literal properties are widened to any
 
-var x = {
->x : Symbol(x, Decl(objectLiteralWidened.ts, 2, 3))
+var x1 = {
+>x1 : Symbol(x1, Decl(objectLiteralWidened.ts, 2, 3))
 
     foo: null,
->foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 9))
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 2, 10))
 
     bar: undefined
 >bar : Symbol(bar, Decl(objectLiteralWidened.ts, 3, 14))
 >undefined : Symbol(undefined)
 }
 
-var y = {
->y : Symbol(y, Decl(objectLiteralWidened.ts, 7, 3))
+var y1 = {
+>y1 : Symbol(y1, Decl(objectLiteralWidened.ts, 7, 3))
 
     foo: null,
->foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 9))
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 7, 10))
 
     bar: {
 >bar : Symbol(bar, Decl(objectLiteralWidened.ts, 8, 14))
@@ -29,3 +29,44 @@ var y = {
 >undefined : Symbol(undefined)
     }
 }
+
+// these are not widened
+
+var u: undefined = undefined;
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+>undefined : Symbol(undefined)
+
+var n: null = null;
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+var x2 = {
+>x2 : Symbol(x2, Decl(objectLiteralWidened.ts, 20, 3))
+
+    foo: n,
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 20, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+    bar: u
+>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 21, 11))
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+}
+
+var y2 = {
+>y2 : Symbol(y2, Decl(objectLiteralWidened.ts, 25, 3))
+
+    foo: n,
+>foo : Symbol(foo, Decl(objectLiteralWidened.ts, 25, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+    bar: {
+>bar : Symbol(bar, Decl(objectLiteralWidened.ts, 26, 11))
+
+        baz: n,
+>baz : Symbol(baz, Decl(objectLiteralWidened.ts, 27, 10))
+>n : Symbol(n, Decl(objectLiteralWidened.ts, 18, 3))
+
+        boo: u
+>boo : Symbol(boo, Decl(objectLiteralWidened.ts, 28, 15))
+>u : Symbol(u, Decl(objectLiteralWidened.ts, 17, 3))
+    }
+}
diff --git a/tests/baselines/reference/objectLiteralWidened.types b/tests/baselines/reference/objectLiteralWidened.types
index 9f47e47795d..66309202617 100644
--- a/tests/baselines/reference/objectLiteralWidened.types
+++ b/tests/baselines/reference/objectLiteralWidened.types
@@ -1,8 +1,8 @@
 === tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts ===
 // object literal properties are widened to any
 
-var x = {
->x : { foo: any; bar: any; }
+var x1 = {
+>x1 : { foo: any; bar: any; }
 >{    foo: null,    bar: undefined} : { foo: null; bar: undefined; }
 
     foo: null,
@@ -14,8 +14,8 @@ var x = {
 >undefined : undefined
 }
 
-var y = {
->y : { foo: any; bar: { baz: any; boo: any; }; }
+var y1 = {
+>y1 : { foo: any; bar: { baz: any; boo: any; }; }
 >{    foo: null,    bar: {        baz: null,        boo: undefined    }} : { foo: null; bar: { baz: null; boo: undefined; }; }
 
     foo: null,
@@ -35,3 +35,49 @@ var y = {
 >undefined : undefined
     }
 }
+
+// these are not widened
+
+var u: undefined = undefined;
+>u : undefined
+>undefined : undefined
+
+var n: null = null;
+>n : null
+>null : null
+>null : null
+
+var x2 = {
+>x2 : { foo: null; bar: undefined; }
+>{    foo: n,    bar: u} : { foo: null; bar: undefined; }
+
+    foo: n,
+>foo : null
+>n : null
+
+    bar: u
+>bar : undefined
+>u : undefined
+}
+
+var y2 = {
+>y2 : { foo: null; bar: { baz: null; boo: undefined; }; }
+>{    foo: n,    bar: {        baz: n,        boo: u    }} : { foo: null; bar: { baz: null; boo: undefined; }; }
+
+    foo: n,
+>foo : null
+>n : null
+
+    bar: {
+>bar : { baz: null; boo: undefined; }
+>{        baz: n,        boo: u    } : { baz: null; boo: undefined; }
+
+        baz: n,
+>baz : null
+>n : null
+
+        boo: u
+>boo : undefined
+>u : undefined
+    }
+}
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.js b/tests/baselines/reference/strictNullChecksNoWidening.js
new file mode 100644
index 00000000000..ba26a04fc13
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.js
@@ -0,0 +1,31 @@
+//// [strictNullChecksNoWidening.ts]
+
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+
+declare function f(x: T): T;
+
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);
+
+
+//// [strictNullChecksNoWidening.js]
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.symbols b/tests/baselines/reference/strictNullChecksNoWidening.symbols
new file mode 100644
index 00000000000..a23a0d2e926
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.symbols
@@ -0,0 +1,48 @@
+=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
+
+var a1 = null;
+>a1 : Symbol(a1, Decl(strictNullChecksNoWidening.ts, 1, 3))
+
+var a2 = undefined;
+>a2 : Symbol(a2, Decl(strictNullChecksNoWidening.ts, 2, 3))
+>undefined : Symbol(undefined)
+
+var a3 = void 0;
+>a3 : Symbol(a3, Decl(strictNullChecksNoWidening.ts, 3, 3))
+
+var b1 = [];
+>b1 : Symbol(b1, Decl(strictNullChecksNoWidening.ts, 5, 3))
+
+var b2 = [,];
+>b2 : Symbol(b2, Decl(strictNullChecksNoWidening.ts, 6, 3))
+
+var b3 = [undefined];
+>b3 : Symbol(b3, Decl(strictNullChecksNoWidening.ts, 7, 3))
+>undefined : Symbol(undefined)
+
+var b4 = [[], []];
+>b4 : Symbol(b4, Decl(strictNullChecksNoWidening.ts, 8, 3))
+
+var b5 = [[], [,]];
+>b5 : Symbol(b5, Decl(strictNullChecksNoWidening.ts, 9, 3))
+
+declare function f(x: T): T;
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+>x : Symbol(x, Decl(strictNullChecksNoWidening.ts, 11, 22))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+>T : Symbol(T, Decl(strictNullChecksNoWidening.ts, 11, 19))
+
+var c1 = f(null);
+>c1 : Symbol(c1, Decl(strictNullChecksNoWidening.ts, 13, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+
+var c2 = f(undefined);
+>c2 : Symbol(c2, Decl(strictNullChecksNoWidening.ts, 14, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+>undefined : Symbol(undefined)
+
+var c3 = f([]);
+>c3 : Symbol(c3, Decl(strictNullChecksNoWidening.ts, 15, 3))
+>f : Symbol(f, Decl(strictNullChecksNoWidening.ts, 9, 19))
+
diff --git a/tests/baselines/reference/strictNullChecksNoWidening.types b/tests/baselines/reference/strictNullChecksNoWidening.types
new file mode 100644
index 00000000000..6dd2ee3fb4c
--- /dev/null
+++ b/tests/baselines/reference/strictNullChecksNoWidening.types
@@ -0,0 +1,67 @@
+=== tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts ===
+
+var a1 = null;
+>a1 : null
+>null : null
+
+var a2 = undefined;
+>a2 : undefined
+>undefined : undefined
+
+var a3 = void 0;
+>a3 : undefined
+>void 0 : undefined
+>0 : number
+
+var b1 = [];
+>b1 : never[]
+>[] : never[]
+
+var b2 = [,];
+>b2 : undefined[]
+>[,] : undefined[]
+> : undefined
+
+var b3 = [undefined];
+>b3 : undefined[]
+>[undefined] : undefined[]
+>undefined : undefined
+
+var b4 = [[], []];
+>b4 : never[][]
+>[[], []] : never[][]
+>[] : never[]
+>[] : never[]
+
+var b5 = [[], [,]];
+>b5 : undefined[][]
+>[[], [,]] : undefined[][]
+>[] : never[]
+>[,] : undefined[]
+> : undefined
+
+declare function f(x: T): T;
+>f : (x: T) => T
+>T : T
+>x : T
+>T : T
+>T : T
+
+var c1 = f(null);
+>c1 : null
+>f(null) : null
+>f : (x: T) => T
+>null : null
+
+var c2 = f(undefined);
+>c2 : undefined
+>f(undefined) : undefined
+>f : (x: T) => T
+>undefined : undefined
+
+var c3 = f([]);
+>c3 : never[]
+>f([]) : never[]
+>f : (x: T) => T
+>[] : never[]
+
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
index 8af0a5842cc..05428422129 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/arrayLiteralWidened.ts
@@ -1,6 +1,7 @@
 // array literals are widened upon assignment according to their element type
 
 var a = []; // any[]
+var a = [,,];
 
 var a = [null, null];
 var a = [undefined, undefined];
@@ -11,3 +12,11 @@ var b = [[undefined, undefined]];
 
 var c = [[[]]]; // any[][][]
 var c = [[[null]],[undefined]]
+
+// no widening when one or more elements are non-widening
+
+var x: undefined = undefined;
+
+var d = [x];
+var d = [, x];
+var d = [undefined, x];
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
index e79cdc9e168..2eeb96194b7 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/initializersWidened.ts
@@ -1,4 +1,24 @@
 // these are widened to any at the point of assignment
 
-var x = null;
-var y = undefined;
\ No newline at end of file
+var x1 = null;
+var y1 = undefined;
+var z1 = void 0;
+
+// these are not widened
+
+var x2: null;
+var y2: undefined;
+
+var x3: null = null;
+var y3: undefined = undefined;
+var z3: undefined = void 0;
+
+// widen only when all constituents of union are widening
+
+var x4 = null || null;
+var y4 = undefined || undefined;
+var z4 = void 0 || void 0;
+
+var x5 = null || x2;
+var y5 = undefined || y2;
+var z5 = void 0 || y2;
\ No newline at end of file
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
index cde44f91168..8b51e526882 100644
--- a/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/objectLiteralWidened.ts
@@ -1,14 +1,32 @@
 // object literal properties are widened to any
 
-var x = {
+var x1 = {
     foo: null,
     bar: undefined
 }
 
-var y = {
+var y1 = {
     foo: null,
     bar: {
         baz: null,
         boo: undefined
     }
+}
+
+// these are not widened
+
+var u: undefined = undefined;
+var n: null = null;
+
+var x2 = {
+    foo: n,
+    bar: u
+}
+
+var y2 = {
+    foo: n,
+    bar: {
+        baz: n,
+        boo: u
+    }
 }
\ No newline at end of file
diff --git a/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts b/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts
new file mode 100644
index 00000000000..8f5b4709abf
--- /dev/null
+++ b/tests/cases/conformance/types/typeRelationships/widenedTypes/strictNullChecksNoWidening.ts
@@ -0,0 +1,17 @@
+// @strictNullChecks: true
+
+var a1 = null;
+var a2 = undefined;
+var a3 = void 0;
+
+var b1 = [];
+var b2 = [,];
+var b3 = [undefined];
+var b4 = [[], []];
+var b5 = [[], [,]];
+
+declare function f(x: T): T;
+
+var c1 = f(null);
+var c2 = f(undefined);
+var c3 = f([]);

From fb2df77a59453e72e3bbc92ab4bb9e5c68cb4a8a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Thu, 2 Jun 2016 10:47:47 -0700
Subject: [PATCH 61/90] Remove unused variable

---
 src/compiler/checker.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 6166234dcf4..b8f078ea9a5 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -120,7 +120,6 @@ namespace ts {
         const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const nullType = createIntrinsicType(TypeFlags.Null, "null");
         const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
-        const emptyArrayElementType = createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
         const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
         const neverType = createIntrinsicType(TypeFlags.Never, "never");
 

From d41ac8aa9a85203f413a52842975af4a4d4e09ec Mon Sep 17 00:00:00 2001
From: zhengbli 
Date: Thu, 2 Jun 2016 11:12:38 -0700
Subject: [PATCH 62/90] Add null check and CR feedback

---
 src/compiler/parser.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 13f16f3848a..e377f988d55 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -441,14 +441,14 @@ namespace ts {
     /* @internal */
     export function parseIsolatedJSDocComment(content: string, start?: number, length?: number) {
         const result = Parser.JSDocParser.parseIsolatedJSDocComment(content, start, length);
-        if (result.jsDocComment) {
+        if (result && result.jsDocComment) {
             // because the jsDocComment was parsed out of the source file, it might
             // not be covered by the fixupParentReferences.
             let parentNode: Node = result.jsDocComment;
             forEachChild(result.jsDocComment, visitNode);
 
             function visitNode(n: Node): void {
-                if (n.parent !== parentNode) {
+                if (n.parent === undefined) {
                     n.parent = parentNode;
 
                     const saveParent = parentNode;

From fc3e040c5167868ed623612e8f33fb3beedf73b1 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders 
Date: Thu, 2 Jun 2016 12:57:24 -0700
Subject: [PATCH 63/90] Revert "Merge pull request #7235 from
 weswigham/narrow-all-types"

This reverts commit ef0f6c8fe4f94a7e294cfe42d7025c9dca6535d5, reversing
changes made to 9f087cb62ade7a879e23c229df752fc8f87d679c.
---
 src/compiler/checker.ts                       |  2 +-
 src/compiler/types.ts                         |  8 +-
 .../typeGuardNarrowsPrimitiveIntersection.js  | 38 ----------
 ...eGuardNarrowsPrimitiveIntersection.symbols | 70 -----------------
 ...ypeGuardNarrowsPrimitiveIntersection.types | 76 -------------------
 .../typeGuardNarrowsToLiteralType.js          | 21 -----
 .../typeGuardNarrowsToLiteralType.symbols     | 32 --------
 .../typeGuardNarrowsToLiteralType.types       | 35 ---------
 .../typeGuardNarrowsToLiteralTypeUnion.js     | 21 -----
 ...typeGuardNarrowsToLiteralTypeUnion.symbols | 32 --------
 .../typeGuardNarrowsToLiteralTypeUnion.types  | 35 ---------
 .../typeGuardNarrowsPrimitiveIntersection.ts  | 21 -----
 .../typeGuardNarrowsToLiteralType.ts          | 10 ---
 .../typeGuardNarrowsToLiteralTypeUnion.ts     | 10 ---
 14 files changed, 2 insertions(+), 409 deletions(-)
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.js
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.types
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
 delete mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
 delete mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
 delete mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
 delete mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ef11dc904ea..06ef2463402 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -7662,7 +7662,7 @@ namespace ts {
 
         function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
             let key: string;
-            if (!reference.flowNode || assumeInitialized && (declaredType.flags & TypeFlags.NotNarrowable)) {
+            if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
                 return declaredType;
             }
             const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 026ee5da960..c7e14a10031 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2218,13 +2218,7 @@ namespace ts {
         ObjectType = Class | Interface | Reference | Tuple | Anonymous,
         UnionOrIntersection = Union | Intersection,
         StructuredType = ObjectType | Union | Intersection,
-
-        // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow.
-        // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep
-        // Void as the only non-narrowable type, since it's a non-value type construct (representing a lack of a value)
-        // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing
-        // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules)
-        NotNarrowable = Void,
+        Narrowable = Any | ObjectType | Union | TypeParameter,
         /* @internal */
         RequiresWidening = ContainsUndefinedOrNull | ContainsObjectLiteral,
         /* @internal */
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
deleted file mode 100644
index a4cba6f4ab5..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
+++ /dev/null
@@ -1,38 +0,0 @@
-//// [typeGuardNarrowsPrimitiveIntersection.ts]
-type Tag = {__tag: any};
-declare function isNonBlank(value: string) : value is (string & Tag);
-declare function doThis(value: string & Tag): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isNonBlank(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-const enum Tag2 {}
-declare function isNonBlank2(value: string) : value is (string & Tag2);
-declare function doThis2(value: string & Tag2): void;
-declare function doThat2(value: string) : void;
-if (isNonBlank2(value)) {
-    doThis2(value);
-} else {
-    doThat2(value);
-}
-
-
-//// [typeGuardNarrowsPrimitiveIntersection.js]
-var value;
-if (isNonBlank(value)) {
-    doThis(value);
-}
-else {
-    doThat(value);
-}
-if (isNonBlank2(value)) {
-    doThis2(value);
-}
-else {
-    doThat2(value);
-}
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
deleted file mode 100644
index da507fb94c5..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
+++ /dev/null
@@ -1,70 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts ===
-type Tag = {__tag: any};
->Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
->__tag : Symbol(__tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 12))
-
-declare function isNonBlank(value: string) : value is (string & Tag);
->isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28))
->Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
-
-declare function doThis(value: string & Tag): void;
->doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 24))
->Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
-
-declare function doThat(value: string) : void;
->doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 3, 24))
-
-let value: string;
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-if (isNonBlank(value)) {
->isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-    doThis(value);
->doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-} else {
-    doThat(value);
->doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-}
-
-
-const enum Tag2 {}
->Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
-
-declare function isNonBlank2(value: string) : value is (string & Tag2);
->isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29))
->Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
-
-declare function doThis2(value: string & Tag2): void;
->doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 25))
->Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
-
-declare function doThat2(value: string) : void;
->doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 15, 25))
-
-if (isNonBlank2(value)) {
->isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-    doThis2(value);
->doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-
-} else {
-    doThat2(value);
->doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53))
->value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
-}
-
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
deleted file mode 100644
index 478363669b4..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
+++ /dev/null
@@ -1,76 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts ===
-type Tag = {__tag: any};
->Tag : { __tag: any; }
->__tag : any
-
-declare function isNonBlank(value: string) : value is (string & Tag);
->isNonBlank : (value: string) => value is string & { __tag: any; }
->value : string
->value : any
->Tag : { __tag: any; }
-
-declare function doThis(value: string & Tag): void;
->doThis : (value: string & { __tag: any; }) => void
->value : string & { __tag: any; }
->Tag : { __tag: any; }
-
-declare function doThat(value: string) : void;
->doThat : (value: string) => void
->value : string
-
-let value: string;
->value : string
-
-if (isNonBlank(value)) {
->isNonBlank(value) : boolean
->isNonBlank : (value: string) => value is string & { __tag: any; }
->value : string
-
-    doThis(value);
->doThis(value) : void
->doThis : (value: string & { __tag: any; }) => void
->value : string & { __tag: any; }
-
-} else {
-    doThat(value);
->doThat(value) : void
->doThat : (value: string) => void
->value : string
-}
-
-
-const enum Tag2 {}
->Tag2 : Tag2
-
-declare function isNonBlank2(value: string) : value is (string & Tag2);
->isNonBlank2 : (value: string) => value is string & Tag2
->value : string
->value : any
->Tag2 : Tag2
-
-declare function doThis2(value: string & Tag2): void;
->doThis2 : (value: string & Tag2) => void
->value : string & Tag2
->Tag2 : Tag2
-
-declare function doThat2(value: string) : void;
->doThat2 : (value: string) => void
->value : string
-
-if (isNonBlank2(value)) {
->isNonBlank2(value) : boolean
->isNonBlank2 : (value: string) => value is string & Tag2
->value : string
-
-    doThis2(value);
->doThis2(value) : void
->doThis2 : (value: string & Tag2) => void
->value : string & Tag2
-
-} else {
-    doThat2(value);
->doThat2(value) : void
->doThat2 : (value: string) => void
->value : string
-}
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js
deleted file mode 100644
index fdd2ee3fb7f..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js
+++ /dev/null
@@ -1,21 +0,0 @@
-//// [typeGuardNarrowsToLiteralType.ts]
-declare function isFoo(value: string) : value is "foo";
-declare function doThis(value: "foo"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-
-//// [typeGuardNarrowsToLiteralType.js]
-var value;
-if (isFoo(value)) {
-    doThis(value);
-}
-else {
-    doThat(value);
-}
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
deleted file mode 100644
index 0d0ddc5f00d..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
+++ /dev/null
@@ -1,32 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts ===
-declare function isFoo(value: string) : value is "foo";
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23))
-
-declare function doThis(value: "foo"): void;
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 1, 24))
-
-declare function doThat(value: string) : void;
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 2, 24))
-
-let value: string;
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-
-if (isFoo(value)) {
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-
-    doThis(value);
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-
-} else {
-    doThat(value);
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
-}
-
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types
deleted file mode 100644
index 9835206deb9..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types
+++ /dev/null
@@ -1,35 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts ===
-declare function isFoo(value: string) : value is "foo";
->isFoo : (value: string) => value is "foo"
->value : string
->value : any
-
-declare function doThis(value: "foo"): void;
->doThis : (value: "foo") => void
->value : "foo"
-
-declare function doThat(value: string) : void;
->doThat : (value: string) => void
->value : string
-
-let value: string;
->value : string
-
-if (isFoo(value)) {
->isFoo(value) : boolean
->isFoo : (value: string) => value is "foo"
->value : string
-
-    doThis(value);
->doThis(value) : void
->doThis : (value: "foo") => void
->value : "foo"
-
-} else {
-    doThat(value);
->doThat(value) : void
->doThat : (value: string) => void
->value : string
-}
-
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
deleted file mode 100644
index 715b362c8e0..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
+++ /dev/null
@@ -1,21 +0,0 @@
-//// [typeGuardNarrowsToLiteralTypeUnion.ts]
-declare function isFoo(value: string) : value is ("foo" | "bar");
-declare function doThis(value: "foo" | "bar"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-
-//// [typeGuardNarrowsToLiteralTypeUnion.js]
-var value;
-if (isFoo(value)) {
-    doThis(value);
-}
-else {
-    doThat(value);
-}
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
deleted file mode 100644
index 356fa06a06c..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
+++ /dev/null
@@ -1,32 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts ===
-declare function isFoo(value: string) : value is ("foo" | "bar");
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23))
-
-declare function doThis(value: "foo" | "bar"): void;
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 24))
-
-declare function doThat(value: string) : void;
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 2, 24))
-
-let value: string;
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-
-if (isFoo(value)) {
->isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-
-    doThis(value);
->doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-
-} else {
-    doThat(value);
->doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52))
->value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
-}
-
-
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
deleted file mode 100644
index 321a8861d55..00000000000
--- a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
+++ /dev/null
@@ -1,35 +0,0 @@
-=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts ===
-declare function isFoo(value: string) : value is ("foo" | "bar");
->isFoo : (value: string) => value is "foo" | "bar"
->value : string
->value : any
-
-declare function doThis(value: "foo" | "bar"): void;
->doThis : (value: "foo" | "bar") => void
->value : "foo" | "bar"
-
-declare function doThat(value: string) : void;
->doThat : (value: string) => void
->value : string
-
-let value: string;
->value : string
-
-if (isFoo(value)) {
->isFoo(value) : boolean
->isFoo : (value: string) => value is "foo" | "bar"
->value : string
-
-    doThis(value);
->doThis(value) : void
->doThis : (value: "foo" | "bar") => void
->value : "foo" | "bar"
-
-} else {
-    doThat(value);
->doThat(value) : void
->doThat : (value: string) => void
->value : string
-}
-
-
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
deleted file mode 100644
index 376a78275c8..00000000000
--- a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-type Tag = {__tag: any};
-declare function isNonBlank(value: string) : value is (string & Tag);
-declare function doThis(value: string & Tag): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isNonBlank(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
-
-const enum Tag2 {}
-declare function isNonBlank2(value: string) : value is (string & Tag2);
-declare function doThis2(value: string & Tag2): void;
-declare function doThat2(value: string) : void;
-if (isNonBlank2(value)) {
-    doThis2(value);
-} else {
-    doThat2(value);
-}
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
deleted file mode 100644
index 3b7d5bba21a..00000000000
--- a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare function isFoo(value: string) : value is "foo";
-declare function doThis(value: "foo"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts
deleted file mode 100644
index 8f4726160f4..00000000000
--- a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-declare function isFoo(value: string) : value is ("foo" | "bar");
-declare function doThis(value: "foo" | "bar"): void;
-declare function doThat(value: string) : void;
-let value: string;
-if (isFoo(value)) {
-    doThis(value);
-} else {
-    doThat(value);
-}
-

From e2a1a78dd30ea9f025b3990b73d68ecbb3eec160 Mon Sep 17 00:00:00 2001
From: zhengbli 
Date: Thu, 2 Jun 2016 13:26:15 -0700
Subject: [PATCH 64/90] reuse the fixupParentReferences function

---
 src/compiler/parser.ts | 20 ++++----------------
 1 file changed, 4 insertions(+), 16 deletions(-)

diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index e377f988d55..b24dd85f2f0 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -444,19 +444,7 @@ namespace ts {
         if (result && result.jsDocComment) {
             // because the jsDocComment was parsed out of the source file, it might
             // not be covered by the fixupParentReferences.
-            let parentNode: Node = result.jsDocComment;
-            forEachChild(result.jsDocComment, visitNode);
-
-            function visitNode(n: Node): void {
-                if (n.parent === undefined) {
-                    n.parent = parentNode;
-
-                    const saveParent = parentNode;
-                    parentNode = n;
-                    forEachChild(n, visitNode);
-                    parentNode = saveParent;
-                }
-            }
+            Parser.fixupParentReferences(result.jsDocComment);
         }
 
         return result;
@@ -671,14 +659,14 @@ namespace ts {
             return node;
         }
 
-        export function fixupParentReferences(sourceFile: Node) {
+        export function fixupParentReferences(rootNode: Node) {
             // normally parent references are set during binding. However, for clients that only need
             // a syntax tree, and no semantic features, then the binding process is an unnecessary
             // overhead.  This functions allows us to set all the parents, without all the expense of
             // binding.
 
-            let parent: Node = sourceFile;
-            forEachChild(sourceFile, visitNode);
+            let parent: Node = rootNode;
+            forEachChild(rootNode, visitNode);
             return;
 
             function visitNode(n: Node): void {

From c8ced6703a1fc5e8fb6de371dcbdfbf2df8af293 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Fri, 3 Jun 2016 08:43:48 -0700
Subject: [PATCH 65/90] Use a function `stringify` to simplify calls to
 `JSON.stringify(xyz, undefined, 2)`

---
 src/harness/fourslash.ts | 36 ++++++++++++++++++++----------------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index bd556c659c7..dde021d1f5c 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -746,7 +746,7 @@ namespace FourSlash {
             }
 
             const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
-            this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(references, undefined, 2)})`);
+            this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`);
         }
 
         public verifyReferencesCountIs(count: number, localFilesOnly = true) {
@@ -800,7 +800,7 @@ namespace FourSlash {
 
         private testDiagnostics(expected: string, diagnostics: ts.Diagnostic[]) {
             const realized = ts.realizeDiagnostics(diagnostics, "\r\n");
-            const actual = JSON.stringify(realized, undefined, 2);
+            const actual = stringify(realized);
             assert.equal(actual, expected);
         }
 
@@ -875,7 +875,7 @@ namespace FourSlash {
                 }
 
                 if (ranges.length !== references.length) {
-                    this.raiseError("Rename location count does not match result.\n\nExpected: " + JSON.stringify(ranges, undefined, 2) + "\n\nActual:" + JSON.stringify(references, undefined, 2));
+                    this.raiseError("Rename location count does not match result.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + stringify(references));
                 }
 
                 ranges = ranges.sort((r1, r2) => r1.start - r2.start);
@@ -888,7 +888,7 @@ namespace FourSlash {
                     if (reference.textSpan.start !== range.start ||
                         ts.textSpanEnd(reference.textSpan) !== range.end) {
 
-                        this.raiseError("Rename location results do not match.\n\nExpected: " + JSON.stringify(ranges, undefined, 2) + "\n\nActual:" + JSON.stringify(references, undefined, 2));
+                        this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + JSON.stringify(references));
                     }
                 }
             }
@@ -972,7 +972,7 @@ namespace FourSlash {
             }
             else {
                 if (actual) {
-                    this.raiseError(`Expected no signature help, but got "${JSON.stringify(actual, undefined, 2)}"`);
+                    this.raiseError(`Expected no signature help, but got "${stringify(actual)}"`);
                 }
             }
         }
@@ -1176,7 +1176,7 @@ namespace FourSlash {
 
         public printCurrentParameterHelp() {
             const help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition);
-            Harness.IO.log(JSON.stringify(help, undefined, 2));
+            Harness.IO.log(stringify(help));
         }
 
         public printCurrentQuickInfo() {
@@ -1218,7 +1218,7 @@ namespace FourSlash {
 
         public printCurrentSignatureHelp() {
             const sigHelp = this.getActiveSignatureHelpItem();
-            Harness.IO.log(JSON.stringify(sigHelp, undefined, 2));
+            Harness.IO.log(stringify(sigHelp));
         }
 
         public printMemberListMembers() {
@@ -1248,7 +1248,7 @@ namespace FourSlash {
         public printReferences() {
             const references = this.getReferencesAtCaret();
             ts.forEach(references, entry => {
-                Harness.IO.log(JSON.stringify(entry, undefined, 2));
+                Harness.IO.log(stringify(entry));
             });
         }
 
@@ -1745,8 +1745,8 @@ namespace FourSlash {
 
             function jsonMismatchString() {
                 return Harness.IO.newLine() +
-                    "expected: '" + Harness.IO.newLine() + JSON.stringify(expected, undefined, 2) + "'" + Harness.IO.newLine() +
-                    "actual:   '" + Harness.IO.newLine() + JSON.stringify(actual, undefined, 2) + "'";
+                    "expected: '" + Harness.IO.newLine() + stringify(expected, undefined, 2) + "'" + Harness.IO.newLine() +
+                    "actual:   '" + Harness.IO.newLine() + stringify(actual) + "'";
             }
         }
 
@@ -1961,14 +1961,14 @@ namespace FourSlash {
             // if there was an explicit match kind specified, then it should be validated.
             if (matchKind !== undefined) {
                 const missingItem = { name: name, kind: kind, searchValue: searchValue, matchKind: matchKind, fileName: fileName, parentName: parentName };
-                this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(items, undefined, 2)})`);
+                this.raiseError(`verifyNavigationItemsListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(items)})`);
             }
         }
 
         public verifyNavigationBar(json: any) {
             const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
             if (JSON.stringify(items, replacer) !== JSON.stringify(json)) {
-                this.raiseError(`verifyNavigationBar failed - expected: ${JSON.stringify(json, undefined, 2)}, got: ${JSON.stringify(items, replacer, 2)}`);
+                this.raiseError(`verifyNavigationBar failed - expected: ${stringify(json)}, got: ${stringify(items, replacer)}`);
             }
 
             // Make the data easier to read.
@@ -2031,7 +2031,7 @@ namespace FourSlash {
             }
 
             const missingItem = { fileName: fileName, start: start, end: end, isWriteAccess: isWriteAccess };
-            this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(occurrences, undefined, 2)})`);
+            this.raiseError(`verifyOccurrencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(occurrences)})`);
         }
 
         public verifyOccurrencesAtPositionListCount(expectedCount: number) {
@@ -2070,7 +2070,7 @@ namespace FourSlash {
             }
 
             const missingItem = { fileName: fileName, start: start, end: end, kind: kind };
-            this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${JSON.stringify(missingItem, undefined, 2)} in the returned list: (${JSON.stringify(documentHighlights, undefined, 2)})`);
+            this.raiseError(`verifyDocumentHighlightsAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(documentHighlights)})`);
         }
 
         public verifyDocumentHighlightsAtPositionListCount(expectedCount: number, fileNamesToSearch: string[]) {
@@ -2136,9 +2136,9 @@ namespace FourSlash {
                 }
             }
 
-            const itemsString = items.map((item) => JSON.stringify({ name: item.name, kind: item.kind }, undefined, 2)).join(",\n");
+            const itemsString = items.map(item => stringify({ name: item.name, kind: item.kind })).join(",\n");
 
-            this.raiseError(`Expected "${JSON.stringify({ name, text, documentation, kind }, undefined, 2)}" to be in list [${itemsString}]`);
+            this.raiseError(`Expected "${stringify({ name, text, documentation, kind )}" to be in list [${itemsString}]`);
         }
 
         private findFile(indexOrName: any) {
@@ -2701,6 +2701,10 @@ ${code}
         }
         return result;
     }
+
+    function stringify(data: any, replacer?: (key: string, value: any) => any): string {
+        return JSON.stringify(data, replacer, 2);
+    }
 }
 
 namespace FourSlashInterface {

From 8b0974a77e6317891fc11259dd06f721b621be5d Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Fri, 3 Jun 2016 09:29:21 -0700
Subject: [PATCH 66/90] Update tests

---
 .../reference/ArrowFunction2.errors.txt       |  9 -------
 tests/baselines/reference/ArrowFunction2.js   |  8 -------
 ...rserErrorRecovery_ArgumentList5.errors.txt | 16 -------------
 .../parserErrorRecovery_ArgumentList5.js      | 11 ---------
 .../parserX_ArrowFunction2.errors.txt         | 12 ----------
 .../reference/parserX_ArrowFunction2.js       |  8 -------
 ...gCommasInFunctionParametersAndArguments.js | 11 +++++++++
 ...asInFunctionParametersAndArguments.symbols |  7 ++++++
 ...mmasInFunctionParametersAndArguments.types | 10 ++++++++
 ...trailingSeparatorInFunctionCall.errors.txt | 24 -------------------
 .../trailingSeparatorInFunctionCall.js        | 18 --------------
 .../trailingSeparatorInFunctionCall.ts        |  9 -------
 ...gCommasInFunctionParametersAndArguments.ts |  4 ++++
 .../parserErrorRecovery_ArgumentList5.ts      |  4 ----
 .../ArrowFunctions/ArrowFunction2.ts          |  3 ---
 .../ArrowFunctions/parserX_ArrowFunction2.ts  |  3 ---
 16 files changed, 32 insertions(+), 125 deletions(-)
 delete mode 100644 tests/baselines/reference/ArrowFunction2.errors.txt
 delete mode 100644 tests/baselines/reference/ArrowFunction2.js
 delete mode 100644 tests/baselines/reference/parserErrorRecovery_ArgumentList5.errors.txt
 delete mode 100644 tests/baselines/reference/parserErrorRecovery_ArgumentList5.js
 delete mode 100644 tests/baselines/reference/parserX_ArrowFunction2.errors.txt
 delete mode 100644 tests/baselines/reference/parserX_ArrowFunction2.js
 delete mode 100644 tests/baselines/reference/trailingSeparatorInFunctionCall.errors.txt
 delete mode 100644 tests/baselines/reference/trailingSeparatorInFunctionCall.js
 delete mode 100644 tests/cases/compiler/trailingSeparatorInFunctionCall.ts
 delete mode 100644 tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts
 delete mode 100644 tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts
 delete mode 100644 tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts

diff --git a/tests/baselines/reference/ArrowFunction2.errors.txt b/tests/baselines/reference/ArrowFunction2.errors.txt
deleted file mode 100644
index d18bc483acc..00000000000
--- a/tests/baselines/reference/ArrowFunction2.errors.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts(1,13): error TS2304: Cannot find name 'b'.
-
-
-==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts (2 errors) ====
-    var v = (a: b,) => {
-                ~
-!!! error TS2304: Cannot find name 'b'.
-       
-    };
\ No newline at end of file
diff --git a/tests/baselines/reference/ArrowFunction2.js b/tests/baselines/reference/ArrowFunction2.js
deleted file mode 100644
index fa6b8bf3b9e..00000000000
--- a/tests/baselines/reference/ArrowFunction2.js
+++ /dev/null
@@ -1,8 +0,0 @@
-//// [ArrowFunction2.ts]
-var v = (a: b,) => {
-   
-};
-
-//// [ArrowFunction2.js]
-var v = function (a) {
-};
diff --git a/tests/baselines/reference/parserErrorRecovery_ArgumentList5.errors.txt b/tests/baselines/reference/parserErrorRecovery_ArgumentList5.errors.txt
deleted file mode 100644
index 5113807faa8..00000000000
--- a/tests/baselines/reference/parserErrorRecovery_ArgumentList5.errors.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts(2,4): error TS2304: Cannot find name 'bar'.
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts(2,8): error TS2304: Cannot find name 'a'.
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts(2,9): error TS1009: Trailing comma not allowed.
-
-
-==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts (3 errors) ====
-    function foo() {
-       bar(a,)
-       ~~~
-!!! error TS2304: Cannot find name 'bar'.
-           ~
-!!! error TS2304: Cannot find name 'a'.
-            ~
-!!! error TS1009: Trailing comma not allowed.
-       return;
-    }
\ No newline at end of file
diff --git a/tests/baselines/reference/parserErrorRecovery_ArgumentList5.js b/tests/baselines/reference/parserErrorRecovery_ArgumentList5.js
deleted file mode 100644
index bbb65a44e07..00000000000
--- a/tests/baselines/reference/parserErrorRecovery_ArgumentList5.js
+++ /dev/null
@@ -1,11 +0,0 @@
-//// [parserErrorRecovery_ArgumentList5.ts]
-function foo() {
-   bar(a,)
-   return;
-}
-
-//// [parserErrorRecovery_ArgumentList5.js]
-function foo() {
-    bar(a);
-    return;
-}
diff --git a/tests/baselines/reference/parserX_ArrowFunction2.errors.txt b/tests/baselines/reference/parserX_ArrowFunction2.errors.txt
deleted file mode 100644
index 20b4cb45e45..00000000000
--- a/tests/baselines/reference/parserX_ArrowFunction2.errors.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts(1,13): error TS2304: Cannot find name 'b'.
-tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts(1,14): error TS1009: Trailing comma not allowed.
-
-
-==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts (2 errors) ====
-    var v = (a: b,) => {
-                ~
-!!! error TS2304: Cannot find name 'b'.
-                 ~
-!!! error TS1009: Trailing comma not allowed.
-       
-    };
\ No newline at end of file
diff --git a/tests/baselines/reference/parserX_ArrowFunction2.js b/tests/baselines/reference/parserX_ArrowFunction2.js
deleted file mode 100644
index a30294b658b..00000000000
--- a/tests/baselines/reference/parserX_ArrowFunction2.js
+++ /dev/null
@@ -1,8 +0,0 @@
-//// [parserX_ArrowFunction2.ts]
-var v = (a: b,) => {
-   
-};
-
-//// [parserX_ArrowFunction2.js]
-var v = function (a) {
-};
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
index 5fb4b7d638f..a9b173f9db2 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
@@ -2,8 +2,19 @@
 function f1(x,) {}
 
 f1(1,);
+
+function f2(...args,) {}
+
+f2(...[],);
 
 
 //// [trailingCommasInFunctionParametersAndArguments.js]
 function f1(x) { }
 f1(1);
+function f2() {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i - 0] = arguments[_i];
+    }
+}
+f2.apply(void 0, []);
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
index 5eb9d75807b..dd9ce5319c9 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
@@ -6,3 +6,10 @@ function f1(x,) {}
 f1(1,);
 >f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
 
+function f2(...args,) {}
+>f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 2, 7))
+>args : Symbol(args, Decl(trailingCommasInFunctionParametersAndArguments.ts, 4, 12))
+
+f2(...[],);
+>f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 2, 7))
+
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
index 4af2326076b..0a8d34d1df2 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
@@ -8,3 +8,13 @@ f1(1,);
 >f1 : (x: any) => void
 >1 : number
 
+function f2(...args,) {}
+>f2 : (...args: any[]) => void
+>args : any[]
+
+f2(...[],);
+>f2(...[],) : void
+>f2 : (...args: any[]) => void
+>...[] : undefined
+>[] : undefined[]
+
diff --git a/tests/baselines/reference/trailingSeparatorInFunctionCall.errors.txt b/tests/baselines/reference/trailingSeparatorInFunctionCall.errors.txt
deleted file mode 100644
index 76e09a1a4de..00000000000
--- a/tests/baselines/reference/trailingSeparatorInFunctionCall.errors.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-tests/cases/compiler/trailingSeparatorInFunctionCall.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
-tests/cases/compiler/trailingSeparatorInFunctionCall.ts(4,7): error TS1009: Trailing comma not allowed.
-tests/cases/compiler/trailingSeparatorInFunctionCall.ts(9,1): error TS2346: Supplied parameters do not match any signature of call target.
-tests/cases/compiler/trailingSeparatorInFunctionCall.ts(9,8): error TS1009: Trailing comma not allowed.
-
-
-==== tests/cases/compiler/trailingSeparatorInFunctionCall.ts (4 errors) ====
-    function f(x, y) {
-    }
-    
-    f(1, 2, );
-    ~~~~~~~~~
-!!! error TS2346: Supplied parameters do not match any signature of call target.
-          ~
-!!! error TS1009: Trailing comma not allowed.
-    
-    function f2(x: T, y: T) {
-    }
-    
-    f2(1, 2, );
-    ~~~~~~~~~~
-!!! error TS2346: Supplied parameters do not match any signature of call target.
-           ~
-!!! error TS1009: Trailing comma not allowed.
\ No newline at end of file
diff --git a/tests/baselines/reference/trailingSeparatorInFunctionCall.js b/tests/baselines/reference/trailingSeparatorInFunctionCall.js
deleted file mode 100644
index 4b531479837..00000000000
--- a/tests/baselines/reference/trailingSeparatorInFunctionCall.js
+++ /dev/null
@@ -1,18 +0,0 @@
-//// [trailingSeparatorInFunctionCall.ts]
-function f(x, y) {
-}
-
-f(1, 2, );
-
-function f2(x: T, y: T) {
-}
-
-f2(1, 2, );
-
-//// [trailingSeparatorInFunctionCall.js]
-function f(x, y) {
-}
-f(1, 2);
-function f2(x, y) {
-}
-f2(1, 2);
diff --git a/tests/cases/compiler/trailingSeparatorInFunctionCall.ts b/tests/cases/compiler/trailingSeparatorInFunctionCall.ts
deleted file mode 100644
index b9f62e1e8cc..00000000000
--- a/tests/cases/compiler/trailingSeparatorInFunctionCall.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-function f(x, y) {
-}
-
-f(1, 2, );
-
-function f2(x: T, y: T) {
-}
-
-f2(1, 2, );
\ No newline at end of file
diff --git a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
index 0492ed989e3..4b628e7e559 100644
--- a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
+++ b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
@@ -1,3 +1,7 @@
 function f1(x,) {}
 
 f1(1,);
+
+function f2(...args,) {}
+
+f2(...[],);
diff --git a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts b/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts
deleted file mode 100644
index 0270f766fef..00000000000
--- a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArgumentLists/parserErrorRecovery_ArgumentList5.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-function foo() {
-   bar(a,)
-   return;
-}
\ No newline at end of file
diff --git a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts b/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts
deleted file mode 100644
index 8b4d99e2ef5..00000000000
--- a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction2.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-var v = (a: b,) => {
-   
-};
\ No newline at end of file
diff --git a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts b/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts
deleted file mode 100644
index 8b4d99e2ef5..00000000000
--- a/tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction2.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-var v = (a: b,) => {
-   
-};
\ No newline at end of file

From 2bed32d30862b9bf6b3c202f1ce907129804eb95 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Fri, 3 Jun 2016 09:08:48 -0700
Subject: [PATCH 67/90] Fix mistake

---
 src/harness/fourslash.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index dde021d1f5c..c05fca5bb47 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -1745,7 +1745,7 @@ namespace FourSlash {
 
             function jsonMismatchString() {
                 return Harness.IO.newLine() +
-                    "expected: '" + Harness.IO.newLine() + stringify(expected, undefined, 2) + "'" + Harness.IO.newLine() +
+                    "expected: '" + Harness.IO.newLine() + stringify(expected) + "'" + Harness.IO.newLine() +
                     "actual:   '" + Harness.IO.newLine() + stringify(actual) + "'";
             }
         }
@@ -2138,7 +2138,7 @@ namespace FourSlash {
 
             const itemsString = items.map(item => stringify({ name: item.name, kind: item.kind })).join(",\n");
 
-            this.raiseError(`Expected "${stringify({ name, text, documentation, kind )}" to be in list [${itemsString}]`);
+            this.raiseError(`Expected "${stringify({ name, text, documentation, kind })}" to be in list [${itemsString}]`);
         }
 
         private findFile(indexOrName: any) {

From e412f52b332f58f360ac68ae51974fa084074d7d Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Fri, 3 Jun 2016 06:50:00 -0700
Subject: [PATCH 68/90] Include indent in navigation bar protocol

Previously navbar01 test had indents when run in the browser but not when run from node. Now they run the same.
---
 src/server/client.ts                     |  2 +-
 src/server/protocol.d.ts                 |  5 +++++
 src/server/session.ts                    |  3 ++-
 tests/cases/fourslash/server/navbar01.ts | 12 ++++++++----
 4 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/src/server/client.ts b/src/server/client.ts
index 5c576347d19..b0a1a8ec9e9 100644
--- a/src/server/client.ts
+++ b/src/server/client.ts
@@ -459,7 +459,7 @@ namespace ts.server {
                 kindModifiers: item.kindModifiers || "",
                 spans: item.spans.map(span => createTextSpanFromBounds(this.lineOffsetToPosition(fileName, span.start), this.lineOffsetToPosition(fileName, span.end))),
                 childItems: this.decodeNavigationBarItems(item.childItems, fileName),
-                indent: 0,
+                indent: item.indent,
                 bolded: false,
                 grayed: false
             }));
diff --git a/src/server/protocol.d.ts b/src/server/protocol.d.ts
index f6a418fb578..d2c15b308ec 100644
--- a/src/server/protocol.d.ts
+++ b/src/server/protocol.d.ts
@@ -1242,6 +1242,11 @@ declare namespace ts.server.protocol {
           * Optional children.
           */
         childItems?: NavigationBarItem[];
+
+        /**
+          * Number of levels deep this item should appear.
+          */
+        indent: number;
     }
 
     export interface NavBarResponse extends Response {
diff --git a/src/server/session.ts b/src/server/session.ts
index 09b86da48cc..7f3ba105999 100644
--- a/src/server/session.ts
+++ b/src/server/session.ts
@@ -872,7 +872,8 @@ namespace ts.server {
                     start: compilerService.host.positionToLineOffset(fileName, span.start),
                     end: compilerService.host.positionToLineOffset(fileName, ts.textSpanEnd(span))
                 })),
-                childItems: this.decorateNavigationBarItem(project, fileName, item.childItems)
+                childItems: this.decorateNavigationBarItem(project, fileName, item.childItems),
+                indent: item.indent
             }));
         }
 
diff --git a/tests/cases/fourslash/server/navbar01.ts b/tests/cases/fourslash/server/navbar01.ts
index 7f2229fcb81..9e0f2a36396 100644
--- a/tests/cases/fourslash/server/navbar01.ts
+++ b/tests/cases/fourslash/server/navbar01.ts
@@ -85,7 +85,8 @@ verify.navigationBar([
                 "text": "prop",
                 "kind": "property"
             }
-        ]
+        ],
+        "indent": 1
     },
     {
         "text": "Shapes",
@@ -100,7 +101,8 @@ verify.navigationBar([
                 "text": "Values",
                 "kind": "enum"
             }
-        ]
+        ],
+        "indent": 1
     },
     {
         "text": "Point",
@@ -143,7 +145,8 @@ verify.navigationBar([
                 "kind": "property",
                 "kindModifiers": "public"
             }
-        ]
+        ],
+        "indent": 2
     },
     {
         "text": "Values",
@@ -161,6 +164,7 @@ verify.navigationBar([
                 "text": "value3",
                 "kind": "property"
             }
-        ]
+        ],
+        "indent": 2
     }
 ]);

From 39c4e8ad1f63b54d642d7896874f7c0f98cf4699 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Fri, 3 Jun 2016 17:30:49 -0700
Subject: [PATCH 69/90] Remove unnecessary restrictions in property access
 narrowing

---
 src/compiler/checker.ts | 20 +++++---------------
 1 file changed, 5 insertions(+), 15 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index b8f078ea9a5..c538134e6d8 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -9991,24 +9991,14 @@ namespace ts {
             }
 
             const propType = getTypeOfSymbol(prop);
+            // Only compute control flow type if this is a property access expression that isn't an
+            // assignment target, and the referenced property was declared as a variable, property,
+            // accessor, or optional method.
             if (node.kind !== SyntaxKind.PropertyAccessExpression || isAssignmentTarget(node) ||
-                !(propType.flags & TypeFlags.Union) && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor))) {
+                !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) &&
+                !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
                 return propType;
             }
-            const leftmostNode = getLeftmostIdentifierOrThis(node);
-            if (!leftmostNode) {
-                return propType;
-            }
-            if (leftmostNode.kind === SyntaxKind.Identifier) {
-                const leftmostSymbol = getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(leftmostNode));
-                if (!leftmostSymbol) {
-                    return propType;
-                }
-                const declaration = leftmostSymbol.valueDeclaration;
-                if (!declaration || declaration.kind !== SyntaxKind.VariableDeclaration && declaration.kind !== SyntaxKind.Parameter && declaration.kind !== SyntaxKind.BindingElement) {
-                    return propType;
-                }
-            }
             return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false);
         }
 

From a5e9071a2f533ff18c51974c73ffaeeb686230b1 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Fri, 3 Jun 2016 17:31:16 -0700
Subject: [PATCH 70/90] Fix fourslash test

---
 .../quickInfoOnNarrowedTypeInModule.ts        | 32 +++++++++++++------
 1 file changed, 22 insertions(+), 10 deletions(-)

diff --git a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts
index 1f95de35403..cfeaabfbb86 100644
--- a/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts
+++ b/tests/cases/fourslash/quickInfoOnNarrowedTypeInModule.ts
@@ -50,14 +50,26 @@ goTo.marker('6');
 verify.quickInfoIs('var m.exportedStrOrNum: string');
 verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
 
-['7', '8', '9'].forEach((marker, index, arr) => {
-    goTo.marker(marker);
-    verify.quickInfoIs('var m.exportedStrOrNum: string | number');
-    verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
-});
+goTo.marker('7');
+verify.quickInfoIs('var m.exportedStrOrNum: string | number');
+verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
 
-['7', '8', '9'].forEach((marker, index, arr) => {
-    goTo.marker(marker);
-    verify.quickInfoIs('var m.exportedStrOrNum: string | number');
-    verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
-});
\ No newline at end of file
+goTo.marker('8');
+verify.quickInfoIs('var m.exportedStrOrNum: number');
+verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: number");
+
+goTo.marker('9');
+verify.quickInfoIs('var m.exportedStrOrNum: string');
+verify.completionListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
+
+goTo.marker('7');
+verify.quickInfoIs('var m.exportedStrOrNum: string | number');
+verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: string | number");
+
+goTo.marker('8');
+verify.quickInfoIs('var m.exportedStrOrNum: number');
+verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: number");
+
+goTo.marker('9');
+verify.quickInfoIs('var m.exportedStrOrNum: string');
+verify.memberListContains("exportedStrOrNum", "var m.exportedStrOrNum: string");
\ No newline at end of file

From 87ee72b25a10588ca5573c377e3fb24eeca5a096 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Fri, 3 Jun 2016 17:31:28 -0700
Subject: [PATCH 71/90] Add regression test

---
 .../reference/classStaticPropertyTypeGuard.js | 32 +++++++++++++++++++
 .../classStaticPropertyTypeGuard.symbols      | 29 +++++++++++++++++
 .../classStaticPropertyTypeGuard.types        | 31 ++++++++++++++++++
 .../compiler/classStaticPropertyTypeGuard.ts  | 15 +++++++++
 4 files changed, 107 insertions(+)
 create mode 100644 tests/baselines/reference/classStaticPropertyTypeGuard.js
 create mode 100644 tests/baselines/reference/classStaticPropertyTypeGuard.symbols
 create mode 100644 tests/baselines/reference/classStaticPropertyTypeGuard.types
 create mode 100644 tests/cases/compiler/classStaticPropertyTypeGuard.ts

diff --git a/tests/baselines/reference/classStaticPropertyTypeGuard.js b/tests/baselines/reference/classStaticPropertyTypeGuard.js
new file mode 100644
index 00000000000..872f78db4c4
--- /dev/null
+++ b/tests/baselines/reference/classStaticPropertyTypeGuard.js
@@ -0,0 +1,32 @@
+//// [classStaticPropertyTypeGuard.ts]
+
+// Repro from #8923
+
+class A {
+    private static _a: string | undefined;
+
+    public get a(): string {
+        if (A._a) {
+            return A._a; // is possibly null or undefined.
+        }
+        return A._a = 'helloworld';
+    }
+}
+
+//// [classStaticPropertyTypeGuard.js]
+// Repro from #8923
+var A = (function () {
+    function A() {
+    }
+    Object.defineProperty(A.prototype, "a", {
+        get: function () {
+            if (A._a) {
+                return A._a; // is possibly null or undefined.
+            }
+            return A._a = 'helloworld';
+        },
+        enumerable: true,
+        configurable: true
+    });
+    return A;
+}());
diff --git a/tests/baselines/reference/classStaticPropertyTypeGuard.symbols b/tests/baselines/reference/classStaticPropertyTypeGuard.symbols
new file mode 100644
index 00000000000..c4d2acd50b0
--- /dev/null
+++ b/tests/baselines/reference/classStaticPropertyTypeGuard.symbols
@@ -0,0 +1,29 @@
+=== tests/cases/compiler/classStaticPropertyTypeGuard.ts ===
+
+// Repro from #8923
+
+class A {
+>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
+
+    private static _a: string | undefined;
+>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+
+    public get a(): string {
+>a : Symbol(A.a, Decl(classStaticPropertyTypeGuard.ts, 4, 42))
+
+        if (A._a) {
+>A._a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
+>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+
+            return A._a; // is possibly null or undefined.
+>A._a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
+>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+        }
+        return A._a = 'helloworld';
+>A._a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+>A : Symbol(A, Decl(classStaticPropertyTypeGuard.ts, 0, 0))
+>_a : Symbol(A._a, Decl(classStaticPropertyTypeGuard.ts, 3, 9))
+    }
+}
diff --git a/tests/baselines/reference/classStaticPropertyTypeGuard.types b/tests/baselines/reference/classStaticPropertyTypeGuard.types
new file mode 100644
index 00000000000..f89854c919d
--- /dev/null
+++ b/tests/baselines/reference/classStaticPropertyTypeGuard.types
@@ -0,0 +1,31 @@
+=== tests/cases/compiler/classStaticPropertyTypeGuard.ts ===
+
+// Repro from #8923
+
+class A {
+>A : A
+
+    private static _a: string | undefined;
+>_a : string | undefined
+
+    public get a(): string {
+>a : string
+
+        if (A._a) {
+>A._a : string | undefined
+>A : typeof A
+>_a : string | undefined
+
+            return A._a; // is possibly null or undefined.
+>A._a : string
+>A : typeof A
+>_a : string
+        }
+        return A._a = 'helloworld';
+>A._a = 'helloworld' : string
+>A._a : string | undefined
+>A : typeof A
+>_a : string | undefined
+>'helloworld' : string
+    }
+}
diff --git a/tests/cases/compiler/classStaticPropertyTypeGuard.ts b/tests/cases/compiler/classStaticPropertyTypeGuard.ts
new file mode 100644
index 00000000000..93655cfb092
--- /dev/null
+++ b/tests/cases/compiler/classStaticPropertyTypeGuard.ts
@@ -0,0 +1,15 @@
+// @strictNullChecks: true
+// @target: ES5
+
+// Repro from #8923
+
+class A {
+    private static _a: string | undefined;
+
+    public get a(): string {
+        if (A._a) {
+            return A._a; // is possibly null or undefined.
+        }
+        return A._a = 'helloworld';
+    }
+}
\ No newline at end of file

From bb7818b837fb4145f9fb42e64df4484c8547b7a7 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Sat, 4 Jun 2016 14:42:45 -0700
Subject: [PATCH 72/90] Consider property declarations to be control flow
 containers

---
 src/compiler/checker.ts   | 15 ++++++++++++---
 src/compiler/utilities.ts |  9 ---------
 2 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index c538134e6d8..67bd747a4ae 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -8091,13 +8091,22 @@ namespace ts {
             return expression;
         }
 
+        function getControlFlowContainer(node: Node): Node {
+            while (true) {
+                node = node.parent;
+                if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration) {
+                    return node;
+                }
+            }
+        }
+
         function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
-            const declarationContainer = getContainingFunctionOrModule(declaration);
-            let container = getContainingFunctionOrModule(reference);
+            const declarationContainer = getControlFlowContainer(declaration);
+            let container = getControlFlowContainer(reference);
             while (container !== declarationContainer &&
                 (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
                 (includeOuterFunctions || getImmediatelyInvokedFunctionExpression(container))) {
-                container = getContainingFunctionOrModule(container);
+                container = getControlFlowContainer(container);
             }
             return container === declarationContainer;
         }
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 74ea3459b23..e2fa4662c1f 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -880,15 +880,6 @@ namespace ts {
         }
     }
 
-    export function getContainingFunctionOrModule(node: Node): Node {
-        while (true) {
-            node = node.parent;
-            if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.SourceFile) {
-                return node;
-            }
-        }
-    }
-
     export function getContainingClass(node: Node): ClassLikeDeclaration {
         while (true) {
             node = node.parent;

From 3b1effb7df5e6221260652280d47b78ae9ecbfac Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg 
Date: Sat, 4 Jun 2016 14:50:37 -0700
Subject: [PATCH 73/90] Adding regression test

---
 .../controlFlowPropertyInitializer.js         | 19 +++++++++++++++++++
 .../controlFlowPropertyInitializer.symbols    | 14 ++++++++++++++
 .../controlFlowPropertyInitializer.types      | 15 +++++++++++++++
 .../controlFlowPropertyInitializer.ts         |  9 +++++++++
 4 files changed, 57 insertions(+)
 create mode 100644 tests/baselines/reference/controlFlowPropertyInitializer.js
 create mode 100644 tests/baselines/reference/controlFlowPropertyInitializer.symbols
 create mode 100644 tests/baselines/reference/controlFlowPropertyInitializer.types
 create mode 100644 tests/cases/compiler/controlFlowPropertyInitializer.ts

diff --git a/tests/baselines/reference/controlFlowPropertyInitializer.js b/tests/baselines/reference/controlFlowPropertyInitializer.js
new file mode 100644
index 00000000000..5d8e10651fa
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyInitializer.js
@@ -0,0 +1,19 @@
+//// [controlFlowPropertyInitializer.ts]
+
+// Repro from #8967
+
+const LANG = "Turbo Pascal"
+
+class BestLanguage {
+    name = LANG;
+}
+
+//// [controlFlowPropertyInitializer.js]
+// Repro from #8967
+var LANG = "Turbo Pascal";
+var BestLanguage = (function () {
+    function BestLanguage() {
+        this.name = LANG;
+    }
+    return BestLanguage;
+}());
diff --git a/tests/baselines/reference/controlFlowPropertyInitializer.symbols b/tests/baselines/reference/controlFlowPropertyInitializer.symbols
new file mode 100644
index 00000000000..70e6c2da8a1
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyInitializer.symbols
@@ -0,0 +1,14 @@
+=== tests/cases/compiler/controlFlowPropertyInitializer.ts ===
+
+// Repro from #8967
+
+const LANG = "Turbo Pascal"
+>LANG : Symbol(LANG, Decl(controlFlowPropertyInitializer.ts, 3, 5))
+
+class BestLanguage {
+>BestLanguage : Symbol(BestLanguage, Decl(controlFlowPropertyInitializer.ts, 3, 27))
+
+    name = LANG;
+>name : Symbol(BestLanguage.name, Decl(controlFlowPropertyInitializer.ts, 5, 20))
+>LANG : Symbol(LANG, Decl(controlFlowPropertyInitializer.ts, 3, 5))
+}
diff --git a/tests/baselines/reference/controlFlowPropertyInitializer.types b/tests/baselines/reference/controlFlowPropertyInitializer.types
new file mode 100644
index 00000000000..d35a4c13698
--- /dev/null
+++ b/tests/baselines/reference/controlFlowPropertyInitializer.types
@@ -0,0 +1,15 @@
+=== tests/cases/compiler/controlFlowPropertyInitializer.ts ===
+
+// Repro from #8967
+
+const LANG = "Turbo Pascal"
+>LANG : string
+>"Turbo Pascal" : string
+
+class BestLanguage {
+>BestLanguage : BestLanguage
+
+    name = LANG;
+>name : string
+>LANG : string
+}
diff --git a/tests/cases/compiler/controlFlowPropertyInitializer.ts b/tests/cases/compiler/controlFlowPropertyInitializer.ts
new file mode 100644
index 00000000000..0b75f280b1d
--- /dev/null
+++ b/tests/cases/compiler/controlFlowPropertyInitializer.ts
@@ -0,0 +1,9 @@
+// @strictNullChecks: true
+
+// Repro from #8967
+
+const LANG = "Turbo Pascal"
+
+class BestLanguage {
+    name = LANG;
+}
\ No newline at end of file

From f5ab9d7289a459edb695277a812e9d2a6e1a2830 Mon Sep 17 00:00:00 2001
From: york yao 
Date: Mon, 6 Jun 2016 07:22:37 +0800
Subject: [PATCH 74/90] change type definition for Object.create

---
 src/lib/es5.d.ts | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts
index dfd2bd4ff0c..8447b31fc89 100644
--- a/src/lib/es5.d.ts
+++ b/src/lib/es5.d.ts
@@ -136,12 +136,24 @@ interface ObjectConstructor {
       */
     getOwnPropertyNames(o: any): string[];
 
+    /**
+      * Creates an object that has null prototype.
+      * @param o Object to use as a prototype. May be null
+      */
+    create(o: null): any;
+
+    /**
+      * Creates an object that has the specified prototype, and that optionally contains specified properties.
+      * @param o Object to use as a prototype. May be null
+      */
+    create(o: T): T;
+
     /**
       * Creates an object that has the specified prototype, and that optionally contains specified properties.
       * @param o Object to use as a prototype. May be null
       * @param properties JavaScript object that contains one or more property descriptors.
       */
-    create(o: any, properties?: PropertyDescriptorMap): any;
+    create(o: any, properties: PropertyDescriptorMap): any;
 
     /**
       * Adds a property to an object, or modifies attributes of an existing property.

From 2fc2f5c4b975f05e46cf0193b345607ea4983d76 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Mon, 6 Jun 2016 07:50:32 -0700
Subject: [PATCH 75/90] Fix signature help

---
 src/compiler/checker.ts                       | 17 +++++----
 ...gCommasInFunctionParametersAndArguments.js | 22 ++++++++++++
 ...asInFunctionParametersAndArguments.symbols | 26 ++++++++++++++
 ...mmasInFunctionParametersAndArguments.types | 35 +++++++++++++++++++
 ...gCommasInFunctionParametersAndArguments.ts | 13 +++++++
 5 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 402d6455387..524b9b2f505 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -10321,7 +10321,7 @@ namespace ts {
             return -1;
         }
 
-        function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) {
+        function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature, signatureHelpTrailingComma = false) {
             let argCount: number;            // Apparent number of arguments we will have in this call
             let typeArguments: NodeArray;  // Type arguments (undefined if none)
             let callIsIncomplete: boolean;           // In incomplete call we want to be lenient when we have too few arguments
@@ -10367,7 +10367,7 @@ namespace ts {
                     return signature.minArgumentCount === 0;
                 }
 
-                argCount = args.length;
+                argCount = signatureHelpTrailingComma ? args.length + 1 : args.length;
 
                 // If we are missing the close paren, the call is incomplete.
                 callIsIncomplete = (callExpression).arguments.end === callExpression.end;
@@ -10968,6 +10968,11 @@ namespace ts {
             let resultOfFailedInference: InferenceContext;
             let result: Signature;
 
+            // If we are in signature help, a trailing comma indicates that we intend to provide another argument,
+            // so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
+            const signatureHelpTrailingComma =
+                candidatesOutArray && node.kind === SyntaxKind.CallExpression && (node).arguments.hasTrailingComma;
+
             // Section 4.12.1:
             // if the candidate list contains one or more signatures for which the type of each argument
             // expression is a subtype of each corresponding parameter type, the return type of the first
@@ -10979,14 +10984,14 @@ namespace ts {
             // is just important for choosing the best signature. So in the case where there is only one
             // signature, the subtype pass is useless. So skipping it is an optimization.
             if (candidates.length > 1) {
-                result = chooseOverload(candidates, subtypeRelation);
+                result = chooseOverload(candidates, subtypeRelation, signatureHelpTrailingComma);
             }
             if (!result) {
                 // Reinitialize these pointers for round two
                 candidateForArgumentError = undefined;
                 candidateForTypeArgumentError = undefined;
                 resultOfFailedInference = undefined;
-                result = chooseOverload(candidates, assignableRelation);
+                result = chooseOverload(candidates, assignableRelation, signatureHelpTrailingComma);
             }
             if (result) {
                 return result;
@@ -11057,9 +11062,9 @@ namespace ts {
                 diagnostics.add(createDiagnosticForNodeFromMessageChain(node, errorInfo));
             }
 
-            function chooseOverload(candidates: Signature[], relation: Map) {
+            function chooseOverload(candidates: Signature[], relation: Map, signatureHelpTrailingComma = false) {
                 for (const originalCandidate of candidates) {
-                    if (!hasCorrectArity(node, args, originalCandidate)) {
+                    if (!hasCorrectArity(node, args, originalCandidate, signatureHelpTrailingComma)) {
                         continue;
                     }
 
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
index a9b173f9db2..87c925fb22b 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
@@ -6,6 +6,19 @@ f1(1,);
 function f2(...args,) {}
 
 f2(...[],);
+
+// Not confused by overloads
+declare function f3(x, ): number;
+declare function f3(x, y,): string;
+
+f3(1,);
+f3(1, 2,);
+
+// Works for constructors too
+class X {
+    constructor(a,) { }
+}
+new X(1,);
 
 
 //// [trailingCommasInFunctionParametersAndArguments.js]
@@ -18,3 +31,12 @@ function f2() {
     }
 }
 f2.apply(void 0, []);
+f3(1);
+f3(1, 2);
+// Works for constructors too
+var X = (function () {
+    function X(a) {
+    }
+    return X;
+}());
+new X(1);
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
index dd9ce5319c9..b146d230bac 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
@@ -13,3 +13,29 @@ function f2(...args,) {}
 f2(...[],);
 >f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 2, 7))
 
+// Not confused by overloads
+declare function f3(x, ): number;
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 20))
+
+declare function f3(x, y,): string;
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 20))
+>y : Symbol(y, Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 22))
+
+f3(1,);
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
+
+f3(1, 2,);
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
+
+// Works for constructors too
+class X {
+>X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 13, 18))
+
+    constructor(a,) { }
+>a : Symbol(a, Decl(trailingCommasInFunctionParametersAndArguments.ts, 17, 16))
+}
+new X(1,);
+>X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 13, 18))
+
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
index 0a8d34d1df2..36de78fc8b8 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
@@ -18,3 +18,38 @@ f2(...[],);
 >...[] : undefined
 >[] : undefined[]
 
+// Not confused by overloads
+declare function f3(x, ): number;
+>f3 : { (x: any): number; (x: any, y: any): string; }
+>x : any
+
+declare function f3(x, y,): string;
+>f3 : { (x: any): number; (x: any, y: any): string; }
+>x : any
+>y : any
+
+f3(1,);
+>f3(1,) : number
+>f3(1,) : number
+>f3 : { (x: any): number; (x: any, y: any): string; }
+>1 : number
+
+f3(1, 2,);
+>f3(1, 2,) : string
+>f3(1, 2,) : string
+>f3 : { (x: any): number; (x: any, y: any): string; }
+>1 : number
+>2 : number
+
+// Works for constructors too
+class X {
+>X : X
+
+    constructor(a,) { }
+>a : any
+}
+new X(1,);
+>new X(1,) : X
+>X : typeof X
+>1 : number
+
diff --git a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
index 4b628e7e559..9c21d7f24ea 100644
--- a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
+++ b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
@@ -5,3 +5,16 @@ f1(1,);
 function f2(...args,) {}
 
 f2(...[],);
+
+// Not confused by overloads
+declare function f3(x, ): number;
+declare function f3(x, y,): string;
+
+f3(1,);
+f3(1, 2,);
+
+// Works for constructors too
+class X {
+    constructor(a,) { }
+}
+new X(1,);

From 543b484ec52371faae2c0ecb8aeba28de5e3695f Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Fri, 3 Jun 2016 09:33:17 -0700
Subject: [PATCH 76/90] Remove trailing whitespace

---
 src/compiler/checker.ts                      | 18 +++++++++---------
 src/compiler/declarationEmitter.ts           |  6 +++---
 src/compiler/sourcemap.ts                    |  2 +-
 src/compiler/utilities.ts                    |  4 ++--
 src/harness/projectsRunner.ts                |  2 +-
 src/harness/runnerbase.ts                    |  2 +-
 src/services/breakpoints.ts                  | 18 +++++++++---------
 src/services/formatting/formattingScanner.ts |  4 ++--
 src/services/formatting/rules.ts             |  6 +++---
 src/services/formatting/rulesMap.ts          |  4 ++--
 src/services/navigateTo.ts                   |  6 +++---
 src/services/services.ts                     |  6 +++---
 src/services/shims.ts                        |  4 ++--
 src/services/signatureHelp.ts                | 16 ++++++++--------
 14 files changed, 49 insertions(+), 49 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index c538134e6d8..01945252e55 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -11269,7 +11269,7 @@ namespace ts {
             const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(declaration.parent.symbol);
             const declaringClass = getDeclaredTypeOfSymbol(declaration.parent.symbol);
 
-            // A private or protected constructor can only be instantiated within it's own class 
+            // A private or protected constructor can only be instantiated within it's own class
             if (!isNodeWithinClass(node, declaringClassDeclaration)) {
                 if (flags & NodeFlags.Private) {
                     error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass));
@@ -16132,12 +16132,12 @@ namespace ts {
             const symbol = getSymbolOfNode(node);
             const target = resolveAlias(symbol);
             if (target !== unknownSymbol) {
-                // For external modules symbol represent local symbol for an alias. 
+                // For external modules symbol represent local symbol for an alias.
                 // This local symbol will merge any other local declarations (excluding other aliases)
                 // and symbol.flags will contains combined representation for all merged declaration.
                 // Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
-                // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export* 
-                // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names). 
+                // otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
+                // in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
                 const excludedMeanings =
                     (symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
                     (symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
@@ -16336,7 +16336,7 @@ namespace ts {
                         continue;
                     }
                     const { declarations, flags } = exports[id];
-                    // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries. 
+                    // ECMA262: 15.2.1.1 It is a Syntax Error if the ExportedNames of ModuleItemList contains any duplicate entries.
                     // (TS Exceptions: namespaces, function overloads, enums, and interfaces)
                     if (flags & (SymbolFlags.Namespace | SymbolFlags.Interface | SymbolFlags.Enum)) {
                         continue;
@@ -17041,10 +17041,10 @@ namespace ts {
         }
 
         // Gets the type of object literal or array literal of destructuring assignment.
-        // { a } from 
+        // { a } from
         //     for ( { a } of elems) {
         //     }
-        // [ a ] from 
+        // [ a ] from
         //     [a] = [ some array ...]
         function getTypeOfArrayLiteralOrObjectLiteralDestructuringAssignment(expr: Expression): Type {
             Debug.assert(expr.kind === SyntaxKind.ObjectLiteralExpression || expr.kind === SyntaxKind.ArrayLiteralExpression);
@@ -17077,10 +17077,10 @@ namespace ts {
         }
 
         // Gets the property symbol corresponding to the property in destructuring assignment
-        // 'property1' from 
+        // 'property1' from
         //     for ( { property1: a } of elems) {
         //     }
-        // 'property1' at location 'a' from: 
+        // 'property1' at location 'a' from:
         //     [a] = [ property1, property2 ]
         function getPropertySymbolOfDestructuringAssignment(location: Identifier) {
             // Get the type of the object or array literal and then look for property of given name in the type
diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts
index 397b135bf23..bda1c7c0cb6 100644
--- a/src/compiler/declarationEmitter.ts
+++ b/src/compiler/declarationEmitter.ts
@@ -95,7 +95,7 @@ namespace ts {
                     // Emit reference in dts, if the file reference was not already emitted
                     if (referencedFile && !contains(emittedReferencedFiles, referencedFile)) {
                         // Add a reference to generated dts file,
-                        // global file reference is added only 
+                        // global file reference is added only
                         //  - if it is not bundled emit (because otherwise it would be self reference)
                         //  - and it is not already added
                         if (writeReferencePath(referencedFile, !isBundledEmit && !addedGlobalFileReference)) {
@@ -148,7 +148,7 @@ namespace ts {
 
             if (!isBundledEmit && isExternalModule(sourceFile) && sourceFile.moduleAugmentations.length && !resultHasExternalModuleIndicator) {
                 // if file was external module with augmentations - this fact should be preserved in .d.ts as well.
-                // in case if we didn't write any external module specifiers in .d.ts we need to emit something 
+                // in case if we didn't write any external module specifiers in .d.ts we need to emit something
                 // that will force compiler to think that this file is an external module - 'export {}' is a reasonable choice here.
                 write("export {};");
                 writeLine();
@@ -766,7 +766,7 @@ namespace ts {
 
         function emitExternalModuleSpecifier(parent: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration) {
             // emitExternalModuleSpecifier is usually called when we emit something in the.d.ts file that will make it an external module (i.e. import/export declarations).
-            // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered 
+            // the only case when it is not true is when we call it to emit correct name for module augmentation - d.ts files with just module augmentations are not considered
             // external modules since they are indistinguishable from script files with ambient modules. To fix this in such d.ts files we'll emit top level 'export {}'
             // so compiler will treat them as external modules.
             resultHasExternalModuleIndicator = resultHasExternalModuleIndicator || parent.kind !== SyntaxKind.ModuleDeclaration;
diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts
index 86bad38cb6e..cf7c3d1eaae 100644
--- a/src/compiler/sourcemap.ts
+++ b/src/compiler/sourcemap.ts
@@ -168,7 +168,7 @@ namespace ts {
                     sourceMapData.sourceMapDecodedMappings[sourceMapData.sourceMapDecodedMappings.length - 1] :
                     defaultLastEncodedSourceMapSpan;
 
-                // TODO: Update lastEncodedNameIndex 
+                // TODO: Update lastEncodedNameIndex
                 // Since we dont support this any more, lets not worry about it right now.
                 // When we start supporting nameIndex, we will get back to this
 
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 74ea3459b23..d246b55556c 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -302,8 +302,8 @@ namespace ts {
             return getTokenPosOfNode(node.jsDocComments[0]);
         }
 
-        // For a syntax list, it is possible that one of its children has JSDocComment nodes, while 
-        // the syntax list itself considers them as normal trivia. Therefore if we simply skip 
+        // For a syntax list, it is possible that one of its children has JSDocComment nodes, while
+        // the syntax list itself considers them as normal trivia. Therefore if we simply skip
         // trivia for the list, we may have skipped the JSDocComment as well. So we should process its
         // first child to determine the actual position of its first token.
         if (node.kind === SyntaxKind.SyntaxList && (node)._children.length > 0) {
diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts
index 60a0813b05d..64d07b1d88a 100644
--- a/src/harness/projectsRunner.ts
+++ b/src/harness/projectsRunner.ts
@@ -244,7 +244,7 @@ class ProjectRunner extends RunnerBase {
                     mapRoot: testCase.resolveMapRoot && testCase.mapRoot ? Harness.IO.resolvePath(testCase.mapRoot) : testCase.mapRoot,
                     sourceRoot: testCase.resolveSourceRoot && testCase.sourceRoot ? Harness.IO.resolvePath(testCase.sourceRoot) : testCase.sourceRoot,
                     module: moduleKind,
-                    moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future 
+                    moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future
                 };
                 // Set the values specified using json
                 const optionNameMap: ts.Map = {};
diff --git a/src/harness/runnerbase.ts b/src/harness/runnerbase.ts
index 81da0907714..a16eddcbeec 100644
--- a/src/harness/runnerbase.ts
+++ b/src/harness/runnerbase.ts
@@ -24,7 +24,7 @@ abstract class RunnerBase {
 
     abstract enumerateTestFiles(): string[];
 
-    /** Setup the runner's tests so that they are ready to be executed by the harness 
+    /** Setup the runner's tests so that they are ready to be executed by the harness
      *  The first test should be a describe/it block that sets up the harness's compiler instance appropriately
      */
     public abstract initializeTests(): void;
diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts
index 12b127ffd2d..fea4ae6bffa 100644
--- a/src/services/breakpoints.ts
+++ b/src/services/breakpoints.ts
@@ -1,4 +1,4 @@
-// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. 
+// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0.
 // See LICENSE.txt in the project root for complete license information.
 
 /// 
@@ -19,8 +19,8 @@ namespace ts.BreakpointResolver {
         if (sourceFile.getLineAndCharacterOfPosition(tokenAtLocation.getStart(sourceFile)).line > lineOfPosition) {
             // Get previous token if the token is returned starts on new line
             // eg: let x =10; |--- cursor is here
-            //     let y = 10; 
-            // token at position will return let keyword on second line as the token but we would like to use 
+            //     let y = 10;
+            // token at position will return let keyword on second line as the token but we would like to use
             // token on same line if trailing trivia (comments or white spaces on same line) part of the last token on that line
             tokenAtLocation = findPrecedingToken(tokenAtLocation.pos, sourceFile);
 
@@ -261,7 +261,7 @@ namespace ts.BreakpointResolver {
                         }
 
                         // Set breakpoint on identifier element of destructuring pattern
-                        // a or ...c  or d: x from 
+                        // a or ...c  or d: x from
                         // [a, b, ...c] or { a, b } or { d: x } from destructuring pattern
                         if ((node.kind === SyntaxKind.Identifier ||
                             node.kind == SyntaxKind.SpreadElementExpression ||
@@ -275,7 +275,7 @@ namespace ts.BreakpointResolver {
                             const binaryExpression = node;
                             // Set breakpoint in destructuring pattern if its destructuring assignment
                             // [a, b, c] or {a, b, c} of
-                            // [a, b, c] = expression or 
+                            // [a, b, c] = expression or
                             // {a, b, c} = expression
                             if (isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.left)) {
                                 return spanInArrayLiteralOrObjectLiteralDestructuringPattern(
@@ -285,8 +285,8 @@ namespace ts.BreakpointResolver {
                             if (binaryExpression.operatorToken.kind === SyntaxKind.EqualsToken &&
                                 isArrayLiteralOrObjectLiteralDestructuringPattern(binaryExpression.parent)) {
                                 // Set breakpoint on assignment expression element of destructuring pattern
-                                // a = expression of 
-                                // [a = expression, b, c] = someExpression or 
+                                // a = expression of
+                                // [a = expression, b, c] = someExpression or
                                 // { a = expression, b, c } = someExpression
                                 return textSpan(node);
                             }
@@ -403,7 +403,7 @@ namespace ts.BreakpointResolver {
                 const declarations = variableDeclaration.parent.declarations;
                 if (declarations && declarations[0] !== variableDeclaration) {
                     // If we cannot set breakpoint on this declaration, set it on previous one
-                    // Because the variable declaration may be binding pattern and 
+                    // Because the variable declaration may be binding pattern and
                     // we would like to set breakpoint in last binding element if that's the case,
                     // use preceding token instead
                     return spanInNode(findPrecedingToken(variableDeclaration.pos, sourceFile, variableDeclaration.parent));
@@ -549,7 +549,7 @@ namespace ts.BreakpointResolver {
                     return spanInNode(firstBindingElement);
                 }
 
-                // Could be ArrayLiteral from destructuring assignment or 
+                // Could be ArrayLiteral from destructuring assignment or
                 // just nested element in another destructuring assignment
                 // set breakpoint on assignment when parent is destructuring assignment
                 // Otherwise set breakpoint for this element
diff --git a/src/services/formatting/formattingScanner.ts b/src/services/formatting/formattingScanner.ts
index ff003e08007..babf0db3fd5 100644
--- a/src/services/formatting/formattingScanner.ts
+++ b/src/services/formatting/formattingScanner.ts
@@ -268,9 +268,9 @@ namespace ts.formatting {
             return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);
         }
 
-        // when containing node in the tree is token 
+        // when containing node in the tree is token
         // but its kind differs from the kind that was returned by the scanner,
-        // then kind needs to be fixed. This might happen in cases 
+        // then kind needs to be fixed. This might happen in cases
         // when parser interprets token differently, i.e keyword treated as identifier
         function fixTokenKind(tokenInfo: TokenInfo, container: Node): TokenInfo {
             if (isToken(container) && tokenInfo.token.kind !== container.kind) {
diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts
index 47a204a5b7e..b4916025af2 100644
--- a/src/services/formatting/rules.ts
+++ b/src/services/formatting/rules.ts
@@ -554,17 +554,17 @@ namespace ts.formatting {
         static IsSameLineTokenOrBeforeMultilineBlockContext(context: FormattingContext): boolean {
             //// This check is mainly used inside SpaceBeforeOpenBraceInControl and SpaceBeforeOpenBraceInFunction.
             ////
-            //// Ex: 
+            //// Ex:
             //// if (1)     { ....
             ////      * ) and { are on the same line so apply the rule. Here we don't care whether it's same or multi block context
             ////
-            //// Ex: 
+            //// Ex:
             //// if (1)
             //// { ... }
             ////      * ) and { are on different lines. We only need to format if the block is multiline context. So in this case we don't format.
             ////
             //// Ex:
-            //// if (1) 
+            //// if (1)
             //// { ...
             //// }
             ////      * ) and { are on different lines. We only need to format if the block is multiline context. So in this case we format.
diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts
index 7f635ea07e3..703ca566bd2 100644
--- a/src/services/formatting/rulesMap.ts
+++ b/src/services/formatting/rulesMap.ts
@@ -95,9 +95,9 @@ namespace ts.formatting {
             ////    4- Context rules with any token combination
             ////    5- Non-context rules with specific token combination
             ////    6- Non-context rules with any token combination
-            //// 
+            ////
             //// The member rulesInsertionIndexBitmap is used to describe the number of rules
-            //// in each sub-bucket (above) hence can be used to know the index of where to insert 
+            //// in each sub-bucket (above) hence can be used to know the index of where to insert
             //// the next rule. It's a bitmap which contains 6 different sections each is given 5 bits.
             ////
             //// Example:
diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts
index 49707263c1f..e9afb6925b5 100644
--- a/src/services/navigateTo.ts
+++ b/src/services/navigateTo.ts
@@ -9,7 +9,7 @@ namespace ts.NavigateTo {
         // This means "compare in a case insensitive manner."
         const baseSensitivity: Intl.CollatorOptions = { sensitivity: "base" };
 
-        // Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[] 
+        // Search the declarations in all files and output matched NavigateToItem into array of NavigateToItem[]
         forEach(program.getSourceFiles(), sourceFile => {
             cancellationToken.throwIfCancellationRequested();
 
@@ -17,7 +17,7 @@ namespace ts.NavigateTo {
             for (const name in nameToDeclarations) {
                 const declarations = getProperty(nameToDeclarations, name);
                 if (declarations) {
-                    // First do a quick check to see if the name of the declaration matches the 
+                    // First do a quick check to see if the name of the declaration matches the
                     // last portion of the (possibly) dotted name they're searching for.
                     let matches = patternMatcher.getMatchesForLastSegmentOfPattern(name);
 
@@ -26,7 +26,7 @@ namespace ts.NavigateTo {
                     }
 
                     for (const declaration of declarations) {
-                        // It was a match!  If the pattern has dots in it, then also see if the 
+                        // It was a match!  If the pattern has dots in it, then also see if the
                         // declaration container matches as well.
                         if (patternMatcher.patternContainsDots) {
                             const containers = getContainers(declaration);
diff --git a/src/services/services.ts b/src/services/services.ts
index 90740b257c6..34b521b3496 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -2657,7 +2657,7 @@ namespace ts {
         return false;
     }
 
-    /** 
+    /**
      * Returns the containing object literal property declaration given a possible name node, e.g. "a" in x = { "a": 1 }
      */
     function getContainingObjectLiteralElement(node: Node): ObjectLiteralElement {
@@ -7604,11 +7604,11 @@ namespace ts {
 
         function isValidBraceCompletionAtPostion(fileName: string, position: number, openingBrace: number): boolean {
 
-            // '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too 
+            // '<' is currently not supported, figuring out if we're in a Generic Type vs. a comparison is too
             // expensive to do during typing scenarios
             // i.e. whether we're dealing with:
             //      var x = new foo<| ( with class foo{} )
-            // or 
+            // or
             //      var y = 3 <|
             if (openingBrace === CharacterCodes.lessThan) {
                 return false;
diff --git a/src/services/shims.ts b/src/services/shims.ts
index eb9c28ff7ac..94ff3367e3c 100644
--- a/src/services/shims.ts
+++ b/src/services/shims.ts
@@ -295,7 +295,7 @@ namespace ts {
 
         constructor(private shimHost: LanguageServiceShimHost) {
             // if shimHost is a COM object then property check will become method call with no arguments.
-            // 'in' does not have this effect. 
+            // 'in' does not have this effect.
             if ("getModuleResolutionsForFile" in this.shimHost) {
                 this.resolveModuleNames = (moduleNames: string[], containingFile: string) => {
                     const resolutionsInFile = >JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile));
@@ -966,7 +966,7 @@ namespace ts {
             return this.forwardJSONCall(
                 "getPreProcessedFileInfo('" + fileName + "')",
                 () => {
-                    // for now treat files as JavaScript 
+                    // for now treat files as JavaScript
                     const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true);
                     return {
                         referencedFiles: this.convertFileReferences(result.referencedFiles),
diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts
index 53fb7a474a7..27077406489 100644
--- a/src/services/signatureHelp.ts
+++ b/src/services/signatureHelp.ts
@@ -3,9 +3,9 @@
 namespace ts.SignatureHelp {
 
     // A partially written generic type expression is not guaranteed to have the correct syntax tree. the expression could be parsed as less than/greater than expression or a comma expression
-    // or some other combination depending on what the user has typed so far. For the purposes of signature help we need to consider any location after "<" as a possible generic type reference. 
-    // To do this, the method will back parse the expression starting at the position required. it will try to parse the current expression as a generic type expression, if it did succeed it 
-    // will return the generic identifier that started the expression (e.g. "foo" in "foo'.  So, in the case where the last child is a comma, we increase the
             // arg count by one to compensate.
             //
-            // Note: this subtlety only applies to the last comma.  If you had "Foo(a,,"  then 
-            // we'll have:  'a' '' '' 
+            // Note: this subtlety only applies to the last comma.  If you had "Foo(a,,"  then
+            // we'll have:  'a' '' ''
             // That will give us 2 non-commas.  We then add one for the last comma, givin us an
             // arg count of 3.
             const listChildren = argumentsList.getChildren();

From e05e11200bb581852c7a0de5b89995200c48fc01 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Mon, 6 Jun 2016 11:37:59 -0700
Subject: [PATCH 77/90] Remove trailing whitespace in jakefile

---
 Jakefile.js | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Jakefile.js b/Jakefile.js
index c4f56c559f3..5da617e2348 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -208,7 +208,7 @@ var librarySourceMap = [
         { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] },
         { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] },
         { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] },
-        
+
         // JavaScript + all host library
         { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) },
         { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }
@@ -521,7 +521,7 @@ compileFile(servicesFileInBrowserTest, servicesSources,[builtLocalDirectory, cop
                 var i = content.lastIndexOf("\n");
                 fs.writeFileSync(servicesFileInBrowserTest, content.substring(0, i) + "\r\n//# sourceURL=../built/local/typeScriptServices.js" + content.substring(i));
             });
-    
+
 
 var serverFile = path.join(builtLocalDirectory, "tsserver.js");
 compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true);
@@ -741,10 +741,10 @@ function runConsoleTests(defaultReporter, runInParallel) {
         }, function(e, status) {
             finish(status);
         });
-        
+
     }
     else {
-        // run task to load all tests and partition them between workers 
+        // run task to load all tests and partition them between workers
         var cmd = "mocha " + " -R min " + colors + run;
         console.log(cmd);
         exec(cmd, function() {
@@ -757,9 +757,9 @@ function runConsoleTests(defaultReporter, runInParallel) {
                 var configPath = path.join(taskConfigsFolder, f);
                 var workerCmd = "mocha" + " -t " + testTimeout + " -R " + reporter + " " + colors + " " + run + " --config='" + configPath + "'";
                 console.log(workerCmd);
-                exec(workerCmd,  finishWorker, finishWorker) 
+                exec(workerCmd,  finishWorker, finishWorker)
             });
-            
+
             function finishWorker(e, errorStatus) {
                 counter--;
                 if (firstErrorStatus === undefined && errorStatus !== undefined) {
@@ -783,11 +783,11 @@ function runConsoleTests(defaultReporter, runInParallel) {
             }
         });
     }
-    
+
     function failWithStatus(status) {
         fail("Process exited with code " + status);
     }
-    
+
     function finish(errorStatus) {
         deleteTemporaryProjectOutput();
         if (errorStatus !== undefined) {

From 52138e0078cb92e3026a64dab1aa40922e66ad83 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Mon, 6 Jun 2016 11:48:08 -0700
Subject: [PATCH 78/90] Make `jake runtests-browser` support test regexes with
 spaces

For example: `jake runtests-browser t="transpile .js files"` now works.
---
 Jakefile.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Jakefile.js b/Jakefile.js
index c4f56c559f3..e03e5e6f5b0 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -855,7 +855,7 @@ task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFi
     }
 
     tests = tests ? tests : '';
-    var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + tests;
+    var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + JSON.stringify(tests);
     console.log(cmd);
     exec(cmd);
 }, {async: true});

From 9761c3be9710a2ce16430f237183dce9ae160dbc Mon Sep 17 00:00:00 2001
From: Wesley Wigham 
Date: Mon, 6 Jun 2016 12:52:34 -0700
Subject: [PATCH 79/90] Revert "Revert "Merge pull request #7235 from
 weswigham/narrow-all-types""

This reverts commit fc3e040c5167868ed623612e8f33fb3beedf73b1.
---
 src/compiler/checker.ts                       |  2 +-
 src/compiler/types.ts                         |  8 +-
 .../typeGuardNarrowsPrimitiveIntersection.js  | 38 ++++++++++
 ...eGuardNarrowsPrimitiveIntersection.symbols | 70 +++++++++++++++++
 ...ypeGuardNarrowsPrimitiveIntersection.types | 76 +++++++++++++++++++
 .../typeGuardNarrowsToLiteralType.js          | 21 +++++
 .../typeGuardNarrowsToLiteralType.symbols     | 32 ++++++++
 .../typeGuardNarrowsToLiteralType.types       | 35 +++++++++
 .../typeGuardNarrowsToLiteralTypeUnion.js     | 21 +++++
 ...typeGuardNarrowsToLiteralTypeUnion.symbols | 32 ++++++++
 .../typeGuardNarrowsToLiteralTypeUnion.types  | 35 +++++++++
 .../typeGuardNarrowsPrimitiveIntersection.ts  | 21 +++++
 .../typeGuardNarrowsToLiteralType.ts          | 10 +++
 .../typeGuardNarrowsToLiteralTypeUnion.ts     | 10 +++
 14 files changed, 409 insertions(+), 2 deletions(-)
 create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
 create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
 create mode 100644 tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
 create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.js
 create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
 create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralType.types
 create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
 create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
 create mode 100644 tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
 create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
 create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
 create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 01945252e55..ae00387fec5 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -7664,7 +7664,7 @@ namespace ts {
 
         function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
             let key: string;
-            if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
+            if (!reference.flowNode || assumeInitialized && (declaredType.flags & TypeFlags.NotNarrowable)) {
                 return declaredType;
             }
             const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 4c0e7a036db..f82d7673291 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2218,7 +2218,13 @@ namespace ts {
         ObjectType = Class | Interface | Reference | Tuple | Anonymous,
         UnionOrIntersection = Union | Intersection,
         StructuredType = ObjectType | Union | Intersection,
-        Narrowable = Any | ObjectType | Union | TypeParameter,
+
+        // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow.
+        // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep
+        // Void as the only non-narrowable type, since it's a non-value type construct (representing a lack of a value)
+        // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing
+        // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules)
+        NotNarrowable = Void,
         /* @internal */
         RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
         /* @internal */
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
new file mode 100644
index 00000000000..a4cba6f4ab5
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.js
@@ -0,0 +1,38 @@
+//// [typeGuardNarrowsPrimitiveIntersection.ts]
+type Tag = {__tag: any};
+declare function isNonBlank(value: string) : value is (string & Tag);
+declare function doThis(value: string & Tag): void;
+declare function doThat(value: string) : void;
+let value: string;
+if (isNonBlank(value)) {
+    doThis(value);
+} else {
+    doThat(value);
+}
+
+
+const enum Tag2 {}
+declare function isNonBlank2(value: string) : value is (string & Tag2);
+declare function doThis2(value: string & Tag2): void;
+declare function doThat2(value: string) : void;
+if (isNonBlank2(value)) {
+    doThis2(value);
+} else {
+    doThat2(value);
+}
+
+
+//// [typeGuardNarrowsPrimitiveIntersection.js]
+var value;
+if (isNonBlank(value)) {
+    doThis(value);
+}
+else {
+    doThat(value);
+}
+if (isNonBlank2(value)) {
+    doThis2(value);
+}
+else {
+    doThat2(value);
+}
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
new file mode 100644
index 00000000000..da507fb94c5
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.symbols
@@ -0,0 +1,70 @@
+=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts ===
+type Tag = {__tag: any};
+>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
+>__tag : Symbol(__tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 12))
+
+declare function isNonBlank(value: string) : value is (string & Tag);
+>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 28))
+>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
+
+declare function doThis(value: string & Tag): void;
+>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 24))
+>Tag : Symbol(Tag, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 0))
+
+declare function doThat(value: string) : void;
+>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 3, 24))
+
+let value: string;
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+
+if (isNonBlank(value)) {
+>isNonBlank : Symbol(isNonBlank, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 0, 24))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+
+    doThis(value);
+>doThis : Symbol(doThis, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 1, 69))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+
+} else {
+    doThat(value);
+>doThat : Symbol(doThat, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 2, 51))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+}
+
+
+const enum Tag2 {}
+>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
+
+declare function isNonBlank2(value: string) : value is (string & Tag2);
+>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 29))
+>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
+
+declare function doThis2(value: string & Tag2): void;
+>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 25))
+>Tag2 : Symbol(Tag2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 9, 1))
+
+declare function doThat2(value: string) : void;
+>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 15, 25))
+
+if (isNonBlank2(value)) {
+>isNonBlank2 : Symbol(isNonBlank2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 12, 18))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+
+    doThis2(value);
+>doThis2 : Symbol(doThis2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 13, 71))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+
+} else {
+    doThat2(value);
+>doThat2 : Symbol(doThat2, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 14, 53))
+>value : Symbol(value, Decl(typeGuardNarrowsPrimitiveIntersection.ts, 4, 3))
+}
+
diff --git a/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
new file mode 100644
index 00000000000..478363669b4
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsPrimitiveIntersection.types
@@ -0,0 +1,76 @@
+=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts ===
+type Tag = {__tag: any};
+>Tag : { __tag: any; }
+>__tag : any
+
+declare function isNonBlank(value: string) : value is (string & Tag);
+>isNonBlank : (value: string) => value is string & { __tag: any; }
+>value : string
+>value : any
+>Tag : { __tag: any; }
+
+declare function doThis(value: string & Tag): void;
+>doThis : (value: string & { __tag: any; }) => void
+>value : string & { __tag: any; }
+>Tag : { __tag: any; }
+
+declare function doThat(value: string) : void;
+>doThat : (value: string) => void
+>value : string
+
+let value: string;
+>value : string
+
+if (isNonBlank(value)) {
+>isNonBlank(value) : boolean
+>isNonBlank : (value: string) => value is string & { __tag: any; }
+>value : string
+
+    doThis(value);
+>doThis(value) : void
+>doThis : (value: string & { __tag: any; }) => void
+>value : string & { __tag: any; }
+
+} else {
+    doThat(value);
+>doThat(value) : void
+>doThat : (value: string) => void
+>value : string
+}
+
+
+const enum Tag2 {}
+>Tag2 : Tag2
+
+declare function isNonBlank2(value: string) : value is (string & Tag2);
+>isNonBlank2 : (value: string) => value is string & Tag2
+>value : string
+>value : any
+>Tag2 : Tag2
+
+declare function doThis2(value: string & Tag2): void;
+>doThis2 : (value: string & Tag2) => void
+>value : string & Tag2
+>Tag2 : Tag2
+
+declare function doThat2(value: string) : void;
+>doThat2 : (value: string) => void
+>value : string
+
+if (isNonBlank2(value)) {
+>isNonBlank2(value) : boolean
+>isNonBlank2 : (value: string) => value is string & Tag2
+>value : string
+
+    doThis2(value);
+>doThis2(value) : void
+>doThis2 : (value: string & Tag2) => void
+>value : string & Tag2
+
+} else {
+    doThat2(value);
+>doThat2(value) : void
+>doThat2 : (value: string) => void
+>value : string
+}
+
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.js b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js
new file mode 100644
index 00000000000..fdd2ee3fb7f
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.js
@@ -0,0 +1,21 @@
+//// [typeGuardNarrowsToLiteralType.ts]
+declare function isFoo(value: string) : value is "foo";
+declare function doThis(value: "foo"): void;
+declare function doThat(value: string) : void;
+let value: string;
+if (isFoo(value)) {
+    doThis(value);
+} else {
+    doThat(value);
+}
+
+
+
+//// [typeGuardNarrowsToLiteralType.js]
+var value;
+if (isFoo(value)) {
+    doThis(value);
+}
+else {
+    doThat(value);
+}
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
new file mode 100644
index 00000000000..0d0ddc5f00d
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.symbols
@@ -0,0 +1,32 @@
+=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts ===
+declare function isFoo(value: string) : value is "foo";
+>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 0, 23))
+
+declare function doThis(value: "foo"): void;
+>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 1, 24))
+
+declare function doThat(value: string) : void;
+>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 2, 24))
+
+let value: string;
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
+
+if (isFoo(value)) {
+>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralType.ts, 0, 0))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
+
+    doThis(value);
+>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralType.ts, 0, 55))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
+
+} else {
+    doThat(value);
+>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralType.ts, 1, 44))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralType.ts, 3, 3))
+}
+
+
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralType.types b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types
new file mode 100644
index 00000000000..9835206deb9
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsToLiteralType.types
@@ -0,0 +1,35 @@
+=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts ===
+declare function isFoo(value: string) : value is "foo";
+>isFoo : (value: string) => value is "foo"
+>value : string
+>value : any
+
+declare function doThis(value: "foo"): void;
+>doThis : (value: "foo") => void
+>value : "foo"
+
+declare function doThat(value: string) : void;
+>doThat : (value: string) => void
+>value : string
+
+let value: string;
+>value : string
+
+if (isFoo(value)) {
+>isFoo(value) : boolean
+>isFoo : (value: string) => value is "foo"
+>value : string
+
+    doThis(value);
+>doThis(value) : void
+>doThis : (value: "foo") => void
+>value : "foo"
+
+} else {
+    doThat(value);
+>doThat(value) : void
+>doThat : (value: string) => void
+>value : string
+}
+
+
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
new file mode 100644
index 00000000000..715b362c8e0
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.js
@@ -0,0 +1,21 @@
+//// [typeGuardNarrowsToLiteralTypeUnion.ts]
+declare function isFoo(value: string) : value is ("foo" | "bar");
+declare function doThis(value: "foo" | "bar"): void;
+declare function doThat(value: string) : void;
+let value: string;
+if (isFoo(value)) {
+    doThis(value);
+} else {
+    doThat(value);
+}
+
+
+
+//// [typeGuardNarrowsToLiteralTypeUnion.js]
+var value;
+if (isFoo(value)) {
+    doThis(value);
+}
+else {
+    doThat(value);
+}
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
new file mode 100644
index 00000000000..356fa06a06c
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.symbols
@@ -0,0 +1,32 @@
+=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts ===
+declare function isFoo(value: string) : value is ("foo" | "bar");
+>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 23))
+
+declare function doThis(value: "foo" | "bar"): void;
+>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 24))
+
+declare function doThat(value: string) : void;
+>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 2, 24))
+
+let value: string;
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
+
+if (isFoo(value)) {
+>isFoo : Symbol(isFoo, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 0))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
+
+    doThis(value);
+>doThis : Symbol(doThis, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 0, 65))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
+
+} else {
+    doThat(value);
+>doThat : Symbol(doThat, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 1, 52))
+>value : Symbol(value, Decl(typeGuardNarrowsToLiteralTypeUnion.ts, 3, 3))
+}
+
+
diff --git a/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
new file mode 100644
index 00000000000..321a8861d55
--- /dev/null
+++ b/tests/baselines/reference/typeGuardNarrowsToLiteralTypeUnion.types
@@ -0,0 +1,35 @@
+=== tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts ===
+declare function isFoo(value: string) : value is ("foo" | "bar");
+>isFoo : (value: string) => value is "foo" | "bar"
+>value : string
+>value : any
+
+declare function doThis(value: "foo" | "bar"): void;
+>doThis : (value: "foo" | "bar") => void
+>value : "foo" | "bar"
+
+declare function doThat(value: string) : void;
+>doThat : (value: string) => void
+>value : string
+
+let value: string;
+>value : string
+
+if (isFoo(value)) {
+>isFoo(value) : boolean
+>isFoo : (value: string) => value is "foo" | "bar"
+>value : string
+
+    doThis(value);
+>doThis(value) : void
+>doThis : (value: "foo" | "bar") => void
+>value : "foo" | "bar"
+
+} else {
+    doThat(value);
+>doThat(value) : void
+>doThat : (value: string) => void
+>value : string
+}
+
+
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
new file mode 100644
index 00000000000..376a78275c8
--- /dev/null
+++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsPrimitiveIntersection.ts
@@ -0,0 +1,21 @@
+type Tag = {__tag: any};
+declare function isNonBlank(value: string) : value is (string & Tag);
+declare function doThis(value: string & Tag): void;
+declare function doThat(value: string) : void;
+let value: string;
+if (isNonBlank(value)) {
+    doThis(value);
+} else {
+    doThat(value);
+}
+
+
+const enum Tag2 {}
+declare function isNonBlank2(value: string) : value is (string & Tag2);
+declare function doThis2(value: string & Tag2): void;
+declare function doThat2(value: string) : void;
+if (isNonBlank2(value)) {
+    doThis2(value);
+} else {
+    doThat2(value);
+}
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
new file mode 100644
index 00000000000..3b7d5bba21a
--- /dev/null
+++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralType.ts
@@ -0,0 +1,10 @@
+declare function isFoo(value: string) : value is "foo";
+declare function doThis(value: "foo"): void;
+declare function doThat(value: string) : void;
+let value: string;
+if (isFoo(value)) {
+    doThis(value);
+} else {
+    doThat(value);
+}
+
diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts
new file mode 100644
index 00000000000..8f4726160f4
--- /dev/null
+++ b/tests/cases/conformance/expressions/typeGuards/typeGuardNarrowsToLiteralTypeUnion.ts
@@ -0,0 +1,10 @@
+declare function isFoo(value: string) : value is ("foo" | "bar");
+declare function doThis(value: "foo" | "bar"): void;
+declare function doThat(value: string) : void;
+let value: string;
+if (isFoo(value)) {
+    doThis(value);
+} else {
+    doThat(value);
+}
+

From 26b1ab499c21713fd360a42e52bf9e9f171df4cc Mon Sep 17 00:00:00 2001
From: Wesley Wigham 
Date: Mon, 6 Jun 2016 13:05:29 -0700
Subject: [PATCH 80/90] Use inclusive flag, as originally done, but include
 almost everything

---
 src/compiler/checker.ts | 2 +-
 src/compiler/types.ts   | 9 +++------
 2 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ae00387fec5..01945252e55 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -7664,7 +7664,7 @@ namespace ts {
 
         function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
             let key: string;
-            if (!reference.flowNode || assumeInitialized && (declaredType.flags & TypeFlags.NotNarrowable)) {
+            if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
                 return declaredType;
             }
             const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index f82d7673291..a86b1ed708d 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2219,12 +2219,9 @@ namespace ts {
         UnionOrIntersection = Union | Intersection,
         StructuredType = ObjectType | Union | Intersection,
 
-        // 'NotNarrowable' types are types where narrowing reverts to the original type, rather than actually narrow.
-        // This is never really correct - you can _always_ narrow to an intersection with that type, _but_ we keep
-        // Void as the only non-narrowable type, since it's a non-value type construct (representing a lack of a value)
-        // and, generally speaking, narrowing `void` should fail in some way, as it is nonsensical. (`void` narrowing
-        // to `void & T`, in a structural sense, is just narrowing to T, which we wouldn't allow under normal rules)
-        NotNarrowable = Void,
+        // 'Narrowable' types are types where narrowing actually narrows.
+        // This *should* be every type other than null, undefined, void, and never
+        Narrowable = Any | StructuredType | TypeParameter | StringLike | NumberLike | Boolean | ESSymbol,
         /* @internal */
         RequiresWidening = ContainsWideningType | ContainsObjectLiteral,
         /* @internal */

From 693cb9c6ca6ad7adf5b05a91a213595c03ad7ff3 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Mon, 6 Jun 2016 13:20:32 -0700
Subject: [PATCH 81/90] Add additional tests

---
 ...nFunctionParametersAndArguments.errors.txt | 35 +++++++++++++++++++
 ...gCommasInFunctionParametersAndArguments.js | 15 ++++++++
 ...gCommasInFunctionParametersAndArguments.ts |  8 +++++
 3 files changed, 58 insertions(+)
 create mode 100644 tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt

diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt
new file mode 100644
index 00000000000..02e44fa0976
--- /dev/null
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt
@@ -0,0 +1,35 @@
+tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts(20,11): error TS1138: Parameter declaration expected.
+
+
+==== tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts (1 errors) ====
+    function f1(x,) {}
+    
+    f1(1,);
+    
+    function f2(...args,) {}
+    
+    f2(...[],);
+    
+    // Not confused by overloads
+    declare function f3(x, ): number;
+    declare function f3(x, y,): string;
+    
+    f3(1,);
+    f3(1, 2,);
+    
+    // Works for constructors too
+    class X {
+        constructor(a,) { }
+        // *Not* allowed in getter
+        get x(,) { return 0; }
+              ~
+!!! error TS1138: Parameter declaration expected.
+        set x(value,) { }
+    }
+    interface Y {
+        new(x,);
+        (x,);
+    }
+    
+    new X(1,);
+    
\ No newline at end of file
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
index 87c925fb22b..70e3e1ccf42 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
@@ -17,7 +17,15 @@ declare function f3(x, y,): string;
 // Works for constructors too
 class X {
     constructor(a,) { }
+    // *Not* allowed in getter
+    get x(,) { return 0; }
+    set x(value,) { }
 }
+interface Y {
+    new(x,);
+    (x,);
+}
+
 new X(1,);
 
 
@@ -37,6 +45,13 @@ f3(1, 2);
 var X = (function () {
     function X(a) {
     }
+    Object.defineProperty(X.prototype, "x", {
+        // *Not* allowed in getter
+        get: function () { return 0; },
+        set: function (value) { },
+        enumerable: true,
+        configurable: true
+    });
     return X;
 }());
 new X(1);
diff --git a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
index 9c21d7f24ea..5f48920e4dd 100644
--- a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
+++ b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
@@ -16,5 +16,13 @@ declare function f3(x, y,): string;
 // Works for constructors too
 class X {
     constructor(a,) { }
+    // *Not* allowed in getter
+    get x(,) { return 0; }
+    set x(value,) { }
 }
+interface Y {
+    new(x,);
+    (x,);
+}
+
 new X(1,);

From 941c863524637c2d50f4df15c2758c5e74317b01 Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Mon, 6 Jun 2016 13:40:24 -0700
Subject: [PATCH 82/90] Fix typo

---
 src/harness/compilerRunner.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts
index e822b31584e..1834b0b3bbc 100644
--- a/src/harness/compilerRunner.ts
+++ b/src/harness/compilerRunner.ts
@@ -154,7 +154,7 @@ class CompilerBaselineRunner extends RunnerBase {
 
             it (`Correct module resolution tracing for ${fileName}`, () => {
                 if (options.traceResolution) {
-                    Harness.Baseline.runBaseline("Correct sourcemap content for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
+                    Harness.Baseline.runBaseline("Correct module resolution tracing for " + fileName, justName.replace(/\.tsx?$/, ".trace.json"), () => {
                         return JSON.stringify(result.traceResults || [], undefined, 4);
                     });
                 }

From 3052913b92e145b985e9c9a83b01e74df6dd9547 Mon Sep 17 00:00:00 2001
From: zhengbli 
Date: Tue, 7 Jun 2016 02:14:56 -0700
Subject: [PATCH 83/90] add tests for tsserver project system

---
 src/compiler/sys.ts                           |   2 +-
 .../cases/unittests/tsserverProjectSystem.ts  | 197 ++++++++++++++++--
 2 files changed, 179 insertions(+), 20 deletions(-)

diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts
index 0c500d5500e..bcf71b4016a 100644
--- a/src/compiler/sys.ts
+++ b/src/compiler/sys.ts
@@ -2,7 +2,7 @@
 
 namespace ts {
     export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
-    export type DirectoryWatcherCallback = (directoryName: string) => void;
+    export type DirectoryWatcherCallback = (fileName: string) => void;
     export interface WatchedFile {
         fileName: string;
         callback: FileWatcherCallback;
diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts
index c69821ced5f..e5b140237e5 100644
--- a/tests/cases/unittests/tsserverProjectSystem.ts
+++ b/tests/cases/unittests/tsserverProjectSystem.ts
@@ -107,6 +107,30 @@ namespace ts {
         }
     }
 
+    function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) {
+        assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
+    }
+
+    function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) {
+        assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
+    }
+
+    function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) {
+        checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
+    }
+
+    function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) {
+        checkMapKeys("watchedDirectories", host.watchedDirectories, expectedDirectories);
+    }
+
+    function checkConfiguredProjectActualFiles(project: server.Project, expectedFiles: string[]) {
+        checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), expectedFiles);
+    }
+
+    function checkConfiguredProjectRootFiles(project: server.Project, expectedFiles: string[]) {
+        checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles);
+    }
+
     class TestServerHost implements server.ServerHost {
         args: string[] = [];
         newLine: "\n";
@@ -188,6 +212,26 @@ namespace ts {
             };
         }
 
+        triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void {
+            const path = this.toPath(directoryName);
+            const callbacks = lookUp(this.watchedDirectories, path);
+            if (callbacks) {
+                for (const callback of callbacks) {
+                    callback.cb(fileName);
+                }
+            }
+        }
+
+        triggerFileWatcherCallback(fileName: string): void {
+            const path = this.toPath(fileName);
+            const callbacks = lookUp(this.watchedFiles, path);
+            if (callbacks) {
+                for (const callback of callbacks) {
+                    callback(path, /*removed*/ true);
+                }
+            }
+        }
+
         watchFile(fileName: string, callback: FileWatcherCallback) {
             const path = this.toPath(fileName);
             const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []);
@@ -204,7 +248,7 @@ namespace ts {
         }
 
         // TOOD: record and invoke callbacks to simulate timer events
-        readonly setTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]): any => void 0;
+        readonly setTimeout = setTimeout;
         readonly clearTimeout = (timeoutId: any): void => void 0;
         readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content;
         readonly resolvePath = (s: string) => s;
@@ -216,7 +260,20 @@ namespace ts {
         readonly exit = () => notImplemented();
     }
 
-    describe("tsserver project system:", () => {
+    describe("tsserver-project-system", () => {
+        const commonFile1: FileOrFolder = {
+            path: "/a/b/commonFile1.ts",
+            content: "let x = 1"
+        };
+        const commonFile2: FileOrFolder = {
+            path: "/a/b/commonFile2.ts",
+            content: "let y = 1"
+        };
+        const libFile: FileOrFolder = {
+            path: "/a/lib/lib.d.ts",
+            content: libFileContent
+        };
+
         it("create inferred project", () => {
             const appFile: FileOrFolder = {
                 path: "/a/b/c/app.ts",
@@ -225,10 +282,7 @@ namespace ts {
                 console.log(f)
                 `
             };
-            const libFile: FileOrFolder = {
-                path: "/a/lib/lib.d.ts",
-                content: libFileContent
-            };
+
             const moduleFile: FileOrFolder = {
                 path: "/a/b/c/module.d.ts",
                 content: `export let x: number`
@@ -238,13 +292,13 @@ namespace ts {
             const { configFileName } = projectService.openClientFile(appFile.path);
 
             assert(!configFileName, `should not find config, got: '${configFileName}`);
-            assert.equal(projectService.inferredProjects.length, 1, "expected one inferred project");
-            assert.equal(projectService.configuredProjects.length, 0, "expected no configured project");
+            checkConfiguredProjectNumber(projectService, 0);
+            checkInferredProjectNumber(projectService, 1);
 
             const project = projectService.inferredProjects[0];
 
             checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]);
-            checkMapKeys("watchedDirectories", host.watchedDirectories, ["/a/b/c", "/a/b", "/a"]);
+            checkWatchedDirectories(host, ["/a/b/c", "/a/b", "/a"]);
         });
 
         it("create configured project without file list", () => {
@@ -258,10 +312,6 @@ namespace ts {
                     ]
                 }`
             };
-            const libFile: FileOrFolder = {
-                path: "/a/lib/lib.d.ts",
-                content: libFileContent
-            };
             const file1: FileOrFolder = {
                 path: "/a/b/c/f1.ts",
                 content: "let x = 1"
@@ -274,21 +324,130 @@ namespace ts {
                 path: "/a/b/e/f3.ts",
                 content: "let z = 1"
             };
+
             const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [ configFile, libFile, file1, file2, file3 ]);
             const projectService = new server.ProjectService(host, nullLogger);
             const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
 
             assert(configFileName, "should find config file");
             assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
-            assert.equal(projectService.inferredProjects.length, 0, "expected no inferred project");
-            assert.equal(projectService.configuredProjects.length, 1, "expected one configured project");
+            checkInferredProjectNumber(projectService, 0);
+            checkConfiguredProjectNumber(projectService, 1);
 
             const project = projectService.configuredProjects[0];
-            checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), [file1.path, libFile.path, file2.path]);
-            checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), [file1.path, file2.path]);
+            checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
+            checkConfiguredProjectRootFiles(project, [file1.path, file2.path]);
+            // watching all files except one that was open
+            checkWatchedFiles(host, [configFile.path, file2.path, libFile.path]);
+            checkWatchedDirectories(host, [getDirectoryPath(configFile.path)]);
+        });
 
-            checkMapKeys("watchedFiles", host.watchedFiles, [configFile.path, file2.path, libFile.path]); // watching all files except one that was open
-            checkMapKeys("watchedDirectories", host.watchedDirectories, [getDirectoryPath(configFile.path)]);
+        it("add and then remove a config file in a folder with loose files", () => {
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{
+                    "files": ["commonFile1.ts"]
+                }`
+            };
+            const filesWithoutConfig = [ libFile, commonFile1, commonFile2 ];
+            const filesWithConfig = [ libFile, commonFile1, commonFile2, configFile ];
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", filesWithoutConfig);
+            const projectService = new server.ProjectService(host, nullLogger);
+            projectService.openClientFile(commonFile1.path);
+            projectService.openClientFile(commonFile2.path);
+
+            checkInferredProjectNumber(projectService, 2);
+            checkWatchedDirectories(host, ["/a/b", "/a"]);
+
+            // Add a tsconfig file
+            host.reloadFS(filesWithConfig);
+            host.triggerDirectoryWatcherCallback("/a/b", configFile.path);
+
+            checkInferredProjectNumber(projectService, 1);
+            checkConfiguredProjectNumber(projectService, 1);
+            // watching all files except one that was open
+            checkWatchedFiles(host, [libFile.path, configFile.path]);
+
+            // remove the tsconfig file
+            host.reloadFS(filesWithoutConfig);
+            host.triggerFileWatcherCallback(configFile.path);
+            checkInferredProjectNumber(projectService, 2);
+            checkConfiguredProjectNumber(projectService, 0);
+            checkWatchedDirectories(host, ["/a/b", "/a"]);
+        });
+
+        it("add new files to a configured project without file list", (done: () => void) => {
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{}`
+            };
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, libFile, configFile]);
+            const projectService = new server.ProjectService(host, nullLogger);
+            projectService.openClientFile(commonFile1.path);
+            checkWatchedDirectories(host, ["/a/b"]);
+            checkConfiguredProjectNumber(projectService, 1);
+
+            const project = projectService.configuredProjects[0];
+            checkConfiguredProjectRootFiles(project, [commonFile1.path]);
+
+            // add a new ts file
+            host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
+            host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
+            // project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
+            setTimeout(() => {
+                checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
+                done();
+            }, 1000);
+        });
+
+        it("should ignore non-existing files specified in the config file", () => {
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{
+                    "compilerOptions": {},
+                    "files": [
+                        "commonFile1.ts",
+                        "commonFile3.ts"
+                    ]
+                }`
+            };
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, configFile]);
+            const projectService = new server.ProjectService(host, nullLogger);
+            projectService.openClientFile(commonFile1.path);
+            projectService.openClientFile(commonFile2.path);
+
+            checkConfiguredProjectNumber(projectService, 1);
+            const project = projectService.configuredProjects[0];
+            checkConfiguredProjectRootFiles(project, [commonFile1.path]);
+            checkInferredProjectNumber(projectService, 1);
+        });
+
+        it("handle recreated files correctly", (done: () => void) => {
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{}`
+            };
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, configFile]);
+            const projectService = new server.ProjectService(host, nullLogger);
+            projectService.openClientFile(commonFile1.path);
+
+            checkConfiguredProjectNumber(projectService, 1);
+            const project = projectService.configuredProjects[0];
+            checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
+
+            // delete commonFile1
+            projectService.closeClientFile(commonFile1.path);
+            host.reloadFS([configFile]);
+            host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path);
+            host.setTimeout(() => {
+                // re-add commonFile1
+                host.reloadFS([commonFile1, configFile]);
+                projectService.openClientFile(commonFile1.path);
+                host.setTimeout(() => {
+                    checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
+                    done();
+                }, 500);
+            }, 500);
         });
     });
 }
\ No newline at end of file

From ed48e58a387dcde1c259a6a9f0d4842c6322114c Mon Sep 17 00:00:00 2001
From: Andy Hanson 
Date: Tue, 7 Jun 2016 06:10:47 -0700
Subject: [PATCH 84/90] Fix test

---
 ...nFunctionParametersAndArguments.errors.txt | 35 ---------------
 ...gCommasInFunctionParametersAndArguments.js |  7 ++-
 ...asInFunctionParametersAndArguments.symbols | 44 +++++++++++++------
 ...mmasInFunctionParametersAndArguments.types | 16 +++++++
 .../trailingCommasInGetter.errors.txt         | 10 +++++
 .../reference/trailingCommasInGetter.js       | 17 +++++++
 ...gCommasInFunctionParametersAndArguments.ts |  5 ++-
 .../conformance/es7/trailingCommasInGetter.ts |  3 ++
 8 files changed, 82 insertions(+), 55 deletions(-)
 delete mode 100644 tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt
 create mode 100644 tests/baselines/reference/trailingCommasInGetter.errors.txt
 create mode 100644 tests/baselines/reference/trailingCommasInGetter.js
 create mode 100644 tests/cases/conformance/es7/trailingCommasInGetter.ts

diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt
deleted file mode 100644
index 02e44fa0976..00000000000
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.errors.txt
+++ /dev/null
@@ -1,35 +0,0 @@
-tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts(20,11): error TS1138: Parameter declaration expected.
-
-
-==== tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts (1 errors) ====
-    function f1(x,) {}
-    
-    f1(1,);
-    
-    function f2(...args,) {}
-    
-    f2(...[],);
-    
-    // Not confused by overloads
-    declare function f3(x, ): number;
-    declare function f3(x, y,): string;
-    
-    f3(1,);
-    f3(1, 2,);
-    
-    // Works for constructors too
-    class X {
-        constructor(a,) { }
-        // *Not* allowed in getter
-        get x(,) { return 0; }
-              ~
-!!! error TS1138: Parameter declaration expected.
-        set x(value,) { }
-    }
-    interface Y {
-        new(x,);
-        (x,);
-    }
-    
-    new X(1,);
-    
\ No newline at end of file
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
index 70e3e1ccf42..6f30140c63e 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.js
@@ -1,4 +1,5 @@
 //// [trailingCommasInFunctionParametersAndArguments.ts]
+
 function f1(x,) {}
 
 f1(1,);
@@ -17,8 +18,7 @@ declare function f3(x, y,): string;
 // Works for constructors too
 class X {
     constructor(a,) { }
-    // *Not* allowed in getter
-    get x(,) { return 0; }
+    // See trailingCommasInGetter.ts
     set x(value,) { }
 }
 interface Y {
@@ -46,8 +46,7 @@ var X = (function () {
     function X(a) {
     }
     Object.defineProperty(X.prototype, "x", {
-        // *Not* allowed in getter
-        get: function () { return 0; },
+        // See trailingCommasInGetter.ts
         set: function (value) { },
         enumerable: true,
         configurable: true
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
index b146d230bac..2ee58a747b7 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.symbols
@@ -1,41 +1,57 @@
 === tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts ===
+
 function f1(x,) {}
 >f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
->x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 12))
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 1, 12))
 
 f1(1,);
 >f1 : Symbol(f1, Decl(trailingCommasInFunctionParametersAndArguments.ts, 0, 0))
 
 function f2(...args,) {}
->f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 2, 7))
->args : Symbol(args, Decl(trailingCommasInFunctionParametersAndArguments.ts, 4, 12))
+>f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 3, 7))
+>args : Symbol(args, Decl(trailingCommasInFunctionParametersAndArguments.ts, 5, 12))
 
 f2(...[],);
->f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 2, 7))
+>f2 : Symbol(f2, Decl(trailingCommasInFunctionParametersAndArguments.ts, 3, 7))
 
 // Not confused by overloads
 declare function f3(x, ): number;
->f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
->x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 20))
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 20))
 
 declare function f3(x, y,): string;
->f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
->x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 20))
->y : Symbol(y, Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 22))
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 11, 20))
+>y : Symbol(y, Decl(trailingCommasInFunctionParametersAndArguments.ts, 11, 22))
 
 f3(1,);
->f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
 
 f3(1, 2,);
->f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 6, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 9, 33))
+>f3 : Symbol(f3, Decl(trailingCommasInFunctionParametersAndArguments.ts, 7, 11), Decl(trailingCommasInFunctionParametersAndArguments.ts, 10, 33))
 
 // Works for constructors too
 class X {
->X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 13, 18))
+>X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 14, 18))
 
     constructor(a,) { }
->a : Symbol(a, Decl(trailingCommasInFunctionParametersAndArguments.ts, 17, 16))
+>a : Symbol(a, Decl(trailingCommasInFunctionParametersAndArguments.ts, 18, 16))
+
+    // See trailingCommasInGetter.ts
+    set x(value,) { }
+>x : Symbol(X.x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 18, 23))
+>value : Symbol(value, Decl(trailingCommasInFunctionParametersAndArguments.ts, 20, 10))
 }
+interface Y {
+>Y : Symbol(Y, Decl(trailingCommasInFunctionParametersAndArguments.ts, 21, 1))
+
+    new(x,);
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 23, 8))
+
+    (x,);
+>x : Symbol(x, Decl(trailingCommasInFunctionParametersAndArguments.ts, 24, 5))
+}
+
 new X(1,);
->X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 13, 18))
+>X : Symbol(X, Decl(trailingCommasInFunctionParametersAndArguments.ts, 14, 18))
 
diff --git a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
index 36de78fc8b8..9e2aa0abd8a 100644
--- a/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
+++ b/tests/baselines/reference/trailingCommasInFunctionParametersAndArguments.types
@@ -1,4 +1,5 @@
 === tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts ===
+
 function f1(x,) {}
 >f1 : (x: any) => void
 >x : any
@@ -47,7 +48,22 @@ class X {
 
     constructor(a,) { }
 >a : any
+
+    // See trailingCommasInGetter.ts
+    set x(value,) { }
+>x : any
+>value : any
 }
+interface Y {
+>Y : Y
+
+    new(x,);
+>x : any
+
+    (x,);
+>x : any
+}
+
 new X(1,);
 >new X(1,) : X
 >X : typeof X
diff --git a/tests/baselines/reference/trailingCommasInGetter.errors.txt b/tests/baselines/reference/trailingCommasInGetter.errors.txt
new file mode 100644
index 00000000000..c4c491a73b7
--- /dev/null
+++ b/tests/baselines/reference/trailingCommasInGetter.errors.txt
@@ -0,0 +1,10 @@
+tests/cases/conformance/es7/trailingCommasInGetter.ts(2,11): error TS1138: Parameter declaration expected.
+
+
+==== tests/cases/conformance/es7/trailingCommasInGetter.ts (1 errors) ====
+    class X {
+        get x(,) { return 0; }
+              ~
+!!! error TS1138: Parameter declaration expected.
+    }
+    
\ No newline at end of file
diff --git a/tests/baselines/reference/trailingCommasInGetter.js b/tests/baselines/reference/trailingCommasInGetter.js
new file mode 100644
index 00000000000..926545a8cba
--- /dev/null
+++ b/tests/baselines/reference/trailingCommasInGetter.js
@@ -0,0 +1,17 @@
+//// [trailingCommasInGetter.ts]
+class X {
+    get x(,) { return 0; }
+}
+
+
+//// [trailingCommasInGetter.js]
+var X = (function () {
+    function X() {
+    }
+    Object.defineProperty(X.prototype, "x", {
+        get: function () { return 0; },
+        enumerable: true,
+        configurable: true
+    });
+    return X;
+}());
diff --git a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
index 5f48920e4dd..6b57235cbeb 100644
--- a/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
+++ b/tests/cases/conformance/es7/trailingCommasInFunctionParametersAndArguments.ts
@@ -1,3 +1,5 @@
+// @target: es5
+
 function f1(x,) {}
 
 f1(1,);
@@ -16,8 +18,7 @@ declare function f3(x, y,): string;
 // Works for constructors too
 class X {
     constructor(a,) { }
-    // *Not* allowed in getter
-    get x(,) { return 0; }
+    // See trailingCommasInGetter.ts
     set x(value,) { }
 }
 interface Y {
diff --git a/tests/cases/conformance/es7/trailingCommasInGetter.ts b/tests/cases/conformance/es7/trailingCommasInGetter.ts
new file mode 100644
index 00000000000..a1b2cf60b2c
--- /dev/null
+++ b/tests/cases/conformance/es7/trailingCommasInGetter.ts
@@ -0,0 +1,3 @@
+class X {
+    get x(,) { return 0; }
+}

From 843aa6c1effe8365bb461a4a953d55eeb5dfa7cf Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders 
Date: Tue, 7 Jun 2016 07:51:34 -0700
Subject: [PATCH 85/90] Allow case comparison to undefined and null in strict
 null checking mode

---
 src/compiler/checker.ts                       |  2 +-
 ...llyTypedObjectLiteralReturnType.errors.txt | 18 ++++++++++++++++
 ...ontextuallyTypedObjectLiteralReturnType.js | 21 +++++++++++++++++++
 .../reference/equalityStrictNulls.errors.txt  | 10 +++++++++
 .../reference/equalityStrictNulls.js          | 20 ++++++++++++++++++
 tests/cases/compiler/inferFromConstraints.ts  |  9 ++++++++
 ...ontextuallyTypedObjectLiteralReturnType.ts |  9 ++++++++
 .../comparable/equalityStrictNulls.ts         | 10 +++++++++
 8 files changed, 98 insertions(+), 1 deletion(-)
 create mode 100644 tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt
 create mode 100644 tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js
 create mode 100644 tests/cases/compiler/inferFromConstraints.ts
 create mode 100644 tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index be21090d780..60acefb6045 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15106,7 +15106,7 @@ namespace ts {
                     // In a 'switch' statement, each 'case' expression must be of a type that is comparable
                     // to or from the type of the 'switch' expression.
                     const caseType = checkExpression(caseClause.expression);
-                    if (!isTypeComparableTo(expressionType, caseType)) {
+                    if (!isTypeEqualityComparableTo(expressionType, caseType)) {
                         // expressionType is not comparable to caseType, try the reversed check and report errors if it fails
                         checkTypeComparableTo(caseType, expressionType, caseClause.expression, /*headMessage*/ undefined);
                     }
diff --git a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt
new file mode 100644
index 00000000000..b010de0f761
--- /dev/null
+++ b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt
@@ -0,0 +1,18 @@
+tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts(6,24): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
+tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts(7,38): error TS2339: Property 'length' does not exist on type 'number'.
+
+
+==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts (2 errors) ====
+    function id(x: T): T { return x };
+    // Correct: type of fnWrapper is (y: number) => { y: number }
+    var fn = function(y: number) { return { y } };
+    var fnWrapper = id(fn);
+    // Incorrect: type of inlineWrapper is (z: number) => any 
+    var inlineWrapper = id(function(z: number) { return { z } });
+                           ~~~~~~~~
+!!! error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
+    let error1: number = fnWrapper(12).y.length; // should error
+                                         ~~~~~~
+!!! error TS2339: Property 'length' does not exist on type 'number'.
+    let error2: number = inlineWrapper(12).z.length; // should error
+    
\ No newline at end of file
diff --git a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js
new file mode 100644
index 00000000000..b687eaab5f5
--- /dev/null
+++ b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js
@@ -0,0 +1,21 @@
+//// [contextuallyTypedObjectLiteralReturnType.ts]
+function id(x: T): T { return x };
+// Correct: type of fnWrapper is (y: number) => { y: number }
+var fn = function(y: number) { return { y } };
+var fnWrapper = id(fn);
+// Incorrect: type of inlineWrapper is (z: number) => any 
+var inlineWrapper = id(function(z: number) { return { z } });
+let error1: number = fnWrapper(12).y.length; // should error
+let error2: number = inlineWrapper(12).z.length; // should error
+
+
+//// [contextuallyTypedObjectLiteralReturnType.js]
+function id(x) { return x; }
+;
+// Correct: type of fnWrapper is (y: number) => { y: number }
+var fn = function (y) { return { y: y }; };
+var fnWrapper = id(fn);
+// Incorrect: type of inlineWrapper is (z: number) => any 
+var inlineWrapper = id(function (z) { return { z: z }; });
+var error1 = fnWrapper(12).y.length; // should error
+var error2 = inlineWrapper(12).z.length; // should error
diff --git a/tests/baselines/reference/equalityStrictNulls.errors.txt b/tests/baselines/reference/equalityStrictNulls.errors.txt
index 54b581c87e6..e0793542b84 100644
--- a/tests/baselines/reference/equalityStrictNulls.errors.txt
+++ b/tests/baselines/reference/equalityStrictNulls.errors.txt
@@ -81,4 +81,14 @@ tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.t
 !!! error TS2365: Operator '<=' cannot be applied to types 'number' and 'undefined'.
         }
     }
+    function f5(x: string) {
+        switch(x) {
+            case null:
+                break;
+            case undefined:
+                break;
+            default:
+                return;
+        }
+    }
     
\ No newline at end of file
diff --git a/tests/baselines/reference/equalityStrictNulls.js b/tests/baselines/reference/equalityStrictNulls.js
index 99ff801a52c..e34e9fe9989 100644
--- a/tests/baselines/reference/equalityStrictNulls.js
+++ b/tests/baselines/reference/equalityStrictNulls.js
@@ -67,6 +67,16 @@ function f4(x: number) {
     if (x <= undefined) {
     }
 }
+function f5(x: string) {
+    switch(x) {
+        case null:
+            break;
+        case undefined:
+            break;
+        default:
+            return;
+    }
+}
 
 
 //// [equalityStrictNulls.js]
@@ -134,3 +144,13 @@ function f4(x) {
     if (x <= undefined) {
     }
 }
+function f5(x) {
+    switch (x) {
+        case null:
+            break;
+        case undefined:
+            break;
+        default:
+            return;
+    }
+}
diff --git a/tests/cases/compiler/inferFromConstraints.ts b/tests/cases/compiler/inferFromConstraints.ts
new file mode 100644
index 00000000000..d41d5ce7c46
--- /dev/null
+++ b/tests/cases/compiler/inferFromConstraints.ts
@@ -0,0 +1,9 @@
+interface Ctor {
+    new(): T;
+}
+// declare function create1(ctor: Ctor): T;
+declare function create2>(ctor: C): T;
+
+class A { a: number }
+// let a1 = create1(A).a; // a: A --> OK
+let a2 = create2(A).a; // a: {} --> Should be A
diff --git a/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts
new file mode 100644
index 00000000000..5e4500d1cb9
--- /dev/null
+++ b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts
@@ -0,0 +1,9 @@
+// @noImplicitAny: true
+function id(x: T): T { return x };
+// Correct: type of fnWrapper is (y: number) => { y: number }
+var fn = function(y: number) { return { y } };
+var fnWrapper = id(fn);
+// Incorrect: type of inlineWrapper is (z: number) => any 
+var inlineWrapper = id(function(z: number) { return { z } });
+let error1: number = fnWrapper(12).y.length; // should error
+let error2: number = inlineWrapper(12).z.length; // should error
diff --git a/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts b/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts
index 6b2744e8849..da46ab37a2a 100644
--- a/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts
+++ b/tests/cases/conformance/types/typeRelationships/comparable/equalityStrictNulls.ts
@@ -67,3 +67,13 @@ function f4(x: number) {
     if (x <= undefined) {
     }
 }
+function f5(x: string) {
+    switch(x) {
+        case null:
+            break;
+        case undefined:
+            break;
+        default:
+            return;
+    }
+}

From b4f12144d2c9571aa48db44cfc3778c3e721bec9 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders 
Date: Tue, 7 Jun 2016 08:17:28 -0700
Subject: [PATCH 86/90] Remove incorrectly added tests

---
 ...llyTypedObjectLiteralReturnType.errors.txt | 18 ----------------
 ...ontextuallyTypedObjectLiteralReturnType.js | 21 -------------------
 tests/cases/compiler/inferFromConstraints.ts  |  9 --------
 ...ontextuallyTypedObjectLiteralReturnType.ts |  9 --------
 4 files changed, 57 deletions(-)
 delete mode 100644 tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt
 delete mode 100644 tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js
 delete mode 100644 tests/cases/compiler/inferFromConstraints.ts
 delete mode 100644 tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts

diff --git a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt
deleted file mode 100644
index b010de0f761..00000000000
--- a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.errors.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts(6,24): error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
-tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts(7,38): error TS2339: Property 'length' does not exist on type 'number'.
-
-
-==== tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts (2 errors) ====
-    function id(x: T): T { return x };
-    // Correct: type of fnWrapper is (y: number) => { y: number }
-    var fn = function(y: number) { return { y } };
-    var fnWrapper = id(fn);
-    // Incorrect: type of inlineWrapper is (z: number) => any 
-    var inlineWrapper = id(function(z: number) { return { z } });
-                           ~~~~~~~~
-!!! error TS7024: Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.
-    let error1: number = fnWrapper(12).y.length; // should error
-                                         ~~~~~~
-!!! error TS2339: Property 'length' does not exist on type 'number'.
-    let error2: number = inlineWrapper(12).z.length; // should error
-    
\ No newline at end of file
diff --git a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js b/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js
deleted file mode 100644
index b687eaab5f5..00000000000
--- a/tests/baselines/reference/contextuallyTypedObjectLiteralReturnType.js
+++ /dev/null
@@ -1,21 +0,0 @@
-//// [contextuallyTypedObjectLiteralReturnType.ts]
-function id(x: T): T { return x };
-// Correct: type of fnWrapper is (y: number) => { y: number }
-var fn = function(y: number) { return { y } };
-var fnWrapper = id(fn);
-// Incorrect: type of inlineWrapper is (z: number) => any 
-var inlineWrapper = id(function(z: number) { return { z } });
-let error1: number = fnWrapper(12).y.length; // should error
-let error2: number = inlineWrapper(12).z.length; // should error
-
-
-//// [contextuallyTypedObjectLiteralReturnType.js]
-function id(x) { return x; }
-;
-// Correct: type of fnWrapper is (y: number) => { y: number }
-var fn = function (y) { return { y: y }; };
-var fnWrapper = id(fn);
-// Incorrect: type of inlineWrapper is (z: number) => any 
-var inlineWrapper = id(function (z) { return { z: z }; });
-var error1 = fnWrapper(12).y.length; // should error
-var error2 = inlineWrapper(12).z.length; // should error
diff --git a/tests/cases/compiler/inferFromConstraints.ts b/tests/cases/compiler/inferFromConstraints.ts
deleted file mode 100644
index d41d5ce7c46..00000000000
--- a/tests/cases/compiler/inferFromConstraints.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-interface Ctor {
-    new(): T;
-}
-// declare function create1(ctor: Ctor): T;
-declare function create2>(ctor: C): T;
-
-class A { a: number }
-// let a1 = create1(A).a; // a: A --> OK
-let a2 = create2(A).a; // a: {} --> Should be A
diff --git a/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts b/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts
deleted file mode 100644
index 5e4500d1cb9..00000000000
--- a/tests/cases/conformance/types/contextualTypes/methodDeclarations/contextuallyTypedObjectLiteralReturnType.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-// @noImplicitAny: true
-function id(x: T): T { return x };
-// Correct: type of fnWrapper is (y: number) => { y: number }
-var fn = function(y: number) { return { y } };
-var fnWrapper = id(fn);
-// Incorrect: type of inlineWrapper is (z: number) => any 
-var inlineWrapper = id(function(z: number) { return { z } });
-let error1: number = fnWrapper(12).y.length; // should error
-let error2: number = inlineWrapper(12).z.length; // should error

From d941177547741af17ae692b8065f5a03ab3382c5 Mon Sep 17 00:00:00 2001
From: Vladimir Matveev 
Date: Tue, 7 Jun 2016 15:08:46 -0700
Subject: [PATCH 87/90] check if moduleResolution when verifying that program
 can be reused

---
 src/compiler/program.ts | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index bab554927e7..5b64d066da1 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -1143,6 +1143,7 @@ namespace ts {
             // if any of these properties has changed - structure cannot be reused
             const oldOptions = oldProgram.getCompilerOptions();
             if ((oldOptions.module !== options.module) ||
+                (oldOptions.moduleResolution !== options.moduleResolution) ||
                 (oldOptions.noResolve !== options.noResolve) ||
                 (oldOptions.target !== options.target) ||
                 (oldOptions.noLib !== options.noLib) ||

From 37949a3d65fefacff6527c3551e60819176d1e10 Mon Sep 17 00:00:00 2001
From: Zhengbo Li 
Date: Tue, 7 Jun 2016 15:52:34 -0700
Subject: [PATCH 88/90] more tests for module resolution change and exclude

---
 src/compiler/program.ts                       |   1 +
 src/server/editorServices.ts                  |   2 +-
 src/services/services.ts                      |   1 +
 .../cases/unittests/tsserverProjectSystem.ts  | 194 ++++++++++++++----
 4 files changed, 159 insertions(+), 39 deletions(-)

diff --git a/src/compiler/program.ts b/src/compiler/program.ts
index bab554927e7..5b64d066da1 100644
--- a/src/compiler/program.ts
+++ b/src/compiler/program.ts
@@ -1143,6 +1143,7 @@ namespace ts {
             // if any of these properties has changed - structure cannot be reused
             const oldOptions = oldProgram.getCompilerOptions();
             if ((oldOptions.module !== options.module) ||
+                (oldOptions.moduleResolution !== options.moduleResolution) ||
                 (oldOptions.noResolve !== options.noResolve) ||
                 (oldOptions.target !== options.target) ||
                 (oldOptions.noLib !== options.noLib) ||
diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts
index 8eae43b503d..628522c7aa6 100644
--- a/src/server/editorServices.ts
+++ b/src/server/editorServices.ts
@@ -268,7 +268,7 @@ namespace ts.server {
         }
 
         removeRoot(info: ScriptInfo) {
-            if (!this.filenameToScript.contains(info.path)) {
+            if (this.filenameToScript.contains(info.path)) {
                 this.filenameToScript.remove(info.path);
                 this.roots = copyListRemovingItem(info, this.roots);
                 this.resolvedModuleNames.remove(info.path);
diff --git a/src/services/services.ts b/src/services/services.ts
index 90740b257c6..f39eb60a819 100644
--- a/src/services/services.ts
+++ b/src/services/services.ts
@@ -2919,6 +2919,7 @@ namespace ts {
             const changesInCompilationSettingsAffectSyntax = oldSettings &&
                 (oldSettings.target !== newSettings.target ||
                  oldSettings.module !== newSettings.module ||
+                 oldSettings.moduleResolution !== newSettings.moduleResolution ||
                  oldSettings.noResolve !== newSettings.noResolve ||
                  oldSettings.jsx !== newSettings.jsx ||
                  oldSettings.allowJs !== newSettings.allowJs);
diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts
index e5b140237e5..103e5bc7d9b 100644
--- a/tests/cases/unittests/tsserverProjectSystem.ts
+++ b/tests/cases/unittests/tsserverProjectSystem.ts
@@ -107,11 +107,11 @@ namespace ts {
         }
     }
 
-    function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) {
+    function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
         assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
     }
 
-    function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) {
+    function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) {
         assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
     }
 
@@ -131,6 +131,8 @@ namespace ts {
         checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles);
     }
 
+    type TimeOutCallback = () => any;
+
     class TestServerHost implements server.ServerHost {
         args: string[] = [];
         newLine: "\n";
@@ -138,6 +140,7 @@ namespace ts {
         private fs: ts.FileMap;
         private getCanonicalFileName: (s: string) => string;
         private toPath: (f: string) => Path;
+        private callbackQueue: TimeOutCallback[] = [];
         readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {};
         readonly watchedFiles: Map = {};
 
@@ -222,12 +225,12 @@ namespace ts {
             }
         }
 
-        triggerFileWatcherCallback(fileName: string): void {
+        triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
             const path = this.toPath(fileName);
             const callbacks = lookUp(this.watchedFiles, path);
             if (callbacks) {
                 for (const callback of callbacks) {
-                    callback(path, /*removed*/ true);
+                    callback(path, removed);
                 }
             }
         }
@@ -248,8 +251,27 @@ namespace ts {
         }
 
         // TOOD: record and invoke callbacks to simulate timer events
-        readonly setTimeout = setTimeout;
-        readonly clearTimeout = (timeoutId: any): void => void 0;
+        readonly setTimeout = (callback: TimeOutCallback, time: number) => {
+            this.callbackQueue.push(callback);
+            return this.callbackQueue.length - 1;
+        };
+        readonly clearTimeout = (timeoutId: any): void => {
+            if (typeof timeoutId === "number") {
+                this.callbackQueue.splice(timeoutId, 1);
+            }
+        };
+
+        checkTimeoutQueueLength(expected: number) {
+            assert.equal(this.callbackQueue.length, expected, `expected ${expected} timeout callbacks queued but found ${this.callbackQueue.length}.`);
+        }
+
+        runQueuedTimeoutCallbacks() {
+            for (const callback of this.callbackQueue) {
+                callback();
+            }
+            this.callbackQueue = [];
+        }
+
         readonly readFile = (s: string) => (this.fs.get(this.toPath(s))).content;
         readonly resolvePath = (s: string) => s;
         readonly getExecutingFilePath = () => this.executingFilePath;
@@ -292,8 +314,8 @@ namespace ts {
             const { configFileName } = projectService.openClientFile(appFile.path);
 
             assert(!configFileName, `should not find config, got: '${configFileName}`);
-            checkConfiguredProjectNumber(projectService, 0);
-            checkInferredProjectNumber(projectService, 1);
+            checkNumberOfConfiguredProjects(projectService, 0);
+            checkNumberOfInferredProjects(projectService, 1);
 
             const project = projectService.inferredProjects[0];
 
@@ -331,8 +353,8 @@ namespace ts {
 
             assert(configFileName, "should find config file");
             assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
-            checkInferredProjectNumber(projectService, 0);
-            checkConfiguredProjectNumber(projectService, 1);
+            checkNumberOfInferredProjects(projectService, 0);
+            checkNumberOfConfiguredProjects(projectService, 1);
 
             const project = projectService.configuredProjects[0];
             checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
@@ -356,27 +378,28 @@ namespace ts {
             projectService.openClientFile(commonFile1.path);
             projectService.openClientFile(commonFile2.path);
 
-            checkInferredProjectNumber(projectService, 2);
+            checkNumberOfInferredProjects(projectService, 2);
             checkWatchedDirectories(host, ["/a/b", "/a"]);
 
             // Add a tsconfig file
             host.reloadFS(filesWithConfig);
             host.triggerDirectoryWatcherCallback("/a/b", configFile.path);
 
-            checkInferredProjectNumber(projectService, 1);
-            checkConfiguredProjectNumber(projectService, 1);
+            checkNumberOfInferredProjects(projectService, 1);
+            checkNumberOfConfiguredProjects(projectService, 1);
             // watching all files except one that was open
             checkWatchedFiles(host, [libFile.path, configFile.path]);
 
             // remove the tsconfig file
             host.reloadFS(filesWithoutConfig);
             host.triggerFileWatcherCallback(configFile.path);
-            checkInferredProjectNumber(projectService, 2);
-            checkConfiguredProjectNumber(projectService, 0);
+
+            checkNumberOfInferredProjects(projectService, 2);
+            checkNumberOfConfiguredProjects(projectService, 0);
             checkWatchedDirectories(host, ["/a/b", "/a"]);
         });
 
-        it("add new files to a configured project without file list", (done: () => void) => {
+        it("add new files to a configured project without file list", () => {
             const configFile: FileOrFolder = {
                 path: "/a/b/tsconfig.json",
                 content: `{}`
@@ -385,7 +408,7 @@ namespace ts {
             const projectService = new server.ProjectService(host, nullLogger);
             projectService.openClientFile(commonFile1.path);
             checkWatchedDirectories(host, ["/a/b"]);
-            checkConfiguredProjectNumber(projectService, 1);
+            checkNumberOfConfiguredProjects(projectService, 1);
 
             const project = projectService.configuredProjects[0];
             checkConfiguredProjectRootFiles(project, [commonFile1.path]);
@@ -393,11 +416,9 @@ namespace ts {
             // add a new ts file
             host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
             host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
+            host.runQueuedTimeoutCallbacks();
             // project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
-            setTimeout(() => {
-                checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
-                done();
-            }, 1000);
+            checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
         });
 
         it("should ignore non-existing files specified in the config file", () => {
@@ -416,13 +437,13 @@ namespace ts {
             projectService.openClientFile(commonFile1.path);
             projectService.openClientFile(commonFile2.path);
 
-            checkConfiguredProjectNumber(projectService, 1);
+            checkNumberOfConfiguredProjects(projectService, 1);
             const project = projectService.configuredProjects[0];
             checkConfiguredProjectRootFiles(project, [commonFile1.path]);
-            checkInferredProjectNumber(projectService, 1);
+            checkNumberOfInferredProjects(projectService, 1);
         });
 
-        it("handle recreated files correctly", (done: () => void) => {
+        it("handle recreated files correctly", () => {
             const configFile: FileOrFolder = {
                 path: "/a/b/tsconfig.json",
                 content: `{}`
@@ -431,23 +452,120 @@ namespace ts {
             const projectService = new server.ProjectService(host, nullLogger);
             projectService.openClientFile(commonFile1.path);
 
-            checkConfiguredProjectNumber(projectService, 1);
+            checkNumberOfConfiguredProjects(projectService, 1);
             const project = projectService.configuredProjects[0];
             checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
 
-            // delete commonFile1
-            projectService.closeClientFile(commonFile1.path);
-            host.reloadFS([configFile]);
-            host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path);
-            host.setTimeout(() => {
-                // re-add commonFile1
-                host.reloadFS([commonFile1, configFile]);
-                projectService.openClientFile(commonFile1.path);
-                host.setTimeout(() => {
-                    checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
-                    done();
-                }, 500);
-            }, 500);
+            // delete commonFile2
+            host.reloadFS([commonFile1, configFile]);
+            host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
+            host.runQueuedTimeoutCallbacks();
+            checkConfiguredProjectRootFiles(project, [commonFile1.path]);
+
+            // re-add commonFile2
+            host.reloadFS([commonFile1, commonFile2, configFile]);
+            host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
+            host.runQueuedTimeoutCallbacks();
+            checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
         });
+
+        it("should create new inferred projects for files excluded from a configured project", () => {
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{
+                    "compilerOptions": {},
+                    "files": ["${commonFile1.path}", "${commonFile2.path}"]
+                }`
+            };
+            const files = [commonFile1, commonFile2, configFile];
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files);
+            const projectService = new server.ProjectService(host, nullLogger);
+            projectService.openClientFile(commonFile1.path);
+
+            const project = projectService.configuredProjects[0];
+            checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
+            configFile.content = `{
+                "compilerOptions": {},
+                "files": ["${commonFile1.path}"]
+            }`;
+            host.reloadFS(files);
+            host.triggerFileWatcherCallback(configFile.path);
+
+            checkNumberOfConfiguredProjects(projectService, 1);
+            checkConfiguredProjectRootFiles(project, [commonFile1.path]);
+
+            projectService.openClientFile(commonFile2.path);
+            checkNumberOfInferredProjects(projectService, 1);
+        });
+
+        it("files explicitly excluded in config file", () => {
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{
+                    "compilerOptions": {},
+                    "exclude": ["/a/c"]
+                }`
+            };
+            const excludedFile1: FileOrFolder = {
+                path: "/a/c/excluedFile1.ts",
+                content: `let t = 1;`
+            };
+
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, excludedFile1, configFile]);
+            const projectService = new server.ProjectService(host, nullLogger);
+
+            projectService.openClientFile(commonFile1.path);
+            checkNumberOfConfiguredProjects(projectService, 1);
+            const project = projectService.configuredProjects[0];
+            checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
+            projectService.openClientFile(excludedFile1.path);
+            checkNumberOfInferredProjects(projectService, 1);
+        });
+
+        it("should properly handle module resolution changes in config file", () => {
+            const file1: FileOrFolder = {
+                path: "/a/b/file1.ts",
+                content: `import { T } from "module1";`
+            }
+            const nodeModuleFile: FileOrFolder = {
+                path: "/a/b/node_modules/module1.ts",
+                content: `export interface T {}`
+            }
+            const classicModuleFile: FileOrFolder = {
+                path: "/a/module1.ts",
+                content: `export interface T {}`
+            }
+            const configFile: FileOrFolder = {
+                path: "/a/b/tsconfig.json",
+                content: `{
+                    "compilerOptions": {
+                        "moduleResolution": "node"
+                    },
+                    "files": ["${file1.path}"]
+                }`
+            };
+            const files = [file1, nodeModuleFile, classicModuleFile, configFile];
+            const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", files);
+            const projectService = new server.ProjectService(host, nullLogger);
+            projectService.openClientFile(file1.path);
+            projectService.openClientFile(nodeModuleFile.path);
+            projectService.openClientFile(classicModuleFile.path);
+
+            checkNumberOfConfiguredProjects(projectService, 1);
+            const project = projectService.configuredProjects[0];
+            checkConfiguredProjectActualFiles(project, [file1.path, nodeModuleFile.path]);
+            checkNumberOfInferredProjects(projectService, 1);
+
+            configFile.content = `{
+                "compilerOptions": {
+                    "moduleResolution": "classic"
+                },
+                "files": ["${file1.path}"]
+            }`;
+            host.reloadFS(files);
+            host.triggerFileWatcherCallback(configFile.path);
+            checkConfiguredProjectActualFiles(project, [file1.path, classicModuleFile.path]);
+            checkNumberOfInferredProjects(projectService, 1);
+        })
     });
 }
\ No newline at end of file

From 78792df4db2641a6c1b840581c3e064e7c5da39f Mon Sep 17 00:00:00 2001
From: Zhengbo Li 
Date: Tue, 7 Jun 2016 16:11:42 -0700
Subject: [PATCH 89/90] Fix linting issues

---
 tests/cases/unittests/tsserverProjectSystem.ts | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/cases/unittests/tsserverProjectSystem.ts b/tests/cases/unittests/tsserverProjectSystem.ts
index 103e5bc7d9b..cf382d09463 100644
--- a/tests/cases/unittests/tsserverProjectSystem.ts
+++ b/tests/cases/unittests/tsserverProjectSystem.ts
@@ -526,15 +526,15 @@ namespace ts {
             const file1: FileOrFolder = {
                 path: "/a/b/file1.ts",
                 content: `import { T } from "module1";`
-            }
+            };
             const nodeModuleFile: FileOrFolder = {
                 path: "/a/b/node_modules/module1.ts",
                 content: `export interface T {}`
-            }
+            };
             const classicModuleFile: FileOrFolder = {
                 path: "/a/module1.ts",
                 content: `export interface T {}`
-            }
+            };
             const configFile: FileOrFolder = {
                 path: "/a/b/tsconfig.json",
                 content: `{
@@ -566,6 +566,6 @@ namespace ts {
             host.triggerFileWatcherCallback(configFile.path);
             checkConfiguredProjectActualFiles(project, [file1.path, classicModuleFile.path]);
             checkNumberOfInferredProjects(projectService, 1);
-        })
+        });
     });
 }
\ No newline at end of file

From 65bbeb1f8ce3c6a06865f58670082e6c8d7e4a68 Mon Sep 17 00:00:00 2001
From: Dick van den Brink 
Date: Wed, 8 Jun 2016 19:33:03 +0200
Subject: [PATCH 90/90] Force LF newlines for LKG builds/non debug builds Fixes
 6630

---
 Jakefile.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Jakefile.js b/Jakefile.js
index a449dfcfa98..d36d9a6857a 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -313,6 +313,8 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts
             if (!opts.noMapRoot) {
                 options += " -mapRoot file:///" + path.resolve(path.dirname(outFile));
             }
+        } else {
+            options += " --newLine LF";
         }
 
         if (opts.stripInternal) {