From 21587aaae3be8bf7840ab8be66793d4d4b32e1f2 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 2 May 2016 16:29:53 -0700 Subject: [PATCH 01/48] Rewrite isInStringLiteral to accomodate for unterminated strings --- src/services/utilities.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 270c7d85d45..a625a2d6476 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -401,9 +401,27 @@ namespace ts { } } - export function isInString(sourceFile: SourceFile, position: number) { - let token = getTokenAtPosition(sourceFile, position); - return token && (token.kind === SyntaxKind.StringLiteral || token.kind === SyntaxKind.StringLiteralType) && position > token.getStart(sourceFile); + export function isInString(sourceFile: SourceFile, position: number): boolean { + const previousToken = findPrecedingToken(position, sourceFile); + if (previousToken && + (previousToken.kind === SyntaxKind.StringLiteral || previousToken.kind === SyntaxKind.StringLiteralType)) { + const start = previousToken.getStart(); + const end = previousToken.getEnd(); + + // To be "in" one of these literals, the position has to be: + // 1. entirely within the token text. + // 2. at the end position of an unterminated token. + // 3. at the end of a regular expression (due to trailing flags like '/foo/g'). + if (start < position && position < end) { + return true; + } + + if (position === end) { + return !!(previousToken).isUnterminated; + } + } + + return false; } export function isInComment(sourceFile: SourceFile, position: number) { From 0a277a1c601153c355dc76454cfebd770bf4cea9 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 2 May 2016 16:30:18 -0700 Subject: [PATCH 02/48] Refactor signatureHelp to expose helper functions --- src/services/signatureHelp.ts | 786 +++++++++++++++++----------------- 1 file changed, 393 insertions(+), 393 deletions(-) diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 9e8a2f14ed8..fa7cc42e0d1 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -170,7 +170,7 @@ namespace ts.SignatureHelp { TaggedTemplateArguments } - interface ArgumentListInfo { + export interface ArgumentListInfo { kind: ArgumentListKind; invocation: CallLikeExpression; argumentsSpan: TextSpan; @@ -188,7 +188,7 @@ namespace ts.SignatureHelp { return undefined; } - let argumentInfo = getContainingArgumentInfo(startingToken); + let argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile); cancellationToken.throwIfCancellationRequested(); // Semantic filtering of signature help @@ -205,431 +205,431 @@ namespace ts.SignatureHelp { // We didn't have any sig help items produced by the TS compiler. If this is a JS // file, then see if we can figure out anything better. if (isSourceFileJavaScript(sourceFile)) { - return createJavaScriptSignatureHelpItems(argumentInfo); + return createJavaScriptSignatureHelpItems(argumentInfo, program); } return undefined; } - return createSignatureHelpItems(candidates, resolvedSignature, argumentInfo); + return createSignatureHelpItems(candidates, resolvedSignature, argumentInfo, typeChecker); + } - function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo): SignatureHelpItems { - if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) { - return undefined; - } + function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo, program: Program): SignatureHelpItems { + if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) { + return undefined; + } - // See if we can find some symbol with the call expression name that has call signatures. - let callExpression = argumentInfo.invocation; - let expression = callExpression.expression; - let name = expression.kind === SyntaxKind.Identifier - ? expression - : expression.kind === SyntaxKind.PropertyAccessExpression - ? (expression).name - : undefined; + // See if we can find some symbol with the call expression name that has call signatures. + let callExpression = argumentInfo.invocation; + let expression = callExpression.expression; + let name = expression.kind === SyntaxKind.Identifier + ? expression + : expression.kind === SyntaxKind.PropertyAccessExpression + ? (expression).name + : undefined; - if (!name || !name.text) { - return undefined; - } + if (!name || !name.text) { + return undefined; + } - let typeChecker = program.getTypeChecker(); - for (let sourceFile of program.getSourceFiles()) { - let nameToDeclarations = sourceFile.getNamedDeclarations(); - let declarations = getProperty(nameToDeclarations, name.text); + let typeChecker = program.getTypeChecker(); + for (let sourceFile of program.getSourceFiles()) { + let nameToDeclarations = sourceFile.getNamedDeclarations(); + let declarations = getProperty(nameToDeclarations, name.text); - if (declarations) { - for (let declaration of declarations) { - let symbol = declaration.symbol; - if (symbol) { - let type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration); - if (type) { - let callSignatures = type.getCallSignatures(); - if (callSignatures && callSignatures.length) { - return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo); - } + if (declarations) { + for (let declaration of declarations) { + let symbol = declaration.symbol; + if (symbol) { + let type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration); + if (type) { + let callSignatures = type.getCallSignatures(); + if (callSignatures && callSignatures.length) { + return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, typeChecker); } } } } } } + } - /** - * Returns relevant information for the argument list and the current argument if we are - * in the argument of an invocation; returns undefined otherwise. - */ - function getImmediatelyContainingArgumentInfo(node: Node): ArgumentListInfo { - if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { - let callExpression = node.parent; - // There are 3 cases to handle: - // 1. The token introduces a list, and should begin a sig help session - // 2. The token is either not associated with a list, or ends a list, so the session should end - // 3. The token is buried inside a list, and should give sig help - // - // The following are examples of each: - // - // Case 1: - // foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session - // Case 2: - // fo#o#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end - // Case 3: - // foo(a#, #b#) -> The token is buried inside a list, and should give sig help - // Find out if 'node' is an argument, a type argument, or neither - if (node.kind === SyntaxKind.LessThanToken || - node.kind === SyntaxKind.OpenParenToken) { - // Find the list that starts right *after* the < or ( token. - // If the user has just opened a list, consider this item 0. - let list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile); - let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - Debug.assert(list !== undefined); - return { - kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, - invocation: callExpression, - argumentsSpan: getApplicableSpanForArguments(list), - argumentIndex: 0, - argumentCount: getArgumentCount(list) - }; - } - - // findListItemInfo can return undefined if we are not in parent's argument list - // or type argument list. This includes cases where the cursor is: - // - To the right of the closing paren, non-substitution template, or template tail. - // - Between the type arguments and the arguments (greater than token) - // - On the target of the call (parent.func) - // - On the 'new' keyword in a 'new' expression - let listItemInfo = findListItemInfo(node); - if (listItemInfo) { - let list = listItemInfo.list; - let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - - let argumentIndex = getArgumentIndex(list, node); - let argumentCount = getArgumentCount(list); - - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, - `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); - - return { - kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, - invocation: callExpression, - argumentsSpan: getApplicableSpanForArguments(list), - argumentIndex: argumentIndex, - argumentCount: argumentCount - }; - } - } - else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) { - // Check if we're actually inside the template; - // otherwise we'll fall out and return undefined. - if (isInsideTemplateLiteral(node, position)) { - return getArgumentListInfoForTemplate(node.parent, /*argumentIndex*/ 0); - } - } - else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { - let templateExpression = node.parent; - let tagExpression = templateExpression.parent; - Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - - let argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1; - - return getArgumentListInfoForTemplate(tagExpression, argumentIndex); - } - else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { - let templateSpan = node.parent; - let templateExpression = templateSpan.parent; - let tagExpression = templateExpression.parent; - Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - - // If we're just after a template tail, don't show signature help. - if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(node, position)) { - return undefined; - } - - let spanIndex = templateExpression.templateSpans.indexOf(templateSpan); - let argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node); - - return getArgumentListInfoForTemplate(tagExpression, argumentIndex); - } - - return undefined; - } - - function getArgumentIndex(argumentsList: Node, node: Node) { - // The list we got back can include commas. In the presence of errors it may - // also just have nodes without commas. For example "Foo(a b c)" will have 3 - // args without commas. We want to find what index we're at. So we count - // forward until we hit ourselves, only incrementing the index if it isn't a - // comma. + /** + * Returns relevant information for the argument list and the current argument if we are + * in the argument of an invocation; returns undefined otherwise. + */ + function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { + if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { + let callExpression = node.parent; + // There are 3 cases to handle: + // 1. The token introduces a list, and should begin a sig help session + // 2. The token is either not associated with a list, or ends a list, so the session should end + // 3. The token is buried inside a list, and should give sig help // - // Note: the subtlety around trailing commas (in getArgumentCount) does not apply - // here. That's because we're only walking forward until we hit the node we're - // on. In that case, even if we're after the trailing comma, we'll still see - // that trailing comma in the list, and we'll have generated the appropriate - // arg index. - let argumentIndex = 0; - let listChildren = argumentsList.getChildren(); - for (let child of listChildren) { - if (child === node) { - break; - } - if (child.kind !== SyntaxKind.CommaToken) { - argumentIndex++; - } - } - - return argumentIndex; - } - - function getArgumentCount(argumentsList: Node) { - // The argument count for a list is normally the number of non-comma children it has. - // For example, if you have "Foo(a,b)" then there will be three children of the arg - // list 'a' '' 'b'. So, in this case the arg count will be 2. However, there - // is a small subtlety. If you have "Foo(a,)", then the child list will just have - // 'a' ''. So, in the case where the last child is a comma, we increase the - // arg count by one to compensate. + // The following are examples of each: // - // 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. - let listChildren = argumentsList.getChildren(); - - let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken); - if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) { - argumentCount++; + // Case 1: + // foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session + // Case 2: + // fo#o#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end + // Case 3: + // foo(a#, #b#) -> The token is buried inside a list, and should give sig help + // Find out if 'node' is an argument, a type argument, or neither + if (node.kind === SyntaxKind.LessThanToken || + node.kind === SyntaxKind.OpenParenToken) { + // Find the list that starts right *after* the < or ( token. + // If the user has just opened a list, consider this item 0. + let list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile); + let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; + Debug.assert(list !== undefined); + return { + kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, + invocation: callExpression, + argumentsSpan: getApplicableSpanForArguments(list, sourceFile), + argumentIndex: 0, + argumentCount: getArgumentCount(list) + }; } - return argumentCount; - } + // findListItemInfo can return undefined if we are not in parent's argument list + // or type argument list. This includes cases where the cursor is: + // - To the right of the closing paren, non-substitution template, or template tail. + // - Between the type arguments and the arguments (greater than token) + // - On the target of the call (parent.func) + // - On the 'new' keyword in a 'new' expression + let listItemInfo = findListItemInfo(node); + if (listItemInfo) { + let list = listItemInfo.list; + let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - // spanIndex is either the index for a given template span. - // This does not give appropriate results for a NoSubstitutionTemplateLiteral - function getArgumentIndexForTemplatePiece(spanIndex: number, node: Node): number { - // Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1. - // There are three cases we can encounter: - // 1. We are precisely in the template literal (argIndex = 0). - // 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1). - // 3. We are directly to the right of the template literal, but because we look for the token on the left, - // not enough to put us in the substitution expression; we should consider ourselves part of - // the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1). - // - // Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # ` - // ^ ^ ^ ^ ^ ^ ^ ^ ^ - // Case: 1 1 3 2 1 3 2 2 1 - Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node."); - if (isTemplateLiteralKind(node.kind)) { - if (isInsideTemplateLiteral(node, position)) { - return 0; - } - return spanIndex + 2; + let argumentIndex = getArgumentIndex(list, node); + let argumentCount = getArgumentCount(list); + + Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, + `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + + return { + kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, + invocation: callExpression, + argumentsSpan: getApplicableSpanForArguments(list, sourceFile), + argumentIndex: argumentIndex, + argumentCount: argumentCount + }; } - return spanIndex + 1; + } + else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) { + // Check if we're actually inside the template; + // otherwise we'll fall out and return undefined. + if (isInsideTemplateLiteral(node, position)) { + return getArgumentListInfoForTemplate(node.parent, /*argumentIndex*/ 0, sourceFile); + } + } + else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { + let templateExpression = node.parent; + let tagExpression = templateExpression.parent; + Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); + + let argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1; + + return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile); + } + else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { + let templateSpan = node.parent; + let templateExpression = templateSpan.parent; + let tagExpression = templateExpression.parent; + Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); + + // If we're just after a template tail, don't show signature help. + if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(node, position)) { + return undefined; + } + + let spanIndex = templateExpression.templateSpans.indexOf(templateSpan); + let argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node, position); + + return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile); } - function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number): ArgumentListInfo { - // argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument. - let argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral - ? 1 - : (tagExpression.template).templateSpans.length + 1; + return undefined; + } - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + function getArgumentIndex(argumentsList: Node, node: Node) { + // The list we got back can include commas. In the presence of errors it may + // also just have nodes without commas. For example "Foo(a b c)" will have 3 + // args without commas. We want to find what index we're at. So we count + // forward until we hit ourselves, only incrementing the index if it isn't a + // comma. + // + // Note: the subtlety around trailing commas (in getArgumentCount) does not apply + // here. That's because we're only walking forward until we hit the node we're + // on. In that case, even if we're after the trailing comma, we'll still see + // that trailing comma in the list, and we'll have generated the appropriate + // arg index. + let argumentIndex = 0; + let listChildren = argumentsList.getChildren(); + for (let child of listChildren) { + if (child === node) { + break; + } + if (child.kind !== SyntaxKind.CommaToken) { + argumentIndex++; + } + } + + return argumentIndex; + } + + function getArgumentCount(argumentsList: Node) { + // The argument count for a list is normally the number of non-comma children it has. + // For example, if you have "Foo(a,b)" then there will be three children of the arg + // list 'a' '' 'b'. So, in this case the arg count will be 2. However, there + // is a small subtlety. If you have "Foo(a,)", then the child list will just have + // 'a' ''. 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' '' '' + // That will give us 2 non-commas. We then add one for the last comma, givin us an + // arg count of 3. + let listChildren = argumentsList.getChildren(); + + let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken); + if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) { + argumentCount++; + } + + return argumentCount; + } + + // spanIndex is either the index for a given template span. + // This does not give appropriate results for a NoSubstitutionTemplateLiteral + function getArgumentIndexForTemplatePiece(spanIndex: number, node: Node, position: number): number { + // Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1. + // There are three cases we can encounter: + // 1. We are precisely in the template literal (argIndex = 0). + // 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1). + // 3. We are directly to the right of the template literal, but because we look for the token on the left, + // not enough to put us in the substitution expression; we should consider ourselves part of + // the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1). + // + // Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # ` + // ^ ^ ^ ^ ^ ^ ^ ^ ^ + // Case: 1 1 3 2 1 3 2 2 1 + Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node."); + if (isTemplateLiteralKind(node.kind)) { + if (isInsideTemplateLiteral(node, position)) { + return 0; + } + return spanIndex + 2; + } + return spanIndex + 1; + } + + function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number, sourceFile: SourceFile): ArgumentListInfo { + // argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument. + let argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral + ? 1 + : (tagExpression.template).templateSpans.length + 1; + + Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + + return { + kind: ArgumentListKind.TaggedTemplateArguments, + invocation: tagExpression, + argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression, sourceFile), + argumentIndex: argumentIndex, + argumentCount: argumentCount + }; + } + + function getApplicableSpanForArguments(argumentsList: Node, sourceFile: SourceFile): TextSpan { + // We use full start and skip trivia on the end because we want to include trivia on + // both sides. For example, + // + // foo( /*comment */ a, b, c /*comment*/ ) + // | | + // + // The applicable span is from the first bar to the second bar (inclusive, + // but not including parentheses) + let applicableSpanStart = argumentsList.getFullStart(); + let applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false); + return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); + } + + function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression, sourceFile: SourceFile): TextSpan { + let template = taggedTemplate.template; + let applicableSpanStart = template.getStart(); + let applicableSpanEnd = template.getEnd(); + + // We need to adjust the end position for the case where the template does not have a tail. + // Otherwise, we will not show signature help past the expression. + // For example, + // + // ` ${ 1 + 1 foo(10) + // | | + // + // This is because a Missing node has no width. However, what we actually want is to include trivia + // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. + if (template.kind === SyntaxKind.TemplateExpression) { + let lastSpan = lastOrUndefined((template).templateSpans); + if (lastSpan.literal.getFullWidth() === 0) { + applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false); + } + } + + return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); + } + + export function getContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { + for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) { + if (isFunctionBlock(n)) { + return undefined; + } + + // If the node is not a subspan of its parent, this is a big problem. + // There have been crashes that might be caused by this violation. + if (n.pos < n.parent.pos || n.end > n.parent.end) { + Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind); + } + + let argumentInfo = getImmediatelyContainingArgumentInfo(n, position, sourceFile); + if (argumentInfo) { + return argumentInfo; + } + + + // TODO: Handle generic call with incomplete syntax + } + return undefined; + } + + function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { + let children = parent.getChildren(sourceFile); + let indexOfOpenerToken = children.indexOf(openerToken); + Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1); + return children[indexOfOpenerToken + 1]; + } + + /** + * The selectedItemIndex could be negative for several reasons. + * 1. There are too many arguments for all of the overloads + * 2. None of the overloads were type compatible + * The solution here is to try to pick the best overload by picking + * either the first one that has an appropriate number of parameters, + * or the one with the most parameters. + */ + function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number { + let maxParamsSignatureIndex = -1; + let maxParams = -1; + for (let i = 0; i < candidates.length; i++) { + let candidate = candidates[i]; + + if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) { + return i; + } + + if (candidate.parameters.length > maxParams) { + maxParams = candidate.parameters.length; + maxParamsSignatureIndex = i; + } + } + + return maxParamsSignatureIndex; + } + + function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems { + let applicableSpan = argumentListInfo.argumentsSpan; + let isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments; + + let invocation = argumentListInfo.invocation; + let callTarget = getInvokedExpression(invocation) + let callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget); + let callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); + let items: SignatureHelpItem[] = map(candidates, candidateSignature => { + let signatureHelpParameters: SignatureHelpParameter[]; + let prefixDisplayParts: SymbolDisplayPart[] = []; + let suffixDisplayParts: SymbolDisplayPart[] = []; + + if (callTargetDisplayParts) { + addRange(prefixDisplayParts, callTargetDisplayParts); + } + + if (isTypeParameterList) { + prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); + let typeParameters = candidateSignature.typeParameters; + signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; + suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); + let parameterParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation)); + addRange(suffixDisplayParts, parameterParts); + } + else { + let typeParameterParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); + addRange(prefixDisplayParts, typeParameterParts); + prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); + + let parameters = candidateSignature.parameters; + signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; + suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); + } + + let returnTypeParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation)); + addRange(suffixDisplayParts, returnTypeParts); return { - kind: ArgumentListKind.TaggedTemplateArguments, - invocation: tagExpression, - argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression), - argumentIndex: argumentIndex, - argumentCount: argumentCount + isVariadic: candidateSignature.hasRestParameter, + prefixDisplayParts, + suffixDisplayParts, + separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()], + parameters: signatureHelpParameters, + documentation: candidateSignature.getDocumentationComment() + }; + }); + + let argumentIndex = argumentListInfo.argumentIndex; + + // argumentCount is the *apparent* number of arguments. + let argumentCount = argumentListInfo.argumentCount; + + let selectedItemIndex = candidates.indexOf(bestSignature); + if (selectedItemIndex < 0) { + selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount); + } + + Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + + return { + items, + applicableSpan, + selectedItemIndex, + argumentIndex, + argumentCount + }; + + function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { + let displayParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); + + return { + name: parameter.name, + documentation: parameter.getDocumentationComment(), + displayParts, + isOptional: typeChecker.isOptionalParameter(parameter.valueDeclaration) }; } - function getApplicableSpanForArguments(argumentsList: Node): TextSpan { - // We use full start and skip trivia on the end because we want to include trivia on - // both sides. For example, - // - // foo( /*comment */ a, b, c /*comment*/ ) - // | | - // - // The applicable span is from the first bar to the second bar (inclusive, - // but not including parentheses) - let applicableSpanStart = argumentsList.getFullStart(); - let applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false); - return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); - } - - function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression): TextSpan { - let template = taggedTemplate.template; - let applicableSpanStart = template.getStart(); - let applicableSpanEnd = template.getEnd(); - - // We need to adjust the end position for the case where the template does not have a tail. - // Otherwise, we will not show signature help past the expression. - // For example, - // - // ` ${ 1 + 1 foo(10) - // | | - // - // This is because a Missing node has no width. However, what we actually want is to include trivia - // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. - if (template.kind === SyntaxKind.TemplateExpression) { - let lastSpan = lastOrUndefined((template).templateSpans); - if (lastSpan.literal.getFullWidth() === 0) { - applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false); - } - } - - return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); - } - - function getContainingArgumentInfo(node: Node): ArgumentListInfo { - for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) { - if (isFunctionBlock(n)) { - return undefined; - } - - // If the node is not a subspan of its parent, this is a big problem. - // There have been crashes that might be caused by this violation. - if (n.pos < n.parent.pos || n.end > n.parent.end) { - Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind); - } - - let argumentInfo = getImmediatelyContainingArgumentInfo(n); - if (argumentInfo) { - return argumentInfo; - } - - - // TODO: Handle generic call with incomplete syntax - } - return undefined; - } - - function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { - let children = parent.getChildren(sourceFile); - let indexOfOpenerToken = children.indexOf(openerToken); - Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1); - return children[indexOfOpenerToken + 1]; - } - - /** - * The selectedItemIndex could be negative for several reasons. - * 1. There are too many arguments for all of the overloads - * 2. None of the overloads were type compatible - * The solution here is to try to pick the best overload by picking - * either the first one that has an appropriate number of parameters, - * or the one with the most parameters. - */ - function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number { - let maxParamsSignatureIndex = -1; - let maxParams = -1; - for (let i = 0; i < candidates.length; i++) { - let candidate = candidates[i]; - - if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) { - return i; - } - - if (candidate.parameters.length > maxParams) { - maxParams = candidate.parameters.length; - maxParamsSignatureIndex = i; - } - } - - return maxParamsSignatureIndex; - } - - function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo): SignatureHelpItems { - let applicableSpan = argumentListInfo.argumentsSpan; - let isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments; - - let invocation = argumentListInfo.invocation; - let callTarget = getInvokedExpression(invocation) - let callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget); - let callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); - let items: SignatureHelpItem[] = map(candidates, candidateSignature => { - let signatureHelpParameters: SignatureHelpParameter[]; - let prefixDisplayParts: SymbolDisplayPart[] = []; - let suffixDisplayParts: SymbolDisplayPart[] = []; - - if (callTargetDisplayParts) { - addRange(prefixDisplayParts, callTargetDisplayParts); - } - - if (isTypeParameterList) { - prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); - let typeParameters = candidateSignature.typeParameters; - signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; - suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); - let parameterParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation)); - addRange(suffixDisplayParts, parameterParts); - } - else { - let typeParameterParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); - addRange(prefixDisplayParts, typeParameterParts); - prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); - - let parameters = candidateSignature.parameters; - signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; - suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); - } - - let returnTypeParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation)); - addRange(suffixDisplayParts, returnTypeParts); - - return { - isVariadic: candidateSignature.hasRestParameter, - prefixDisplayParts, - suffixDisplayParts, - separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()], - parameters: signatureHelpParameters, - documentation: candidateSignature.getDocumentationComment() - }; - }); - - let argumentIndex = argumentListInfo.argumentIndex; - - // argumentCount is the *apparent* number of arguments. - let argumentCount = argumentListInfo.argumentCount; - - let selectedItemIndex = candidates.indexOf(bestSignature); - if (selectedItemIndex < 0) { - selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount); - } - - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter { + let displayParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation)); return { - items, - applicableSpan, - selectedItemIndex, - argumentIndex, - argumentCount + name: typeParameter.symbol.name, + documentation: emptyArray, + displayParts, + isOptional: false }; - - function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { - let displayParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); - - return { - name: parameter.name, - documentation: parameter.getDocumentationComment(), - displayParts, - isOptional: typeChecker.isOptionalParameter(parameter.valueDeclaration) - }; - } - - function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter { - let displayParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation)); - - return { - name: typeParameter.symbol.name, - documentation: emptyArray, - displayParts, - isOptional: false - }; - } } } } \ No newline at end of file From 060f2a85637efdc79c7af3126fc8cef25d87a241 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 2 May 2016 16:30:50 -0700 Subject: [PATCH 03/48] Add support for completion in string literals --- src/services/services.ts | 118 +++++++++++++++--- .../fourslash/completionForStringLiteral.ts | 15 +++ .../fourslash/completionForStringLiteral2.ts | 20 +++ .../fourslash/completionForStringLiteral3.ts | 20 +++ 4 files changed, 158 insertions(+), 15 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteral.ts create mode 100644 tests/cases/fourslash/completionForStringLiteral2.ts create mode 100644 tests/cases/fourslash/completionForStringLiteral3.ts diff --git a/src/services/services.ts b/src/services/services.ts index 8d28d5d958a..eeaef60afca 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1032,10 +1032,10 @@ namespace ts { getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; getDefaultLibFileName(options: CompilerOptions): string; - log? (s: string): void; - trace? (s: string): void; - error? (s: string): void; - useCaseSensitiveFileNames? (): boolean; + log?(s: string): void; + trace?(s: string): void; + error?(s: string): void; + useCaseSensitiveFileNames?(): boolean; /* * LS host can optionally implement this method if it wants to be completely in charge of module name resolution. @@ -2416,7 +2416,7 @@ namespace ts { } // should be start of dependency list - if (token !== SyntaxKind.OpenBracketToken) { + if (token !== SyntaxKind.OpenBracketToken) { return true; } @@ -3912,10 +3912,15 @@ namespace ts { } } - function getCompletionsAtPosition(fileName: string, position: number): CompletionInfo { synchronizeHostData(); + const sourceFile = getValidSourceFile(fileName); + + if (isInString(sourceFile, position)) { + return getStringLiteralCompletionEntries(sourceFile, position); + } + const completionData = getCompletionData(fileName, position); if (!completionData) { return undefined; @@ -3928,12 +3933,10 @@ namespace ts { return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: getAllJsDocCompletionEntries() }; } - const sourceFile = getValidSourceFile(fileName); - const entries: CompletionEntry[] = []; if (isSourceFileJavaScript(sourceFile)) { - const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries); + const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ false); addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames)); } else { @@ -3957,7 +3960,7 @@ namespace ts { } } - getCompletionEntriesFromSymbols(symbols, entries); + getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true); } // Add keywords if this is not a member completion list @@ -4007,11 +4010,11 @@ namespace ts { })); } - function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry { + function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean): CompletionEntry { // Try to get a valid display name for this symbol, if we could not find one, then ignore it. // We would like to only show things that can be added after a dot, so for instance numeric properties can // not be accessed with a dot (a.1 <- invalid) - const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, /*performCharacterChecks*/ true, location); + const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, performCharacterChecks, location); if (!displayName) { return undefined; } @@ -4032,12 +4035,12 @@ namespace ts { }; } - function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[]): Map { + function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map { const start = new Date().getTime(); const uniqueNames: Map = {}; if (symbols) { for (const symbol of symbols) { - const entry = createCompletionEntry(symbol, location); + const entry = createCompletionEntry(symbol, location, performCharacterChecks); if (entry) { const id = escapeIdentifier(entry.name); if (!lookUp(uniqueNames, id)) { @@ -4051,6 +4054,91 @@ namespace ts { log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start)); return uniqueNames; } + + function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number) { + const node = findPrecedingToken(position, sourceFile); + if (!node || node.kind !== SyntaxKind.StringLiteral) { + return undefined; + } + + isNameOfExternalModuleImportOrDeclaration + const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); + if (argumentInfo) { + return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo); + } + else if (isElementAccessExpression(node.parent) && node.parent.argumentExpression === node) { + return getStringLiteralCompletionEntriesFromElementAccess(node.parent); + } + else { + return getStringLiteralCompletionEntriesFromContextualType(node); + } + } + + function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo) { + const typeChecker = program.getTypeChecker(); + const candidates: Signature[] = []; + const entries: CompletionEntry[] = []; + + typeChecker.getResolvedSignature(argumentInfo.invocation, candidates); + + for (const candidate of candidates) { + if (candidate.parameters.length > argumentInfo.argumentIndex) { + const parameter = candidate.parameters[argumentInfo.argumentIndex]; + addStringLiteralCompletionsFromType(typeChecker.getTypeAtLocation(parameter.valueDeclaration), entries); + } + } + + if (entries.length) { + return { isMemberCompletion: false, isNewIdentifierLocation: true, entries: entries }; + } + + return undefined; + } + + function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression) { + const typeChecker = program.getTypeChecker(); + const type = typeChecker.getTypeAtLocation(node.expression); + const entries: CompletionEntry[] = []; + if (type) { + getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/false); + if (entries.length) { + return { isMemberCompletion: true, isNewIdentifierLocation: true, entries: entries }; + } + } + return undefined; + } + + function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral) { + const typeChecker = program.getTypeChecker(); + const type = typeChecker.getContextualType(node); + if (type) { + const entries: CompletionEntry[] = []; + addStringLiteralCompletionsFromType(type, entries); + if (entries.length) { + return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: entries }; + } + } + return undefined; + } + + function addStringLiteralCompletionsFromType(type: Type, result: CompletionEntry[]): void { + if (!type) { + return; + } + if (type.flags & TypeFlags.Union) { + forEach((type).types, t => addStringLiteralCompletionsFromType(t, result)); + } + else { + if (type.flags & TypeFlags.StringLiteral) { + result.push({ + name: (type).text, + kindModifiers: ScriptElementKindModifier.none, + kind: ScriptElementKind.variableElement, + sortText: "0" + }); + } + } + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { @@ -4215,7 +4303,7 @@ namespace ts { // try get the call/construct signature from the type if it matches let callExpression: CallExpression; if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) { - callExpression = location; + callExpression = location; } else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { callExpression = location.parent; diff --git a/tests/cases/fourslash/completionForStringLiteral.ts b/tests/cases/fourslash/completionForStringLiteral.ts new file mode 100644 index 00000000000..cdbe1589f73 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral.ts @@ -0,0 +1,15 @@ +/// + +////type Options = "Option 1" | "Option 2" | "Option 3"; +////var x: Options = "/*1*/Option 3"; +//// +////function f(a: Options) { }; +////f("/*2*/ + +goTo.marker('1'); +verify.completionListContains("Option 1"); +verify.memberListCount(3); + +goTo.marker('2'); +verify.completionListContains("Option 2"); +verify.memberListCount(3); diff --git a/tests/cases/fourslash/completionForStringLiteral2.ts b/tests/cases/fourslash/completionForStringLiteral2.ts new file mode 100644 index 00000000000..6f0768d6c6b --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral2.ts @@ -0,0 +1,20 @@ +/// + +////var o = { +//// foo() { }, +//// bar: 0, +//// "some other name": 1 +////}; +//// +////o["/*1*/bar"]; +////o["/*2*/ + +goTo.marker('1'); +verify.completionListContains("foo"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); + +goTo.marker('2'); +verify.completionListContains("some other name"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); diff --git a/tests/cases/fourslash/completionForStringLiteral3.ts b/tests/cases/fourslash/completionForStringLiteral3.ts new file mode 100644 index 00000000000..8c1a7cab2ed --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral3.ts @@ -0,0 +1,20 @@ +/// + +////declare function f(a: "A", b: number): void; +////declare function f(a: "B", b: number): void; +////declare function f(a: "C", b: number): void; +////declare function f(a: string, b: number): void; +//// +////f("/*1*/C", 2); +//// +////f("/*2*/ + +goTo.marker('1'); +verify.completionListContains("A"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); + +goTo.marker('2'); +verify.completionListContains("A"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); From 7b0d664394c3a3742e6c52d9fec65e3f3e445267 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 2 May 2016 16:44:11 -0700 Subject: [PATCH 04/48] Remove unused check --- src/services/services.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index eeaef60afca..967e9949d49 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4061,7 +4061,6 @@ namespace ts { return undefined; } - isNameOfExternalModuleImportOrDeclaration const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo); From 291ad3360751ee0669912fe5d55ba2f3461fd7c7 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 3 May 2016 12:30:27 -0700 Subject: [PATCH 05/48] Use const instead of let --- src/services/signatureHelp.ts | 130 +++++++++++++++++----------------- src/services/utilities.ts | 10 +-- 2 files changed, 70 insertions(+), 70 deletions(-) diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index fa7cc42e0d1..b6c328dcea5 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -162,7 +162,7 @@ namespace ts.SignatureHelp { // // Did not find matching token // return null; //} - let emptyArray: any[] = []; + const emptyArray: any[] = []; const enum ArgumentListKind { TypeArguments, @@ -179,16 +179,16 @@ namespace ts.SignatureHelp { } export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, position: number, cancellationToken: CancellationToken): SignatureHelpItems { - let typeChecker = program.getTypeChecker(); + const typeChecker = program.getTypeChecker(); // Decide whether to show signature help - let startingToken = findTokenOnLeftOfPosition(sourceFile, position); + const startingToken = findTokenOnLeftOfPosition(sourceFile, position); if (!startingToken) { // We are at the beginning of the file return undefined; } - let argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile); + const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile); cancellationToken.throwIfCancellationRequested(); // Semantic filtering of signature help @@ -196,9 +196,9 @@ namespace ts.SignatureHelp { return undefined; } - let call = argumentInfo.invocation; - let candidates = []; - let resolvedSignature = typeChecker.getResolvedSignature(call, candidates); + const call = argumentInfo.invocation; + const candidates = []; + const resolvedSignature = typeChecker.getResolvedSignature(call, candidates); cancellationToken.throwIfCancellationRequested(); if (!candidates.length) { @@ -220,9 +220,9 @@ namespace ts.SignatureHelp { } // See if we can find some symbol with the call expression name that has call signatures. - let callExpression = argumentInfo.invocation; - let expression = callExpression.expression; - let name = expression.kind === SyntaxKind.Identifier + const callExpression = argumentInfo.invocation; + const expression = callExpression.expression; + const name = expression.kind === SyntaxKind.Identifier ? expression : expression.kind === SyntaxKind.PropertyAccessExpression ? (expression).name @@ -232,18 +232,18 @@ namespace ts.SignatureHelp { return undefined; } - let typeChecker = program.getTypeChecker(); - for (let sourceFile of program.getSourceFiles()) { - let nameToDeclarations = sourceFile.getNamedDeclarations(); - let declarations = getProperty(nameToDeclarations, name.text); + const typeChecker = program.getTypeChecker(); + for (const sourceFile of program.getSourceFiles()) { + const nameToDeclarations = sourceFile.getNamedDeclarations(); + const declarations = getProperty(nameToDeclarations, name.text); if (declarations) { - for (let declaration of declarations) { - let symbol = declaration.symbol; + for (const declaration of declarations) { + const symbol = declaration.symbol; if (symbol) { - let type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration); + const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration); if (type) { - let callSignatures = type.getCallSignatures(); + const callSignatures = type.getCallSignatures(); if (callSignatures && callSignatures.length) { return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, typeChecker); } @@ -260,7 +260,7 @@ namespace ts.SignatureHelp { */ function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { - let callExpression = node.parent; + const callExpression = node.parent; // There are 3 cases to handle: // 1. The token introduces a list, and should begin a sig help session // 2. The token is either not associated with a list, or ends a list, so the session should end @@ -279,8 +279,8 @@ namespace ts.SignatureHelp { node.kind === SyntaxKind.OpenParenToken) { // Find the list that starts right *after* the < or ( token. // If the user has just opened a list, consider this item 0. - let list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile); - let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; + const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile); + const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; Debug.assert(list !== undefined); return { kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, @@ -297,13 +297,13 @@ namespace ts.SignatureHelp { // - Between the type arguments and the arguments (greater than token) // - On the target of the call (parent.func) // - On the 'new' keyword in a 'new' expression - let listItemInfo = findListItemInfo(node); + const listItemInfo = findListItemInfo(node); if (listItemInfo) { - let list = listItemInfo.list; - let isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; + const list = listItemInfo.list; + const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - let argumentIndex = getArgumentIndex(list, node); - let argumentCount = getArgumentCount(list); + const argumentIndex = getArgumentIndex(list, node); + const argumentCount = getArgumentCount(list); Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); @@ -325,18 +325,18 @@ namespace ts.SignatureHelp { } } else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { - let templateExpression = node.parent; - let tagExpression = templateExpression.parent; + const templateExpression = node.parent; + const tagExpression = templateExpression.parent; Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - let argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1; + const argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1; return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile); } else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { - let templateSpan = node.parent; - let templateExpression = templateSpan.parent; - let tagExpression = templateExpression.parent; + const templateSpan = node.parent; + const templateExpression = templateSpan.parent; + const tagExpression = templateExpression.parent; Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); // If we're just after a template tail, don't show signature help. @@ -344,8 +344,8 @@ namespace ts.SignatureHelp { return undefined; } - let spanIndex = templateExpression.templateSpans.indexOf(templateSpan); - let argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node, position); + const spanIndex = templateExpression.templateSpans.indexOf(templateSpan); + const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node, position); return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile); } @@ -366,8 +366,8 @@ namespace ts.SignatureHelp { // that trailing comma in the list, and we'll have generated the appropriate // arg index. let argumentIndex = 0; - let listChildren = argumentsList.getChildren(); - for (let child of listChildren) { + const listChildren = argumentsList.getChildren(); + for (const child of listChildren) { if (child === node) { break; } @@ -391,7 +391,7 @@ namespace ts.SignatureHelp { // 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. - let listChildren = argumentsList.getChildren(); + const listChildren = argumentsList.getChildren(); let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken); if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) { @@ -427,7 +427,7 @@ namespace ts.SignatureHelp { function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number, sourceFile: SourceFile): ArgumentListInfo { // argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument. - let argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral + const argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral ? 1 : (tagExpression.template).templateSpans.length + 1; @@ -451,14 +451,14 @@ namespace ts.SignatureHelp { // // The applicable span is from the first bar to the second bar (inclusive, // but not including parentheses) - let applicableSpanStart = argumentsList.getFullStart(); - let applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false); + const applicableSpanStart = argumentsList.getFullStart(); + const applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false); return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); } function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression, sourceFile: SourceFile): TextSpan { - let template = taggedTemplate.template; - let applicableSpanStart = template.getStart(); + const template = taggedTemplate.template; + const applicableSpanStart = template.getStart(); let applicableSpanEnd = template.getEnd(); // We need to adjust the end position for the case where the template does not have a tail. @@ -471,7 +471,7 @@ namespace ts.SignatureHelp { // This is because a Missing node has no width. However, what we actually want is to include trivia // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. if (template.kind === SyntaxKind.TemplateExpression) { - let lastSpan = lastOrUndefined((template).templateSpans); + const lastSpan = lastOrUndefined((template).templateSpans); if (lastSpan.literal.getFullWidth() === 0) { applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false); } @@ -492,7 +492,7 @@ namespace ts.SignatureHelp { Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind); } - let argumentInfo = getImmediatelyContainingArgumentInfo(n, position, sourceFile); + const argumentInfo = getImmediatelyContainingArgumentInfo(n, position, sourceFile); if (argumentInfo) { return argumentInfo; } @@ -504,8 +504,8 @@ namespace ts.SignatureHelp { } function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { - let children = parent.getChildren(sourceFile); - let indexOfOpenerToken = children.indexOf(openerToken); + const children = parent.getChildren(sourceFile); + const indexOfOpenerToken = children.indexOf(openerToken); Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1); return children[indexOfOpenerToken + 1]; } @@ -522,7 +522,7 @@ namespace ts.SignatureHelp { let maxParamsSignatureIndex = -1; let maxParams = -1; for (let i = 0; i < candidates.length; i++) { - let candidate = candidates[i]; + const candidate = candidates[i]; if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) { return i; @@ -538,17 +538,17 @@ namespace ts.SignatureHelp { } function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems { - let applicableSpan = argumentListInfo.argumentsSpan; - let isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments; + const applicableSpan = argumentListInfo.argumentsSpan; + const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments; - let invocation = argumentListInfo.invocation; - let callTarget = getInvokedExpression(invocation) - let callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget); - let callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); - let items: SignatureHelpItem[] = map(candidates, candidateSignature => { + const invocation = argumentListInfo.invocation; + const callTarget = getInvokedExpression(invocation) + const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget); + const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); + const items: SignatureHelpItem[] = map(candidates, candidateSignature => { let signatureHelpParameters: SignatureHelpParameter[]; - let prefixDisplayParts: SymbolDisplayPart[] = []; - let suffixDisplayParts: SymbolDisplayPart[] = []; + const prefixDisplayParts: SymbolDisplayPart[] = []; + const suffixDisplayParts: SymbolDisplayPart[] = []; if (callTargetDisplayParts) { addRange(prefixDisplayParts, callTargetDisplayParts); @@ -556,25 +556,25 @@ namespace ts.SignatureHelp { if (isTypeParameterList) { prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); - let typeParameters = candidateSignature.typeParameters; + const typeParameters = candidateSignature.typeParameters; signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); - let parameterParts = mapToDisplayParts(writer => + const parameterParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation)); addRange(suffixDisplayParts, parameterParts); } else { - let typeParameterParts = mapToDisplayParts(writer => + const typeParameterParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); addRange(prefixDisplayParts, typeParameterParts); prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); - let parameters = candidateSignature.parameters; + const parameters = candidateSignature.parameters; signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); } - let returnTypeParts = mapToDisplayParts(writer => + const returnTypeParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation)); addRange(suffixDisplayParts, returnTypeParts); @@ -588,10 +588,10 @@ namespace ts.SignatureHelp { }; }); - let argumentIndex = argumentListInfo.argumentIndex; + const argumentIndex = argumentListInfo.argumentIndex; // argumentCount is the *apparent* number of arguments. - let argumentCount = argumentListInfo.argumentCount; + const argumentCount = argumentListInfo.argumentCount; let selectedItemIndex = candidates.indexOf(bestSignature); if (selectedItemIndex < 0) { @@ -609,7 +609,7 @@ namespace ts.SignatureHelp { }; function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { - let displayParts = mapToDisplayParts(writer => + const displayParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); return { @@ -621,7 +621,7 @@ namespace ts.SignatureHelp { } function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter { - let displayParts = mapToDisplayParts(writer => + const displayParts = mapToDisplayParts(writer => typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation)); return { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index a625a2d6476..4f9632afde5 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -508,8 +508,8 @@ namespace ts { return forEach(commentRanges, jsDocPrefix); function jsDocPrefix(c: CommentRange): boolean { - var text = sourceFile.text; - return text.length >= c.pos + 3 && text[c.pos] === '/' && text[c.pos + 1] === '*' && text[c.pos + 2] === '*'; + const text = sourceFile.text; + return text.length >= c.pos + 3 && text[c.pos] === "/" && text[c.pos + 1] === "*" && text[c.pos + 2] === "*"; } } @@ -564,7 +564,7 @@ namespace ts { if (flags & NodeFlags.Export) result.push(ScriptElementKindModifier.exportedModifier); if (isInAmbientContext(node)) result.push(ScriptElementKindModifier.ambientModifier); - return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none; + return result.length > 0 ? result.join(",") : ScriptElementKindModifier.none; } export function getTypeArgumentOrTypeParameterList(node: Node): NodeArray { @@ -647,7 +647,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; } @@ -786,7 +786,7 @@ namespace ts { } export function textOrKeywordPart(text: string) { - var kind = stringToToken(text); + const kind = stringToToken(text); return kind === undefined ? textPart(text) : keywordPart(kind); From e5a32b71aa537e2c5b241b5b0e6f60895ea1cea6 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 3 May 2016 12:30:53 -0700 Subject: [PATCH 06/48] Fix error --- tests/cases/fourslash/fourslash.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 48d69f0a85e..c0df85f0f7b 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -52,13 +52,13 @@ declare module ts { None = 0, Block = 1, Smart = 2, - } + } interface OutputFile { name: string; writeByteOrderMark: boolean; text: string; - } + } } declare namespace FourSlashInterface { @@ -139,7 +139,7 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPostion(openingBrace?: string): void; } class verify extends verifyNegatable { - assertHasRanges(ranges: FourSlash.Range[]): void; + assertHasRanges(ranges: Range[]): void; caretAtMarker(markerName?: string): void; indentationIs(numberOfSpaces: number): void; indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle): void; From 02f30ff04c5559da41de9d3a28f3c8fb8a3c00b6 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 3 May 2016 12:31:12 -0700 Subject: [PATCH 07/48] Formatting changes --- src/services/breakpoints.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index c79a34b1bfa..bc3530b7822 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -259,13 +259,13 @@ namespace ts.BreakpointResolver { if (isArrayLiteralOrObjectLiteralDestructuringPattern(node)) { return spanInArrayLiteralOrObjectLiteralDestructuringPattern(node); } - + // Set breakpoint on identifier element of destructuring pattern // 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 || - node.kind === SyntaxKind.PropertyAssignment || + node.kind == SyntaxKind.SpreadElementExpression || + node.kind === SyntaxKind.PropertyAssignment || node.kind === SyntaxKind.ShorthandPropertyAssignment) && isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent)) { return textSpan(node); @@ -325,14 +325,14 @@ namespace ts.BreakpointResolver { break; } } - + // If this is name of property assignment, set breakpoint in the initializer if (node.parent.kind === SyntaxKind.PropertyAssignment && - (node.parent).name === node && + (node.parent).name === node && !isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent.parent)) { return spanInNode((node.parent).initializer); } - + // Breakpoint in type assertion goes to its operand if (node.parent.kind === SyntaxKind.TypeAssertionExpression && (node.parent).type === node) { return spanInNextNode((node.parent).type); @@ -386,7 +386,7 @@ namespace ts.BreakpointResolver { if (variableDeclaration.parent.parent.kind === SyntaxKind.ForInStatement) { return spanInNode(variableDeclaration.parent.parent); } - + // If this is a destructuring pattern set breakpoint in binding pattern if (isBindingPattern(variableDeclaration.name)) { return spanInBindingPattern(variableDeclaration.name); @@ -686,7 +686,7 @@ namespace ts.BreakpointResolver { function spanInColonToken(node: Node): TextSpan { // Is this : specifying return annotation of the function declaration if (isFunctionLike(node.parent) || - node.parent.kind === SyntaxKind.PropertyAssignment || + node.parent.kind === SyntaxKind.PropertyAssignment || node.parent.kind === SyntaxKind.Parameter) { return spanInPreviousNode(node); } @@ -722,5 +722,5 @@ namespace ts.BreakpointResolver { return spanInNode(node.parent); } } - } + } } \ No newline at end of file From b9ab4d3e08115516bd099c54aa04c81ca4f59a57 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 3 May 2016 12:34:47 -0700 Subject: [PATCH 08/48] Use shorthand properties --- src/services/services.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 967e9949d49..d2a9a3988e6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4088,7 +4088,7 @@ namespace ts { } if (entries.length) { - return { isMemberCompletion: false, isNewIdentifierLocation: true, entries: entries }; + return { isMemberCompletion: false, isNewIdentifierLocation: true, entries }; } return undefined; @@ -4101,7 +4101,7 @@ namespace ts { if (type) { getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/false); if (entries.length) { - return { isMemberCompletion: true, isNewIdentifierLocation: true, entries: entries }; + return { isMemberCompletion: true, isNewIdentifierLocation: true, entries }; } } return undefined; @@ -4114,7 +4114,7 @@ namespace ts { const entries: CompletionEntry[] = []; addStringLiteralCompletionsFromType(type, entries); if (entries.length) { - return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: entries }; + return { isMemberCompletion: false, isNewIdentifierLocation: false, entries }; } } return undefined; From fa16a99514e53e97cb2ea95269ba71964c604d03 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 31 May 2016 09:26:21 -0700 Subject: [PATCH 09/48] Allow an import of "foo.js" to be matched by a file "foo.ts" --- src/compiler/core.ts | 9 +++- src/compiler/program.ts | 27 ++++++++--- src/harness/harness.ts | 14 +++--- tests/baselines/reference/moduleResolution.js | 45 +++++++++++++++++++ .../reference/moduleResolution.symbols | 36 +++++++++++++++ .../reference/moduleResolution.types | 36 +++++++++++++++ .../nameWithFileExtension.errors.txt | 12 ----- .../reference/nameWithFileExtension.js | 3 ++ .../reference/nameWithFileExtension.symbols | 14 ++++++ .../reference/nameWithFileExtension.types | 17 +++++++ .../amd/cantFindTheModule.errors.txt | 6 +-- .../node/cantFindTheModule.errors.txt | 6 +-- tests/cases/compiler/moduleResolution.ts | 26 +++++++++++ tests/cases/projects/NoModule/decl.ts | 2 +- 14 files changed, 220 insertions(+), 33 deletions(-) create mode 100644 tests/baselines/reference/moduleResolution.js create mode 100644 tests/baselines/reference/moduleResolution.symbols create mode 100644 tests/baselines/reference/moduleResolution.types delete mode 100644 tests/baselines/reference/nameWithFileExtension.errors.txt create mode 100644 tests/baselines/reference/nameWithFileExtension.symbols create mode 100644 tests/baselines/reference/nameWithFileExtension.types create mode 100644 tests/cases/compiler/moduleResolution.ts diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 79b6249539d..ba81ee7d103 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -849,13 +849,18 @@ namespace ts { const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; export function removeFileExtension(path: string): string { for (const ext of extensionsToRemove) { - if (fileExtensionIs(path, ext)) { - return path.substr(0, path.length - ext.length); + const extensionless = tryRemoveExtension(path, ext); + if (extensionless !== undefined) { + return extensionless; } } return path; } + export function tryRemoveExtension(path: string, extension: string): string { + return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; + } + export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bab554927e7..f9a583da0cc 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -615,6 +615,7 @@ namespace ts { } /** + * @param extensions - Either supportedTypeScriptExtensions or if --allowJs, allSupportedExtensions * @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. */ @@ -626,13 +627,29 @@ namespace ts { onlyRecordFailures = !directoryProbablyExists(directory, state.host); } } - return forEach(extensions, tryLoad); - function tryLoad(ext: string): string { - if (ext === ".tsx" && state.skipTsx) { - return undefined; + if (state.skipTsx) + extensions = filter(extensions, ext => ext !== "tsx"); + + // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" + const keepOrAddExtension = forEach(extensions, ext => + tryLoad(fileExtensionIs(candidate, ext) ? candidate : candidate + ext)); + if (keepOrAddExtension) { + return keepOrAddExtension; + } + + // Then try stripping a ".js" or ".jsx" extension and replacing it with a different one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" + return forEach(supportedJavascriptExtensions, jsExt => { + const extensionless = tryRemoveExtension(candidate, jsExt); + if (extensionless !== undefined) { + return forEach(supportedTypeScriptExtensions, ext => { + if (ext !== jsExt) + return tryLoad(extensionless + ext); + }); } - const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext; + }); + + function tryLoad(fileName: string): string { if (!onlyRecordFailures && state.host.fileExists(fileName)) { if (state.traceEnabled) { trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName); diff --git a/src/harness/harness.ts b/src/harness/harness.ts index d6959c2a54f..56d66e75396 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 @@ -350,7 +350,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"); @@ -751,7 +751,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; @@ -790,7 +790,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. @@ -935,7 +935,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" }, @@ -1187,7 +1187,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 @@ -1497,7 +1497,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/tests/baselines/reference/moduleResolution.js b/tests/baselines/reference/moduleResolution.js new file mode 100644 index 00000000000..be81b926062 --- /dev/null +++ b/tests/baselines/reference/moduleResolution.js @@ -0,0 +1,45 @@ +//// [tests/cases/compiler/moduleResolution.ts] //// + +//// [a.ts] +export default 0; + +// No extension: '.ts' added +//// [b.ts] +import a from './a'; + +// Matching extension +//// [c.ts] +import a from './a.ts'; + +// '.js' extension: stripped and replaced with '.ts' +//// [d.ts] +import a from './a.js'; + +//// [jquery.d.ts] +declare var x: number; +export default x; + +// No extension: '.d.ts' added +//// [jquery_user_1.ts] +import j from "./jquery"; + +// '.js' extension: stripped and replaced with '.d.ts' +//// [jquery_user_1.ts] +import j from "./jquery.js" + + +//// [a.js] +"use strict"; +exports.__esModule = true; +exports["default"] = 0; +// No extension: '.ts' added +//// [b.js] +"use strict"; +// Matching extension +//// [c.js] +"use strict"; +// '.js' extension: stripped and replaced with '.ts' +//// [d.js] +"use strict"; +//// [jquery_user_1.js] +"use strict"; diff --git a/tests/baselines/reference/moduleResolution.symbols b/tests/baselines/reference/moduleResolution.symbols new file mode 100644 index 00000000000..2a51d416a4a --- /dev/null +++ b/tests/baselines/reference/moduleResolution.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/a.ts === +export default 0; +No type information for this code. +No type information for this code.// No extension: '.ts' added +No type information for this code.=== tests/cases/compiler/b.ts === +import a from './a'; +>a : Symbol(a, Decl(b.ts, 0, 6)) + +// Matching extension +=== tests/cases/compiler/c.ts === +import a from './a.ts'; +>a : Symbol(a, Decl(c.ts, 0, 6)) + +// '.js' extension: stripped and replaced with '.ts' +=== tests/cases/compiler/d.ts === +import a from './a.js'; +>a : Symbol(a, Decl(d.ts, 0, 6)) + +=== tests/cases/compiler/jquery.d.ts === +declare var x: number; +>x : Symbol(x, Decl(jquery.d.ts, 0, 11)) + +export default x; +>x : Symbol(x, Decl(jquery.d.ts, 0, 11)) + +// No extension: '.d.ts' added +=== tests/cases/compiler/jquery_user_1.ts === +import j from "./jquery"; +>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) + +// '.js' extension: stripped and replaced with '.d.ts' +=== tests/cases/compiler/jquery_user_1.ts === +import j from "./jquery.js" +>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) +>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) + diff --git a/tests/baselines/reference/moduleResolution.types b/tests/baselines/reference/moduleResolution.types new file mode 100644 index 00000000000..2e1b0f560bc --- /dev/null +++ b/tests/baselines/reference/moduleResolution.types @@ -0,0 +1,36 @@ +=== tests/cases/compiler/a.ts === +export default 0; +No type information for this code. +No type information for this code.// No extension: '.ts' added +No type information for this code.=== tests/cases/compiler/b.ts === +import a from './a'; +>a : number + +// Matching extension +=== tests/cases/compiler/c.ts === +import a from './a.ts'; +>a : number + +// '.js' extension: stripped and replaced with '.ts' +=== tests/cases/compiler/d.ts === +import a from './a.js'; +>a : number + +=== tests/cases/compiler/jquery.d.ts === +declare var x: number; +>x : number + +export default x; +>x : number + +// No extension: '.d.ts' added +=== tests/cases/compiler/jquery_user_1.ts === +import j from "./jquery"; +>j : number + +// '.js' extension: stripped and replaced with '.d.ts' +=== tests/cases/compiler/jquery_user_1.ts === +import j from "./jquery.js" +>j : number +>j : number + diff --git a/tests/baselines/reference/nameWithFileExtension.errors.txt b/tests/baselines/reference/nameWithFileExtension.errors.txt deleted file mode 100644 index 2aa341271cd..00000000000 --- a/tests/baselines/reference/nameWithFileExtension.errors.txt +++ /dev/null @@ -1,12 +0,0 @@ -tests/cases/conformance/externalModules/foo_1.ts(1,22): error TS2307: Cannot find module './foo_0.js'. - - -==== tests/cases/conformance/externalModules/foo_1.ts (1 errors) ==== - import foo = require('./foo_0.js'); - ~~~~~~~~~~~~ -!!! error TS2307: Cannot find module './foo_0.js'. - var x = foo.foo + 42; - -==== tests/cases/conformance/externalModules/foo_0.ts (0 errors) ==== - export var foo = 42; - \ No newline at end of file diff --git a/tests/baselines/reference/nameWithFileExtension.js b/tests/baselines/reference/nameWithFileExtension.js index 2d487ede245..8a7506c2d42 100644 --- a/tests/baselines/reference/nameWithFileExtension.js +++ b/tests/baselines/reference/nameWithFileExtension.js @@ -8,6 +8,9 @@ import foo = require('./foo_0.js'); var x = foo.foo + 42; +//// [foo_0.js] +"use strict"; +exports.foo = 42; //// [foo_1.js] "use strict"; var foo = require('./foo_0.js'); diff --git a/tests/baselines/reference/nameWithFileExtension.symbols b/tests/baselines/reference/nameWithFileExtension.symbols new file mode 100644 index 00000000000..61e6a768ea6 --- /dev/null +++ b/tests/baselines/reference/nameWithFileExtension.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/externalModules/foo_1.ts === +import foo = require('./foo_0.js'); +>foo : Symbol(foo, Decl(foo_1.ts, 0, 0)) + +var x = foo.foo + 42; +>x : Symbol(x, Decl(foo_1.ts, 1, 3)) +>foo.foo : Symbol(foo.foo, Decl(foo_0.ts, 0, 10)) +>foo : Symbol(foo, Decl(foo_1.ts, 0, 0)) +>foo : Symbol(foo.foo, Decl(foo_0.ts, 0, 10)) + +=== tests/cases/conformance/externalModules/foo_0.ts === +export var foo = 42; +>foo : Symbol(foo, Decl(foo_0.ts, 0, 10)) + diff --git a/tests/baselines/reference/nameWithFileExtension.types b/tests/baselines/reference/nameWithFileExtension.types new file mode 100644 index 00000000000..a0fca3d4562 --- /dev/null +++ b/tests/baselines/reference/nameWithFileExtension.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/externalModules/foo_1.ts === +import foo = require('./foo_0.js'); +>foo : typeof foo + +var x = foo.foo + 42; +>x : number +>foo.foo + 42 : number +>foo.foo : number +>foo : typeof foo +>foo : number +>42 : number + +=== tests/cases/conformance/externalModules/foo_0.ts === +export var foo = 42; +>foo : number +>42 : number + diff --git a/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt b/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt index 7c071fdc208..e9f8842d5cb 100644 --- a/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt +++ b/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt @@ -1,12 +1,12 @@ -decl.ts(1,26): error TS2307: Cannot find module './foo/bar.js'. +decl.ts(1,26): error TS2307: Cannot find module './foo/bar.tx'. decl.ts(2,26): error TS2307: Cannot find module 'baz'. decl.ts(3,26): error TS2307: Cannot find module './baz'. ==== decl.ts (3 errors) ==== - import modErr = require("./foo/bar.js"); + import modErr = require("./foo/bar.tx"); ~~~~~~~~~~~~~~ -!!! error TS2307: Cannot find module './foo/bar.js'. +!!! error TS2307: Cannot find module './foo/bar.tx'. import modErr1 = require("baz"); ~~~~~ !!! error TS2307: Cannot find module 'baz'. diff --git a/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt b/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt index 7c071fdc208..e9f8842d5cb 100644 --- a/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt +++ b/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt @@ -1,12 +1,12 @@ -decl.ts(1,26): error TS2307: Cannot find module './foo/bar.js'. +decl.ts(1,26): error TS2307: Cannot find module './foo/bar.tx'. decl.ts(2,26): error TS2307: Cannot find module 'baz'. decl.ts(3,26): error TS2307: Cannot find module './baz'. ==== decl.ts (3 errors) ==== - import modErr = require("./foo/bar.js"); + import modErr = require("./foo/bar.tx"); ~~~~~~~~~~~~~~ -!!! error TS2307: Cannot find module './foo/bar.js'. +!!! error TS2307: Cannot find module './foo/bar.tx'. import modErr1 = require("baz"); ~~~~~ !!! error TS2307: Cannot find module 'baz'. diff --git a/tests/cases/compiler/moduleResolution.ts b/tests/cases/compiler/moduleResolution.ts new file mode 100644 index 00000000000..c21614577c0 --- /dev/null +++ b/tests/cases/compiler/moduleResolution.ts @@ -0,0 +1,26 @@ +// @Filename: a.ts +export default 0; + +// No extension: '.ts' added +// @Filename: b.ts +import a from './a'; + +// Matching extension +// @Filename: c.ts +import a from './a.ts'; + +// '.js' extension: stripped and replaced with '.ts' +// @Filename: d.ts +import a from './a.js'; + +// @Filename: jquery.d.ts +declare var x: number; +export default x; + +// No extension: '.d.ts' added +// @Filename: jquery_user_1.ts +import j from "./jquery"; + +// '.js' extension: stripped and replaced with '.d.ts' +// @Filename: jquery_user_1.ts +import j from "./jquery.js" diff --git a/tests/cases/projects/NoModule/decl.ts b/tests/cases/projects/NoModule/decl.ts index a8a29852ac2..064f777c551 100644 --- a/tests/cases/projects/NoModule/decl.ts +++ b/tests/cases/projects/NoModule/decl.ts @@ -1,4 +1,4 @@ -import modErr = require("./foo/bar.js"); +import modErr = require("./foo/bar.tx"); import modErr1 = require("baz"); import modErr2 = require("./baz"); From a0546a9310aa18a097fa8edb5276dc0c16fedfc2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 31 May 2016 10:39:29 -0700 Subject: [PATCH 10/48] Improve loadModuleFromFile code --- src/compiler/program.ts | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index f9a583da0cc..720b99d8900 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -628,24 +628,25 @@ namespace ts { } } - if (state.skipTsx) - extensions = filter(extensions, ext => ext !== "tsx"); - // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" - const keepOrAddExtension = forEach(extensions, ext => - tryLoad(fileExtensionIs(candidate, ext) ? candidate : candidate + ext)); + const keepOrAddExtension = forEach(extensions, ext => { + if (state.skipTsx && (ext === ".jsx" || ext === ".tsx")) { + return; + } + return tryLoad(fileExtensionIs(candidate, ext) ? candidate : candidate + ext); + }); if (keepOrAddExtension) { return keepOrAddExtension; } - // Then try stripping a ".js" or ".jsx" extension and replacing it with a different one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" + // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" return forEach(supportedJavascriptExtensions, jsExt => { + if (state.skipTsx && jsExt === ".jsx") { + return; + } const extensionless = tryRemoveExtension(candidate, jsExt); if (extensionless !== undefined) { - return forEach(supportedTypeScriptExtensions, ext => { - if (ext !== jsExt) - return tryLoad(extensionless + ext); - }); + return forEach(supportedTypeScriptExtensions, tsExt => tryLoad(extensionless + tsExt)); } }); From f4d6b67cb0973ed3366190e10f61c0cf3a4e4229 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 31 May 2016 11:39:11 -0700 Subject: [PATCH 11/48] Respond to PR comments --- src/compiler/core.ts | 7 ++++ src/compiler/diagnosticMessages.json | 4 +++ src/compiler/program.ts | 34 +++++++++---------- .../reference/moduleResolution.trace.json | 32 +++++++++++++++++ tests/cases/compiler/moduleResolution.ts | 1 + 5 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 tests/baselines/reference/moduleResolution.trace.json diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ba81ee7d103..932acdc6324 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -861,6 +861,13 @@ namespace ts { return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; } + export function getFileExtension(path: string): string { + const dot = path.lastIndexOf("."); + if (dot !== -1) { + return path.substring(dot); + } + } + export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index af8dd8ceb69..373e5fc3388 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2768,6 +2768,10 @@ "category": "Error", "code": 6131 }, + "File name '{0}' has a '{1}' extension - stripping it": { + "category": "Message", + "code": 6132 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 720b99d8900..c7b2cd66caf 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -615,11 +615,26 @@ namespace ts { } /** - * @param extensions - Either supportedTypeScriptExtensions or if --allowJs, allSupportedExtensions * @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. */ function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { + // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" + const keepOrAddExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); + if (keepOrAddExtension) { + return keepOrAddExtension; + } + // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" + if (hasJavaScriptFileExtension(candidate)) { + const extensionless = removeFileExtension(candidate); + if (state.traceEnabled) { + const extension = candidate.substring(extensionless.length); + trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension); + } + return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state); + } + } + function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { if (!onlyRecordFailures) { // check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing const directory = getDirectoryPath(candidate); @@ -628,27 +643,12 @@ namespace ts { } } - // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" - const keepOrAddExtension = forEach(extensions, ext => { + return forEach(extensions, ext => { if (state.skipTsx && (ext === ".jsx" || ext === ".tsx")) { return; } return tryLoad(fileExtensionIs(candidate, ext) ? candidate : candidate + ext); }); - if (keepOrAddExtension) { - return keepOrAddExtension; - } - - // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" - return forEach(supportedJavascriptExtensions, jsExt => { - if (state.skipTsx && jsExt === ".jsx") { - return; - } - const extensionless = tryRemoveExtension(candidate, jsExt); - if (extensionless !== undefined) { - return forEach(supportedTypeScriptExtensions, tsExt => tryLoad(extensionless + tsExt)); - } - }); function tryLoad(fileName: string): string { if (!onlyRecordFailures && state.host.fileExists(fileName)) { diff --git a/tests/baselines/reference/moduleResolution.trace.json b/tests/baselines/reference/moduleResolution.trace.json new file mode 100644 index 00000000000..f51251810a7 --- /dev/null +++ b/tests/baselines/reference/moduleResolution.trace.json @@ -0,0 +1,32 @@ +[ + "======== Resolving module './a' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/b.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "======== Module name './a' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './a.ts' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/c.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.ts' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './a.js' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/d.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.ts' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.tsx' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.d.ts' does not exist.", + "File name 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js' has a '.js' extension - stripping it", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.js' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", + "======== Resolving module './jquery.js' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery_user_1.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js'.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.ts' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.tsx' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.d.ts' does not exist.", + "File name 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js' has a '.js' extension - stripping it", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.ts' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.tsx' does not exist.", + "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.d.ts' exist - use it as a name resolution result.", + "======== Module name './jquery.js' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/cases/compiler/moduleResolution.ts b/tests/cases/compiler/moduleResolution.ts index c21614577c0..0af824100a7 100644 --- a/tests/cases/compiler/moduleResolution.ts +++ b/tests/cases/compiler/moduleResolution.ts @@ -1,3 +1,4 @@ +// @traceResolution: true // @Filename: a.ts export default 0; From d4b88899840edd218ae2638538aebcaf7b13c15c Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 31 May 2016 12:58:05 -0700 Subject: [PATCH 12/48] Respond to more PR comments --- src/compiler/core.ts | 7 ------- src/compiler/program.ts | 9 +++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 932acdc6324..ba81ee7d103 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -861,13 +861,6 @@ namespace ts { return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; } - export function getFileExtension(path: string): string { - const dot = path.lastIndexOf("."); - if (dot !== -1) { - return path.substring(dot); - } - } - export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index c7b2cd66caf..dd3102410ef 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -620,9 +620,9 @@ namespace ts { */ function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" - const keepOrAddExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); - if (keepOrAddExtension) { - return keepOrAddExtension; + const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); + if (resolvedByAddingOrKeepingExtension) { + return resolvedByAddingOrKeepingExtension; } // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" if (hasJavaScriptFileExtension(candidate)) { @@ -634,9 +634,10 @@ namespace ts { return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state); } } + function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { if (!onlyRecordFailures) { - // check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing + // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing const directory = getDirectoryPath(candidate); if (directory) { onlyRecordFailures = !directoryProbablyExists(directory, state.host); From 9179f4ca42c81f9ebd6e054abe2ff5bb39a69554 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 1 Jun 2016 10:43:39 -0700 Subject: [PATCH 13/48] Fix test --- tests/baselines/reference/moduleResolution.js | 1 + .../reference/moduleResolution.symbols | 17 +++--- .../reference/moduleResolution.trace.json | 52 +++++++++---------- .../reference/moduleResolution.types | 17 +++--- tests/cases/compiler/moduleResolution.ts | 15 +++--- 5 files changed, 53 insertions(+), 49 deletions(-) diff --git a/tests/baselines/reference/moduleResolution.js b/tests/baselines/reference/moduleResolution.js index be81b926062..101089b6355 100644 --- a/tests/baselines/reference/moduleResolution.js +++ b/tests/baselines/reference/moduleResolution.js @@ -1,6 +1,7 @@ //// [tests/cases/compiler/moduleResolution.ts] //// //// [a.ts] + export default 0; // No extension: '.ts' added diff --git a/tests/baselines/reference/moduleResolution.symbols b/tests/baselines/reference/moduleResolution.symbols index 2a51d416a4a..e9934946a64 100644 --- a/tests/baselines/reference/moduleResolution.symbols +++ b/tests/baselines/reference/moduleResolution.symbols @@ -1,22 +1,23 @@ -=== tests/cases/compiler/a.ts === -export default 0; +=== /src/a.ts === + +No type information for this code.export default 0; No type information for this code. No type information for this code.// No extension: '.ts' added -No type information for this code.=== tests/cases/compiler/b.ts === +No type information for this code.=== /src/b.ts === import a from './a'; >a : Symbol(a, Decl(b.ts, 0, 6)) // Matching extension -=== tests/cases/compiler/c.ts === +=== /src/c.ts === import a from './a.ts'; >a : Symbol(a, Decl(c.ts, 0, 6)) // '.js' extension: stripped and replaced with '.ts' -=== tests/cases/compiler/d.ts === +=== /src/d.ts === import a from './a.js'; >a : Symbol(a, Decl(d.ts, 0, 6)) -=== tests/cases/compiler/jquery.d.ts === +=== /src/jquery.d.ts === declare var x: number; >x : Symbol(x, Decl(jquery.d.ts, 0, 11)) @@ -24,12 +25,12 @@ export default x; >x : Symbol(x, Decl(jquery.d.ts, 0, 11)) // No extension: '.d.ts' added -=== tests/cases/compiler/jquery_user_1.ts === +=== /src/jquery_user_1.ts === import j from "./jquery"; >j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) // '.js' extension: stripped and replaced with '.d.ts' -=== tests/cases/compiler/jquery_user_1.ts === +=== /src/jquery_user_1.ts === import j from "./jquery.js" >j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) >j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) diff --git a/tests/baselines/reference/moduleResolution.trace.json b/tests/baselines/reference/moduleResolution.trace.json index f51251810a7..6e6589d7c1f 100644 --- a/tests/baselines/reference/moduleResolution.trace.json +++ b/tests/baselines/reference/moduleResolution.trace.json @@ -1,32 +1,32 @@ [ - "======== Resolving module './a' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/b.ts'. ========", + "======== Resolving module './a' from '/src/b.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a'.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", - "======== Module name './a' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", - "======== Resolving module './a.ts' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/c.ts'. ========", + "Loading module as file / folder, candidate module location '/src/a'.", + "File '/src/a.ts' exist - use it as a name resolution result.", + "======== Module name './a' was successfully resolved to '/src/a.ts'. ========", + "======== Resolving module './a.ts' from '/src/c.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", - "======== Module name './a.ts' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", - "======== Resolving module './a.js' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/d.ts'. ========", + "Loading module as file / folder, candidate module location '/src/a.ts'.", + "File '/src/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.ts' was successfully resolved to '/src/a.ts'. ========", + "======== Resolving module './a.js' from '/src/d.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js'.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.ts' does not exist.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.tsx' does not exist.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js.d.ts' does not exist.", - "File name 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.js' has a '.js' extension - stripping it", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts' exist - use it as a name resolution result.", - "======== Module name './a.js' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/a.ts'. ========", - "======== Resolving module './jquery.js' from 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery_user_1.ts'. ========", + "Loading module as file / folder, candidate module location '/src/a.js'.", + "File '/src/a.js.ts' does not exist.", + "File '/src/a.js.tsx' does not exist.", + "File '/src/a.js.d.ts' does not exist.", + "File name '/src/a.js' has a '.js' extension - stripping it", + "File '/src/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.js' was successfully resolved to '/src/a.ts'. ========", + "======== Resolving module './jquery.js' from '/src/jquery_user_1.ts'. ========", "Module resolution kind is not specified, using 'NodeJs'.", - "Loading module as file / folder, candidate module location 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js'.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.ts' does not exist.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.tsx' does not exist.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js.d.ts' does not exist.", - "File name 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.js' has a '.js' extension - stripping it", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.ts' does not exist.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.tsx' does not exist.", - "File 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.d.ts' exist - use it as a name resolution result.", - "======== Module name './jquery.js' was successfully resolved to 'C:/Users/anhans/TypeScript/tests/cases/compiler/jquery.d.ts'. ========" + "Loading module as file / folder, candidate module location '/src/jquery.js'.", + "File '/src/jquery.js.ts' does not exist.", + "File '/src/jquery.js.tsx' does not exist.", + "File '/src/jquery.js.d.ts' does not exist.", + "File name '/src/jquery.js' has a '.js' extension - stripping it", + "File '/src/jquery.ts' does not exist.", + "File '/src/jquery.tsx' does not exist.", + "File '/src/jquery.d.ts' exist - use it as a name resolution result.", + "======== Module name './jquery.js' was successfully resolved to '/src/jquery.d.ts'. ========" ] \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolution.types b/tests/baselines/reference/moduleResolution.types index 2e1b0f560bc..fbc0091ee3b 100644 --- a/tests/baselines/reference/moduleResolution.types +++ b/tests/baselines/reference/moduleResolution.types @@ -1,22 +1,23 @@ -=== tests/cases/compiler/a.ts === -export default 0; +=== /src/a.ts === + +No type information for this code.export default 0; No type information for this code. No type information for this code.// No extension: '.ts' added -No type information for this code.=== tests/cases/compiler/b.ts === +No type information for this code.=== /src/b.ts === import a from './a'; >a : number // Matching extension -=== tests/cases/compiler/c.ts === +=== /src/c.ts === import a from './a.ts'; >a : number // '.js' extension: stripped and replaced with '.ts' -=== tests/cases/compiler/d.ts === +=== /src/d.ts === import a from './a.js'; >a : number -=== tests/cases/compiler/jquery.d.ts === +=== /src/jquery.d.ts === declare var x: number; >x : number @@ -24,12 +25,12 @@ export default x; >x : number // No extension: '.d.ts' added -=== tests/cases/compiler/jquery_user_1.ts === +=== /src/jquery_user_1.ts === import j from "./jquery"; >j : number // '.js' extension: stripped and replaced with '.d.ts' -=== tests/cases/compiler/jquery_user_1.ts === +=== /src/jquery_user_1.ts === import j from "./jquery.js" >j : number >j : number diff --git a/tests/cases/compiler/moduleResolution.ts b/tests/cases/compiler/moduleResolution.ts index 0af824100a7..6ad35658623 100644 --- a/tests/cases/compiler/moduleResolution.ts +++ b/tests/cases/compiler/moduleResolution.ts @@ -1,27 +1,28 @@ // @traceResolution: true -// @Filename: a.ts + +// @Filename: /src/a.ts export default 0; // No extension: '.ts' added -// @Filename: b.ts +// @Filename: /src/b.ts import a from './a'; // Matching extension -// @Filename: c.ts +// @Filename: /src/c.ts import a from './a.ts'; // '.js' extension: stripped and replaced with '.ts' -// @Filename: d.ts +// @Filename: /src/d.ts import a from './a.js'; -// @Filename: jquery.d.ts +// @Filename: /src/jquery.d.ts declare var x: number; export default x; // No extension: '.d.ts' added -// @Filename: jquery_user_1.ts +// @Filename: /src/jquery_user_1.ts import j from "./jquery"; // '.js' extension: stripped and replaced with '.d.ts' -// @Filename: jquery_user_1.ts +// @Filename: /src/jquery_user_1.ts import j from "./jquery.js" From 9575b3c3ca06935ab22e36d9d1dfb04b6c133bc4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 1 Jun 2016 11:44:12 -0700 Subject: [PATCH 14/48] Revert to old tryLoad implementation --- src/compiler/program.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index dd3102410ef..0c060807b1f 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -643,15 +643,13 @@ namespace ts { onlyRecordFailures = !directoryProbablyExists(directory, state.host); } } + return forEach(extensions, tryLoad); - return forEach(extensions, ext => { + function tryLoad(ext: string): string { if (state.skipTsx && (ext === ".jsx" || ext === ".tsx")) { - return; + return undefined; } - return tryLoad(fileExtensionIs(candidate, ext) ? candidate : candidate + ext); - }); - - function tryLoad(fileName: string): string { + const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext; if (!onlyRecordFailures && state.host.fileExists(fileName)) { if (state.traceEnabled) { trace(state.host, Diagnostics.File_0_exist_use_it_as_a_name_resolution_result, fileName); From 3b1982589007e0419c025ad716ec47e7ff2566cf Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 2 Jun 2016 05:43:40 -0700 Subject: [PATCH 15/48] Allow wildcard ("*") patterns in ambient module declarations --- src/compiler/binder.ts | 21 +++- src/compiler/checker.ts | 26 +++++ src/compiler/program.ts | 110 ++++++++++++------ src/compiler/types.ts | 13 +++ .../ambientDeclarationsPatterns.errors.txt | 25 ++++ .../reference/ambientDeclarationsPatterns.js | 28 +++++ .../ambient/ambientDeclarationsPatterns.ts | 19 +++ 7 files changed, 207 insertions(+), 35 deletions(-) create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.errors.txt create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.js create mode 100644 tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 5fdfc7f02be..d3363285afc 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1287,7 +1287,26 @@ namespace ts { declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + let pattern: Pattern | undefined; + if (node.name.kind === SyntaxKind.StringLiteral) { + const text = (node.name).text; + if (hasZeroOrOneAsteriskCharacter(text)) { + pattern = tryParsePattern(text); + } + else { + errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text); + } + } + + if (pattern) { + // TODO: don't really need such a symbol in container.locals... + const symbol = declareSymbol(container.locals, undefined, node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + file.patternAmbientModules = file.patternAmbientModules || []; + file.patternAmbientModules.push({ pattern, symbol }); + } + else { + declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + } } } else { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 06ef2463402..3e66cf1661f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -140,6 +140,12 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); const globals: SymbolTable = {}; + /** + * List of every ambient module with a "*" wildcard. + * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. + * This is only used if there is no exact match. + */ + let patternAmbientModules: PatternAmbientModule[]; let getGlobalESSymbolConstructorSymbol: () => Symbol; @@ -1285,6 +1291,12 @@ namespace ts { } return undefined; } + + const patternModuleSymbol = getPatternAmbientModule(moduleName); + if (patternModuleSymbol) { + return getMergedSymbol(patternModuleSymbol); + } + if (moduleNotFoundError) { // report errors only if it was requested error(moduleReferenceLiteral, moduleNotFoundError, moduleName); @@ -1292,6 +1304,16 @@ namespace ts { return undefined; } + /** Get an ambient module with a wildcard ("*") in it. */ + function getPatternAmbientModule(name: string): Symbol | undefined { + if (patternAmbientModules) { + const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, name); + if (pattern) { + return pattern.symbol; + } + } + } + // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol { @@ -17627,6 +17649,10 @@ namespace ts { if (!isExternalOrCommonJsModule(file)) { mergeSymbolTable(globals, file.locals); } + if (file.patternAmbientModules && file.patternAmbientModules.length) { + (patternAmbientModules || (patternAmbientModules = [])).push(...file.patternAmbientModules); + } + if (file.moduleAugmentations.length) { (augmentations || (augmentations = [])).push(file.moduleAugmentations); } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index bab554927e7..1677f61c15d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -95,7 +95,7 @@ namespace ts { return compilerOptions.traceResolution && host.trace !== undefined; } - function hasZeroOrOneAsteriskCharacter(str: string): boolean { + export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { if (str.charCodeAt(i) === CharacterCodes.asterisk) { @@ -496,48 +496,23 @@ namespace ts { trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName); } - let longestMatchPrefixLength = -1; - let matchedPattern: string; - let matchedStar: string; - + // string is for exact match + let matchedPattern: Pattern | string | undefined = undefined; if (state.compilerOptions.paths) { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - - for (const key in state.compilerOptions.paths) { - const pattern: string = key; - const indexOfStar = pattern.indexOf("*"); - if (indexOfStar !== -1) { - const prefix = pattern.substr(0, indexOfStar); - const suffix = pattern.substr(indexOfStar + 1); - if (moduleName.length >= prefix.length + suffix.length && - startsWith(moduleName, prefix) && - endsWith(moduleName, suffix)) { - - // use length of prefix as betterness criteria - if (prefix.length > longestMatchPrefixLength) { - longestMatchPrefixLength = prefix.length; - matchedPattern = pattern; - matchedStar = moduleName.substr(prefix.length, moduleName.length - suffix.length); - } - } - } - else if (pattern === moduleName) { - // pattern was matched as is - no need to search further - matchedPattern = pattern; - matchedStar = undefined; - break; - } - } + matchedPattern = matchPatternOrExact(Object.keys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { + const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName); + const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern); if (state.traceEnabled) { - trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPattern); + trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } - for (const subst of state.compilerOptions.paths[matchedPattern]) { - const path = matchedStar ? subst.replace("\*", matchedStar) : subst; + for (const subst of state.compilerOptions.paths[matchedPatternText]) { + const path = matchedStar ? subst.replace("*", matchedStar) : subst; const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path)); if (state.traceEnabled) { trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); @@ -560,6 +535,73 @@ namespace ts { } } + /** + * patternStrings contains both pattern strings (containing "*") and regular strings. + * Return an exact match if possible, or a pattern match, or undefined. + * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.) + */ + function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined { + const patterns: Pattern[] = []; + for (const patternString of patternStrings) { + const pattern = tryParsePattern(patternString); + if (pattern) { + patterns.push(pattern); + } + else if (patternString === candidate) { + // pattern was matched as is - no need to search further + return patternString; + } + } + + return findBestPatternMatch(patterns, _ => _, candidate); + } + + function patternText({prefix, suffix}: Pattern): string { + return `${prefix}*${suffix}`; + } + + /** + * Given that candidate matches pattern, returns the text matching the '*'. + * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar" + */ + function matchedText(pattern: Pattern, candidate: string): string { + Debug.assert(isPatternMatch(pattern, candidate)); + return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length); + } + + /** Return the object corresponding to the best pattern to match `candidate`. */ + export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { + let matchedValue: T | undefined = undefined; + // use length of prefix as betterness criteria + let longestMatchPrefixLength = -1; + + for (const v of values) { + const pattern = getPattern(v); + if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) { + longestMatchPrefixLength = pattern.prefix.length; + matchedValue = v; + } + } + + return matchedValue; + } + + function isPatternMatch({prefix, suffix}: Pattern, candidate: string) { + return candidate.length >= prefix.length + suffix.length && + startsWith(candidate, prefix) && + endsWith(candidate, suffix); + } + + export function tryParsePattern(pattern: string): Pattern | undefined { + // This should be verified outside of here and a proper error thrown. + Debug.assert(hasZeroOrOneAsteriskCharacter(pattern)); + const indexOfStar = pattern.indexOf("*"); + return indexOfStar === -1 ? undefined : { + prefix: pattern.substr(0, indexOfStar), + suffix: pattern.substr(indexOfStar + 1) + }; + } + export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { const containingDirectory = getDirectoryPath(containingFile); const supportedExtensions = getSupportedExtensions(compilerOptions); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c7e14a10031..a6f19f121cf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1658,6 +1658,7 @@ namespace ts { /* @internal */ resolvedTypeReferenceDirectiveNames: Map; /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; + /* @internal */ patternAmbientModules?: PatternAmbientModule[]; } export interface ScriptReferenceHost { @@ -2135,6 +2136,18 @@ namespace ts { [index: string]: Symbol; } + /** Represents a "prefix*suffix" pattern. */ + export interface Pattern { + prefix: string; + suffix: string; + } + + /** Used to track a `declare module "foo*"`-like declaration. */ + export interface PatternAmbientModule { + pattern: Pattern; + symbol: Symbol; + } + /* @internal */ export const enum NodeCheckFlags { TypeChecked = 0x00000001, // Node has been type checked diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt new file mode 100644 index 00000000000..c197fe54f8d --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt @@ -0,0 +1,25 @@ +tests/cases/conformance/ambient/declarations.d.ts(6,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + +==== tests/cases/conformance/ambient/user.ts (0 errors) ==== + /// + import {foo} from "foobarbaz"; + foo(0); + + import {foos} from "foosball"; + +==== tests/cases/conformance/ambient/declarations.d.ts (1 errors) ==== + declare module "foo*baz" { + export function foo(n: number): void; + } + + // Should be an error + declare module "too*many*asterisks" { } + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + // Longest prefix wins + declare module "foos*" { + export const foos: number; + } + \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js new file mode 100644 index 00000000000..056416f1086 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -0,0 +1,28 @@ +//// [tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts] //// + +//// [declarations.d.ts] +declare module "foo*baz" { + export function foo(n: number): void; +} + +// Should be an error +declare module "too*many*asterisks" { } + +// Longest prefix wins +declare module "foos*" { + export const foos: number; +} + +//// [user.ts] +/// +import {foo} from "foobarbaz"; +foo(0); + +import {foos} from "foosball"; + + +//// [user.js] +"use strict"; +/// +var foobarbaz_1 = require("foobarbaz"); +foobarbaz_1.foo(0); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts new file mode 100644 index 00000000000..0d3b2e1c60e --- /dev/null +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -0,0 +1,19 @@ +// @Filename: declarations.d.ts +declare module "foo*baz" { + export function foo(n: number): void; +} + +// Should be an error +declare module "too*many*asterisks" { } + +// Longest prefix wins +declare module "foos*" { + export const foos: number; +} + +// @Filename: user.ts +/// +import {foo} from "foobarbaz"; +foo(0); + +import {foos} from "foosball"; From bcc9220451f7af0b8700b1255bead698e26cf7ca Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 17:37:14 -0700 Subject: [PATCH 16/48] Improve typing of && operator with --strictNullChecks --- src/compiler/checker.ts | 50 ++++++++++++++++++----------------------- src/compiler/types.ts | 1 + 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b8f078ea9a5..66e49ffff13 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2844,7 +2844,7 @@ namespace ts { } // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. - if (strictNullChecks && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { + if (strictNullChecks && declaration.initializer && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { type = getTypeWithFacts(type, TypeFacts.NEUndefined); } return type; @@ -2887,7 +2887,7 @@ namespace ts { } function addOptionality(type: Type, optional: boolean): Type { - return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type; + return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type; } // Return the inferred type for a variable, parameter, or property declaration @@ -3222,7 +3222,7 @@ namespace ts { if (!links.type) { const type = createObjectType(TypeFlags.Anonymous, symbol); links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? - addNullableKind(type, TypeFlags.Undefined) : type; + addTypeKind(type, TypeFlags.Undefined) : type; } return links.type; } @@ -6746,7 +6746,7 @@ namespace ts { return getUnionType(types); } const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined); - return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); + return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); } function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void { @@ -6817,28 +6817,22 @@ namespace ts { return !!(type.flags & TypeFlags.Tuple); } - function getNullableKind(type: Type): TypeFlags { - let flags = type.flags; - if (flags & TypeFlags.Union) { - for (const t of (type as UnionType).types) { - flags |= t.flags; - } - } - return flags & TypeFlags.Nullable; + function getCombinedTypeFlags(type: Type): TypeFlags { + return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((type).types) : type.flags; } - function addNullableKind(type: Type, kind: TypeFlags): Type { - if ((getNullableKind(type) & kind) !== kind) { - const types = [type]; - if (kind & TypeFlags.Undefined) { - types.push(undefinedType); - } - if (kind & TypeFlags.Null) { - types.push(nullType); - } - type = getUnionType(types); + function addTypeKind(type: Type, kind: TypeFlags) { + if ((getCombinedTypeFlags(type) & kind) === kind) { + return type; } - return type; + const types = [type]; + if (kind & TypeFlags.String) types.push(stringType); + if (kind & TypeFlags.Number) types.push(numberType); + if (kind & TypeFlags.Boolean) types.push(booleanType); + if (kind & TypeFlags.Void) types.push(voidType); + if (kind & TypeFlags.Undefined) types.push(undefinedType); + if (kind & TypeFlags.Null) types.push(nullType); + return getUnionType(types); } function getNonNullableType(type: Type): Type { @@ -7667,7 +7661,7 @@ namespace ts { if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } - const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined); + const initialType = assumeInitialized ? declaredType : addTypeKind(declaredType, TypeFlags.Undefined); const visitedFlowStart = visitedFlowCount; const result = getTypeAtFlowNode(reference.flowNode); visitedFlowCount = visitedFlowStart; @@ -8163,7 +8157,7 @@ namespace ts { 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) { + if (!assumeInitialized && !(getCombinedTypeFlags(type) & TypeFlags.Undefined) && getCombinedTypeFlags(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; @@ -9945,7 +9939,7 @@ namespace ts { function checkNonNullExpression(node: Expression | QualifiedName) { const type = checkExpression(node); if (strictNullChecks) { - const kind = getNullableKind(type); + const kind = getCombinedTypeFlags(type) & TypeFlags.Nullable; if (kind) { error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ? Diagnostics.Object_is_possibly_null_or_undefined : @@ -11485,7 +11479,7 @@ namespace ts { if (strictNullChecks) { const declaration = symbol.valueDeclaration; if (declaration && (declaration).initializer) { - return addNullableKind(type, TypeFlags.Undefined); + return addTypeKind(type, TypeFlags.Undefined); } } return type; @@ -12411,7 +12405,7 @@ namespace ts { case SyntaxKind.InKeyword: return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: - return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType; + return strictNullChecks ? addTypeKind(rightType, getCombinedTypeFlags(leftType) & TypeFlags.Falsy) : rightType; case SyntaxKind.BarBarToken: return getUnionType([getNonNullableType(leftType), rightType]); case SyntaxKind.EqualsToken: diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4c0e7a036db..3e8c2378b4c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2209,6 +2209,7 @@ namespace ts { /* @internal */ Nullable = Undefined | Null, + Falsy = String | Number | Boolean | Void | Undefined | Null, /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never, /* @internal */ From 13698a99525782f9b2cd80b4a9bed5e20f6759b7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 2 Jun 2016 17:37:25 -0700 Subject: [PATCH 17/48] Add test --- .../reference/logicalAndOperatorStrictMode.js | 156 +++++++ .../logicalAndOperatorStrictMode.symbols | 351 +++++++++++++++ .../logicalAndOperatorStrictMode.types | 423 ++++++++++++++++++ .../logicalAndOperatorStrictMode.ts | 82 ++++ 4 files changed, 1012 insertions(+) create mode 100644 tests/baselines/reference/logicalAndOperatorStrictMode.js create mode 100644 tests/baselines/reference/logicalAndOperatorStrictMode.symbols create mode 100644 tests/baselines/reference/logicalAndOperatorStrictMode.types create mode 100644 tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts diff --git a/tests/baselines/reference/logicalAndOperatorStrictMode.js b/tests/baselines/reference/logicalAndOperatorStrictMode.js new file mode 100644 index 00000000000..ecb5bc16429 --- /dev/null +++ b/tests/baselines/reference/logicalAndOperatorStrictMode.js @@ -0,0 +1,156 @@ +//// [logicalAndOperatorStrictMode.ts] + +const a = [0]; +const s = ""; +const x = 0; +const b = false; +const v: void = undefined; +const u = undefined; +const n = null; +const z = s || x || u; + +const a1 = a && a; +const a2 = a && s; +const a3 = a && x; +const a4 = a && b; +const a5 = a && v; +const a6 = a && u; +const a7 = a && n; +const a8 = a && z; + +const s1 = s && a; +const s2 = s && s; +const s3 = s && x; +const s4 = s && b; +const s5 = s && v; +const s6 = s && u; +const s7 = s && n; +const s8 = s && z; + +const x1 = x && a; +const x2 = x && s; +const x3 = x && x; +const x4 = x && b; +const x5 = x && v; +const x6 = x && u; +const x7 = x && n; +const x8 = x && z; + +const b1 = b && a; +const b2 = b && s; +const b3 = b && x; +const b4 = b && b; +const b5 = b && v; +const b6 = b && u; +const b7 = b && n; +const b8 = b && z; + +const v1 = v && a; +const v2 = v && s; +const v3 = v && x; +const v4 = v && b; +const v5 = v && v; +const v6 = v && u; +const v7 = v && n; +const v8 = v && z; + +const u1 = u && a; +const u2 = u && s; +const u3 = u && x; +const u4 = u && b; +const u5 = u && v; +const u6 = u && u; +const u7 = u && n; +const u8 = u && z; + +const n1 = n && a; +const n2 = n && s; +const n3 = n && x; +const n4 = n && b; +const n5 = n && v; +const n6 = n && u; +const n7 = n && n; +const n8 = n && z; + +const z1 = z && a; +const z2 = z && s; +const z3 = z && x; +const z4 = z && b; +const z5 = z && v; +const z6 = z && u; +const z7 = z && n; +const z8 = z && z; + +//// [logicalAndOperatorStrictMode.js] +var a = [0]; +var s = ""; +var x = 0; +var b = false; +var v = undefined; +var u = undefined; +var n = null; +var z = s || x || u; +var a1 = a && a; +var a2 = a && s; +var a3 = a && x; +var a4 = a && b; +var a5 = a && v; +var a6 = a && u; +var a7 = a && n; +var a8 = a && z; +var s1 = s && a; +var s2 = s && s; +var s3 = s && x; +var s4 = s && b; +var s5 = s && v; +var s6 = s && u; +var s7 = s && n; +var s8 = s && z; +var x1 = x && a; +var x2 = x && s; +var x3 = x && x; +var x4 = x && b; +var x5 = x && v; +var x6 = x && u; +var x7 = x && n; +var x8 = x && z; +var b1 = b && a; +var b2 = b && s; +var b3 = b && x; +var b4 = b && b; +var b5 = b && v; +var b6 = b && u; +var b7 = b && n; +var b8 = b && z; +var v1 = v && a; +var v2 = v && s; +var v3 = v && x; +var v4 = v && b; +var v5 = v && v; +var v6 = v && u; +var v7 = v && n; +var v8 = v && z; +var u1 = u && a; +var u2 = u && s; +var u3 = u && x; +var u4 = u && b; +var u5 = u && v; +var u6 = u && u; +var u7 = u && n; +var u8 = u && z; +var n1 = n && a; +var n2 = n && s; +var n3 = n && x; +var n4 = n && b; +var n5 = n && v; +var n6 = n && u; +var n7 = n && n; +var n8 = n && z; +var z1 = z && a; +var z2 = z && s; +var z3 = z && x; +var z4 = z && b; +var z5 = z && v; +var z6 = z && u; +var z7 = z && n; +var z8 = z && z; diff --git a/tests/baselines/reference/logicalAndOperatorStrictMode.symbols b/tests/baselines/reference/logicalAndOperatorStrictMode.symbols new file mode 100644 index 00000000000..e3425a26149 --- /dev/null +++ b/tests/baselines/reference/logicalAndOperatorStrictMode.symbols @@ -0,0 +1,351 @@ +=== tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts === + +const a = [0]; +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const s = ""; +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const x = 0; +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const b = false; +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const v: void = undefined; +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>undefined : Symbol(undefined) + +const u = undefined; +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>undefined : Symbol(undefined) + +const n = null; +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const z = s || x || u; +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const a1 = a && a; +>a1 : Symbol(a1, Decl(logicalAndOperatorStrictMode.ts, 10, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const a2 = a && s; +>a2 : Symbol(a2, Decl(logicalAndOperatorStrictMode.ts, 11, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const a3 = a && x; +>a3 : Symbol(a3, Decl(logicalAndOperatorStrictMode.ts, 12, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const a4 = a && b; +>a4 : Symbol(a4, Decl(logicalAndOperatorStrictMode.ts, 13, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const a5 = a && v; +>a5 : Symbol(a5, Decl(logicalAndOperatorStrictMode.ts, 14, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const a6 = a && u; +>a6 : Symbol(a6, Decl(logicalAndOperatorStrictMode.ts, 15, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const a7 = a && n; +>a7 : Symbol(a7, Decl(logicalAndOperatorStrictMode.ts, 16, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const a8 = a && z; +>a8 : Symbol(a8, Decl(logicalAndOperatorStrictMode.ts, 17, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const s1 = s && a; +>s1 : Symbol(s1, Decl(logicalAndOperatorStrictMode.ts, 19, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const s2 = s && s; +>s2 : Symbol(s2, Decl(logicalAndOperatorStrictMode.ts, 20, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const s3 = s && x; +>s3 : Symbol(s3, Decl(logicalAndOperatorStrictMode.ts, 21, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const s4 = s && b; +>s4 : Symbol(s4, Decl(logicalAndOperatorStrictMode.ts, 22, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const s5 = s && v; +>s5 : Symbol(s5, Decl(logicalAndOperatorStrictMode.ts, 23, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const s6 = s && u; +>s6 : Symbol(s6, Decl(logicalAndOperatorStrictMode.ts, 24, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const s7 = s && n; +>s7 : Symbol(s7, Decl(logicalAndOperatorStrictMode.ts, 25, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const s8 = s && z; +>s8 : Symbol(s8, Decl(logicalAndOperatorStrictMode.ts, 26, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const x1 = x && a; +>x1 : Symbol(x1, Decl(logicalAndOperatorStrictMode.ts, 28, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const x2 = x && s; +>x2 : Symbol(x2, Decl(logicalAndOperatorStrictMode.ts, 29, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const x3 = x && x; +>x3 : Symbol(x3, Decl(logicalAndOperatorStrictMode.ts, 30, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const x4 = x && b; +>x4 : Symbol(x4, Decl(logicalAndOperatorStrictMode.ts, 31, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const x5 = x && v; +>x5 : Symbol(x5, Decl(logicalAndOperatorStrictMode.ts, 32, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const x6 = x && u; +>x6 : Symbol(x6, Decl(logicalAndOperatorStrictMode.ts, 33, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const x7 = x && n; +>x7 : Symbol(x7, Decl(logicalAndOperatorStrictMode.ts, 34, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const x8 = x && z; +>x8 : Symbol(x8, Decl(logicalAndOperatorStrictMode.ts, 35, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const b1 = b && a; +>b1 : Symbol(b1, Decl(logicalAndOperatorStrictMode.ts, 37, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const b2 = b && s; +>b2 : Symbol(b2, Decl(logicalAndOperatorStrictMode.ts, 38, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const b3 = b && x; +>b3 : Symbol(b3, Decl(logicalAndOperatorStrictMode.ts, 39, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const b4 = b && b; +>b4 : Symbol(b4, Decl(logicalAndOperatorStrictMode.ts, 40, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const b5 = b && v; +>b5 : Symbol(b5, Decl(logicalAndOperatorStrictMode.ts, 41, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const b6 = b && u; +>b6 : Symbol(b6, Decl(logicalAndOperatorStrictMode.ts, 42, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const b7 = b && n; +>b7 : Symbol(b7, Decl(logicalAndOperatorStrictMode.ts, 43, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const b8 = b && z; +>b8 : Symbol(b8, Decl(logicalAndOperatorStrictMode.ts, 44, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const v1 = v && a; +>v1 : Symbol(v1, Decl(logicalAndOperatorStrictMode.ts, 46, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const v2 = v && s; +>v2 : Symbol(v2, Decl(logicalAndOperatorStrictMode.ts, 47, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const v3 = v && x; +>v3 : Symbol(v3, Decl(logicalAndOperatorStrictMode.ts, 48, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const v4 = v && b; +>v4 : Symbol(v4, Decl(logicalAndOperatorStrictMode.ts, 49, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const v5 = v && v; +>v5 : Symbol(v5, Decl(logicalAndOperatorStrictMode.ts, 50, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const v6 = v && u; +>v6 : Symbol(v6, Decl(logicalAndOperatorStrictMode.ts, 51, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const v7 = v && n; +>v7 : Symbol(v7, Decl(logicalAndOperatorStrictMode.ts, 52, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const v8 = v && z; +>v8 : Symbol(v8, Decl(logicalAndOperatorStrictMode.ts, 53, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const u1 = u && a; +>u1 : Symbol(u1, Decl(logicalAndOperatorStrictMode.ts, 55, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const u2 = u && s; +>u2 : Symbol(u2, Decl(logicalAndOperatorStrictMode.ts, 56, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const u3 = u && x; +>u3 : Symbol(u3, Decl(logicalAndOperatorStrictMode.ts, 57, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const u4 = u && b; +>u4 : Symbol(u4, Decl(logicalAndOperatorStrictMode.ts, 58, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const u5 = u && v; +>u5 : Symbol(u5, Decl(logicalAndOperatorStrictMode.ts, 59, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const u6 = u && u; +>u6 : Symbol(u6, Decl(logicalAndOperatorStrictMode.ts, 60, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const u7 = u && n; +>u7 : Symbol(u7, Decl(logicalAndOperatorStrictMode.ts, 61, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const u8 = u && z; +>u8 : Symbol(u8, Decl(logicalAndOperatorStrictMode.ts, 62, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const n1 = n && a; +>n1 : Symbol(n1, Decl(logicalAndOperatorStrictMode.ts, 64, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const n2 = n && s; +>n2 : Symbol(n2, Decl(logicalAndOperatorStrictMode.ts, 65, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const n3 = n && x; +>n3 : Symbol(n3, Decl(logicalAndOperatorStrictMode.ts, 66, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const n4 = n && b; +>n4 : Symbol(n4, Decl(logicalAndOperatorStrictMode.ts, 67, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const n5 = n && v; +>n5 : Symbol(n5, Decl(logicalAndOperatorStrictMode.ts, 68, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const n6 = n && u; +>n6 : Symbol(n6, Decl(logicalAndOperatorStrictMode.ts, 69, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const n7 = n && n; +>n7 : Symbol(n7, Decl(logicalAndOperatorStrictMode.ts, 70, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const n8 = n && z; +>n8 : Symbol(n8, Decl(logicalAndOperatorStrictMode.ts, 71, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const z1 = z && a; +>z1 : Symbol(z1, Decl(logicalAndOperatorStrictMode.ts, 73, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const z2 = z && s; +>z2 : Symbol(z2, Decl(logicalAndOperatorStrictMode.ts, 74, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const z3 = z && x; +>z3 : Symbol(z3, Decl(logicalAndOperatorStrictMode.ts, 75, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const z4 = z && b; +>z4 : Symbol(z4, Decl(logicalAndOperatorStrictMode.ts, 76, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const z5 = z && v; +>z5 : Symbol(z5, Decl(logicalAndOperatorStrictMode.ts, 77, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const z6 = z && u; +>z6 : Symbol(z6, Decl(logicalAndOperatorStrictMode.ts, 78, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const z7 = z && n; +>z7 : Symbol(z7, Decl(logicalAndOperatorStrictMode.ts, 79, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const z8 = z && z; +>z8 : Symbol(z8, Decl(logicalAndOperatorStrictMode.ts, 80, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + diff --git a/tests/baselines/reference/logicalAndOperatorStrictMode.types b/tests/baselines/reference/logicalAndOperatorStrictMode.types new file mode 100644 index 00000000000..ca225d4561a --- /dev/null +++ b/tests/baselines/reference/logicalAndOperatorStrictMode.types @@ -0,0 +1,423 @@ +=== tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts === + +const a = [0]; +>a : number[] +>[0] : number[] +>0 : number + +const s = ""; +>s : string +>"" : string + +const x = 0; +>x : number +>0 : number + +const b = false; +>b : boolean +>false : boolean + +const v: void = undefined; +>v : void +>undefined : undefined + +const u = undefined; +>u : undefined +>undefined : undefined + +const n = null; +>n : null +>null : null + +const z = s || x || u; +>z : string | number | undefined +>s || x || u : string | number | undefined +>s || x : string | number +>s : string +>x : number +>u : undefined + +const a1 = a && a; +>a1 : number[] +>a && a : number[] +>a : number[] +>a : number[] + +const a2 = a && s; +>a2 : string +>a && s : string +>a : number[] +>s : string + +const a3 = a && x; +>a3 : number +>a && x : number +>a : number[] +>x : number + +const a4 = a && b; +>a4 : boolean +>a && b : boolean +>a : number[] +>b : boolean + +const a5 = a && v; +>a5 : void +>a && v : void +>a : number[] +>v : void + +const a6 = a && u; +>a6 : undefined +>a && u : undefined +>a : number[] +>u : undefined + +const a7 = a && n; +>a7 : null +>a && n : null +>a : number[] +>n : null + +const a8 = a && z; +>a8 : string | number | undefined +>a && z : string | number | undefined +>a : number[] +>z : string | number | undefined + +const s1 = s && a; +>s1 : number[] | string +>s && a : number[] | string +>s : string +>a : number[] + +const s2 = s && s; +>s2 : string +>s && s : string +>s : string +>s : string + +const s3 = s && x; +>s3 : number | string +>s && x : number | string +>s : string +>x : number + +const s4 = s && b; +>s4 : boolean | string +>s && b : boolean | string +>s : string +>b : boolean + +const s5 = s && v; +>s5 : void | string +>s && v : void | string +>s : string +>v : void + +const s6 = s && u; +>s6 : string | undefined +>s && u : string | undefined +>s : string +>u : undefined + +const s7 = s && n; +>s7 : string | null +>s && n : string | null +>s : string +>n : null + +const s8 = s && z; +>s8 : string | number | undefined +>s && z : string | number | undefined +>s : string +>z : string | number | undefined + +const x1 = x && a; +>x1 : number[] | number +>x && a : number[] | number +>x : number +>a : number[] + +const x2 = x && s; +>x2 : string | number +>x && s : string | number +>x : number +>s : string + +const x3 = x && x; +>x3 : number +>x && x : number +>x : number +>x : number + +const x4 = x && b; +>x4 : boolean | number +>x && b : boolean | number +>x : number +>b : boolean + +const x5 = x && v; +>x5 : void | number +>x && v : void | number +>x : number +>v : void + +const x6 = x && u; +>x6 : number | undefined +>x && u : number | undefined +>x : number +>u : undefined + +const x7 = x && n; +>x7 : number | null +>x && n : number | null +>x : number +>n : null + +const x8 = x && z; +>x8 : string | number | undefined +>x && z : string | number | undefined +>x : number +>z : string | number | undefined + +const b1 = b && a; +>b1 : number[] | boolean +>b && a : number[] | boolean +>b : boolean +>a : number[] + +const b2 = b && s; +>b2 : string | boolean +>b && s : string | boolean +>b : boolean +>s : string + +const b3 = b && x; +>b3 : number | boolean +>b && x : number | boolean +>b : boolean +>x : number + +const b4 = b && b; +>b4 : boolean +>b && b : boolean +>b : boolean +>b : boolean + +const b5 = b && v; +>b5 : void | boolean +>b && v : void | boolean +>b : boolean +>v : void + +const b6 = b && u; +>b6 : boolean | undefined +>b && u : boolean | undefined +>b : boolean +>u : undefined + +const b7 = b && n; +>b7 : boolean | null +>b && n : boolean | null +>b : boolean +>n : null + +const b8 = b && z; +>b8 : string | number | boolean | undefined +>b && z : string | number | boolean | undefined +>b : boolean +>z : string | number | undefined + +const v1 = v && a; +>v1 : number[] | void +>v && a : number[] | void +>v : void +>a : number[] + +const v2 = v && s; +>v2 : string | void +>v && s : string | void +>v : void +>s : string + +const v3 = v && x; +>v3 : number | void +>v && x : number | void +>v : void +>x : number + +const v4 = v && b; +>v4 : boolean | void +>v && b : boolean | void +>v : void +>b : boolean + +const v5 = v && v; +>v5 : void +>v && v : void +>v : void +>v : never + +const v6 = v && u; +>v6 : void +>v && u : void +>v : void +>u : undefined + +const v7 = v && n; +>v7 : void | null +>v && n : void | null +>v : void +>n : null + +const v8 = v && z; +>v8 : string | number | void +>v && z : string | number | void +>v : void +>z : string | number | undefined + +const u1 = u && a; +>u1 : number[] | undefined +>u && a : number[] | undefined +>u : undefined +>a : number[] + +const u2 = u && s; +>u2 : string | undefined +>u && s : string | undefined +>u : undefined +>s : string + +const u3 = u && x; +>u3 : number | undefined +>u && x : number | undefined +>u : undefined +>x : number + +const u4 = u && b; +>u4 : boolean | undefined +>u && b : boolean | undefined +>u : undefined +>b : boolean + +const u5 = u && v; +>u5 : void +>u && v : void +>u : undefined +>v : void + +const u6 = u && u; +>u6 : undefined +>u && u : undefined +>u : undefined +>u : never + +const u7 = u && n; +>u7 : null | undefined +>u && n : null | undefined +>u : undefined +>n : null + +const u8 = u && z; +>u8 : string | number | undefined +>u && z : string | number | undefined +>u : undefined +>z : string | number | undefined + +const n1 = n && a; +>n1 : number[] | null +>n && a : number[] | null +>n : null +>a : number[] + +const n2 = n && s; +>n2 : string | null +>n && s : string | null +>n : null +>s : string + +const n3 = n && x; +>n3 : number | null +>n && x : number | null +>n : null +>x : number + +const n4 = n && b; +>n4 : boolean | null +>n && b : boolean | null +>n : null +>b : boolean + +const n5 = n && v; +>n5 : void | null +>n && v : void | null +>n : null +>v : void + +const n6 = n && u; +>n6 : null | undefined +>n && u : null | undefined +>n : null +>u : undefined + +const n7 = n && n; +>n7 : null +>n && n : null +>n : null +>n : never + +const n8 = n && z; +>n8 : string | number | null | undefined +>n && z : string | number | null | undefined +>n : null +>z : string | number | undefined + +const z1 = z && a; +>z1 : number[] | string | number | undefined +>z && a : number[] | string | number | undefined +>z : string | number | undefined +>a : number[] + +const z2 = z && s; +>z2 : string | number | undefined +>z && s : string | number | undefined +>z : string | number | undefined +>s : string + +const z3 = z && x; +>z3 : number | string | undefined +>z && x : number | string | undefined +>z : string | number | undefined +>x : number + +const z4 = z && b; +>z4 : boolean | string | number | undefined +>z && b : boolean | string | number | undefined +>z : string | number | undefined +>b : boolean + +const z5 = z && v; +>z5 : void | string | number +>z && v : void | string | number +>z : string | number | undefined +>v : void + +const z6 = z && u; +>z6 : string | number | undefined +>z && u : string | number | undefined +>z : string | number | undefined +>u : undefined + +const z7 = z && n; +>z7 : string | number | null | undefined +>z && n : string | number | null | undefined +>z : string | number | undefined +>n : null + +const z8 = z && z; +>z8 : string | number | undefined +>z && z : string | number | undefined +>z : string | number | undefined +>z : string | number + diff --git a/tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts b/tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts new file mode 100644 index 00000000000..a4ad5ca7719 --- /dev/null +++ b/tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts @@ -0,0 +1,82 @@ +// @strictNullChecks: true + +const a = [0]; +const s = ""; +const x = 0; +const b = false; +const v: void = undefined; +const u = undefined; +const n = null; +const z = s || x || u; + +const a1 = a && a; +const a2 = a && s; +const a3 = a && x; +const a4 = a && b; +const a5 = a && v; +const a6 = a && u; +const a7 = a && n; +const a8 = a && z; + +const s1 = s && a; +const s2 = s && s; +const s3 = s && x; +const s4 = s && b; +const s5 = s && v; +const s6 = s && u; +const s7 = s && n; +const s8 = s && z; + +const x1 = x && a; +const x2 = x && s; +const x3 = x && x; +const x4 = x && b; +const x5 = x && v; +const x6 = x && u; +const x7 = x && n; +const x8 = x && z; + +const b1 = b && a; +const b2 = b && s; +const b3 = b && x; +const b4 = b && b; +const b5 = b && v; +const b6 = b && u; +const b7 = b && n; +const b8 = b && z; + +const v1 = v && a; +const v2 = v && s; +const v3 = v && x; +const v4 = v && b; +const v5 = v && v; +const v6 = v && u; +const v7 = v && n; +const v8 = v && z; + +const u1 = u && a; +const u2 = u && s; +const u3 = u && x; +const u4 = u && b; +const u5 = u && v; +const u6 = u && u; +const u7 = u && n; +const u8 = u && z; + +const n1 = n && a; +const n2 = n && s; +const n3 = n && x; +const n4 = n && b; +const n5 = n && v; +const n6 = n && u; +const n7 = n && n; +const n8 = n && z; + +const z1 = z && a; +const z2 = z && s; +const z3 = z && x; +const z4 = z && b; +const z5 = z && v; +const z6 = z && u; +const z7 = z && n; +const z8 = z && z; \ No newline at end of file From 094f97e366ee0df43461bb813be8fe4a1df7dfc0 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 3 Jun 2016 06:22:34 -0700 Subject: [PATCH 18/48] Respond to PR comments --- src/compiler/binder.ts | 18 +++++++----------- src/compiler/checker.ts | 18 +++++------------- src/compiler/program.ts | 5 ++++- src/compiler/types.ts | 4 +++- 4 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d3363285afc..753e02c7a09 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1181,9 +1181,9 @@ namespace ts { lastContainer = next; } - function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { // Just call this directly so that the return type of this function stays "void". - declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); + return declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); } function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { @@ -1298,14 +1298,10 @@ namespace ts { } } + const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + if (pattern) { - // TODO: don't really need such a symbol in container.locals... - const symbol = declareSymbol(container.locals, undefined, node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); - file.patternAmbientModules = file.patternAmbientModules || []; - file.patternAmbientModules.push({ pattern, symbol }); - } - else { - declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + (file.patternAmbientModules || (file.patternAmbientModules = [])).push({ pattern, symbol }); } } } @@ -2084,10 +2080,10 @@ namespace ts { checkStrictModeFunctionName(node); if (inStrictMode) { checkStrictModeFunctionDeclaration(node); - return bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); } else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3e66cf1661f..149f21734a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1292,9 +1292,11 @@ namespace ts { return undefined; } - const patternModuleSymbol = getPatternAmbientModule(moduleName); - if (patternModuleSymbol) { - return getMergedSymbol(patternModuleSymbol); + if (patternAmbientModules) { + const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleName); + if (pattern) { + return getMergedSymbol(pattern.symbol); + } } if (moduleNotFoundError) { @@ -1304,16 +1306,6 @@ namespace ts { return undefined; } - /** Get an ambient module with a wildcard ("*") in it. */ - function getPatternAmbientModule(name: string): Symbol | undefined { - if (patternAmbientModules) { - const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, name); - if (pattern) { - return pattern.symbol; - } - } - } - // An external module with an 'export =' declaration resolves to the target of the 'export =' declaration, // and an external module with no 'export =' declaration resolves to the module itself. function resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 1677f61c15d..3267737a3e5 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -95,6 +95,7 @@ namespace ts { return compilerOptions.traceResolution && host.trace !== undefined; } + /* @internal */ export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { @@ -502,7 +503,7 @@ namespace ts { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - matchedPattern = matchPatternOrExact(Object.keys(state.compilerOptions.paths), moduleName); + matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { @@ -570,6 +571,7 @@ namespace ts { } /** Return the object corresponding to the best pattern to match `candidate`. */ + /* @internal */ export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { let matchedValue: T | undefined = undefined; // use length of prefix as betterness criteria @@ -592,6 +594,7 @@ namespace ts { endsWith(candidate, suffix); } + /* @internal */ export function tryParsePattern(pattern: string): Pattern | undefined { // This should be verified outside of here and a proper error thrown. Debug.assert(hasZeroOrOneAsteriskCharacter(pattern)); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a6f19f121cf..bcc4bbcf696 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2137,12 +2137,14 @@ namespace ts { } /** Represents a "prefix*suffix" pattern. */ + /* @internal */ export interface Pattern { prefix: string; suffix: string; } - + /** Used to track a `declare module "foo*"`-like declaration. */ + /* @internal */ export interface PatternAmbientModule { pattern: Pattern; symbol: Symbol; From d9ec5125bed869d848586ab9610395d8c78a7dc1 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Jun 2016 11:29:03 -0700 Subject: [PATCH 19/48] Support using string values in enums for CompilerOptions in transpile methods --- src/compiler/commandLineParser.ts | 2 +- src/services/services.ts | 36 +++++++++++++++++++++++++++--- tests/cases/unittests/transpile.ts | 33 ++++++++++++++++++++------- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 506a2d5bee4..55e7a2a3a2f 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -6,7 +6,7 @@ namespace ts { /* @internal */ - export let optionDeclarations: CommandLineOption[] = [ + export const optionDeclarations: CommandLineOption[] = [ { name: "charset", type: "string", diff --git a/src/services/services.ts b/src/services/services.ts index 34b521b3496..2664c430912 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1920,6 +1920,36 @@ namespace ts { sourceMapText?: string; } + /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ + function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]) { + options = clone(options); + + for (const opt of stringValuedEnums) { + if (!hasProperty(options, opt.name)) { + continue; + } + + const value = options[opt.name]; + // Value should be a key of opt.type + if (typeof value === "string") { + // If value is not a string, this will fail + options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); + } + else { + if (!forEachValue(opt.type, v => v === value)) { + // Supplied value isn't a valid enum value. + diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); + } + } + } + + return options; + } + + const stringValuedEnums = filter(optionDeclarations, o => { + return typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number"); + }); + /* * This function will compile source text from 'input' argument using specified compiler options. * If not options are provided - it will use a set of default compiler options. @@ -1930,7 +1960,9 @@ namespace ts { * - noResolve = true */ export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput { - const options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions(); + const diagnostics: Diagnostic[] = []; + + const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions(); options.isolatedModules = true; @@ -1988,9 +2020,7 @@ namespace ts { const program = createProgram([inputFileName], options, compilerHost); - let diagnostics: Diagnostic[]; if (transpileOptions.reportDiagnostics) { - diagnostics = []; addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); } diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index 904cc435c44..ca2c5939f35 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -9,11 +9,7 @@ namespace ts { expectedDiagnosticCodes?: number[]; } - function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) { - if (!expectedDiagnosticCodes) { - return; - } - + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = []) { for (let i = 0; i < expectedDiagnosticCodes.length; i++) { assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`); } @@ -26,7 +22,7 @@ namespace ts { if (!transpileOptions.compilerOptions) { transpileOptions.compilerOptions = {}; } - if (transpileOptions.compilerOptions.newLine === undefined) { // + if (transpileOptions.compilerOptions.newLine === undefined) { // use \r\n as default new line transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed; } @@ -292,13 +288,34 @@ var x = 0;`, const output = `"use strict";\nvar a = 10;\n`; test(input, { expectedOutput: output, - options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true }, - expectedDiagnosticCodes: [] + options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } }); }); it("Supports urls in file name", () => { test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "http://somewhere/directory//directory2/file.ts" } }); }); + + describe("String values for enums", () => { + it("Accepts strings instead of enum values", () => { + test(`export const x = 0`, { + options: { + compilerOptions: { + module: "es6", + // Capitalization and spaces ignored + target: " Es6 " + } + }, + expectedOutput: "export const x = 0;\r\n" + }); + }); + + it("Fails on bad value", () => { + test(``, { + options: { compilerOptions: { module: {} } }, + expectedDiagnosticCodes: [6046] + }); + }); + }); }); } From f215ac94c2741c79f512896125c1f43b018052a2 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Jun 2016 12:00:53 -0700 Subject: [PATCH 20/48] Add another test --- .../reference/ambientDeclarationsPatterns.errors.txt | 10 +++++++--- .../baselines/reference/ambientDeclarationsPatterns.js | 10 +++++++--- .../conformance/ambient/ambientDeclarationsPatterns.ts | 8 ++++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt index c197fe54f8d..8fc7e4a3d18 100644 --- a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt +++ b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt @@ -1,10 +1,10 @@ -tests/cases/conformance/ambient/declarations.d.ts(6,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character +tests/cases/conformance/ambient/declarations.d.ts(10,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character ==== tests/cases/conformance/ambient/user.ts (0 errors) ==== /// - import {foo} from "foobarbaz"; - foo(0); + import {foo, baz} from "foobarbaz"; + foo(baz); import {foos} from "foosball"; @@ -12,6 +12,10 @@ tests/cases/conformance/ambient/declarations.d.ts(6,16): error TS5061: Pattern ' declare module "foo*baz" { export function foo(n: number): void; } + // Augmentations still work + declare module "foo*baz" { + export const baz: number; + } // Should be an error declare module "too*many*asterisks" { } diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js index 056416f1086..ee9833a3a37 100644 --- a/tests/baselines/reference/ambientDeclarationsPatterns.js +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -4,6 +4,10 @@ declare module "foo*baz" { export function foo(n: number): void; } +// Augmentations still work +declare module "foo*baz" { + export const baz: number; +} // Should be an error declare module "too*many*asterisks" { } @@ -15,8 +19,8 @@ declare module "foos*" { //// [user.ts] /// -import {foo} from "foobarbaz"; -foo(0); +import {foo, baz} from "foobarbaz"; +foo(baz); import {foos} from "foosball"; @@ -25,4 +29,4 @@ import {foos} from "foosball"; "use strict"; /// var foobarbaz_1 = require("foobarbaz"); -foobarbaz_1.foo(0); +foobarbaz_1.foo(foobarbaz_1.baz); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts index 0d3b2e1c60e..43e373ba9de 100644 --- a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -2,6 +2,10 @@ declare module "foo*baz" { export function foo(n: number): void; } +// Augmentations still work +declare module "foo*baz" { + export const baz: number; +} // Should be an error declare module "too*many*asterisks" { } @@ -13,7 +17,7 @@ declare module "foos*" { // @Filename: user.ts /// -import {foo} from "foobarbaz"; -foo(0); +import {foo, baz} from "foobarbaz"; +foo(baz); import {foos} from "foosball"; From a918730df0919357fbe8accd88a873991ef11fa6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Jun 2016 12:17:11 -0700 Subject: [PATCH 21/48] factor out isJsxOrTsxExtension --- src/compiler/core.ts | 4 ++++ src/compiler/program.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ba81ee7d103..8a2040a83df 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -861,6 +861,10 @@ namespace ts { return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; } + export function isJsxOrTsxExtension(ext: string): boolean { + return ext === ".jsx" || ext === ".tsx"; + } + export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile; diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 0c060807b1f..5440b1a782b 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -646,7 +646,7 @@ namespace ts { return forEach(extensions, tryLoad); function tryLoad(ext: string): string { - if (state.skipTsx && (ext === ".jsx" || ext === ".tsx")) { + if (state.skipTsx && isJsxOrTsxExtension(ext)) { return undefined; } const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext; From 704f9874e058da4bad7bcb003710002c410570fd Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Jun 2016 12:28:36 -0700 Subject: [PATCH 22/48] Move to a conformance test --- .../{moduleResolution.js => moduleResolutionWithExtensions.js} | 2 +- ...esolution.symbols => moduleResolutionWithExtensions.symbols} | 0 ...ion.trace.json => moduleResolutionWithExtensions.trace.json} | 0 ...uleResolution.types => moduleResolutionWithExtensions.types} | 0 .../externalModules/moduleResolutionWithExtensions.ts} | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename tests/baselines/reference/{moduleResolution.js => moduleResolutionWithExtensions.js} (87%) rename tests/baselines/reference/{moduleResolution.symbols => moduleResolutionWithExtensions.symbols} (100%) rename tests/baselines/reference/{moduleResolution.trace.json => moduleResolutionWithExtensions.trace.json} (100%) rename tests/baselines/reference/{moduleResolution.types => moduleResolutionWithExtensions.types} (100%) rename tests/cases/{compiler/moduleResolution.ts => conformance/externalModules/moduleResolutionWithExtensions.ts} (100%) diff --git a/tests/baselines/reference/moduleResolution.js b/tests/baselines/reference/moduleResolutionWithExtensions.js similarity index 87% rename from tests/baselines/reference/moduleResolution.js rename to tests/baselines/reference/moduleResolutionWithExtensions.js index 101089b6355..df12a3531bc 100644 --- a/tests/baselines/reference/moduleResolution.js +++ b/tests/baselines/reference/moduleResolutionWithExtensions.js @@ -1,4 +1,4 @@ -//// [tests/cases/compiler/moduleResolution.ts] //// +//// [tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts] //// //// [a.ts] diff --git a/tests/baselines/reference/moduleResolution.symbols b/tests/baselines/reference/moduleResolutionWithExtensions.symbols similarity index 100% rename from tests/baselines/reference/moduleResolution.symbols rename to tests/baselines/reference/moduleResolutionWithExtensions.symbols diff --git a/tests/baselines/reference/moduleResolution.trace.json b/tests/baselines/reference/moduleResolutionWithExtensions.trace.json similarity index 100% rename from tests/baselines/reference/moduleResolution.trace.json rename to tests/baselines/reference/moduleResolutionWithExtensions.trace.json diff --git a/tests/baselines/reference/moduleResolution.types b/tests/baselines/reference/moduleResolutionWithExtensions.types similarity index 100% rename from tests/baselines/reference/moduleResolution.types rename to tests/baselines/reference/moduleResolutionWithExtensions.types diff --git a/tests/cases/compiler/moduleResolution.ts b/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts similarity index 100% rename from tests/cases/compiler/moduleResolution.ts rename to tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts From 1fe8a0808503db4f20c65b9d4f99bbe6ae9f582a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 6 Jun 2016 13:31:37 -0700 Subject: [PATCH 23/48] Respond to PR comments --- src/services/services.ts | 10 +++++----- tests/cases/unittests/transpile.ts | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 2664c430912..c64610c7509 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1920,11 +1920,15 @@ namespace ts { sourceMapText?: string; } + const commandLineOptions_stringToEnum = filter(optionDeclarations, o => { + return typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number"); + }); + /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]) { options = clone(options); - for (const opt of stringValuedEnums) { + for (const opt of commandLineOptions_stringToEnum) { if (!hasProperty(options, opt.name)) { continue; } @@ -1946,10 +1950,6 @@ namespace ts { return options; } - const stringValuedEnums = filter(optionDeclarations, o => { - return typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number"); - }); - /* * This function will compile source text from 'input' argument using specified compiler options. * If not options are provided - it will use a set of default compiler options. diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index ca2c5939f35..e694f943e1a 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -7,13 +7,17 @@ namespace ts { options?: TranspileOptions; expectedOutput?: string; expectedDiagnosticCodes?: number[]; + expectedDiagnosticTexts?: string[]; } - function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = []) { - for (let i = 0; i < expectedDiagnosticCodes.length; i++) { - assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`); + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = [], expectedDiagnosticTexts: string[] = []) { + const n = expectedDiagnosticCodes.length; + assert.equal(n, expectedDiagnosticTexts.length); + for (let i = 0; i < n; i++) { + assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expected diagnostic.`); + assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); } - assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected"); + assert.equal(diagnostics.length, n, "Resuting diagnostics count does not match expected"); } function test(input: string, testSettings: TranspileTestSettings): void { @@ -32,7 +36,7 @@ namespace ts { transpileOptions.reportDiagnostics = true; const transpileModuleResult = transpileModule(input, transpileOptions); - checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes, testSettings.expectedDiagnosticTexts); if (testSettings.expectedOutput !== undefined) { assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput); @@ -41,7 +45,7 @@ namespace ts { if (canUseOldTranspile) { const diagnostics: Diagnostic[] = []; const transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName); - checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes, testSettings.expectedDiagnosticTexts); if (testSettings.expectedOutput) { assert.equal(transpileResult, testSettings.expectedOutput); } @@ -313,7 +317,8 @@ var x = 0;`, it("Fails on bad value", () => { test(``, { options: { compilerOptions: { module: {} } }, - expectedDiagnosticCodes: [6046] + expectedDiagnosticCodes: [6046], + expectedDiagnosticTexts: ["Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'"] }); }); }); From 6a243e3d1d9a9d474fd4dc4c223e7bde99663915 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 7 Jun 2016 17:37:03 -0700 Subject: [PATCH 24/48] Allow nested assignments in type guards --- src/compiler/checker.ts | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 650bc488b0e..3c512de4277 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7662,6 +7662,21 @@ namespace ts { getInitialTypeOfBindingElement(node); } + function getReferenceFromExpression(node: Expression): Expression { + switch (node.kind) { + case SyntaxKind.ParenthesizedExpression: + return getReferenceFromExpression((node).expression); + case SyntaxKind.BinaryExpression: + switch ((node).operatorToken.kind) { + case SyntaxKind.EqualsToken: + return getReferenceFromExpression((node).left); + case SyntaxKind.CommaToken: + return getReferenceFromExpression((node).right); + } + } + return node; + } + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { @@ -7882,7 +7897,7 @@ namespace ts { if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (!strictNullChecks || !isMatchingReference(reference, expr.left)) { + if (!strictNullChecks || !isMatchingReference(reference, getReferenceFromExpression(expr.left))) { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; @@ -7897,12 +7912,12 @@ namespace ts { function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { // We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left // and string literal on the right - const left = expr.left; + const left = getReferenceFromExpression((expr.left).expression); const right = expr.right; - if (!isMatchingReference(reference, left.expression)) { + if (!isMatchingReference(reference, left)) { // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, left.expression)) { + if (containsMatchingReference(reference, left)) { return declaredType; } return type; @@ -7927,10 +7942,11 @@ namespace ts { } function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - if (!isMatchingReference(reference, expr.left)) { + const left = getReferenceFromExpression(expr.left); + if (!isMatchingReference(reference, left)) { // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, expr.left)) { + if (containsMatchingReference(reference, left)) { return declaredType; } return type; From cdf5b7aeb382be8ccc946a687be0c803c6ff89ab Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 7 Jun 2016 17:37:18 -0700 Subject: [PATCH 25/48] Add tests --- .../reference/typeGuardsNestedAssignments.js | 86 ++++++++++ .../typeGuardsNestedAssignments.symbols | 113 +++++++++++++ .../typeGuardsNestedAssignments.types | 155 ++++++++++++++++++ .../typeGuardsNestedAssignments.ts | 47 ++++++ 4 files changed, 401 insertions(+) create mode 100644 tests/baselines/reference/typeGuardsNestedAssignments.js create mode 100644 tests/baselines/reference/typeGuardsNestedAssignments.symbols create mode 100644 tests/baselines/reference/typeGuardsNestedAssignments.types create mode 100644 tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts diff --git a/tests/baselines/reference/typeGuardsNestedAssignments.js b/tests/baselines/reference/typeGuardsNestedAssignments.js new file mode 100644 index 00000000000..f37db041006 --- /dev/null +++ b/tests/baselines/reference/typeGuardsNestedAssignments.js @@ -0,0 +1,86 @@ +//// [typeGuardsNestedAssignments.ts] + +class Foo { + x: string; +} + +declare function getFooOrNull(): Foo | null; +declare function getStringOrNumberOrNull(): string | number | null; + +function f1() { + let foo: Foo | null; + if ((foo = getFooOrNull()) !== null) { + foo; // Foo + } +} + +function f2() { + let foo1: Foo | null; + let foo2: Foo | null; + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { + foo1; // Foo | null + foo2; // Foo + } +} + +function f3() { + let obj: Object | null; + if ((obj = getFooOrNull()) instanceof Foo) { + obj; + } +} + +function f4() { + let x: string | number | null; + if (typeof (x = getStringOrNumberOrNull()) === "number") { + x; + } +} + +// Repro from #8851 + +const re = /./g +let match: RegExpExecArray | null + +while ((match = re.exec("xxx")) != null) { + const length = match[1].length + match[2].length +} + +//// [typeGuardsNestedAssignments.js] +var Foo = (function () { + function Foo() { + } + return Foo; +}()); +function f1() { + var foo; + if ((foo = getFooOrNull()) !== null) { + foo; // Foo + } +} +function f2() { + var foo1; + var foo2; + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { + foo1; // Foo | null + foo2; // Foo + } +} +function f3() { + var obj; + if ((obj = getFooOrNull()) instanceof Foo) { + obj; + } +} +function f4() { + var x; + if (typeof (x = getStringOrNumberOrNull()) === "number") { + x; + } +} +// Repro from #8851 +var re = /./g; +var match; +while ((match = re.exec("xxx")) != null) { + var length = match[1].length + match[2].length; +} diff --git a/tests/baselines/reference/typeGuardsNestedAssignments.symbols b/tests/baselines/reference/typeGuardsNestedAssignments.symbols new file mode 100644 index 00000000000..1cca9c7de73 --- /dev/null +++ b/tests/baselines/reference/typeGuardsNestedAssignments.symbols @@ -0,0 +1,113 @@ +=== tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts === + +class Foo { +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + x: string; +>x : Symbol(Foo.x, Decl(typeGuardsNestedAssignments.ts, 1, 11)) +} + +declare function getFooOrNull(): Foo | null; +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + +declare function getStringOrNumberOrNull(): string | number | null; +>getStringOrNumberOrNull : Symbol(getStringOrNumberOrNull, Decl(typeGuardsNestedAssignments.ts, 5, 44)) + +function f1() { +>f1 : Symbol(f1, Decl(typeGuardsNestedAssignments.ts, 6, 67)) + + let foo: Foo | null; +>foo : Symbol(foo, Decl(typeGuardsNestedAssignments.ts, 9, 7)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + if ((foo = getFooOrNull()) !== null) { +>foo : Symbol(foo, Decl(typeGuardsNestedAssignments.ts, 9, 7)) +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) + + foo; // Foo +>foo : Symbol(foo, Decl(typeGuardsNestedAssignments.ts, 9, 7)) + } +} + +function f2() { +>f2 : Symbol(f2, Decl(typeGuardsNestedAssignments.ts, 13, 1)) + + let foo1: Foo | null; +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + let foo2: Foo | null; +>foo2 : Symbol(foo2, Decl(typeGuardsNestedAssignments.ts, 17, 7)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) +>foo2 : Symbol(foo2, Decl(typeGuardsNestedAssignments.ts, 17, 7)) +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) + + foo1; // Foo | null +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) + + foo2; // Foo +>foo2 : Symbol(foo2, Decl(typeGuardsNestedAssignments.ts, 17, 7)) + } +} + +function f3() { +>f3 : Symbol(f3, Decl(typeGuardsNestedAssignments.ts, 22, 1)) + + let obj: Object | null; +>obj : Symbol(obj, Decl(typeGuardsNestedAssignments.ts, 25, 7)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if ((obj = getFooOrNull()) instanceof Foo) { +>obj : Symbol(obj, Decl(typeGuardsNestedAssignments.ts, 25, 7)) +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + obj; +>obj : Symbol(obj, Decl(typeGuardsNestedAssignments.ts, 25, 7)) + } +} + +function f4() { +>f4 : Symbol(f4, Decl(typeGuardsNestedAssignments.ts, 29, 1)) + + let x: string | number | null; +>x : Symbol(x, Decl(typeGuardsNestedAssignments.ts, 32, 7)) + + if (typeof (x = getStringOrNumberOrNull()) === "number") { +>x : Symbol(x, Decl(typeGuardsNestedAssignments.ts, 32, 7)) +>getStringOrNumberOrNull : Symbol(getStringOrNumberOrNull, Decl(typeGuardsNestedAssignments.ts, 5, 44)) + + x; +>x : Symbol(x, Decl(typeGuardsNestedAssignments.ts, 32, 7)) + } +} + +// Repro from #8851 + +const re = /./g +>re : Symbol(re, Decl(typeGuardsNestedAssignments.ts, 40, 5)) + +let match: RegExpExecArray | null +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>RegExpExecArray : Symbol(RegExpExecArray, Decl(lib.d.ts, --, --)) + +while ((match = re.exec("xxx")) != null) { +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>re.exec : Symbol(RegExp.exec, Decl(lib.d.ts, --, --)) +>re : Symbol(re, Decl(typeGuardsNestedAssignments.ts, 40, 5)) +>exec : Symbol(RegExp.exec, Decl(lib.d.ts, --, --)) + + const length = match[1].length + match[2].length +>length : Symbol(length, Decl(typeGuardsNestedAssignments.ts, 44, 9)) +>match[1].length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>match[2].length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} diff --git a/tests/baselines/reference/typeGuardsNestedAssignments.types b/tests/baselines/reference/typeGuardsNestedAssignments.types new file mode 100644 index 00000000000..566a39a9c23 --- /dev/null +++ b/tests/baselines/reference/typeGuardsNestedAssignments.types @@ -0,0 +1,155 @@ +=== tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts === + +class Foo { +>Foo : Foo + + x: string; +>x : string +} + +declare function getFooOrNull(): Foo | null; +>getFooOrNull : () => Foo | null +>Foo : Foo +>null : null + +declare function getStringOrNumberOrNull(): string | number | null; +>getStringOrNumberOrNull : () => string | number | null +>null : null + +function f1() { +>f1 : () => void + + let foo: Foo | null; +>foo : Foo | null +>Foo : Foo +>null : null + + if ((foo = getFooOrNull()) !== null) { +>(foo = getFooOrNull()) !== null : boolean +>(foo = getFooOrNull()) : Foo | null +>foo = getFooOrNull() : Foo | null +>foo : Foo | null +>getFooOrNull() : Foo | null +>getFooOrNull : () => Foo | null +>null : null + + foo; // Foo +>foo : Foo + } +} + +function f2() { +>f2 : () => void + + let foo1: Foo | null; +>foo1 : Foo | null +>Foo : Foo +>null : null + + let foo2: Foo | null; +>foo2 : Foo | null +>Foo : Foo +>null : null + + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { +>(foo1 = getFooOrNull(), foo2 = foo1) !== null : boolean +>(foo1 = getFooOrNull(), foo2 = foo1) : Foo | null +>foo1 = getFooOrNull(), foo2 = foo1 : Foo | null +>foo1 = getFooOrNull() : Foo | null +>foo1 : Foo | null +>getFooOrNull() : Foo | null +>getFooOrNull : () => Foo | null +>foo2 = foo1 : Foo | null +>foo2 : Foo | null +>foo1 : Foo | null +>null : null + + foo1; // Foo | null +>foo1 : Foo | null + + foo2; // Foo +>foo2 : Foo + } +} + +function f3() { +>f3 : () => void + + let obj: Object | null; +>obj : Object | null +>Object : Object +>null : null + + if ((obj = getFooOrNull()) instanceof Foo) { +>(obj = getFooOrNull()) instanceof Foo : boolean +>(obj = getFooOrNull()) : Foo | null +>obj = getFooOrNull() : Foo | null +>obj : Object | null +>getFooOrNull() : Foo | null +>getFooOrNull : () => Foo | null +>Foo : typeof Foo + + obj; +>obj : Foo + } +} + +function f4() { +>f4 : () => void + + let x: string | number | null; +>x : string | number | null +>null : null + + if (typeof (x = getStringOrNumberOrNull()) === "number") { +>typeof (x = getStringOrNumberOrNull()) === "number" : boolean +>typeof (x = getStringOrNumberOrNull()) : string +>(x = getStringOrNumberOrNull()) : string | number | null +>x = getStringOrNumberOrNull() : string | number | null +>x : string | number | null +>getStringOrNumberOrNull() : string | number | null +>getStringOrNumberOrNull : () => string | number | null +>"number" : string + + x; +>x : number + } +} + +// Repro from #8851 + +const re = /./g +>re : RegExp +>/./g : RegExp + +let match: RegExpExecArray | null +>match : RegExpExecArray | null +>RegExpExecArray : RegExpExecArray +>null : null + +while ((match = re.exec("xxx")) != null) { +>(match = re.exec("xxx")) != null : boolean +>(match = re.exec("xxx")) : RegExpExecArray | null +>match = re.exec("xxx") : RegExpExecArray | null +>match : RegExpExecArray | null +>re.exec("xxx") : RegExpExecArray | null +>re.exec : (string: string) => RegExpExecArray | null +>re : RegExp +>exec : (string: string) => RegExpExecArray | null +>"xxx" : string +>null : null + + const length = match[1].length + match[2].length +>length : number +>match[1].length + match[2].length : number +>match[1].length : number +>match[1] : string +>match : RegExpExecArray +>1 : number +>length : number +>match[2].length : number +>match[2] : string +>match : RegExpExecArray +>2 : number +>length : number +} diff --git a/tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts b/tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts new file mode 100644 index 00000000000..41e3ffe572e --- /dev/null +++ b/tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts @@ -0,0 +1,47 @@ +// @strictNullChecks: true + +class Foo { + x: string; +} + +declare function getFooOrNull(): Foo | null; +declare function getStringOrNumberOrNull(): string | number | null; + +function f1() { + let foo: Foo | null; + if ((foo = getFooOrNull()) !== null) { + foo; // Foo + } +} + +function f2() { + let foo1: Foo | null; + let foo2: Foo | null; + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { + foo1; // Foo | null + foo2; // Foo + } +} + +function f3() { + let obj: Object | null; + if ((obj = getFooOrNull()) instanceof Foo) { + obj; + } +} + +function f4() { + let x: string | number | null; + if (typeof (x = getStringOrNumberOrNull()) === "number") { + x; + } +} + +// Repro from #8851 + +const re = /./g +let match: RegExpExecArray | null + +while ((match = re.exec("xxx")) != null) { + const length = match[1].length + match[2].length +} \ No newline at end of file From 83971d0900b48a6c88c287b154a2c74bdc6409d0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 8 Jun 2016 10:51:32 -0700 Subject: [PATCH 26/48] Create intersection types in type guards for unrelated types --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 650bc488b0e..ea494725fc6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7997,7 +7997,7 @@ namespace ts { const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; return isTypeAssignableTo(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - neverType; + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { From ea1bdff096cad9d21149f9f9713eb5e60e8949b5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 8 Jun 2016 11:41:34 -0700 Subject: [PATCH 27/48] Accept new baselines --- .../reference/instanceOfAssignability.types | 4 +-- .../stringLiteralTypesAsTags01.types | 4 +-- .../stringLiteralTypesAsTags02.types | 4 +-- .../stringLiteralTypesAsTags03.types | 4 +-- .../typeGuardsWithInstanceOf.errors.txt | 14 --------- .../typeGuardsWithInstanceOf.symbols | 26 ++++++++++++++++ .../reference/typeGuardsWithInstanceOf.types | 31 +++++++++++++++++++ 7 files changed, 65 insertions(+), 22 deletions(-) delete mode 100644 tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt create mode 100644 tests/baselines/reference/typeGuardsWithInstanceOf.symbols create mode 100644 tests/baselines/reference/typeGuardsWithInstanceOf.types diff --git a/tests/baselines/reference/instanceOfAssignability.types b/tests/baselines/reference/instanceOfAssignability.types index 70d068fb0cc..44ec45e2069 100644 --- a/tests/baselines/reference/instanceOfAssignability.types +++ b/tests/baselines/reference/instanceOfAssignability.types @@ -133,8 +133,8 @@ function fn5(x: Derived1) { // 1.5: y: Derived1 // Want: ??? let y = x; ->y : never ->x : never +>y : Derived1 & Derived2 +>x : Derived1 & Derived2 } } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.types b/tests/baselines/reference/stringLiteralTypesAsTags01.types index 7b8ac0a8c02..64b099b34f2 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.types @@ -116,6 +116,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : never ->x : never +>d : A & B +>x : A & B } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags02.types b/tests/baselines/reference/stringLiteralTypesAsTags02.types index 290402cd299..92b294a2498 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags02.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags02.types @@ -110,6 +110,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : never ->x : never +>d : A & B +>x : A & B } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags03.types b/tests/baselines/reference/stringLiteralTypesAsTags03.types index 9d004982dda..49ae3da4be0 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags03.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags03.types @@ -113,6 +113,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : never ->x : never +>d : A & B +>x : A & B } diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt deleted file mode 100644 index dfcb8a598da..00000000000 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt +++ /dev/null @@ -1,14 +0,0 @@ -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20): error TS2339: Property 'global' does not exist on type 'never'. - - -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (1 errors) ==== - interface I { global: string; } - var result: I; - var result2: I; - - if (!(result instanceof RegExp)) { - result = result2; - } else if (!result.global) { - ~~~~~~ -!!! error TS2339: Property 'global' does not exist on type 'never'. - } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols new file mode 100644 index 00000000000..6d1667a6519 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols @@ -0,0 +1,26 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts === +interface I { global: string; } +>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) +>global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13)) + +var result: I; +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) + +var result2: I; +>result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) +>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) + +if (!(result instanceof RegExp)) { +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + result = result2; +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) + +} else if (!result.global) { +>result.global : Symbol(global, Decl(typeGuardsWithInstanceOf.ts, 0, 13), Decl(lib.d.ts, --, --)) +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>global : Symbol(global, Decl(typeGuardsWithInstanceOf.ts, 0, 13), Decl(lib.d.ts, --, --)) +} diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types new file mode 100644 index 00000000000..f54498f93c8 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -0,0 +1,31 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts === +interface I { global: string; } +>I : I +>global : string + +var result: I; +>result : I +>I : I + +var result2: I; +>result2 : I +>I : I + +if (!(result instanceof RegExp)) { +>!(result instanceof RegExp) : boolean +>(result instanceof RegExp) : boolean +>result instanceof RegExp : boolean +>result : I +>RegExp : RegExpConstructor + + result = result2; +>result = result2 : I +>result : I +>result2 : I + +} else if (!result.global) { +>!result.global : boolean +>result.global : string & boolean +>result : I & RegExp +>global : string & boolean +} From a57ee29ff7f9ddef02f12d4540928b11adc9333f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 8 Jun 2016 11:41:44 -0700 Subject: [PATCH 28/48] Add tests --- .../reference/typeGuardIntersectionTypes.js | 181 ++++++++++ .../typeGuardIntersectionTypes.symbols | 262 +++++++++++++++ .../typeGuardIntersectionTypes.types | 310 ++++++++++++++++++ .../typeGuards/typeGuardIntersectionTypes.ts | 115 +++++++ 4 files changed, 868 insertions(+) create mode 100644 tests/baselines/reference/typeGuardIntersectionTypes.js create mode 100644 tests/baselines/reference/typeGuardIntersectionTypes.symbols create mode 100644 tests/baselines/reference/typeGuardIntersectionTypes.types create mode 100644 tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.js b/tests/baselines/reference/typeGuardIntersectionTypes.js new file mode 100644 index 00000000000..16d35c43889 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.js @@ -0,0 +1,181 @@ +//// [typeGuardIntersectionTypes.ts] + +interface X { + x: string; +} + +interface Y { + y: string; +} + +interface Z { + z: string; +} + +declare function isX(obj: any): obj is X; +declare function isY(obj: any): obj is Y; +declare function isZ(obj: any): obj is Z; + +function f1(obj: Object) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} + +// Repro from #8911 + +// two interfaces +interface A { + a: string; +} + +interface B { + b: string; +} + +// a type guard for B +function isB(toTest: any): toTest is B { + return toTest && toTest.b; +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { + if (isB(a)) { + return a; + } else { + return null; + } +} + +// Repro from #9011 + +declare function log(s: string): void; + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +interface Legged { legs: number; } +interface Winged { wings: boolean; } + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { + + // All beasts with legs + if (hasLegs(beast)) { + + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + log(`pegasus - 4 legs, wings`); + } + else if (beast.legs === 2) { // ERROR TS2339... + log(`bird - 2 legs, wings`); + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { + log(`quetzalcoatl - no legs, wings`) + } + else { + log(`snake - no legs, no wings`) + } + } +} + +function beastFoo(beast: Object) { + if (hasWings(beast) && hasLegs(beast)) { + beast // beast is Legged + // ideally, beast would be Winged && Legged here... + } + else { + beast + } + + if (hasLegs(beast) && hasWings(beast)) { + beast // beast is Winged + // ideally, beast would be Legged && Winged here... + } +} + +//// [typeGuardIntersectionTypes.js] +function f1(obj) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} +// a type guard for B +function isB(toTest) { + return toTest && toTest.b; +} +// a function that turns an A into an A & B +function union(a) { + if (isB(a)) { + return a; + } + else { + return null; + } +} +// Beast feature detection via user-defined type guards +function hasLegs(x) { return x && typeof x.legs === 'number'; } +function hasWings(x) { return x && !!x.wings; } +// Function to identify a given beast by detecting its features +function identifyBeast(beast) { + // All beasts with legs + if (hasLegs(beast)) { + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { + log("pegasus - 4 legs, wings"); + } + else if (beast.legs === 2) { + log("bird - 2 legs, wings"); + } + else { + log("unknown - " + beast.legs + " legs, wings"); // ERROR TS2339... + } + } + else { + log("manbearpig - " + beast.legs + " legs, no wings"); + } + } + else { + if (hasWings(beast)) { + log("quetzalcoatl - no legs, wings"); + } + else { + log("snake - no legs, no wings"); + } + } +} +function beastFoo(beast) { + if (hasWings(beast) && hasLegs(beast)) { + beast; // beast is Legged + } + else { + beast; + } + if (hasLegs(beast) && hasWings(beast)) { + beast; // beast is Winged + } +} diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols new file mode 100644 index 00000000000..c59d972cd30 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -0,0 +1,262 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts === + +interface X { +>X : Symbol(X, Decl(typeGuardIntersectionTypes.ts, 0, 0)) + + x: string; +>x : Symbol(X.x, Decl(typeGuardIntersectionTypes.ts, 1, 13)) +} + +interface Y { +>Y : Symbol(Y, Decl(typeGuardIntersectionTypes.ts, 3, 1)) + + y: string; +>y : Symbol(Y.y, Decl(typeGuardIntersectionTypes.ts, 5, 13)) +} + +interface Z { +>Z : Symbol(Z, Decl(typeGuardIntersectionTypes.ts, 7, 1)) + + z: string; +>z : Symbol(Z.z, Decl(typeGuardIntersectionTypes.ts, 9, 13)) +} + +declare function isX(obj: any): obj is X; +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 13, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 13, 21)) +>X : Symbol(X, Decl(typeGuardIntersectionTypes.ts, 0, 0)) + +declare function isY(obj: any): obj is Y; +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 14, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 14, 21)) +>Y : Symbol(Y, Decl(typeGuardIntersectionTypes.ts, 3, 1)) + +declare function isZ(obj: any): obj is Z; +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 15, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 15, 21)) +>Z : Symbol(Z, Decl(typeGuardIntersectionTypes.ts, 7, 1)) + +function f1(obj: Object) { +>f1 : Symbol(f1, Decl(typeGuardIntersectionTypes.ts, 15, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if (isX(obj) || isY(obj) || isZ(obj)) { +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + + obj; +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + } + if (isX(obj) && isY(obj) && isZ(obj)) { +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + + obj; +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + } +} + +// Repro from #8911 + +// two interfaces +interface A { +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) + + a: string; +>a : Symbol(A.a, Decl(typeGuardIntersectionTypes.ts, 29, 13)) +} + +interface B { +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + b: string; +>b : Symbol(B.b, Decl(typeGuardIntersectionTypes.ts, 33, 13)) +} + +// a type guard for B +function isB(toTest: any): toTest is B { +>isB : Symbol(isB, Decl(typeGuardIntersectionTypes.ts, 35, 1)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + return toTest && toTest.b; +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { +>union : Symbol(union, Decl(typeGuardIntersectionTypes.ts, 40, 1)) +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + if (isB(a)) { +>isB : Symbol(isB, Decl(typeGuardIntersectionTypes.ts, 35, 1)) +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) + + return a; +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) + + } else { + return null; + } +} + +// Repro from #9011 + +declare function log(s: string): void; +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>s : Symbol(s, Decl(typeGuardIntersectionTypes.ts, 53, 21)) + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) + +interface Legged { legs: number; } +>Legged : Symbol(Legged, Decl(typeGuardIntersectionTypes.ts, 56, 54)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + +interface Winged { wings: boolean; } +>Winged : Symbol(Winged, Decl(typeGuardIntersectionTypes.ts, 57, 37)) +>wings : Symbol(Winged.wings, Decl(typeGuardIntersectionTypes.ts, 58, 21)) + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>Legged : Symbol(Legged, Decl(typeGuardIntersectionTypes.ts, 56, 54)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>x.legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) + +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>Winged : Symbol(Winged, Decl(typeGuardIntersectionTypes.ts, 57, 37)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>x.wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { +>identifyBeast : Symbol(identifyBeast, Decl(typeGuardIntersectionTypes.ts, 62, 67)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) + + // All beasts with legs + if (hasLegs(beast)) { +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + // All winged beasts with legs + if (hasWings(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + + log(`pegasus - 4 legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else if (beast.legs === 2) { // ERROR TS2339... +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + + log(`bird - 2 legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + log(`quetzalcoatl - no legs, wings`) +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else { + log(`snake - no legs, no wings`) +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + } +} + +function beastFoo(beast: Object) { +>beastFoo : Symbol(beastFoo, Decl(typeGuardIntersectionTypes.ts, 98, 1)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if (hasWings(beast) && hasLegs(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + beast // beast is Legged +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + // ideally, beast would be Winged && Legged here... + } + else { + beast +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + } + + if (hasLegs(beast) && hasWings(beast)) { +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + beast // beast is Winged +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + // ideally, beast would be Legged && Winged here... + } +} diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types new file mode 100644 index 00000000000..4018036c5ce --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -0,0 +1,310 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts === + +interface X { +>X : X + + x: string; +>x : string +} + +interface Y { +>Y : Y + + y: string; +>y : string +} + +interface Z { +>Z : Z + + z: string; +>z : string +} + +declare function isX(obj: any): obj is X; +>isX : (obj: any) => obj is X +>obj : any +>obj : any +>X : X + +declare function isY(obj: any): obj is Y; +>isY : (obj: any) => obj is Y +>obj : any +>obj : any +>Y : Y + +declare function isZ(obj: any): obj is Z; +>isZ : (obj: any) => obj is Z +>obj : any +>obj : any +>Z : Z + +function f1(obj: Object) { +>f1 : (obj: Object) => void +>obj : Object +>Object : Object + + if (isX(obj) || isY(obj) || isZ(obj)) { +>isX(obj) || isY(obj) || isZ(obj) : boolean +>isX(obj) || isY(obj) : boolean +>isX(obj) : boolean +>isX : (obj: any) => obj is X +>obj : Object +>isY(obj) : boolean +>isY : (obj: any) => obj is Y +>obj : Object +>isZ(obj) : boolean +>isZ : (obj: any) => obj is Z +>obj : Object + + obj; +>obj : X | Y | Z + } + if (isX(obj) && isY(obj) && isZ(obj)) { +>isX(obj) && isY(obj) && isZ(obj) : boolean +>isX(obj) && isY(obj) : boolean +>isX(obj) : boolean +>isX : (obj: any) => obj is X +>obj : Object +>isY(obj) : boolean +>isY : (obj: any) => obj is Y +>obj : X +>isZ(obj) : boolean +>isZ : (obj: any) => obj is Z +>obj : X & Y + + obj; +>obj : X & Y & Z + } +} + +// Repro from #8911 + +// two interfaces +interface A { +>A : A + + a: string; +>a : string +} + +interface B { +>B : B + + b: string; +>b : string +} + +// a type guard for B +function isB(toTest: any): toTest is B { +>isB : (toTest: any) => toTest is B +>toTest : any +>toTest : any +>B : B + + return toTest && toTest.b; +>toTest && toTest.b : any +>toTest : any +>toTest.b : any +>toTest : any +>b : any +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { +>union : (a: A) => (A & B) | null +>a : A +>A : A +>A : A +>B : B +>null : null + + if (isB(a)) { +>isB(a) : boolean +>isB : (toTest: any) => toTest is B +>a : A + + return a; +>a : A & B + + } else { + return null; +>null : null + } +} + +// Repro from #9011 + +declare function log(s: string): void; +>log : (s: string) => void +>s : string + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +>Beast : Beast +>wings : boolean | undefined +>legs : number | undefined + +interface Legged { legs: number; } +>Legged : Legged +>legs : number + +interface Winged { wings: boolean; } +>Winged : Winged +>wings : boolean + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +>hasLegs : (x: Beast) => x is Legged +>x : Beast +>Beast : Beast +>x : any +>Legged : Legged +>x && typeof x.legs === 'number' : boolean +>x : Beast +>typeof x.legs === 'number' : boolean +>typeof x.legs : string +>x.legs : number | undefined +>x : Beast +>legs : number | undefined +>'number' : string + +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } +>hasWings : (x: Beast) => x is Winged +>x : Beast +>Beast : Beast +>x : any +>Winged : Winged +>x && !!x.wings : boolean +>x : Beast +>!!x.wings : boolean +>!x.wings : boolean +>x.wings : boolean | undefined +>x : Beast +>wings : boolean | undefined + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { +>identifyBeast : (beast: Beast) => void +>beast : Beast +>Beast : Beast + + // All beasts with legs + if (hasLegs(beast)) { +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Beast + + // All winged beasts with legs + if (hasWings(beast)) { +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Legged + + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. +>beast.legs === 4 : boolean +>beast.legs : number +>beast : Legged & Winged +>legs : number +>4 : number + + log(`pegasus - 4 legs, wings`); +>log(`pegasus - 4 legs, wings`) : void +>log : (s: string) => void +>`pegasus - 4 legs, wings` : string + } + else if (beast.legs === 2) { // ERROR TS2339... +>beast.legs === 2 : boolean +>beast.legs : number +>beast : Legged & Winged +>legs : number +>2 : number + + log(`bird - 2 legs, wings`); +>log(`bird - 2 legs, wings`) : void +>log : (s: string) => void +>`bird - 2 legs, wings` : string + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... +>log(`unknown - ${beast.legs} legs, wings`) : void +>log : (s: string) => void +>`unknown - ${beast.legs} legs, wings` : string +>beast.legs : number +>beast : Legged & Winged +>legs : number + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); +>log(`manbearpig - ${beast.legs} legs, no wings`) : void +>log : (s: string) => void +>`manbearpig - ${beast.legs} legs, no wings` : string +>beast.legs : number +>beast : Legged +>legs : number + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Beast + + log(`quetzalcoatl - no legs, wings`) +>log(`quetzalcoatl - no legs, wings`) : void +>log : (s: string) => void +>`quetzalcoatl - no legs, wings` : string + } + else { + log(`snake - no legs, no wings`) +>log(`snake - no legs, no wings`) : void +>log : (s: string) => void +>`snake - no legs, no wings` : string + } + } +} + +function beastFoo(beast: Object) { +>beastFoo : (beast: Object) => void +>beast : Object +>Object : Object + + if (hasWings(beast) && hasLegs(beast)) { +>hasWings(beast) && hasLegs(beast) : boolean +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Object +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Winged + + beast // beast is Legged +>beast : Winged & Legged + + // ideally, beast would be Winged && Legged here... + } + else { + beast +>beast : Object + } + + if (hasLegs(beast) && hasWings(beast)) { +>hasLegs(beast) && hasWings(beast) : boolean +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Object +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Legged + + beast // beast is Winged +>beast : Legged & Winged + + // ideally, beast would be Legged && Winged here... + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts new file mode 100644 index 00000000000..efde587e5e5 --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts @@ -0,0 +1,115 @@ +// @strictNullChecks: true + +interface X { + x: string; +} + +interface Y { + y: string; +} + +interface Z { + z: string; +} + +declare function isX(obj: any): obj is X; +declare function isY(obj: any): obj is Y; +declare function isZ(obj: any): obj is Z; + +function f1(obj: Object) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} + +// Repro from #8911 + +// two interfaces +interface A { + a: string; +} + +interface B { + b: string; +} + +// a type guard for B +function isB(toTest: any): toTest is B { + return toTest && toTest.b; +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { + if (isB(a)) { + return a; + } else { + return null; + } +} + +// Repro from #9011 + +declare function log(s: string): void; + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +interface Legged { legs: number; } +interface Winged { wings: boolean; } + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { + + // All beasts with legs + if (hasLegs(beast)) { + + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + log(`pegasus - 4 legs, wings`); + } + else if (beast.legs === 2) { // ERROR TS2339... + log(`bird - 2 legs, wings`); + } + else { + log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { + log(`quetzalcoatl - no legs, wings`) + } + else { + log(`snake - no legs, no wings`) + } + } +} + +function beastFoo(beast: Object) { + if (hasWings(beast) && hasLegs(beast)) { + beast // beast is Legged + // ideally, beast would be Winged && Legged here... + } + else { + beast + } + + if (hasLegs(beast) && hasWings(beast)) { + beast // beast is Winged + // ideally, beast would be Legged && Winged here... + } +} \ No newline at end of file From d8667ae323791902006b03440a59bf8ae4a5d7c4 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 8 Jun 2016 13:14:22 -0700 Subject: [PATCH 29/48] Fix test helper --- tests/cases/unittests/transpile.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index e694f943e1a..8231f3c9745 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -10,12 +10,16 @@ namespace ts { expectedDiagnosticTexts?: string[]; } - function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = [], expectedDiagnosticTexts: string[] = []) { + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = [], expectedDiagnosticTexts?: string[]) { const n = expectedDiagnosticCodes.length; - assert.equal(n, expectedDiagnosticTexts.length); + if (expectedDiagnosticTexts) { + assert.equal(n, expectedDiagnosticTexts.length); + } for (let i = 0; i < n; i++) { assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expected diagnostic.`); - assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); + if (expectedDiagnosticTexts) { + assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); + } } assert.equal(diagnostics.length, n, "Resuting diagnostics count does not match expected"); } From cc5dd5bf797dddb75304eedff9fd9aa61d39d7b1 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Wed, 8 Jun 2016 13:22:15 -0700 Subject: [PATCH 30/48] Add comments --- src/services/services.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/services.ts b/src/services/services.ts index 7c743aa86de..fa4333b7492 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4164,12 +4164,15 @@ namespace ts { const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { + // Get string literal completions from specialized signatures of the target return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo); } else if (isElementAccessExpression(node.parent) && node.parent.argumentExpression === node) { + // Get all names of properties on the expression return getStringLiteralCompletionEntriesFromElementAccess(node.parent); } else { + // Otherwise, get the completions from the contextual type if one exists return getStringLiteralCompletionEntriesFromContextualType(node); } } From de8ddd6e1431826a8be6c81c1be5cbac231b76ff Mon Sep 17 00:00:00 2001 From: Zhengbo Li Date: Wed, 8 Jun 2016 14:42:53 -0700 Subject: [PATCH 31/48] Return trace when exception happens --- src/server/session.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/server/session.ts b/src/server/session.ts index 7f3ba105999..0ec3d87e58c 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1207,7 +1207,11 @@ namespace ts.server { // Handle cancellation exceptions } this.logError(err, message); - this.output(undefined, request ? request.command : CommandNames.Unknown, request ? request.seq : 0, "Error processing request. " + err.message); + this.output( + undefined, + request ? request.command : CommandNames.Unknown, + request ? request.seq : 0, + "Error processing request. " + (err).message + "\n" + (err).stack); } } } From 5a0ae4ad2266d3c60e9d5033c2f9b5ec29a6736b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 8 Jun 2016 15:31:03 -0700 Subject: [PATCH 32/48] Remove Long-Done TODO AFAIK, the harness sources have been concatenated into `run.js` for as long as I've known. This stops executing them twice (and in turn makes debugging tests much easier, since you no longer have to debug into eval'd code). --- src/harness/harness.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 5fe5b84ac91..b17abc613a3 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1692,6 +1692,3 @@ namespace Harness { if (Error) (Error).stackTraceLimit = 1; } - -// TODO: not sure why Utils.evalFile isn't working with this, eventually will concat it like old compiler instead of eval -eval(Harness.tcServicesFile); From d182a5978223f80154f4bd1b54e191b3546cf8ba Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 8 Jun 2016 16:35:39 -0700 Subject: [PATCH 33/48] Fix comments in tests --- .../reference/typeGuardIntersectionTypes.js | 20 +++++++++---------- .../typeGuardIntersectionTypes.symbols | 16 ++++++--------- .../typeGuardIntersectionTypes.types | 16 ++++++--------- .../typeGuards/typeGuardIntersectionTypes.ts | 14 ++++++------- 4 files changed, 27 insertions(+), 39 deletions(-) diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.js b/tests/baselines/reference/typeGuardIntersectionTypes.js index 16d35c43889..6c46b982024 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.js +++ b/tests/baselines/reference/typeGuardIntersectionTypes.js @@ -71,14 +71,14 @@ function identifyBeast(beast: Beast) { // All winged beasts with legs if (hasWings(beast)) { - if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + if (beast.legs === 4) { log(`pegasus - 4 legs, wings`); } - else if (beast.legs === 2) { // ERROR TS2339... + else if (beast.legs === 2) { log(`bird - 2 legs, wings`); } else { - log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + log(`unknown - ${beast.legs} legs, wings`); } } @@ -101,16 +101,14 @@ function identifyBeast(beast: Beast) { function beastFoo(beast: Object) { if (hasWings(beast) && hasLegs(beast)) { - beast // beast is Legged - // ideally, beast would be Winged && Legged here... + beast; // Winged & Legged } else { - beast + beast; } if (hasLegs(beast) && hasWings(beast)) { - beast // beast is Winged - // ideally, beast would be Legged && Winged here... + beast; // Legged & Winged } } @@ -152,7 +150,7 @@ function identifyBeast(beast) { log("bird - 2 legs, wings"); } else { - log("unknown - " + beast.legs + " legs, wings"); // ERROR TS2339... + log("unknown - " + beast.legs + " legs, wings"); } } else { @@ -170,12 +168,12 @@ function identifyBeast(beast) { } function beastFoo(beast) { if (hasWings(beast) && hasLegs(beast)) { - beast; // beast is Legged + beast; // Winged & Legged } else { beast; } if (hasLegs(beast) && hasWings(beast)) { - beast; // beast is Winged + beast; // Legged & Winged } } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols index c59d972cd30..707fb95b339 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.symbols +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -176,7 +176,7 @@ function identifyBeast(beast: Beast) { >hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) - if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + if (beast.legs === 4) { >beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) >legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) @@ -184,7 +184,7 @@ function identifyBeast(beast: Beast) { log(`pegasus - 4 legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) } - else if (beast.legs === 2) { // ERROR TS2339... + else if (beast.legs === 2) { >beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) >legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) @@ -193,7 +193,7 @@ function identifyBeast(beast: Beast) { >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) } else { - log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + log(`unknown - ${beast.legs} legs, wings`); >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) >beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) @@ -238,13 +238,11 @@ function beastFoo(beast: Object) { >hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) - beast // beast is Legged + beast; // Winged & Legged >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) - - // ideally, beast would be Winged && Legged here... } else { - beast + beast; >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) } @@ -254,9 +252,7 @@ function beastFoo(beast: Object) { >hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) - beast // beast is Winged + beast; // Legged & Winged >beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) - - // ideally, beast would be Legged && Winged here... } } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types index 4018036c5ce..5ce768d4a5e 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.types +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -201,7 +201,7 @@ function identifyBeast(beast: Beast) { >hasWings : (x: Beast) => x is Winged >beast : Legged - if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + if (beast.legs === 4) { >beast.legs === 4 : boolean >beast.legs : number >beast : Legged & Winged @@ -213,7 +213,7 @@ function identifyBeast(beast: Beast) { >log : (s: string) => void >`pegasus - 4 legs, wings` : string } - else if (beast.legs === 2) { // ERROR TS2339... + else if (beast.legs === 2) { >beast.legs === 2 : boolean >beast.legs : number >beast : Legged & Winged @@ -226,7 +226,7 @@ function identifyBeast(beast: Beast) { >`bird - 2 legs, wings` : string } else { - log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + log(`unknown - ${beast.legs} legs, wings`); >log(`unknown - ${beast.legs} legs, wings`) : void >log : (s: string) => void >`unknown - ${beast.legs} legs, wings` : string @@ -283,13 +283,11 @@ function beastFoo(beast: Object) { >hasLegs : (x: Beast) => x is Legged >beast : Winged - beast // beast is Legged + beast; // Winged & Legged >beast : Winged & Legged - - // ideally, beast would be Winged && Legged here... } else { - beast + beast; >beast : Object } @@ -302,9 +300,7 @@ function beastFoo(beast: Object) { >hasWings : (x: Beast) => x is Winged >beast : Legged - beast // beast is Winged + beast; // Legged & Winged >beast : Legged & Winged - - // ideally, beast would be Legged && Winged here... } } diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts index efde587e5e5..d0a999acd1c 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts @@ -71,14 +71,14 @@ function identifyBeast(beast: Beast) { // All winged beasts with legs if (hasWings(beast)) { - if (beast.legs === 4) { // ERROR TS2339: Property 'legs' does not exist on type 'Winged'. + if (beast.legs === 4) { log(`pegasus - 4 legs, wings`); } - else if (beast.legs === 2) { // ERROR TS2339... + else if (beast.legs === 2) { log(`bird - 2 legs, wings`); } else { - log(`unknown - ${beast.legs} legs, wings`); // ERROR TS2339... + log(`unknown - ${beast.legs} legs, wings`); } } @@ -101,15 +101,13 @@ function identifyBeast(beast: Beast) { function beastFoo(beast: Object) { if (hasWings(beast) && hasLegs(beast)) { - beast // beast is Legged - // ideally, beast would be Winged && Legged here... + beast; // Winged & Legged } else { - beast + beast; } if (hasLegs(beast) && hasWings(beast)) { - beast // beast is Winged - // ideally, beast would be Legged && Winged here... + beast; // Legged & Winged } } \ No newline at end of file From 64468278864b466c9d29fb2638fc889d4e9d7ed0 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Wed, 8 Jun 2016 16:54:24 -0700 Subject: [PATCH 34/48] addExportToArgumentListKind --- src/services/signatureHelp.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 34c4ad5062a..b5df2b5af20 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -165,7 +165,7 @@ namespace ts.SignatureHelp { const emptyArray: any[] = []; - const enum ArgumentListKind { + export const enum ArgumentListKind { TypeArguments, CallArguments, TaggedTemplateArguments From 19baf1f38ab3cbf9ec463d932f86fd7c2ee693df Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Jun 2016 18:09:36 -0700 Subject: [PATCH 35/48] Adds progress indicators to the runtests-parallel build task. --- Jakefile.js | 48 +---- scripts/mocha-none-reporter.js | 26 +++ scripts/mocha-parallel.js | 367 +++++++++++++++++++++++++++++++++ 3 files changed, 404 insertions(+), 37 deletions(-) create mode 100644 scripts/mocha-none-reporter.js create mode 100644 scripts/mocha-parallel.js diff --git a/Jakefile.js b/Jakefile.js index a449dfcfa98..3b75dfb6adf 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -5,6 +5,7 @@ var os = require("os"); var path = require("path"); var child_process = require("child_process"); var Linter = require("tslint"); +var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel; // Variables var compilerDirectory = "src/compiler/"; @@ -683,7 +684,6 @@ function cleanTestDirs() { // used to pass data from jake command line directly to run.js 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); } @@ -744,42 +744,16 @@ function runConsoleTests(defaultReporter, runInParallel) { } else { - // run task to load all tests and partition them 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; - var firstErrorStatus; - // 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(e, errorStatus) { - counter--; - if (firstErrorStatus === undefined && errorStatus !== undefined) { - firstErrorStatus = errorStatus; - } - if (counter !== 0) { - complete(); - } - else { - // last worker clean everything and runs linter in case if there were no errors - deleteTemporaryProjectOutput(); - jake.rmRf(taskConfigsFolder); - if (firstErrorStatus === undefined) { - runLinter(); - complete(); - } - else { - failWithStatus(firstErrorStatus); - } - } + runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { + // last worker clean everything and runs linter in case if there were no errors + deleteTemporaryProjectOutput(); + jake.rmRf(taskConfigsFolder); + if (err) { + fail(err); + } + else { + runLinter(); + complete(); } }); } diff --git a/scripts/mocha-none-reporter.js b/scripts/mocha-none-reporter.js new file mode 100644 index 00000000000..5787b0c042e --- /dev/null +++ b/scripts/mocha-none-reporter.js @@ -0,0 +1,26 @@ +/** + * Module dependencies. + */ + +var Base = require('mocha').reporters.Base; + +/** + * Expose `None`. + */ + +exports = module.exports = None; + +/** + * Initialize a new `None` test reporter. + * + * @api public + * @param {Runner} runner + */ +function None(runner) { + Base.call(this); +} + +/** + * Inherit from `Base.prototype`. + */ +None.prototype.__proto__ = Base.prototype; diff --git a/scripts/mocha-parallel.js b/scripts/mocha-parallel.js new file mode 100644 index 00000000000..adba8ebf57c --- /dev/null +++ b/scripts/mocha-parallel.js @@ -0,0 +1,367 @@ +var tty = require("tty") + , readline = require("readline") + , fs = require("fs") + , path = require("path") + , child_process = require("child_process") + , os = require("os") + , mocha = require("mocha") + , Base = mocha.reporters.Base + , color = Base.color + , cursor = Base.cursor + , ms = require("mocha/lib/ms"); + +var isatty = tty.isatty(1) && tty.isatty(2); +var tapRangePattern = /^(\d+)\.\.(\d+)(?:$|\r\n?|\n)/; +var tapTestPattern = /^(not\sok|ok)\s+(\d+)\s+(?:-\s+)?(.*)$/; +var tapCommentPattern = /^#(?: (tests|pass|fail) (\d+)$)?/; + +exports.runTestsInParallel = runTestsInParallel; +exports.ProgressBars = ProgressBars; + +function runTestsInParallel(taskConfigsFolder, run, options, cb) { + if (options === undefined) options = { }; + + return discoverTests(run, options, function (error) { + if (error) { + return cb(error); + } + + return runTests(taskConfigsFolder, run, options, cb); + }); +} + +function discoverTests(run, options, cb) { + console.log("Discovering tests..."); + + var cmd = "mocha -R " + require.resolve("./mocha-none-reporter.js") + " " + run; + var p = child_process.spawn( + process.platform === "win32" ? "cmd" : "/bin/sh", + process.platform === "win32" ? ["/c", cmd] : ["-c", cmd], { + windowsVerbatimArguments: true, + env: { NODE_ENV: "development" } + }); + + p.on("exit", function (status) { + if (status) { + cb(new Error("Process exited width code " + status)); + } + else { + cb(); + } + }); +} + +function runTests(taskConfigsFolder, run, options, cb) { + var configFiles = fs.readdirSync(taskConfigsFolder); + var numPartitions = configFiles.length; + if (numPartitions <= 0) { + cb(); + return; + } + + console.log("Running tests on " + numPartitions + " threads..."); + + var partitions = Array(numPartitions); + var progressBars = new ProgressBars(); + progressBars.enable(); + + var counter = numPartitions; + configFiles.forEach(runTestsInPartition); + + function runTestsInPartition(file, index) { + var partition = { + file: path.join(taskConfigsFolder, file), + tests: 0, + passed: 0, + failed: 0, + completed: 0, + current: undefined, + start: undefined, + end: undefined, + failures: [] + }; + partitions[index] = partition; + + // Set up the progress bar. + updateProgress(0); + + // Start the background process. + var cmd = "mocha -t " + (options.testTimeout || 20000) + " -R tap --no-colors " + run + " --config='" + partition.file + "'"; + var p = child_process.spawn( + process.platform === "win32" ? "cmd" : "/bin/sh", + process.platform === "win32" ? ["/c", cmd] : ["-c", cmd], { + windowsVerbatimArguments: true, + env: { NODE_ENV: "development" } + }); + + var rl = readline.createInterface({ + input: p.stdout, + terminal: false + }); + + rl.on("line", onmessage); + p.on("exit", onexit) + + function onmessage(line) { + if (partition.start === undefined) { + partition.start = Date.now(); + } + + var rangeMatch = tapRangePattern.exec(line); + if (rangeMatch) { + partition.tests = parseInt(rangeMatch[2]); + return; + } + + var testMatch = tapTestPattern.exec(line); + if (testMatch) { + var test = { + result: testMatch[1], + id: parseInt(testMatch[2]), + name: testMatch[3], + output: [] + }; + + partition.current = test; + partition.completed++; + + if (test.result === "ok") { + partition.passed++; + } + else { + partition.failed++; + partition.failures.push(test); + } + + var progress = partition.completed / partition.tests; + if (progress < 1) { + updateProgress(progress); + } + + return; + } + + var commentMatch = tapCommentPattern.exec(line); + if (commentMatch) { + switch (commentMatch[1]) { + case "tests": + partition.current = undefined; + partition.tests = parseInt(commentMatch[2]); + break; + + case "pass": + partition.passed = parseInt(commentMatch[2]); + break; + + case "fail": + partition.failed = parseInt(commentMatch[2]); + break; + } + + return; + } + + if (partition.current) { + partition.current.output.push(line); + } + } + + function onexit() { + if (partition.end === undefined) { + partition.end = Date.now(); + } + + partition.duration = partition.end - partition.start; + var summaryColor = partition.failed ? "fail" : "green"; + var summarySymbol = partition.failed ? Base.symbols.err : Base.symbols.ok; + var summaryTests = (partition.passed === partition.tests ? partition.passed : partition.passed + "/" + partition.tests) + " passing"; + var summaryDuration = "(" + ms(partition.duration) + ")"; + var savedUseColors = Base.useColors; + Base.useColors = !options.noColors; + + var summary = color(summaryColor, summarySymbol + " " + summaryTests) + " " + color("light", summaryDuration); + Base.useColors = savedUseColors; + + updateProgress(1, summary); + + signal(); + } + + function updateProgress(percentComplete, title) { + var progressColor = "pending"; + if (partition.failed) { + progressColor = "fail"; + } + + progressBars.update( + index, + percentComplete, + progressColor, + title + ); + } + } + + function signal() { + counter--; + + if (counter <= 0) { + var failed = 0; + var reporter = new Base(), + stats = reporter.stats, + failures = reporter.failures; + + var duration = 0; + for (var i = 0; i < numPartitions; i++) { + var partition = partitions[i]; + stats.passes += partition.passed; + stats.failures += partition.failed; + stats.tests += partition.tests; + duration += partition.duration; + for (var j = 0; j < partition.failures.length; j++) { + var failure = partition.failures[j]; + failures.push(makeMochaTest(failure)); + } + } + + stats.duration = duration; + progressBars.disable(); + + if (options.noColors) { + var savedUseColors = Base.useColors; + Base.useColors = false; + reporter.epilogue(); + Base.useColors = savedUseColors; + } + else { + reporter.epilogue(); + } + + if (failed) { + return cb(new Error("Test failures reported: " + failed)); + } + else { + return cb(); + } + } + } + + function makeMochaTest(test) { + return { + fullTitle: function() { + return test.name; + }, + err: { + message: test.output[0], + stack: test.output.join(os.EOL) + } + }; + } +} + +function ProgressBars(options) { + if (!options) options = {}; + var open = options.open || '['; + var close = options.close || ']'; + var complete = options.complete || '▬'; + var incomplete = options.incomplete || Base.symbols.dot; + var maxWidth = Math.floor(Base.window.width * .30) - open.length - close.length - 2; + var width = minMax(options.width || maxWidth, 10, maxWidth); + this._options = { + open: open, + complete: complete, + incomplete: incomplete, + close: close, + width: width + }; + + this._progressBars = []; + this._lineCount = 0; + this._enabled = false; +} +ProgressBars.prototype = { + enable: function () { + if (!this._enabled) { + process.stdout.write(os.EOL); + this._enabled = true; + } + }, + disable: function () { + if (this._enabled) { + process.stdout.write(os.EOL); + this._enabled = false; + } + }, + update: function (index, percentComplete, color, title) { + percentComplete = minMax(percentComplete, 0, 1); + + var progressBar = this._progressBars[index] || (this._progressBars[index] = { }); + var width = this._options.width; + var n = Math.floor(width * percentComplete); + var i = width - n; + if (n === progressBar.lastN && title === progressBar.title && color === progressBar.progressColor) { + return; + } + + progressBar.lastN = n; + progressBar.title = title; + progressBar.progressColor = color; + + var progress = " "; + progress += this._color('progress', this._options.open); + progress += this._color(color, fill(this._options.complete, n)); + progress += this._color('progress', fill(this._options.incomplete, i)); + progress += this._color('progress', this._options.close); + + if (title) { + progress += this._color('progress', ' ' + title); + } + + if (progressBar.text !== progress) { + progressBar.text = progress; + this._render(index); + } + }, + _render: function (index) { + if (!this._enabled || !isatty) { + return; + } + + cursor.hide(); + readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount); + var lineCount = 0; + var numProgressBars = this._progressBars.length; + for (var i = 0; i < numProgressBars; i++) { + if (i === index) { + readline.clearLine(process.stdout, 1); + process.stdout.write(this._progressBars[i].text + os.EOL); + } + else { + readline.moveCursor(process.stdout, -process.stdout.columns, +1); + } + + lineCount++; + } + + this._lineCount = lineCount; + cursor.show(); + }, + _color: function (type, text) { + return type && !this._options.noColors ? color(type, text) : text; + } +}; + +function fill(ch, size) { + var s = ""; + while (s.length < size) { + s += ch; + } + + return s.length > size ? s.substr(0, size) : s; +} + +function minMax(value, min, max) { + if (value < min) return min; + if (value > max) return max; + return value; +} \ No newline at end of file From 8fc3422e7c67b28b2d46246a8a3008d899b79553 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 8 Jun 2016 18:18:26 -0700 Subject: [PATCH 36/48] Fixed typo --- scripts/mocha-parallel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mocha-parallel.js b/scripts/mocha-parallel.js index adba8ebf57c..159dd630cc2 100644 --- a/scripts/mocha-parallel.js +++ b/scripts/mocha-parallel.js @@ -43,7 +43,7 @@ function discoverTests(run, options, cb) { p.on("exit", function (status) { if (status) { - cb(new Error("Process exited width code " + status)); + cb(new Error("Process exited with code " + status)); } else { cb(); From b1a749895a26bcb1115eb93e953f27100c33bdb5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 8 Jun 2016 18:39:07 -0700 Subject: [PATCH 37/48] Fix comment --- tests/baselines/reference/typeGuardIntersectionTypes.js | 2 +- tests/baselines/reference/typeGuardIntersectionTypes.symbols | 2 +- tests/baselines/reference/typeGuardIntersectionTypes.types | 2 +- .../expressions/typeGuards/typeGuardIntersectionTypes.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.js b/tests/baselines/reference/typeGuardIntersectionTypes.js index 6c46b982024..6de7d06e323 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.js +++ b/tests/baselines/reference/typeGuardIntersectionTypes.js @@ -50,7 +50,7 @@ function union(a: A): A & B | null { } } -// Repro from #9011 +// Repro from #9016 declare function log(s: string): void; diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols index 707fb95b339..ea8e903c765 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.symbols +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -117,7 +117,7 @@ function union(a: A): A & B | null { } } -// Repro from #9011 +// Repro from #9016 declare function log(s: string): void; >log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types index 5ce768d4a5e..8be50453572 100644 --- a/tests/baselines/reference/typeGuardIntersectionTypes.types +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -133,7 +133,7 @@ function union(a: A): A & B | null { } } -// Repro from #9011 +// Repro from #9016 declare function log(s: string): void; >log : (s: string) => void diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts index d0a999acd1c..85c003787fd 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts @@ -50,7 +50,7 @@ function union(a: A): A & B | null { } } -// Repro from #9011 +// Repro from #9016 declare function log(s: string): void; From 0aa2fe328f938e0f23dd45e3d596754de942cb92 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 05:59:26 -0700 Subject: [PATCH 38/48] Add test for out-of-range error --- tests/cases/unittests/transpile.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index 8231f3c9745..a4b1416ed29 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -20,7 +20,7 @@ namespace ts { if (expectedDiagnosticTexts) { assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); } - } + }; assert.equal(diagnostics.length, n, "Resuting diagnostics count does not match expected"); } @@ -319,11 +319,13 @@ var x = 0;`, }); it("Fails on bad value", () => { - test(``, { - options: { compilerOptions: { module: {} } }, - expectedDiagnosticCodes: [6046], - expectedDiagnosticTexts: ["Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'"] - }); + for (const value in [123, {}, ""]) { + test(``, { + options: { compilerOptions: { module: value } }, + expectedDiagnosticCodes: [6046], + expectedDiagnosticTexts: ["Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'"] + }); + } }); }); }); From b08fe6f4f6eb67936d7ea917d5cef974ff897ee3 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 06:39:59 -0700 Subject: [PATCH 39/48] Use proper method of not resolving alias --- src/compiler/checker.ts | 14 ++--- tests/cases/fourslash/quickInfoMeaning.ts | 67 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 10 deletions(-) create mode 100644 tests/cases/fourslash/quickInfoMeaning.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65335117cf2..e2f832c9580 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1207,7 +1207,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol { + function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors = false, dontResolveAlias = false): Symbol { if (nodeIsMissing(name)) { return undefined; } @@ -16873,10 +16873,7 @@ namespace ts { return getIntrinsicTagSymbol(entityName.parent); } - // Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead - // return the alias symbol. - const meaning: SymbolFlags = SymbolFlags.Value | SymbolFlags.Alias; - return resolveEntityName(entityName, meaning); + return resolveEntityName(entityName, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); } else if (entityName.kind === SyntaxKind.PropertyAccessExpression) { const symbol = getNodeLinks(entityName).resolvedSymbol; @@ -16894,11 +16891,8 @@ namespace ts { } } else if (isTypeReferenceIdentifier(entityName)) { - 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; - return resolveEntityName(entityName, meaning); + const meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace; + return resolveEntityName(entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/true); } else if (entityName.parent.kind === SyntaxKind.JsxAttribute) { return getJsxAttributePropertySymbol(entityName.parent); diff --git a/tests/cases/fourslash/quickInfoMeaning.ts b/tests/cases/fourslash/quickInfoMeaning.ts new file mode 100644 index 00000000000..51b17337486 --- /dev/null +++ b/tests/cases/fourslash/quickInfoMeaning.ts @@ -0,0 +1,67 @@ +/// + +// Testing that quickInfo gets information with a corresponding meaning: values to values, types to types. +// For quick info purposes, we don't resolve past aliases. +// However, when we have an alias for a type, the quickInfo for a value with the same should skip the alias, and vice versa. +// goToDefinition should work the same way. + +// @Filename: foo.d.ts +////declare const /*foo_value_declaration*/foo: number; +////declare module "foo_module" { +//// interface I { x: number; y: number } +//// export = I; +////} + +// @Filename: foo_user.ts +/////// +/////*foo_type_declaration*/import foo = require("foo_module"); +////const x = foo/*foo_value*/; +////const i: foo/*foo_type*/ = { x: 1, y: 2 }; + +verify.numberOfErrorsInCurrentFile(0); + +verify.navigationItemsListCount(2, "foo", "exact"); +verify.navigationItemsListContains("foo", "alias", "foo", "exact"); +verify.navigationItemsListContains("foo", "const", "foo", "exact"); + +goTo.marker("foo_value"); +verify.quickInfoIs("const foo: number"); +goTo.definition(); +verify.caretAtMarker("foo_value_declaration"); + +goTo.marker("foo_type"); +verify.quickInfoIs("import foo = require(\"foo_module\")"); +goTo.definition(); +verify.caretAtMarker("foo_type_declaration"); + + +// Above tested for global const and imported interface. Now test with global interface and imported const. + + +// @Filename: bar.d.ts +/////*bar_type_declaration*/declare interface bar { x: number; y: number } +////declare module "bar_module" { +//// const x: number; +//// export = x; +////} + +// @Filename: bar_user.ts +/////// +/////*bar_value_declaration*/import bar = require("bar_module"); +////const x = bar/*bar_value*/; +////const i: bar/*bar_type*/ = { x: 1, y: 2 }; + +verify.numberOfErrorsInCurrentFile(0); +verify.navigationItemsListCount(2, "bar", "exact"); +verify.navigationItemsListContains("bar", "alias", "bar", "exact"); +verify.navigationItemsListContains("bar", "interface", "bar", "exact"); + +goTo.marker("bar_value"); +verify.quickInfoIs("import bar = require(\"bar_module\")"); +goTo.definition(); +verify.caretAtMarker("bar_value_declaration"); + +goTo.marker("bar_type"); +verify.quickInfoIs("interface bar"); +goTo.definition(); +verify.caretAtMarker("bar_type_declaration"); From 9ef9b743d10e26d0eda593a086b7516c82774e3c Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 08:22:58 -0700 Subject: [PATCH 40/48] Fix module loading error (commandLineOptions_stringToEnum would be undefined if optionDeclarations wasn't loaded yet) --- src/services/services.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index c64610c7509..0002bb1bacb 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1920,12 +1920,16 @@ namespace ts { sourceMapText?: string; } - const commandLineOptions_stringToEnum = filter(optionDeclarations, o => { - return typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number"); - }); + + + let commandLineOptions_stringToEnum: CommandLineOptionOfCustomType[]; /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ - function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]) { + function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { + // Lazily create this value to fix module loading errors. + commandLineOptions_stringToEnum = commandLineOptions_stringToEnum || filter(optionDeclarations, o => + typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number")); + options = clone(options); for (const opt of commandLineOptions_stringToEnum) { From 3ab1e273178f6387cd757d040b783f2244701801 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 09:20:15 -0700 Subject: [PATCH 41/48] Update tests --- tests/baselines/reference/typeReferenceDirectives13.symbols | 2 +- tests/baselines/reference/typeReferenceDirectives5.symbols | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/typeReferenceDirectives13.symbols b/tests/baselines/reference/typeReferenceDirectives13.symbols index 90a32b16341..396cde9c3b4 100644 --- a/tests/baselines/reference/typeReferenceDirectives13.symbols +++ b/tests/baselines/reference/typeReferenceDirectives13.symbols @@ -8,7 +8,7 @@ export interface A { x: () => typeof $ >x : Symbol(A.x, Decl(app.ts, 2, 20)) ->$ : Symbol($, Decl(app.ts, 1, 8)) +>$ : Symbol($, Decl(index.d.ts, 0, 11)) } === /ref.d.ts === diff --git a/tests/baselines/reference/typeReferenceDirectives5.symbols b/tests/baselines/reference/typeReferenceDirectives5.symbols index fbbb06d6834..16f7b4ce808 100644 --- a/tests/baselines/reference/typeReferenceDirectives5.symbols +++ b/tests/baselines/reference/typeReferenceDirectives5.symbols @@ -8,7 +8,7 @@ export interface A { x: typeof $; >x : Symbol(A.x, Decl(app.ts, 2, 20)) ->$ : Symbol($, Decl(app.ts, 1, 8)) +>$ : Symbol($, Decl(index.d.ts, 0, 11)) } === /ref.d.ts === From 7a2ef428cdfdf476975518df40102d8bc16aabf5 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 10:55:45 -0700 Subject: [PATCH 42/48] Contextually type return statement in async function --- src/compiler/checker.ts | 10 ++++++ .../reference/asyncFunctionReturnType.js | 33 +++++++++++++++++++ .../reference/asyncFunctionReturnType.symbols | 16 +++++++++ .../reference/asyncFunctionReturnType.types | 22 +++++++++++++ .../cases/compiler/asyncFunctionReturnType.ts | 10 ++++++ 5 files changed, 91 insertions(+) create mode 100644 tests/baselines/reference/asyncFunctionReturnType.js create mode 100644 tests/baselines/reference/asyncFunctionReturnType.symbols create mode 100644 tests/baselines/reference/asyncFunctionReturnType.types create mode 100644 tests/cases/compiler/asyncFunctionReturnType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65335117cf2..ac378ef5fd8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8756,6 +8756,16 @@ namespace ts { function getContextualTypeForReturnExpression(node: Expression): Type { const func = getContainingFunction(node); + + if (isAsyncFunctionLike(func)) { + const contextualReturnType = getContextualReturnType(func); + if (contextualReturnType) { + return getPromisedType(contextualReturnType); + } + + return undefined; + } + if (func && !func.asteriskToken) { return getContextualReturnType(func); } diff --git a/tests/baselines/reference/asyncFunctionReturnType.js b/tests/baselines/reference/asyncFunctionReturnType.js new file mode 100644 index 00000000000..12cb44fbcf0 --- /dev/null +++ b/tests/baselines/reference/asyncFunctionReturnType.js @@ -0,0 +1,33 @@ +//// [asyncFunctionReturnType.ts] +async function fAsync() { + // Without explicit type annotation, this is just an array. + return [1, true]; +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { + // This is contextually typed as a tuple. + return [1, true]; +} + + +//// [asyncFunctionReturnType.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function fAsync() { + return __awaiter(this, void 0, void 0, function* () { + // Without explicit type annotation, this is just an array. + return [1, true]; + }); +} +function fAsyncExplicit() { + return __awaiter(this, void 0, void 0, function* () { + // This is contextually typed as a tuple. + return [1, true]; + }); +} diff --git a/tests/baselines/reference/asyncFunctionReturnType.symbols b/tests/baselines/reference/asyncFunctionReturnType.symbols new file mode 100644 index 00000000000..75e456b6a8b --- /dev/null +++ b/tests/baselines/reference/asyncFunctionReturnType.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/asyncFunctionReturnType.ts === +async function fAsync() { +>fAsync : Symbol(fAsync, Decl(asyncFunctionReturnType.ts, 0, 0)) + + // Without explicit type annotation, this is just an array. + return [1, true]; +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { +>fAsyncExplicit : Symbol(fAsyncExplicit, Decl(asyncFunctionReturnType.ts, 3, 1)) +>Promise : Symbol(Promise, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --)) + + // This is contextually typed as a tuple. + return [1, true]; +} + diff --git a/tests/baselines/reference/asyncFunctionReturnType.types b/tests/baselines/reference/asyncFunctionReturnType.types new file mode 100644 index 00000000000..c6289ab012d --- /dev/null +++ b/tests/baselines/reference/asyncFunctionReturnType.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/asyncFunctionReturnType.ts === +async function fAsync() { +>fAsync : () => Promise<(number | boolean)[]> + + // Without explicit type annotation, this is just an array. + return [1, true]; +>[1, true] : (number | boolean)[] +>1 : number +>true : boolean +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { +>fAsyncExplicit : () => Promise<[number, boolean]> +>Promise : Promise + + // This is contextually typed as a tuple. + return [1, true]; +>[1, true] : [number, boolean] +>1 : number +>true : boolean +} + diff --git a/tests/cases/compiler/asyncFunctionReturnType.ts b/tests/cases/compiler/asyncFunctionReturnType.ts new file mode 100644 index 00000000000..a1bcde38079 --- /dev/null +++ b/tests/cases/compiler/asyncFunctionReturnType.ts @@ -0,0 +1,10 @@ +// @target: ES6 +async function fAsync() { + // Without explicit type annotation, this is just an array. + return [1, true]; +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { + // This is contextually typed as a tuple. + return [1, true]; +} From 5e72b8a098d97f6d18444ce787c72077faa4dddd Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 10:58:38 -0700 Subject: [PATCH 43/48] Undo change --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e2f832c9580..2cfd0ca94c0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1207,7 +1207,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors = false, dontResolveAlias = false): Symbol { + function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol { if (nodeIsMissing(name)) { return undefined; } From 03371c0e6f20d49fda583ff96a52e9d0c4967460 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 11:12:12 -0700 Subject: [PATCH 44/48] Improve perf --- src/compiler/checker.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 149f21734a4..8e5d5e61b75 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11291,7 +11291,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)); @@ -16154,12 +16154,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) | @@ -16358,7 +16358,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; @@ -17063,10 +17063,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); @@ -17099,10 +17099,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 @@ -17642,7 +17642,7 @@ namespace ts { mergeSymbolTable(globals, file.locals); } if (file.patternAmbientModules && file.patternAmbientModules.length) { - (patternAmbientModules || (patternAmbientModules = [])).push(...file.patternAmbientModules); + patternAmbientModules = concatenate(patternAmbientModules, file.patternAmbientModules); } if (file.moduleAugmentations.length) { From 559b49baa9380bfd77681aa466444e1d9c37d5cf Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 9 Jun 2016 11:33:53 -0700 Subject: [PATCH 45/48] Improve tests --- .../ambientDeclarationsPatterns.errors.txt | 29 ---------- .../reference/ambientDeclarationsPatterns.js | 24 ++++++--- .../ambientDeclarationsPatterns.symbols | 51 ++++++++++++++++++ .../ambientDeclarationsPatterns.types | 54 +++++++++++++++++++ ...ationsPatterns_tooManyAsterisks.errors.txt | 8 +++ ...ntDeclarationsPatterns_tooManyAsterisks.js | 5 ++ .../ambient/ambientDeclarationsPatterns.ts | 19 ++++--- ...ntDeclarationsPatterns_tooManyAsterisks.ts | 1 + 8 files changed, 150 insertions(+), 41 deletions(-) delete mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.errors.txt create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.symbols create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns.types create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt create mode 100644 tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js create mode 100644 tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt deleted file mode 100644 index 8fc7e4a3d18..00000000000 --- a/tests/baselines/reference/ambientDeclarationsPatterns.errors.txt +++ /dev/null @@ -1,29 +0,0 @@ -tests/cases/conformance/ambient/declarations.d.ts(10,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character - - -==== tests/cases/conformance/ambient/user.ts (0 errors) ==== - /// - import {foo, baz} from "foobarbaz"; - foo(baz); - - import {foos} from "foosball"; - -==== tests/cases/conformance/ambient/declarations.d.ts (1 errors) ==== - declare module "foo*baz" { - export function foo(n: number): void; - } - // Augmentations still work - declare module "foo*baz" { - export const baz: number; - } - - // Should be an error - declare module "too*many*asterisks" { } - ~~~~~~~~~~~~~~~~~~~~ -!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character - - // Longest prefix wins - declare module "foos*" { - export const foos: number; - } - \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js index ee9833a3a37..143f26550e9 100644 --- a/tests/baselines/reference/ambientDeclarationsPatterns.js +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -2,19 +2,21 @@ //// [declarations.d.ts] declare module "foo*baz" { - export function foo(n: number): void; + export function foo(s: string): void; } // Augmentations still work declare module "foo*baz" { - export const baz: number; + export const baz: string; } -// Should be an error -declare module "too*many*asterisks" { } - // Longest prefix wins declare module "foos*" { - export const foos: number; + export const foos: string; +} + +declare module "*!text" { + const x: string; + export default x; } //// [user.ts] @@ -23,6 +25,11 @@ import {foo, baz} from "foobarbaz"; foo(baz); import {foos} from "foosball"; +foo(foos); + +// Works with relative file name +import fileText from "./file!text"; +foo(fileText); //// [user.js] @@ -30,3 +37,8 @@ import {foos} from "foosball"; /// var foobarbaz_1 = require("foobarbaz"); foobarbaz_1.foo(foobarbaz_1.baz); +var foosball_1 = require("foosball"); +foobarbaz_1.foo(foosball_1.foos); +// Works with relative file name +var file_text_1 = require("./file!text"); +foobarbaz_1.foo(file_text_1["default"]); diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.symbols b/tests/baselines/reference/ambientDeclarationsPatterns.symbols new file mode 100644 index 00000000000..4c0acc93f8f --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.symbols @@ -0,0 +1,51 @@ +=== tests/cases/conformance/ambient/user.ts === +/// +import {foo, baz} from "foobarbaz"; +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>baz : Symbol(baz, Decl(user.ts, 1, 12)) + +foo(baz); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>baz : Symbol(baz, Decl(user.ts, 1, 12)) + +import {foos} from "foosball"; +>foos : Symbol(foos, Decl(user.ts, 4, 8)) + +foo(foos); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>foos : Symbol(foos, Decl(user.ts, 4, 8)) + +// Works with relative file name +import fileText from "./file!text"; +>fileText : Symbol(fileText, Decl(user.ts, 8, 6)) + +foo(fileText); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>fileText : Symbol(fileText, Decl(user.ts, 8, 6)) + +=== tests/cases/conformance/ambient/declarations.d.ts === +declare module "foo*baz" { + export function foo(s: string): void; +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 26)) +>s : Symbol(s, Decl(declarations.d.ts, 1, 24)) +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +>baz : Symbol(baz, Decl(declarations.d.ts, 5, 16)) +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +>foos : Symbol(foos, Decl(declarations.d.ts, 10, 16)) +} + +declare module "*!text" { + const x: string; +>x : Symbol(x, Decl(declarations.d.ts, 14, 9)) + + export default x; +>x : Symbol(x, Decl(declarations.d.ts, 14, 9)) +} + diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.types b/tests/baselines/reference/ambientDeclarationsPatterns.types new file mode 100644 index 00000000000..adf8ae1ab3b --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.types @@ -0,0 +1,54 @@ +=== tests/cases/conformance/ambient/user.ts === +/// +import {foo, baz} from "foobarbaz"; +>foo : (s: string) => void +>baz : string + +foo(baz); +>foo(baz) : void +>foo : (s: string) => void +>baz : string + +import {foos} from "foosball"; +>foos : string + +foo(foos); +>foo(foos) : void +>foo : (s: string) => void +>foos : string + +// Works with relative file name +import fileText from "./file!text"; +>fileText : string + +foo(fileText); +>foo(fileText) : void +>foo : (s: string) => void +>fileText : string + +=== tests/cases/conformance/ambient/declarations.d.ts === +declare module "foo*baz" { + export function foo(s: string): void; +>foo : (s: string) => void +>s : string +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +>baz : string +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +>foos : string +} + +declare module "*!text" { + const x: string; +>x : string + + export default x; +>x : string +} + diff --git a/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt new file mode 100644 index 00000000000..7a3ff02aa5e --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt @@ -0,0 +1,8 @@ +tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts(1,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + +==== tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts (1 errors) ==== + declare module "too*many*asterisks" { } + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js new file mode 100644 index 00000000000..a664eb84dc5 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js @@ -0,0 +1,5 @@ +//// [ambientDeclarationsPatterns_tooManyAsterisks.ts] +declare module "too*many*asterisks" { } + + +//// [ambientDeclarationsPatterns_tooManyAsterisks.js] diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts index 43e373ba9de..d48f50bfa50 100644 --- a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -1,18 +1,20 @@ // @Filename: declarations.d.ts declare module "foo*baz" { - export function foo(n: number): void; + export function foo(s: string): void; } // Augmentations still work declare module "foo*baz" { - export const baz: number; + export const baz: string; } -// Should be an error -declare module "too*many*asterisks" { } - // Longest prefix wins declare module "foos*" { - export const foos: number; + export const foos: string; +} + +declare module "*!text" { + const x: string; + export default x; } // @Filename: user.ts @@ -21,3 +23,8 @@ import {foo, baz} from "foobarbaz"; foo(baz); import {foos} from "foosball"; +foo(foos); + +// Works with relative file name +import fileText from "./file!text"; +foo(fileText); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts new file mode 100644 index 00000000000..76f9081906c --- /dev/null +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts @@ -0,0 +1 @@ +declare module "too*many*asterisks" { } From 88be1ac1c357559bed8629215d8deba4eb260b8a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 9 Jun 2016 11:47:41 -0700 Subject: [PATCH 46/48] Fix sourcemaps for debugging tests --- Jakefile.js | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 3540655af4a..873ab41eb0f 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -312,10 +312,7 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts } if (useDebugMode) { - options += " -sourcemap"; - if (!opts.noMapRoot) { - options += " -mapRoot file:///" + path.resolve(path.dirname(outFile)); - } + options += " --inlineSourceMap --inlineSources"; } else { options += " --newLine LF"; } @@ -485,7 +482,6 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename); compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); -var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js"); var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts"); var nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); @@ -517,16 +513,6 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); }); -compileFile(servicesFileInBrowserTest, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources), - /*prefixes*/ [copyright], - /*useBuiltCompiler*/ true, - { noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true, noMapRoot: true }, - /*callback*/ function () { - var content = fs.readFileSync(servicesFileInBrowserTest).toString(); - 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); @@ -839,12 +825,12 @@ compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() { - var cmd = 'browserify built/local/run.js -o built/local/bundle.js'; + var cmd = 'browserify built/local/run.js -d -o built/local/bundle.js'; exec(cmd); }, {async: true}); desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], port=, browser=[chrome|IE]"); -task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function() { +task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFile], function() { cleanTestDirs(); host = "node"; port = process.env.port || process.env.p || '8888'; From b521fd55a45f1013aca0ba59b0bf7928b3962b31 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 9 Jun 2016 11:51:53 -0700 Subject: [PATCH 47/48] Allow --sourceRoot with --inlineSources option Fixes #8445 --- src/compiler/diagnosticMessages.json | 2 +- src/compiler/program.ts | 19 +++++++------------ ...ourcemapInlineSourcesSourceRoot.errors.txt | 7 ------- ...nsSourcemapInlineSourcesSourceRoot.symbols | 5 +++++ ...ionsSourcemapInlineSourcesSourceRoot.types | 6 ++++++ ...SourceRootWithNoSourceMapOption.errors.txt | 4 ++-- ...SourceRootWithNoSourceMapOption.errors.txt | 4 ++-- ...sourceRootWithNoSourceMapOption.errors.txt | 4 ++-- ...sourceRootWithNoSourceMapOption.errors.txt | 4 ++-- 9 files changed, 27 insertions(+), 28 deletions(-) delete mode 100644 tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt create mode 100644 tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols create mode 100644 tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 034bd27b147..b6f7191049f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2260,7 +2260,7 @@ "category": "Error", "code": 5047 }, - "Option 'inlineSources' can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": { + "Option '{0} can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": { "category": "Error", "code": 5051 }, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 5b64d066da1..b765f580135 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2043,12 +2043,12 @@ namespace ts { } } - if (options.inlineSources) { - if (!options.sourceMap && !options.inlineSourceMap) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided)); + if (!options.sourceMap && !options.inlineSourceMap) { + if (options.inlineSources) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources")); } if (options.sourceRoot) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceRoot", "inlineSources")); + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot")); } } @@ -2056,14 +2056,9 @@ namespace ts { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile")); } - if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) { - // Error to specify --mapRoot or --sourceRoot without mapSourceFiles - if (options.mapRoot) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap")); - } - if (options.sourceRoot && !options.inlineSourceMap) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "sourceRoot", "sourceMap")); - } + if (options.mapRoot && !options.sourceMap) { + // Error to specify --mapRoot without --sourcemap + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap")); } if (options.declarationDir) { diff --git a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt deleted file mode 100644 index bfa77ff5a3e..00000000000 --- a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt +++ /dev/null @@ -1,7 +0,0 @@ -error TS5053: Option 'sourceRoot' cannot be specified with option 'inlineSources'. - - -!!! error TS5053: Option 'sourceRoot' cannot be specified with option 'inlineSources'. -==== tests/cases/compiler/optionsSourcemapInlineSourcesSourceRoot.ts (0 errors) ==== - - var a = 10; \ No newline at end of file diff --git a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols new file mode 100644 index 00000000000..76f2dc5fdc4 --- /dev/null +++ b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols @@ -0,0 +1,5 @@ +=== tests/cases/compiler/optionsSourcemapInlineSourcesSourceRoot.ts === + +var a = 10; +>a : Symbol(a, Decl(optionsSourcemapInlineSourcesSourceRoot.ts, 1, 3)) + diff --git a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types new file mode 100644 index 00000000000..3689afc382c --- /dev/null +++ b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/optionsSourcemapInlineSourcesSourceRoot.ts === + +var a = 10; +>a : number +>10 : number + diff --git a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt index 44d2c93d9a5..2a780bdeda0 100644 --- a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt @@ -1,9 +1,9 @@ +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. !!! error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt index 44d2c93d9a5..2a780bdeda0 100644 --- a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt @@ -1,9 +1,9 @@ +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. !!! error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt index 56dab79dacf..b79cae049ff 100644 --- a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt @@ -1,7 +1,7 @@ -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt index 56dab79dacf..b79cae049ff 100644 --- a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt @@ -1,7 +1,7 @@ -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { From 574a64dec9033ce1a026a51a93d77dfd97111be6 Mon Sep 17 00:00:00 2001 From: Yui Date: Thu, 9 Jun 2016 15:01:08 -0700 Subject: [PATCH 48/48] Fix7334 Disallow async in functionExpression and ArrowFunction (#9062) * Error when using async modifier in function-expression and arrow-function when target es5 * Add tests and baselines --- src/compiler/checker.ts | 2 ++ .../disallowAsyncModifierInES5.errors.txt | 27 +++++++++++++++++++ .../reference/disallowAsyncModifierInES5.js | 22 +++++++++++++++ .../compiler/disallowAsyncModifierInES5.ts | 5 ++++ 4 files changed, 56 insertions(+) create mode 100644 tests/baselines/reference/disallowAsyncModifierInES5.errors.txt create mode 100644 tests/baselines/reference/disallowAsyncModifierInES5.js create mode 100644 tests/cases/compiler/disallowAsyncModifierInES5.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33931ac304f..a60866b90f0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17794,6 +17794,8 @@ namespace ts { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ExportDeclaration: case SyntaxKind.ExportAssignment: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: case SyntaxKind.Parameter: break; case SyntaxKind.FunctionDeclaration: diff --git a/tests/baselines/reference/disallowAsyncModifierInES5.errors.txt b/tests/baselines/reference/disallowAsyncModifierInES5.errors.txt new file mode 100644 index 00000000000..655416f784c --- /dev/null +++ b/tests/baselines/reference/disallowAsyncModifierInES5.errors.txt @@ -0,0 +1,27 @@ +error TS2318: Cannot find global type 'Promise'. +tests/cases/compiler/disallowAsyncModifierInES5.ts(2,1): error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. +tests/cases/compiler/disallowAsyncModifierInES5.ts(2,16): error TS1057: An async function or method must have a valid awaitable return type. +tests/cases/compiler/disallowAsyncModifierInES5.ts(3,11): error TS1057: An async function or method must have a valid awaitable return type. +tests/cases/compiler/disallowAsyncModifierInES5.ts(3,11): error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. +tests/cases/compiler/disallowAsyncModifierInES5.ts(4,11): error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. +tests/cases/compiler/disallowAsyncModifierInES5.ts(4,11): error TS1057: An async function or method must have a valid awaitable return type. + + +!!! error TS2318: Cannot find global type 'Promise'. +==== tests/cases/compiler/disallowAsyncModifierInES5.ts (6 errors) ==== + + async function foo() { return 42; } // ERROR: Async functions are only available in ES6+ + ~~~~~ +!!! error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. + ~~~ +!!! error TS1057: An async function or method must have a valid awaitable return type. + let bar = async function () { return 42; } // OK, but should be an error + ~~~~~ +!!! error TS1057: An async function or method must have a valid awaitable return type. + ~~~~~ +!!! error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. + let baz = async () => 42; // OK, but should be an error + ~~~~~ +!!! error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. + ~~~~~~~~~~~~~~ +!!! error TS1057: An async function or method must have a valid awaitable return type. \ No newline at end of file diff --git a/tests/baselines/reference/disallowAsyncModifierInES5.js b/tests/baselines/reference/disallowAsyncModifierInES5.js new file mode 100644 index 00000000000..8216e5fd0e6 --- /dev/null +++ b/tests/baselines/reference/disallowAsyncModifierInES5.js @@ -0,0 +1,22 @@ +//// [disallowAsyncModifierInES5.ts] + +async function foo() { return 42; } // ERROR: Async functions are only available in ES6+ +let bar = async function () { return 42; } // OK, but should be an error +let baz = async () => 42; // OK, but should be an error + +//// [disallowAsyncModifierInES5.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function foo() { + return __awaiter(this, void 0, void 0, function* () { return 42; }); +} // ERROR: Async functions are only available in ES6+ +var bar = function () { + return __awaiter(this, void 0, void 0, function* () { return 42; }); +}; // OK, but should be an error +var baz = function () __awaiter(this, void 0, void 0, function* () { return 42; }); // OK, but should be an error diff --git a/tests/cases/compiler/disallowAsyncModifierInES5.ts b/tests/cases/compiler/disallowAsyncModifierInES5.ts new file mode 100644 index 00000000000..5417e678dce --- /dev/null +++ b/tests/cases/compiler/disallowAsyncModifierInES5.ts @@ -0,0 +1,5 @@ +// @target: es5 + +async function foo() { return 42; } // ERROR: Async functions are only available in ES6+ +let bar = async function () { return 42; } // OK, but should be an error +let baz = async () => 42; // OK, but should be an error \ No newline at end of file