From 7e2517975c0dd86b7549f3ab4e231d9c207d0e65 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 30 Mar 2017 14:14:15 -0700 Subject: [PATCH 01/77] Basic functionality * pass context as argument in xToNode methods * make sourcefile optional in printer * start consolidating NodeBuilderFlags and TypeFormatFlags --- src/compiler/checker.ts | 186 +++++++++++++++++------------- src/compiler/emitter.ts | 24 ++-- src/compiler/factory.ts | 25 ++++ src/compiler/types.ts | 11 +- src/compiler/utilities.ts | 14 ++- src/services/codefixes/helpers.ts | 2 +- src/services/textChanges.ts | 24 +--- 7 files changed, 162 insertions(+), 124 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4e55a1c46b5..28ba350f38b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2188,8 +2188,7 @@ namespace ts { return result; } - - function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { + function oldTypeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { const writer = getSingleLineStringWriter(); getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); let result = writer.string(); @@ -2202,25 +2201,43 @@ namespace ts { return result; } - function createNodeBuilder() { - let context: NodeBuilderContext; + function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { + const str = oldTypeToString(type, enclosingDeclaration, flags); str; + const str2 = oldTypeToString(type, enclosingDeclaration, flags); str2; + const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, NodeBuilderFlags.ignoreErrors); + Debug.assert(typeNode !== undefined, "should always get typenode?"); + const newLine = NewLineKind.None; + const options = { newLine, removeComments: true }; + const writer = createTextWriter(""); + // writer.writeLine = noop; + const printer = createPrinter(options, writer); + printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ undefined, writer); + const result = writer.getText(); + const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; + if (maxLength && result.length >= maxLength) { + return result.substr(0, maxLength - "...".length) + "..."; + } + return result; + } + + function createNodeBuilder() { return { typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { - context = createNodeBuilderContext(enclosingDeclaration, flags); - const resultingNode = typeToTypeNodeHelper(type); + const context = createNodeBuilderContext(enclosingDeclaration, flags); + const resultingNode = typeToTypeNodeHelper(type, context); const result = context.encounteredError ? undefined : resultingNode; return result; }, indexInfoToIndexSignatureDeclaration: (indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { - context = createNodeBuilderContext(enclosingDeclaration, flags); - const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind); + const context = createNodeBuilderContext(enclosingDeclaration, flags); + const resultingNode = indexInfoToIndexSignatureDeclarationHelper(indexInfo, kind, context); const result = context.encounteredError ? undefined : resultingNode; return result; }, signatureToSignatureDeclaration: (signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { - context = createNodeBuilderContext(enclosingDeclaration, flags); - const resultingNode = signatureToSignatureDeclarationHelper(signature, kind); + const context = createNodeBuilderContext(enclosingDeclaration, flags); + const resultingNode = signatureToSignatureDeclarationHelper(signature, kind, context); const result = context.encounteredError ? undefined : resultingNode; return result; } @@ -2246,7 +2263,7 @@ namespace ts { }; } - function typeToTypeNodeHelper(type: Type): TypeNode { + function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { if (!type) { context.encounteredError = true; // TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)? @@ -2266,7 +2283,7 @@ namespace ts { return createKeywordTypeNode(SyntaxKind.BooleanKeyword); } if (type.flags & TypeFlags.Enum) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { @@ -2279,8 +2296,11 @@ namespace ts { return (type).intrinsicName === "true" ? createTrue() : createFalse(); } if (type.flags & TypeFlags.EnumLiteral) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); - return createTypeReferenceNode(name, /*typeArguments*/ undefined); + const parentSymbol = getParentOfSymbol(type.symbol); + const parentName = symbolToName(parentSymbol, /*expectsIdentifier*/ false, context); + const name = getNameOfSymbol(type.symbol, context); + const enumLiteralName = createQualifiedName(parentName, name); + return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.Void) { return createKeywordTypeNode(SyntaxKind.VoidKeyword); @@ -2317,18 +2337,18 @@ namespace ts { } if (objectFlags & ObjectFlags.ClassOrInterface) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); // TODO(aozgaa): handle type arguments. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.TypeParameter) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (context.checkAlias && type.aliasSymbol) { - const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false); + const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context); const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments); return createTypeReferenceNode(name, typeArgumentNodes); } @@ -2360,12 +2380,12 @@ namespace ts { if (type.flags & TypeFlags.Index) { const indexedType = (type).type; - const indexTypeNode = typeToTypeNodeHelper(indexedType); + const indexTypeNode = typeToTypeNodeHelper(indexedType, context); return createTypeOperatorNode(indexTypeNode); } if (type.flags & TypeFlags.IndexedAccess) { - const objectTypeNode = typeToTypeNodeHelper((type).objectType); - const indexTypeNode = typeToTypeNodeHelper((type).indexType); + const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); + const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } @@ -2374,7 +2394,7 @@ namespace ts { function mapToTypeNodeArray(types: Type[]): TypeNode[] { const result = []; for (const type of types) { - const typeNode = typeToTypeNodeHelper(type); + const typeNode = typeToTypeNodeHelper(type, context); if (typeNode) { result.push(typeNode); } @@ -2385,10 +2405,10 @@ namespace ts { function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); const typeParameter = getTypeParameterFromMappedType(type); - const typeParameterNode = typeParameterToDeclaration(typeParameter); + const typeParameterNode = typeParameterToDeclaration(typeParameter, context); const templateType = getTemplateTypeFromMappedType(type); - const templateTypeNode = typeToTypeNodeHelper(templateType); + const templateTypeNode = typeToTypeNodeHelper(templateType, context); const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; @@ -2409,7 +2429,7 @@ namespace ts { const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags - const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false); + const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false, context); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { @@ -2462,11 +2482,11 @@ namespace ts { if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const signature = resolved.callSignatures[0]; - return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType); + return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { const signature = resolved.constructSignatures[0]; - return signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType); + return signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context); } } @@ -2478,14 +2498,14 @@ namespace ts { } function createTypeQueryNodeFromSymbol(symbol: Symbol) { - const entityName = symbolToName(symbol, /*expectsIdentifier*/ false); + const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, context); return createTypeQueryNode(entityName); } function typeReferenceToTypeNode(type: TypeReference) { const typeArguments: Type[] = type.typeArguments || emptyArray; if (type.target === globalArrayType) { - const elementType = typeToTypeNodeHelper(typeArguments[0]); + const elementType = typeToTypeNodeHelper(typeArguments[0], context); return createArrayTypeNode(elementType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { @@ -2516,7 +2536,7 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true); + const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, context); if (!qualifiedName) { qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); } @@ -2529,7 +2549,7 @@ namespace ts { } } let entityName: EntityName = undefined; - const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true); + const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true, context); if (qualifiedName) { Debug.assert(!qualifiedName.right); qualifiedName.right = nameIdentifier; @@ -2547,16 +2567,16 @@ namespace ts { function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { const typeElements: TypeElement[] = []; for (const signature of resolvedType.callSignatures) { - typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature)); + typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.CallSignature, context)); } for (const signature of resolvedType.constructSignatures) { - typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature)); + typeElements.push(signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructSignature, context)); } if (resolvedType.stringIndexInfo) { - typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String)); + typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.stringIndexInfo, IndexKind.String, context)); } if (resolvedType.numberIndexInfo) { - typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number)); + typeElements.push(indexInfoToIndexSignatureDeclarationHelper(resolvedType.numberIndexInfo, IndexKind.Number, context)); } const properties = resolvedType.properties; @@ -2570,12 +2590,12 @@ namespace ts { if (!oldDeclaration) { return; } - const propertyName = oldDeclaration.name; + const propertyName = getDeepSynthesizedClone(oldDeclaration.name); const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); for (const signature of signatures) { - const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature); + const methodDeclaration = signatureToSignatureDeclarationHelper(signature, SyntaxKind.MethodSignature, context); methodDeclaration.name = propertyName; methodDeclaration.questionToken = optionalToken; typeElements.push(methodDeclaration); @@ -2584,7 +2604,7 @@ namespace ts { else { // TODO(aozgaa): should we create a node with explicit or implict any? - const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType) : createKeywordTypeNode(SyntaxKind.AnyKeyword); + const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword); typeElements.push(createPropertySignature( propertyName, optionalToken, @@ -2596,7 +2616,7 @@ namespace ts { } } - function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind): IndexSignatureDeclaration { + function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const name = getNameFromIndexInfo(indexInfo); @@ -2608,7 +2628,7 @@ namespace ts { /*questionToken*/ undefined, indexerTypeNode, /*initializer*/ undefined); - const typeNode = typeToTypeNodeHelper(indexInfo.type); + const typeNode = typeToTypeNodeHelper(indexInfo.type, context); return createIndexSignatureDeclaration( /*decorators*/ undefined, indexInfo.isReadonly ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined, @@ -2616,55 +2636,62 @@ namespace ts { typeNode); } - function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind): SignatureDeclaration { + function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, context: NodeBuilderContext): SignatureDeclaration { - const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter)); - const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter)); - let returnTypeNode: TypeNode | TypePredicate; + const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); + const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context)); + let returnTypeNode: TypeNode; if (signature.typePredicate) { const typePredicate = signature.typePredicate; const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? createIdentifier((typePredicate).parameterName) : createThisTypeNode(); - const typeNode = typeToTypeNodeHelper(typePredicate.type); + const typeNode = typeToTypeNodeHelper(typePredicate.type, context); returnTypeNode = createTypePredicateNode(parameterName, typeNode); } else { const returnType = getReturnTypeOfSignature(signature); - returnTypeNode = returnType && typeToTypeNodeHelper(returnType); + returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context); } - const returnTypeNodeExceptAny = returnTypeNode && returnTypeNode.kind !== SyntaxKind.AnyKeyword ? returnTypeNode : undefined; - - return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNodeExceptAny); + if(context.flags & NodeBuilderFlags.suppressAnyReturnType) { + if(returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) { + returnTypeNode = undefined; + } + } + else if (!returnTypeNode) { + returnTypeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); + } + return createSignatureDeclaration(kind, typeParameters, parameters, returnTypeNode); } - function typeParameterToDeclaration(type: TypeParameter): TypeParameterDeclaration { + function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext): TypeParameterDeclaration { const constraint = getConstraintFromTypeParameter(type); - const constraintNode = constraint && typeToTypeNodeHelper(constraint); + const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); const defaultParameter = getDefaultFromTypeParameter(type); - const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter); - const name = symbolToName(type.symbol, /*expectsIdentifier*/ true); + const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ true, context); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } - function symbolToParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration { + function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration { const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); const parameterType = getTypeOfSymbol(parameterSymbol); - const parameterTypeNode = typeToTypeNodeHelper(parameterType); + const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); + const name = getDeepSynthesizedClone(parameterDeclaration.name); // TODO(aozgaa): In the future, check initializer accessibility. const parameterNode = createParameter( parameterDeclaration.decorators, parameterDeclaration.modifiers, parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken), // Clone name to remove trivia. - getSynthesizedClone(parameterDeclaration.name), + name, parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken), parameterTypeNode, parameterDeclaration.initializer); return parameterNode; } - function symbolToName(symbol: Symbol, expectsIdentifier: true): Identifier; - function symbolToName(symbol: Symbol, expectsIdentifier: false): EntityName; - function symbolToName(symbol: Symbol, expectsIdentifier: boolean): EntityName { + function symbolToName(symbol: Symbol, expectsIdentifier: true, context: NodeBuilderContext): Identifier; + function symbolToName(symbol: Symbol, expectsIdentifier: false, context: NodeBuilderContext): EntityName; + function symbolToName(symbol: Symbol, expectsIdentifier: boolean, context: NodeBuilderContext): EntityName { // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. let chain: Symbol[]; @@ -2714,7 +2741,7 @@ namespace ts { } } - const symbolName = getNameOfSymbol(symbol); + const symbolName = getNameOfSymbol(symbol, context); const symbolNameWithTypeParameters = typeParameterString.length > 0 ? `${symbolName}<${typeParameterString}>` : symbolName; const identifier = createIdentifier(symbolNameWithTypeParameters); @@ -2754,29 +2781,28 @@ namespace ts { return [symbol]; } } - - function getNameOfSymbol(symbol: Symbol): string { - const declaration = firstOrUndefined(symbol.declarations); - if (declaration) { - if (declaration.name) { - return declarationNameToString(declaration.name); - } - if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { - return declarationNameToString((declaration.parent).name); - } - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowAnonymousIdentifier)) { - context.encounteredError = true; - } - switch (declaration.kind) { - case SyntaxKind.ClassExpression: - return "(Anonymous class)"; - case SyntaxKind.FunctionExpression: - case SyntaxKind.ArrowFunction: - return "(Anonymous function)"; - } + } + function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string { + const declaration = firstOrUndefined(symbol.declarations); + if (declaration) { + if (declaration.name) { + return declarationNameToString(declaration.name); + } + if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { + return declarationNameToString((declaration.parent).name); + } + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowAnonymousIdentifier)) { + context.encounteredError = true; + } + switch (declaration.kind) { + case SyntaxKind.ClassExpression: + return "(Anonymous class)"; + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + return "(Anonymous function)"; } - return symbol.name; } + return symbol.name; } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 616be35270e..f72d2bb19c1 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -213,7 +213,7 @@ namespace ts { emitLeadingCommentsOfPosition, } = comments; - let currentSourceFile: SourceFile; + let currentSourceFile: SourceFile | undefined; let nodeIdToGeneratedName: string[]; // Map of generated names for specific nodes. let autoGeneratedIdToGeneratedName: string[]; // Map of generated names for temp and loop variables. let generatedNames: Map; // Set of names generated by the NameGenerator. @@ -235,7 +235,7 @@ namespace ts { writeBundle }; - function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { + function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined): string { switch (hint) { case EmitHint.SourceFile: Debug.assert(isSourceFile(node), "Expected a SourceFile node."); @@ -265,7 +265,7 @@ namespace ts { return endPrint(); } - function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter) { + function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); print(hint, node, sourceFile); @@ -302,8 +302,10 @@ namespace ts { return text; } - function print(hint: EmitHint, node: Node, sourceFile: SourceFile) { - setSourceFile(sourceFile); + function print(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined) { + if (sourceFile) { + setSourceFile(sourceFile); + } pipelineEmitWithNotification(hint, node); } @@ -1101,7 +1103,7 @@ namespace ts { function emitPropertyAccessExpression(node: PropertyAccessExpression) { let indentBeforeDot = false; let indentAfterDot = false; - if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { + if (currentSourceFile && !(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; @@ -2461,7 +2463,7 @@ namespace ts { const firstChild = children[0]; if (firstChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile); + return !(currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile)); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { return synthesizedNodeStartsOnNewLine(firstChild, format); @@ -2487,7 +2489,7 @@ namespace ts { return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); } else { - return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile); + return !(currentSourceFile && rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile)); } } else { @@ -2506,13 +2508,13 @@ namespace ts { const lastChild = lastOrUndefined(children); if (lastChild === undefined) { - return !rangeIsOnSingleLine(parentNode, currentSourceFile); + return !(currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile)); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { return synthesizedNodeStartsOnNewLine(lastChild, format); } else { - return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); + return !(currentSourceFile && rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile)); } } else { @@ -2901,7 +2903,7 @@ namespace ts { // Precomputed Formats Modifiers = SingleLine | SpaceBetweenSiblings, HeritageClauses = SingleLine | SpaceBetweenSiblings, - TypeLiteralMembers = MultiLine | Indented, + TypeLiteralMembers = SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented, TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 66b3eb713e5..4bf4d1a9519 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2,6 +2,26 @@ /// namespace ts { + export const nullTransformationContext: TransformationContext = { + enableEmitNotification: noop, + enableSubstitution: noop, + endLexicalEnvironment: () => undefined, + getCompilerOptions: notImplemented, + getEmitHost: notImplemented, + getEmitResolver: notImplemented, + hoistFunctionDeclaration: noop, + hoistVariableDeclaration: noop, + isEmitNotificationEnabled: notImplemented, + isSubstitutionEnabled: notImplemented, + onEmitNode: noop, + onSubstituteNode: notImplemented, + readEmitHelpers: notImplemented, + requestEmitHelper: noop, + resumeLexicalEnvironment: noop, + startLexicalEnvironment: noop, + suspendLexicalEnvironment: noop + }; + function createSynthesizedNode(kind: SyntaxKind): Node { const node = createNode(kind, -1, -1); node.flags |= NodeFlags.Synthesized; @@ -64,6 +84,11 @@ namespace ts { return clone; } + export function getDeepSynthesizedClone(node: T | undefined): T { + const clone = visitEachChild(node, getDeepSynthesizedClone, nullTransformationContext, /*nodeVisitor*/ undefined, getSynthesizedClone); + return nodeIsSynthesized(clone) ? clone : getSynthesizedClone(clone); + } + // Literals export function createLiteral(value: string): StringLiteral; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 038f8bf3166..12b9d333e3d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2541,8 +2541,10 @@ namespace ts { allowQualifedNameInPlaceOfIdentifier = 1 << 1, allowTypeParameterInQualifiedName = 1 << 2, allowAnonymousIdentifier = 1 << 3, - allowEmptyUnionOrIntersection = 1 << 4, - allowEmptyTuple = 1 << 5 + allowEmptyUnionOrIntersection = 1 << 4, + allowEmptyTuple = 1 << 5, + suppressAnyReturnType = 1 << 6, + ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple } export interface SymbolDisplayBuilder { @@ -3470,6 +3472,7 @@ namespace ts { export const enum NewLineKind { CarriageReturnLineFeed = 0, LineFeed = 1, + None = 2 } export interface LineAndCharacter { @@ -4115,7 +4118,7 @@ namespace ts { * the identifiers of the source file are used when generating unique names to avoid * collisions. */ - printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; + printNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined): string; /** * Prints a source file as-is, without any emit transformations. */ @@ -4124,7 +4127,7 @@ namespace ts { * Prints a bundle of source files as-is, without any emit transformations. */ printBundle(bundle: Bundle): string; - /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, writer: EmitTextWriter): void; + /*@internal*/ writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, writer: EmitTextWriter): void; /*@internal*/ writeFile(sourceFile: SourceFile, writer: EmitTextWriter): void; /*@internal*/ writeBundle(bundle: Bundle, writer: EmitTextWriter): void; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 33cb0d8f489..9e39e66b646 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3272,13 +3272,15 @@ namespace ts { const carriageReturnLineFeed = "\r\n"; const lineFeed = "\n"; export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string { - if (options.newLine === NewLineKind.CarriageReturnLineFeed) { - return carriageReturnLineFeed; + switch (options.newLine) { + case NewLineKind.None: + return ""; + case NewLineKind.CarriageReturnLineFeed: + return carriageReturnLineFeed; + case NewLineKind.LineFeed: + return lineFeed; } - else if (options.newLine === NewLineKind.LineFeed) { - return lineFeed; - } - else if (sys) { + if (sys) { return sys.newLine; } return carriageReturnLineFeed; diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index b836815fc27..631fb560cd7 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -130,7 +130,7 @@ namespace ts.codefix { } function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) { - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration); + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.suppressAnyReturnType); if (signatureDeclaration) { signatureDeclaration.decorators = undefined; signatureDeclaration.modifiers = modifiers; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index afc98927495..63e3a95bf18 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -497,8 +497,8 @@ namespace ts.textChanges { readonly node: Node; } - export function getNonformattedText(node: Node, sourceFile: SourceFile, newLine: NewLineKind): NonFormattedText { - const options = { newLine, target: sourceFile.languageVersion }; + export function getNonformattedText(node: Node, sourceFile: SourceFile | undefined, newLine: NewLineKind): NonFormattedText { + const options = { newLine, target: sourceFile && sourceFile.languageVersion }; const writer = new Writer(getNewLineCharacter(options)); const printer = createPrinter(options, writer); printer.writeNode(EmitHint.Unspecified, node, sourceFile, writer); @@ -528,26 +528,6 @@ namespace ts.textChanges { return skipTrivia(s, 0) === s.length; } - const nullTransformationContext: TransformationContext = { - enableEmitNotification: noop, - enableSubstitution: noop, - endLexicalEnvironment: () => undefined, - getCompilerOptions: notImplemented, - getEmitHost: notImplemented, - getEmitResolver: notImplemented, - hoistFunctionDeclaration: noop, - hoistVariableDeclaration: noop, - isEmitNotificationEnabled: notImplemented, - isSubstitutionEnabled: notImplemented, - onEmitNode: noop, - onSubstituteNode: notImplemented, - readEmitHelpers: notImplemented, - requestEmitHelper: noop, - resumeLexicalEnvironment: noop, - startLexicalEnvironment: noop, - suspendLexicalEnvironment: noop - }; - function assignPositionsToNode(node: Node): Node { const visited = visitEachChild(node, assignPositionsToNode, nullTransformationContext, assignPositionsToNodeArray, assignPositionsToNode); // create proxy node for non synthesized nodes From 55388d16b4c9de3686465c857eeb3d6db33f1492 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 19 Apr 2017 10:36:40 -0700 Subject: [PATCH 02/77] Readonly type literal members --- src/compiler/checker.ts | 9 ++++++--- src/compiler/factory.ts | 10 ++++++---- src/compiler/visitor.ts | 2 ++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 28ba350f38b..0fd09251024 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2602,14 +2602,17 @@ namespace ts { } } else { - // TODO(aozgaa): should we create a node with explicit or implict any? const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword); - typeElements.push(createPropertySignature( + + const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined; + const propertySignature = createPropertySignature( + modifiers, propertyName, optionalToken, propertyTypeNode, - /*initializer*/undefined)); + /*initializer*/undefined); + typeElements.push(propertySignature); } } return typeElements.length ? typeElements : undefined; diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 4bf4d1a9519..6b265c03d25 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -480,8 +480,9 @@ namespace ts { // Signature elements - export function createPropertySignature(name: PropertyName | string, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { + export function createPropertySignature(modifiers: Modifier[] | undefined, name: PropertyName | string, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { const propertySignature = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature; + propertySignature.modifiers = asNodeArray(modifiers); propertySignature.name = asName(name); propertySignature.questionToken = questionToken; propertySignature.type = type; @@ -489,12 +490,13 @@ namespace ts { return propertySignature; } - export function updatePropertySignature(node: PropertySignature, name: PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { - return node.name !== name + export function updatePropertySignature(node: PropertySignature, modifiers: Modifier[] | undefined, name: PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { + return node.modifiers !== modifiers + || node.name !== name || node.questionToken !== questionToken || node.type !== type || node.initializer !== initializer - ? updateNode(createPropertySignature(name, questionToken, type, initializer), node) + ? updateNode(createPropertySignature(modifiers, name, questionToken, type, initializer), node) : node; } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 297d2cc0dde..523d4cb8e32 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -355,6 +355,8 @@ namespace ts { case SyntaxKind.PropertySignature: return updatePropertySignature((node), + // TODO: tokenVisitor or visitor for a nodearray of tokens? + nodesVisitor((node).modifiers, visitor, isToken), visitNode((node).name, visitor, isPropertyName), visitNode((node).questionToken, tokenVisitor, isToken), visitNode((node).type, visitor, isTypeNode), From 10afa2f6420202507bb4eb453878bfa5886046dc Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 19 Apr 2017 11:11:19 -0700 Subject: [PATCH 03/77] No space in empty type litereal --- src/compiler/emitter.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f283bc0217a..afafee51bf7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -954,7 +954,9 @@ namespace ts { function emitTypeLiteral(node: TypeLiteralNode) { write("{"); - emitList(node, node.members, ListFormat.TypeLiteralMembers); + if (node.members.length > 0) { + emitList(node, node.members, ListFormat.SingleLineTypeLiteralMembers); + } write("}"); } @@ -2945,7 +2947,8 @@ namespace ts { // Precomputed Formats Modifiers = SingleLine | SpaceBetweenSiblings, HeritageClauses = SingleLine | SpaceBetweenSiblings, - TypeLiteralMembers = SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented, + SingleLineTypeLiteralMembers = SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented, + MultiLineTypeLiteralMembers = MultiLine | Indented, TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, From d0f371659004673b7c767ec0511143f69682e36c Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 19 Apr 2017 11:11:26 -0700 Subject: [PATCH 04/77] typo --- src/compiler/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4d8e886b2ce..53507e3aa38 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2572,7 +2572,7 @@ namespace ts { export interface SymbolDisplayBuilder { buildTypeDisplay(type: Type, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildSymbolDisplay(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, meaning?: SymbolFlags, flags?: SymbolFormatFlags): void; - buildSignatureDisplay(signatures: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void; + buildSignatureDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): void; buildIndexSignatureDisplay(info: IndexInfo, writer: SymbolWriter, kind: IndexKind, enclosingDeclaration?: Node, globalFlags?: TypeFormatFlags, symbolStack?: Symbol[]): void; buildParameterDisplay(parameter: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; From e12b708221ab7abe22e841d6b77a51d3d238c993 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 2 May 2017 15:33:13 -0700 Subject: [PATCH 05/77] For completions of union type, get all possible properties --- src/compiler/checker.ts | 19 ++++++++++++++++- src/compiler/types.ts | 5 +++++ src/services/completions.ts | 21 +++++++------------ .../cases/fourslash/completionListOfUnion.ts | 18 ++++++++++++++++ 4 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 tests/cases/fourslash/completionListOfUnion.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca5d65068a0..3c45942fd62 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -204,7 +204,8 @@ namespace ts { // since we are only interested in declarations of the module itself return tryFindAmbientModule(moduleName, /*withAugmentations*/ false); }, - getApparentType + getApparentType, + getAllPossiblePropertiesOfType, }; const tupleTypes: GenericType[] = []; @@ -5648,6 +5649,22 @@ namespace ts { getPropertiesOfObjectType(type); } + function getAllPossiblePropertiesOfType(type: Type): Symbol[] { + if (type.flags & TypeFlags.Union) { + const props = createMap(); + for (const memberType of (type as UnionType).types) { + for (const { name } of getPropertiesOfType(memberType)) { + if (!props.has(name)) { + props.set(name, createUnionOrIntersectionProperty(type as UnionType, name)); + } + } + } + return arrayFrom(props.values()); + } else { + return getPropertiesOfType(type); + } + } + function getConstraintOfType(type: TypeVariable | UnionOrIntersectionType): Type { return type.flags & TypeFlags.TypeParameter ? getConstraintOfTypeParameter(type) : type.flags & TypeFlags.IndexedAccess ? getConstraintOfIndexedAccess(type) : diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d9b4ca192f4..aad4f2680e3 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2564,6 +2564,11 @@ namespace ts { /* @internal */ getIdentifierCount(): number; /* @internal */ getSymbolCount(): number; /* @internal */ getTypeCount(): number; + + /** For a union, will include a property if it's defined in *any* of the member types. + * So for `{ a } | { b }`, this will include both `a` and `b`. + */ + /* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[]; } export enum NodeBuilderFlags { diff --git a/src/services/completions.ts b/src/services/completions.ts index 6ac3762b4c7..f429ed34b6b 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -813,19 +813,16 @@ namespace ts.Completions { // We're looking up possible property names from contextual/inferred/declared type. isMemberCompletion = true; - let typeForObject: Type; + let typeMembers: Symbol[]; let existingMembers: Declaration[]; if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { // We are completing on contextual types, but may also include properties // other than those within the declared type. isNewIdentifierLocation = true; - - // If the object literal is being assigned to something of type 'null | { hello: string }', - // it clearly isn't trying to satisfy the 'null' type. So we grab the non-nullable type if possible. - typeForObject = typeChecker.getContextualType(objectLikeContainer); - typeForObject = typeForObject && typeForObject.getNonNullableType(); - + const typeForObject = typeChecker.getContextualType(objectLikeContainer); + if (!typeForObject) return false; + typeMembers = typeChecker.getAllPossiblePropertiesOfType(typeForObject); existingMembers = (objectLikeContainer).properties; } else if (objectLikeContainer.kind === SyntaxKind.ObjectBindingPattern) { @@ -849,7 +846,10 @@ namespace ts.Completions { } } if (canGetType) { - typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); + const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); + if (!typeForObject) return false; + // In a binding pattern, get only known properties. Everywhere else we will get all possible properties. + typeMembers = typeChecker.getPropertiesOfType(typeForObject); existingMembers = (objectLikeContainer).elements; } } @@ -861,11 +861,6 @@ namespace ts.Completions { Debug.fail("Expected object literal or binding pattern, got " + objectLikeContainer.kind); } - if (!typeForObject) { - return false; - } - - const typeMembers = typeChecker.getPropertiesOfType(typeForObject); if (typeMembers && typeMembers.length > 0) { // Add filtered items to the completion list symbols = filterObjectMembersList(typeMembers, existingMembers); diff --git a/tests/cases/fourslash/completionListOfUnion.ts b/tests/cases/fourslash/completionListOfUnion.ts new file mode 100644 index 00000000000..6026615f8c1 --- /dev/null +++ b/tests/cases/fourslash/completionListOfUnion.ts @@ -0,0 +1,18 @@ +/// + +// @strictNullChecks: true + +// Non-objects should be skipped, so `| number | null` should have no effect on completions. +////const x: { a: number, b: number } | { a: string, c: string } | { b: boolean } | number | null = { /*x*/ }; + +////interface I { a: number; } +////function f(...args: Array) {} +////f({ /*f*/ }); + +goTo.marker("x"); +verify.completionListContains("a", "(property) a: string | number"); +verify.completionListContains("b", "(property) b: number | boolean"); +verify.completionListContains("c", "(property) c: string"); + +goTo.marker("f"); +verify.completionListContains("a", "(property) a: number"); From 384d54603f00db19d32045bca29645e6ca8aea4d Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 4 May 2017 15:27:50 -0700 Subject: [PATCH 06/77] Basic parens handling and stubbed TypeFormatFlags --- src/compiler/checker.ts | 74 +++++++++++++++++++++++++------ src/compiler/emitter.ts | 3 +- src/compiler/factory.ts | 12 +++++ src/compiler/types.ts | 38 ++++++++++++---- src/compiler/visitor.ts | 3 +- src/services/codefixes/helpers.ts | 2 +- 6 files changed, 107 insertions(+), 25 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 91eee774ee4..fc067d63d59 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2287,8 +2287,16 @@ namespace ts { interface NodeBuilderContext { readonly enclosingDeclaration: Node | undefined; readonly flags: NodeBuilderFlags | undefined; + + // State encounteredError: boolean; inObjectTypeLiteral: boolean; + // TODO: needed for part of parens handling + InElementType: boolean; // Writing an array or union element type + // TODO: ??? + InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type + // TODO: ??? + InTypeAlias: boolean; // Writing type in type alias declaration checkAlias: boolean; symbolStack: Symbol[] | undefined; } @@ -2299,18 +2307,28 @@ namespace ts { flags, encounteredError: false, inObjectTypeLiteral: false, + InElementType: false, + InFirstTypeArgument: false, + InTypeAlias: false, checkAlias: true, symbolStack: undefined }; } function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { + const InElementType = context.InElementType; + // TODO: why doesn't tts unset the flag? + context.InElementType = false; + + // TODO: should be assert? if (!type) { context.encounteredError = true; // TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)? return undefined; } + + if (type.flags & TypeFlags.Any) { return createKeywordTypeNode(SyntaxKind.AnyKeyword); } @@ -2390,16 +2408,17 @@ namespace ts { if (context.checkAlias && type.aliasSymbol) { const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context); - const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments); + const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false); return createTypeReferenceNode(name, typeArgumentNodes); } context.checkAlias = false; - if (type.flags & TypeFlags.Union) { - const formattedUnionTypes = formatUnionTypes((type).types); - const unionTypeNodes = formattedUnionTypes && mapToTypeNodeArray(formattedUnionTypes); - if (unionTypeNodes && unionTypeNodes.length > 0) { - return createUnionOrIntersectionTypeNode(SyntaxKind.UnionType, unionTypeNodes); + if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { + const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; + const typeNodes = types && mapToTypeNodeArray(types, /*addInElementTypeFlag*/ true); + if (typeNodes && typeNodes.length > 0) { + const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); + return InElementType ? createParenthesizedTypeNode(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode; } else { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) { @@ -2409,10 +2428,6 @@ namespace ts { } } - if (type.flags & TypeFlags.Intersection) { - return createUnionOrIntersectionTypeNode(SyntaxKind.IntersectionType, mapToTypeNodeArray((type as UnionType).types)); - } - if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { Debug.assert(!!(type.flags & TypeFlags.Object)); // The type is an object literal type. @@ -2421,25 +2436,33 @@ namespace ts { if (type.flags & TypeFlags.Index) { const indexedType = (type).type; + context.InElementType = true; const indexTypeNode = typeToTypeNodeHelper(indexedType, context); + Debug.assert(context.InElementType === false); return createTypeOperatorNode(indexTypeNode); } + if (type.flags & TypeFlags.IndexedAccess) { + context.InElementType = true; const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); + Debug.assert(context.InElementType === false); const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } Debug.fail("Should be unreachable."); - function mapToTypeNodeArray(types: Type[]): TypeNode[] { + function mapToTypeNodeArray(types: Type[], addInElementTypeFlag: boolean): TypeNode[] { const result = []; + Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); for (const type of types) { + context.InElementType = addInElementTypeFlag; const typeNode = typeToTypeNodeHelper(type, context); if (typeNode) { result.push(typeNode); } } + Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); return result; } @@ -2523,6 +2546,7 @@ namespace ts { if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const signature = resolved.callSignatures[0]; + shouldAddParenthesisAroundFunctionType(signature, context); return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { @@ -2538,6 +2562,20 @@ namespace ts { return createTypeLiteralNode(members); } + + function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { + if (context.InElementType) { + return true; + } + else if (context.InFirstTypeArgument) { + // Add parenthesis around function type for the first type argument to avoid ambiguity + const typeParameters = callSignature.target && (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature) ? + callSignature.target.typeParameters : callSignature.typeParameters; + return typeParameters && typeParameters.length !== 0; + } + return false; + } + function createTypeQueryNodeFromSymbol(symbol: Symbol) { const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, context); return createTypeQueryNode(entityName); @@ -2546,12 +2584,14 @@ namespace ts { function typeReferenceToTypeNode(type: TypeReference) { const typeArguments: Type[] = type.typeArguments || emptyArray; if (type.target === globalArrayType) { + context.InElementType = true; const elementType = typeToTypeNodeHelper(typeArguments[0], context); + context.InElementType = false; return createArrayTypeNode(elementType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { if (typeArguments.length > 0) { - const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type))); + const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), /*addInElementTypeFlag*/ false); if (tupleConstituentNodes && tupleConstituentNodes.length > 0) { return createTupleTypeNode(tupleConstituentNodes); } @@ -2566,6 +2606,7 @@ namespace ts { let i = 0; let qualifiedName: QualifiedName | undefined = undefined; if (outerTypeParameters) { + let inFirstTypeArgument = true; const length = outerTypeParameters.length; while (i < length) { // Find group of type arguments for type parameters with the same declaring container. @@ -2577,6 +2618,7 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { + // inFirstTypeArgument??? const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, context); if (!qualifiedName) { qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); @@ -2587,6 +2629,7 @@ namespace ts { qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); } } + inFirstTypeArgument = false; } } let entityName: EntityName = undefined; @@ -2600,7 +2643,7 @@ namespace ts { entityName = nameIdentifier; } const typeParameterCount = (type.target.typeParameters || emptyArray).length; - const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i)) : undefined; + const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i), /*addInElementTypeFlag*/ false) : undefined; return createTypeReferenceNode(entityName, typeArgumentNodes); } } @@ -2695,7 +2738,7 @@ namespace ts { const returnType = getReturnTypeOfSignature(signature); returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context); } - if(context.flags & NodeBuilderFlags.suppressAnyReturnType) { + if(context.flags & NodeBuilderFlags.SuppressAnyReturnType) { if(returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) { returnTypeNode = undefined; } @@ -2733,6 +2776,8 @@ namespace ts { return parameterNode; } + // TODO: add meaning: SymbolFlags argument. + // TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR. function symbolToName(symbol: Symbol, expectsIdentifier: true, context: NodeBuilderContext): Identifier; function symbolToName(symbol: Symbol, expectsIdentifier: false, context: NodeBuilderContext): EntityName; function symbolToName(symbol: Symbol, expectsIdentifier: boolean, context: NodeBuilderContext): EntityName { @@ -2826,6 +2871,7 @@ namespace ts { } } } + function getNameOfSymbol(symbol: Symbol, context: NodeBuilderContext): string { const declaration = firstOrUndefined(symbol.declarations); if (declaration) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index afafee51bf7..eebbe302898 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2947,8 +2947,9 @@ namespace ts { // Precomputed Formats Modifiers = SingleLine | SpaceBetweenSiblings, HeritageClauses = SingleLine | SpaceBetweenSiblings, - SingleLineTypeLiteralMembers = SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented, + SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented, MultiLineTypeLiteralMembers = MultiLine | Indented, + TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 9dfd38a0a5c..172710f39aa 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -393,6 +393,18 @@ namespace ts { : node; } + export function createParenthesizedTypeNode(type: TypeNode) { + const parenthesizedTypeNode = createSynthesizedNode(SyntaxKind.ParenthesizedType) as ParenthesizedTypeNode; + parenthesizedTypeNode.type = type; + return parenthesizedTypeNode; + } + + export function updateParenthesizedTypeNode(node: ParenthesizedTypeNode, type: TypeNode) { + return node.type !== type + ? updateNode(createParenthesizedTypeNode(type), node) + : node; + } + export function createTypeLiteralNode(members: TypeElement[]) { const typeLiteralNode = createSynthesizedNode(SyntaxKind.TypeLiteral) as TypeLiteralNode; typeLiteralNode.members = createNodeArray(members); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 53507e3aa38..17df0243a1b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2559,14 +2559,36 @@ namespace ts { export enum NodeBuilderFlags { None = 0, - allowThisInObjectLiteral = 1 << 0, - allowQualifedNameInPlaceOfIdentifier = 1 << 1, - allowTypeParameterInQualifiedName = 1 << 2, - allowAnonymousIdentifier = 1 << 3, - allowEmptyUnionOrIntersection = 1 << 4, - allowEmptyTuple = 1 << 5, - suppressAnyReturnType = 1 << 6, - ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple + // Options + NoTruncation = 1 << 0, // Don't truncate result + // TODO: part of emit. + WriteArrayAsGenericType = 1 << 1, // Write Array instead T[] + // TODO: part of emit. + UseTypeOfFunction = 1 << 2, // Write typeof instead of function type literal + // TODO: part of emit. + WriteArrowStyleSignature = 1 << 3, // Write arrow style signature + // TODO: turn it into a failing type reference? + WriteOwnNameForAnyLike = 1 << 4, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc) + // TODO + WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature + // TODO + UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type) + // TODO + UseTypeAliasValue = 1 << 7, // Serialize the type instead of using type-alias. This is needed when we emit declaration file. + SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. + // TODO + AddUndefined = 1 << 9, // Add undefined to types of initialized, non-optional parameters + + // Error handling + allowThisInObjectLiteral = 1 << 10, + allowQualifedNameInPlaceOfIdentifier = 1 << 11, + allowTypeParameterInQualifiedName = 1 << 12, + allowAnonymousIdentifier = 1 << 13, + allowEmptyUnionOrIntersection = 1 << 14, + allowEmptyTuple = 1 << 15, + + ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple, + } export interface SymbolDisplayBuilder { diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 262520292e1..1e196a925f8 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -322,7 +322,8 @@ namespace ts { nodesVisitor((node).types, visitor, isTypeNode)); case SyntaxKind.ParenthesizedType: - throw Debug.fail("not implemented."); + return updateParenthesizedTypeNode(node, + visitNode((node).type, visitor, isTypeNode)); case SyntaxKind.TypeOperator: return updateTypeOperatorNode(node, visitNode((node).type, visitor, isTypeNode)); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 0f636bb09ca..7c1a8c407fc 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -130,7 +130,7 @@ namespace ts.codefix { } function signatureToMethodDeclaration(signature: Signature, enclosingDeclaration: Node, body?: Block) { - const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.suppressAnyReturnType); + const signatureDeclaration = checker.signatureToSignatureDeclaration(signature, SyntaxKind.MethodDeclaration, enclosingDeclaration, NodeBuilderFlags.SuppressAnyReturnType); if (signatureDeclaration) { signatureDeclaration.decorators = undefined; signatureDeclaration.modifiers = modifiers; From c0adf99f3510092ea190b34d7bdb5526f5187e6b Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 10:54:53 -0700 Subject: [PATCH 07/77] Add aliasing test --- .../codeFixClassImplementInterfaceMemberTypeAlias.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts new file mode 100644 index 00000000000..eab4c80c786 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts @@ -0,0 +1,12 @@ +/// + +//// type MyType = [string, number]; +//// interface I { test(a: MyType): void; } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + test(a: MyType): void { + throw new Error("Method not implemented."); + } +`); + From 1774c67afaa79abb0cba5b36fbf16bb20c7d2e3a Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 11:00:00 -0700 Subject: [PATCH 08/77] parens and this param for function type --- src/compiler/checker.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fc067d63d59..6ee3377d88a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2406,7 +2406,7 @@ namespace ts { return createTypeReferenceNode(name, /*typeArguments*/ undefined); } - if (context.checkAlias && type.aliasSymbol) { + if (!context.InTypeAlias && context.checkAlias && type.aliasSymbol) { const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context); const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false); return createTypeReferenceNode(name, typeArgumentNodes); @@ -2546,8 +2546,11 @@ namespace ts { if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const signature = resolved.callSignatures[0]; - shouldAddParenthesisAroundFunctionType(signature, context); - return signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); + const functionTypeNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); + return shouldAddParenthesisAroundFunctionType(signature, context) ? + createParenthesizedTypeNode(functionTypeNode) : + functionTypeNode; + } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { const signature = resolved.constructSignatures[0]; @@ -2564,7 +2567,7 @@ namespace ts { function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { - if (context.InElementType) { + if (InElementType) { return true; } else if (context.InFirstTypeArgument) { @@ -2727,6 +2730,10 @@ namespace ts { const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context)); + if (signature.thisParameter) { + const thisParameter = symbolToParameterDeclaration(signature.thisParameter, context); + parameters.unshift(thisParameter); + } let returnTypeNode: TypeNode; if (signature.typePredicate) { const typePredicate = signature.typePredicate; From b3d793608d8525b2dc3ee1f2a403bf733ba6d102 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 27 Apr 2017 15:46:07 -0700 Subject: [PATCH 09/77] Completion list for a class extending another class should contain members from base class Handles #7158 --- src/compiler/checker.ts | 23 --- src/compiler/utilities.ts | 23 +++ src/services/completions.ts | 92 +++++++++++- .../completionEntryForClassMembers.ts | 142 ++++++++++++++++++ .../completionListInNamedClassExpression.ts | 4 +- ...ListInNamedClassExpressionWithShadowing.ts | 8 +- .../completionListIsGlobalCompletion.ts | 2 +- ...pletionListWithModulesInsideModuleScope.ts | 41 ++--- ...etionListWithModulesOutsideModuleScope2.ts | 35 ++++- 9 files changed, 313 insertions(+), 57 deletions(-) create mode 100644 tests/cases/fourslash/completionEntryForClassMembers.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 84ff54391de..a4470a15ea3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -679,10 +679,6 @@ namespace ts { return type.flags & TypeFlags.Object ? (type).objectFlags : 0; } - function getCheckFlags(symbol: Symbol): CheckFlags { - return symbol.flags & SymbolFlags.Transient ? (symbol).checkFlags : 0; - } - function isGlobalSourceFile(node: Node) { return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node); } @@ -13928,25 +13924,6 @@ namespace ts { return s.valueDeclaration ? s.valueDeclaration.kind : SyntaxKind.PropertyDeclaration; } - function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags { - if (s.valueDeclaration) { - const flags = getCombinedModifierFlags(s.valueDeclaration); - return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; - } - if (getCheckFlags(s) & CheckFlags.Synthetic) { - const checkFlags = (s).checkFlags; - const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : - checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : - ModifierFlags.Protected; - const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; - return accessModifier | staticModifier; - } - if (s.flags & SymbolFlags.Prototype) { - return ModifierFlags.Public | ModifierFlags.Static; - } - return 0; - } - function getDeclarationNodeFlagsFromSymbol(s: Symbol): NodeFlags { return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f1b181a03b0..f341e912ccc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4200,6 +4200,29 @@ namespace ts { // Firefox has Object.prototype.watch return options.watch && options.hasOwnProperty("watch"); } + + export function getCheckFlags(symbol: Symbol): CheckFlags { + return symbol.flags & SymbolFlags.Transient ? (symbol).checkFlags : 0; + } + + export function getDeclarationModifierFlagsFromSymbol(s: Symbol): ModifierFlags { + if (s.valueDeclaration) { + const flags = getCombinedModifierFlags(s.valueDeclaration); + return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; + } + if (getCheckFlags(s) & CheckFlags.Synthetic) { + const checkFlags = (s).checkFlags; + const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : + checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : + ModifierFlags.Protected; + const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; + return accessModifier | staticModifier; + } + if (s.flags & SymbolFlags.Prototype) { + return ModifierFlags.Public | ModifierFlags.Static; + } + return 0; + } } namespace ts { diff --git a/src/services/completions.ts b/src/services/completions.ts index 6ac3762b4c7..44915a958c2 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -18,7 +18,7 @@ namespace ts.Completions { return undefined; } - const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag } = completionData; + const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } = completionData; if (requestJsDocTagName) { // If the current position is a jsDoc tag name, only tag names should be provided for completion @@ -52,7 +52,7 @@ namespace ts.Completions { sortText: "0", }); } - else { + else if (!hasFilteredClassMemberKeywords) { return undefined; } } @@ -60,8 +60,11 @@ namespace ts.Completions { getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true, typeChecker, compilerOptions.target, log); } + if (hasFilteredClassMemberKeywords) { + addRange(entries, classMemberKeywordCompletions); + } // Add keywords if this is not a member completion list - if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) { + else if (!isMemberCompletion && !requestJsDocTag && !requestJsDocTagName) { addRange(entries, keywordCompletions); } @@ -406,7 +409,7 @@ namespace ts.Completions { } if (requestJsDocTagName || requestJsDocTag) { - return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag }; + return { symbols: undefined, isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, location: undefined, isRightOfDot: false, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords: false }; } if (!insideJsDocTagExpression) { @@ -505,6 +508,7 @@ namespace ts.Completions { let isGlobalCompletion = false; let isMemberCompletion: boolean; let isNewIdentifierLocation: boolean; + let hasFilteredClassMemberKeywords = false; let symbols: Symbol[] = []; if (isRightOfDot) { @@ -542,7 +546,7 @@ namespace ts.Completions { log("getCompletionData: Semantic work: " + (timestamp() - semanticStart)); - return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag }; + return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords }; function getTypeScriptMemberSymbols(): void { // Right of dot member completion list @@ -599,6 +603,7 @@ namespace ts.Completions { function tryGetGlobalSymbols(): boolean { let objectLikeContainer: ObjectLiteralExpression | BindingPattern; let namedImportsOrExports: NamedImportsOrExports; + let classLikeContainer: ClassLikeDeclaration; let jsxContainer: JsxOpeningLikeElement; if (objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken)) { @@ -611,6 +616,11 @@ namespace ts.Completions { return tryGetImportOrExportClauseCompletionSymbols(namedImportsOrExports); } + if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) { + // cursor inside class declaration + return tryGetClassLikeCompletionSymbols(classLikeContainer); + } + if (jsxContainer = tryGetContainingJsxElement(contextToken)) { let attrsType: Type; if ((jsxContainer.kind === SyntaxKind.JsxSelfClosingElement) || (jsxContainer.kind === SyntaxKind.JsxOpeningElement)) { @@ -913,6 +923,31 @@ namespace ts.Completions { return true; } + /** + * Aggregates relevant symbols for completion in class declaration + * Relevant symbols are stored in the captured 'symbols' variable. + * + * @returns true if 'symbols' was successfully populated; false otherwise. + */ + function tryGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration): boolean { + // We're looking up possible property names from parent type. + isMemberCompletion = true; + // Declaring new property/method/accessor + isNewIdentifierLocation = true; + // Has keywords for class elements + hasFilteredClassMemberKeywords = true; + + const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration); + if (baseTypeNode) { + const baseType = typeChecker.getTypeAtLocation(baseTypeNode); + // List of property symbols of base type that are not private + symbols = filter(typeChecker.getPropertiesOfType(baseType), + baseProperty => !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); + } + + return true; + } + /** * Returns the immediate owning object literal or binding pattern of a context token, * on the condition that one exists and that the context implies completion should be given. @@ -953,6 +988,38 @@ namespace ts.Completions { return undefined; } + /** + * Returns the immediate owning class declaration of a context token, + * on the condition that one exists and that the context implies completion should be given. + */ + function tryGetClassLikeCompletionContainer(contextToken: Node): ClassLikeDeclaration { + if (contextToken) { + switch (contextToken.kind) { + case SyntaxKind.OpenBraceToken: // class c { | + if (isClassLike(contextToken.parent)) { + return contextToken.parent; + } + break; + + // class c {getValue(): number; | } + case SyntaxKind.CommaToken: + case SyntaxKind.SemicolonToken: + // class c { method() { } | } + case SyntaxKind.CloseBraceToken: + if (isClassLike(location)) { + return location; + } + break; + } + } + + // class c { method() { } | method2() { } } + if (location && location.kind === SyntaxKind.SyntaxList && isClassLike(location.parent)) { + return location.parent; + } + return undefined; + } + function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement { if (contextToken) { const parent = contextToken.parent; @@ -1306,6 +1373,21 @@ namespace ts.Completions { }); } + const classMemberKeywordCompletions = filter(keywordCompletions, entry => { + switch (stringToToken(entry.name)) { + case SyntaxKind.PublicKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.ConstructorKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + return true; + } + }); + function isEqualityExpression(node: Node): node is BinaryExpression { return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind); } diff --git a/tests/cases/fourslash/completionEntryForClassMembers.ts b/tests/cases/fourslash/completionEntryForClassMembers.ts new file mode 100644 index 00000000000..da1051e784a --- /dev/null +++ b/tests/cases/fourslash/completionEntryForClassMembers.ts @@ -0,0 +1,142 @@ +/// + +////abstract class B { +//// abstract getValue(): number; +//// /*abstractClass*/ +////} +////class C extends B { +//// /*classThatIsEmptyAndExtendingAnotherClass*/ +////} +////class D extends B { +//// /*classThatHasAlreadyImplementedAnotherClassMethod*/ +//// getValue() { +//// return 10; +//// } +//// /*classThatHasAlreadyImplementedAnotherClassMethodAfterMethod*/ +////} +////class E { +//// /*classThatDoesNotExtendAnotherClass*/ +////} +////class F extends B { +//// public /*classThatHasWrittenPublicKeyword*/ +////} +////class G extends B { +//// static /*classElementContainingStatic*/ +////} +////class H extends B { +//// prop/*classThatStartedWritingIdentifier*/ +////} +////class I extends B { +//// prop0: number +//// /*propDeclarationWithoutSemicolon*/ +//// prop: number; +//// /*propDeclarationWithSemicolon*/ +//// prop1 = 10; +//// /*propAssignmentWithSemicolon*/ +//// prop2 = 10 +//// /*propAssignmentWithoutSemicolon*/ +//// method(): number +//// /*methodSignatureWithoutSemicolon*/ +//// method2(): number; +//// /*methodSignatureWithSemicolon*/ +//// method3() { +//// /*InsideMethod*/ +//// } +//// /*methodImplementation*/ +//// get c() +//// /*accessorSignatureWithoutSemicolon*/ +//// set c() +//// { +//// } +//// /*accessorSignatureImplementation*/ +////} +////class J extends B { +//// get /*classThatHasWrittenGetKeyword*/ +////} +////class K extends B { +//// set /*classThatHasWrittenSetKeyword*/ +////} +////class J extends B { +//// get identi/*classThatStartedWritingIdentifierOfGetAccessor*/ +////} +////class K extends B { +//// set identi/*classThatStartedWritingIdentifierOfSetAccessor*/ +////} +////class L extends B { +//// public identi/*classThatStartedWritingIdentifierAfterModifier*/ +////} +////class L extends B { +//// static identi/*classThatStartedWritingIdentifierAfterStaticModifier*/ +////} + +const allowedKeywords = [ + "public", + "private", + "protected", + "static", + "abstract", + "readonly", + "get", + "set", + "constructor" +]; + +const allowedKeywordCount = allowedKeywords.length; +function verifyAllowedKeyWords() { + for (const keyword of allowedKeywords) { + verify.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword"); + } +} + +const nonClassElementMarkers = [ + "InsideMethod" +]; +for (const marker of nonClassElementMarkers) { + goTo.marker(marker); + verify.not.completionListContains("getValue"); + verify.not.completionListIsEmpty(); +} + +// Only keywords allowed at this position since they dont extend the class +const onlyClassElementKeywordLocations = [ + "abstractClass", + "classThatDoesNotExtendAnotherClass" +]; +for (const marker of onlyClassElementKeywordLocations) { + goTo.marker(marker); + verifyAllowedKeyWords(); + verify.completionListCount(allowedKeywordCount); +} + +// Base members and class member keywords allowed +const classElementCompletionLocations = [ + "classThatIsEmptyAndExtendingAnotherClass", + "classThatHasAlreadyImplementedAnotherClassMethod", + "classThatHasAlreadyImplementedAnotherClassMethodAfterMethod", + // TODO should we give completion for these keywords + //"classThatHasWrittenPublicKeyword", + //"classElementContainingStatic", + "classThatStartedWritingIdentifier", + "propDeclarationWithoutSemicolon", + "propDeclarationWithSemicolon", + "propAssignmentWithSemicolon", + "propAssignmentWithoutSemicolon", + "methodSignatureWithoutSemicolon", + "methodSignatureWithSemicolon", + "methodImplementation", + "accessorSignatureWithoutSemicolon", + "accessorSignatureImplementation", + // TODO should we give completion for these keywords + //"classThatHasWrittenGetKeyword", + //"classThatHasWrittenSetKeyword", + //"classThatStartedWritingIdentifierOfGetAccessor", + //"classThatStartedWritingIdentifierOfSetAccessor", + //"classThatStartedWritingIdentifierAfterModifier", + //"classThatStartedWritingIdentifierAfterStaticModifier" +]; +for (const marker of classElementCompletionLocations) { + goTo.marker(marker); + verify.completionListContains("getValue", "(method) B.getValue(): number", /*documentation*/ undefined, "method"); + verifyAllowedKeyWords(); + verify.completionListCount(allowedKeywordCount + 1); +} \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInNamedClassExpression.ts b/tests/cases/fourslash/completionListInNamedClassExpression.ts index cf0d170f6a5..8ca6806ce7f 100644 --- a/tests/cases/fourslash/completionListInNamedClassExpression.ts +++ b/tests/cases/fourslash/completionListInNamedClassExpression.ts @@ -1,4 +1,4 @@ -/// +/// //// var x = class myClass { //// getClassName (){ @@ -11,4 +11,4 @@ goTo.marker("0"); verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); goTo.marker("1"); -verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); \ No newline at end of file +verify.not.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInNamedClassExpressionWithShadowing.ts b/tests/cases/fourslash/completionListInNamedClassExpressionWithShadowing.ts index e1274e6f592..fd347d58037 100644 --- a/tests/cases/fourslash/completionListInNamedClassExpressionWithShadowing.ts +++ b/tests/cases/fourslash/completionListInNamedClassExpressionWithShadowing.ts @@ -1,4 +1,4 @@ -/// +/// //// class myClass { /*0*/ } //// /*1*/ @@ -16,7 +16,7 @@ //// } goTo.marker("0"); -verify.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); +verify.not.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); verify.not.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); goTo.marker("1"); @@ -28,7 +28,7 @@ verify.completionListContains("myClass", "(local class) myClass", /*documentatio verify.not.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); goTo.marker("3"); -verify.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); +verify.not.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); verify.not.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); goTo.marker("4"); @@ -36,5 +36,5 @@ verify.completionListContains("myClass", "class myClass", /*documentation*/ unde verify.not.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); goTo.marker("5"); -verify.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); +verify.not.completionListContains("myClass", "class myClass", /*documentation*/ undefined, "class"); verify.not.completionListContains("myClass", "(local class) myClass", /*documentation*/ undefined, "local class"); diff --git a/tests/cases/fourslash/completionListIsGlobalCompletion.ts b/tests/cases/fourslash/completionListIsGlobalCompletion.ts index a1fadc6bb12..121ab940d65 100644 --- a/tests/cases/fourslash/completionListIsGlobalCompletion.ts +++ b/tests/cases/fourslash/completionListIsGlobalCompletion.ts @@ -53,7 +53,7 @@ verify.completionListIsGlobal(false); goTo.marker("9"); verify.completionListIsGlobal(false); goTo.marker("10"); -verify.completionListIsGlobal(true); +verify.completionListIsGlobal(false); goTo.marker("11"); verify.completionListIsGlobal(true); goTo.marker("12"); diff --git a/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts b/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts index 51e4013e53a..916ad10fe9c 100644 --- a/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts +++ b/tests/cases/fourslash/completionListWithModulesInsideModuleScope.ts @@ -225,27 +225,28 @@ //// ////var shwvar = 1; -function goToMarkAndGeneralVerify(marker: string) +function goToMarkAndGeneralVerify(marker: string, isClassScope?: boolean) { goTo.marker(marker); - verify.completionListContains('mod1var', 'var mod1var: number'); - verify.completionListContains('mod1fn', 'function mod1fn(): void'); - verify.completionListContains('mod1cls', 'class mod1cls'); - verify.completionListContains('mod1int', 'interface mod1int'); - verify.completionListContains('mod1mod', 'namespace mod1mod'); - verify.completionListContains('mod1evar', 'var mod1.mod1evar: number'); - verify.completionListContains('mod1efn', 'function mod1.mod1efn(): void'); - verify.completionListContains('mod1ecls', 'class mod1.mod1ecls'); - verify.completionListContains('mod1eint', 'interface mod1.mod1eint'); - verify.completionListContains('mod1emod', 'namespace mod1.mod1emod'); - verify.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number'); - verify.completionListContains('mod2', 'namespace mod2'); - verify.completionListContains('mod3', 'namespace mod3'); - verify.completionListContains('shwvar', 'var shwvar: number'); - verify.completionListContains('shwfn', 'function shwfn(): void'); - verify.completionListContains('shwcls', 'class shwcls'); - verify.completionListContains('shwint', 'interface shwint'); + const verifyModule = isClassScope ? verify.not : verify; + verifyModule.completionListContains('mod1var', 'var mod1var: number'); + verifyModule.completionListContains('mod1fn', 'function mod1fn(): void'); + verifyModule.completionListContains('mod1cls', 'class mod1cls'); + verifyModule.completionListContains('mod1int', 'interface mod1int'); + verifyModule.completionListContains('mod1mod', 'namespace mod1mod'); + verifyModule.completionListContains('mod1evar', 'var mod1.mod1evar: number'); + verifyModule.completionListContains('mod1efn', 'function mod1.mod1efn(): void'); + verifyModule.completionListContains('mod1ecls', 'class mod1.mod1ecls'); + verifyModule.completionListContains('mod1eint', 'interface mod1.mod1eint'); + verifyModule.completionListContains('mod1emod', 'namespace mod1.mod1emod'); + verifyModule.completionListContains('mod1eexvar', 'var mod1.mod1eexvar: number'); + verifyModule.completionListContains('mod2', 'namespace mod2'); + verifyModule.completionListContains('mod3', 'namespace mod3'); + verifyModule.completionListContains('shwvar', 'var shwvar: number'); + verifyModule.completionListContains('shwfn', 'function shwfn(): void'); + verifyModule.completionListContains('shwcls', 'class shwcls'); + verifyModule.completionListContains('shwint', 'interface shwint'); verify.not.completionListContains('mod2var'); verify.not.completionListContains('mod2fn'); @@ -280,7 +281,7 @@ verify.completionListContains('bar', '(local var) bar: number'); verify.completionListContains('foob', '(local function) foob(): void'); // from class in mod1 -goToMarkAndGeneralVerify('class'); +goToMarkAndGeneralVerify('class', /*isClassScope*/ true); //verify.not.completionListContains('ceFunc'); //verify.not.completionListContains('ceVar'); @@ -306,7 +307,7 @@ verify.completionListContains('bar', '(local var) bar: number'); verify.completionListContains('foob', '(local function) foob(): void'); // from exported class in mod1 -goToMarkAndGeneralVerify('exportedClass'); +goToMarkAndGeneralVerify('exportedClass', /*isClassScope*/ true); //verify.not.completionListContains('ceFunc'); //verify.not.completionListContains('ceVar'); diff --git a/tests/cases/fourslash/completionListWithModulesOutsideModuleScope2.ts b/tests/cases/fourslash/completionListWithModulesOutsideModuleScope2.ts index 865887661e9..340d0bd3191 100644 --- a/tests/cases/fourslash/completionListWithModulesOutsideModuleScope2.ts +++ b/tests/cases/fourslash/completionListWithModulesOutsideModuleScope2.ts @@ -231,6 +231,39 @@ //// x: /*objectLiteral*/ ////} +goTo.marker('extendedClass'); + +verify.not.completionListContains('mod1'); +verify.not.completionListContains('mod2'); +verify.not.completionListContains('mod3'); +verify.not.completionListContains('shwvar', 'var shwvar: number'); +verify.not.completionListContains('shwfn', 'function shwfn(): void'); +verify.not.completionListContains('shwcls', 'class shwcls'); +verify.not.completionListContains('shwint', 'interface shwint'); + +verify.not.completionListContains('mod2var'); +verify.not.completionListContains('mod2fn'); +verify.not.completionListContains('mod2cls'); +verify.not.completionListContains('mod2int'); +verify.not.completionListContains('mod2mod'); +verify.not.completionListContains('mod2evar'); +verify.not.completionListContains('mod2efn'); +verify.not.completionListContains('mod2ecls'); +verify.not.completionListContains('mod2eint'); +verify.not.completionListContains('mod2emod'); +verify.not.completionListContains('sfvar'); +verify.not.completionListContains('sffn'); +verify.not.completionListContains('scvar'); +verify.not.completionListContains('scfn'); +verify.completionListContains('scpfn'); +verify.completionListContains('scpvar'); +verify.not.completionListContains('scsvar'); +verify.not.completionListContains('scsfn'); +verify.not.completionListContains('sivar'); +verify.not.completionListContains('sifn'); +verify.not.completionListContains('mod1exvar'); +verify.not.completionListContains('mod2eexvar'); + function goToMarkerAndVerify(marker: string) { goTo.marker(marker); @@ -267,8 +300,6 @@ function goToMarkerAndVerify(marker: string) verify.not.completionListContains('mod2eexvar'); } -goToMarkerAndVerify('extendedClass'); - goToMarkerAndVerify('objectLiteral'); goTo.marker('localVar'); From 37a2cddabc552e7e3ce86f94d92f171be76f2f97 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 1 May 2017 11:45:18 -0700 Subject: [PATCH 10/77] Give the class element completion on typing keywords like public, private, readonly Also when name of the function is location, make sure we are actually looking at the same symbol before using the declaration to get signature to display --- src/harness/fourslash.ts | 17 +++++ src/services/completions.ts | 41 ++++++++++-- src/services/symbolDisplay.ts | 44 +++++++------ .../completionEntryForClassMembers.ts | 63 +++++++++---------- ...mpletionListBuilderLocations_properties.ts | 2 +- tests/cases/fourslash/fourslash.ts | 2 + ...cesForClassMembersExtendingGenericClass.ts | 2 +- 7 files changed, 111 insertions(+), 60 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a56d21ae345..74790400b98 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -3417,6 +3417,17 @@ namespace FourSlashInterface { export class VerifyNegatable { public not: VerifyNegatable; + public allowedClassElementKeywords = [ + "public", + "private", + "protected", + "static", + "abstract", + "readonly", + "get", + "set", + "constructor" + ]; constructor(protected state: FourSlash.TestState, private negative = false) { if (!negative) { @@ -3453,6 +3464,12 @@ namespace FourSlashInterface { this.state.verifyCompletionListIsEmpty(this.negative); } + public completionListContainsClassElementKeywords() { + for (const keyword of this.allowedClassElementKeywords) { + this.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword"); + } + } + public completionListIsGlobal(expected: boolean) { this.state.verifyCompletionListIsGlobal(expected); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 44915a958c2..da59dab8828 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -988,6 +988,10 @@ namespace ts.Completions { return undefined; } + function isFromClassElementDeclaration(node: Node) { + return isClassElement(node.parent) && isClassLike(node.parent.parent); + } + /** * Returns the immediate owning class declaration of a context token, * on the condition that one exists and that the context implies completion should be given. @@ -1010,6 +1014,13 @@ namespace ts.Completions { return location; } break; + + default: + if (isFromClassElementDeclaration(contextToken) && + (isClassMemberCompletionKeyword(contextToken.kind) || + isClassMemberCompletionKeywordText(contextToken.getText()))) { + return contextToken.parent.parent as ClassLikeDeclaration; + } } } @@ -1148,7 +1159,7 @@ namespace ts.Completions { isFunction(containingNodeKind); case SyntaxKind.StaticKeyword: - return containingNodeKind === SyntaxKind.PropertyDeclaration; + return containingNodeKind === SyntaxKind.PropertyDeclaration && !isClassLike(contextToken.parent.parent); case SyntaxKind.DotDotDotToken: return containingNodeKind === SyntaxKind.Parameter || @@ -1165,13 +1176,17 @@ namespace ts.Completions { containingNodeKind === SyntaxKind.ExportSpecifier || containingNodeKind === SyntaxKind.NamespaceImport; + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + if (isFromClassElementDeclaration(contextToken)) { + return false; + } + // falls through case SyntaxKind.ClassKeyword: case SyntaxKind.EnumKeyword: case SyntaxKind.InterfaceKeyword: case SyntaxKind.FunctionKeyword: case SyntaxKind.VarKeyword: - case SyntaxKind.GetKeyword: - case SyntaxKind.SetKeyword: case SyntaxKind.ImportKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: @@ -1180,6 +1195,13 @@ namespace ts.Completions { return true; } + // If the previous token is keyword correspoding to class member completion keyword + // there will be completion available here + if (isClassMemberCompletionKeywordText(contextToken.getText()) && + isFromClassElementDeclaration(contextToken)) { + return false; + } + // Previous token may have been a keyword that was converted to an identifier. switch (contextToken.getText()) { case "abstract": @@ -1373,8 +1395,8 @@ namespace ts.Completions { }); } - const classMemberKeywordCompletions = filter(keywordCompletions, entry => { - switch (stringToToken(entry.name)) { + function isClassMemberCompletionKeyword(kind: SyntaxKind) { + switch (kind) { case SyntaxKind.PublicKeyword: case SyntaxKind.ProtectedKeyword: case SyntaxKind.PrivateKeyword: @@ -1386,7 +1408,14 @@ namespace ts.Completions { case SyntaxKind.SetKeyword: return true; } - }); + } + + function isClassMemberCompletionKeywordText(text: string) { + return isClassMemberCompletionKeyword(stringToToken(text)); + } + + const classMemberKeywordCompletions = filter(keywordCompletions, entry => + isClassMemberCompletionKeywordText(entry.name)); function isEqualityExpression(node: Node): node is BinaryExpression { return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind); diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 5bf3e37f28a..4268a52c419 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -200,27 +200,33 @@ namespace ts.SymbolDisplay { (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration // get the signature from the declaration and write it const functionDeclaration = location.parent; - const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); - if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { - signature = typeChecker.getSignatureFromDeclaration(functionDeclaration); - } - else { - signature = allSignatures[0]; - } + // Use function declaration to write the signatures only if the symbol corresponding to this declaration + const locationIsSymbolDeclaration = findDeclaration(symbol, declaration => + declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration)); - if (functionDeclaration.kind === SyntaxKind.Constructor) { - // show (constructor) Type(...) signature - symbolKind = ScriptElementKind.constructorImplementationElement; - addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); - } - else { - // (function/method) symbol(..signature) - addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature && - !(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); - } + if (locationIsSymbolDeclaration) { + const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); + if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { + signature = typeChecker.getSignatureFromDeclaration(functionDeclaration); + } + else { + signature = allSignatures[0]; + } - addSignatureDisplayParts(signature, allSignatures); - hasAddedSymbolInfo = true; + if (functionDeclaration.kind === SyntaxKind.Constructor) { + // show (constructor) Type(...) signature + symbolKind = ScriptElementKind.constructorImplementationElement; + addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); + } + else { + // (function/method) symbol(..signature) + addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature && + !(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); + } + + addSignatureDisplayParts(signature, allSignatures); + hasAddedSymbolInfo = true; + } } } } diff --git a/tests/cases/fourslash/completionEntryForClassMembers.ts b/tests/cases/fourslash/completionEntryForClassMembers.ts index da1051e784a..c52977a0002 100644 --- a/tests/cases/fourslash/completionEntryForClassMembers.ts +++ b/tests/cases/fourslash/completionEntryForClassMembers.ts @@ -1,6 +1,9 @@ /// ////abstract class B { +//// private privateMethod() { } +//// protected protectedMethod() { }; +//// static staticMethod() { } //// abstract getValue(): number; //// /*abstractClass*/ ////} @@ -69,25 +72,7 @@ //// static identi/*classThatStartedWritingIdentifierAfterStaticModifier*/ ////} -const allowedKeywords = [ - "public", - "private", - "protected", - "static", - "abstract", - "readonly", - "get", - "set", - "constructor" -]; - -const allowedKeywordCount = allowedKeywords.length; -function verifyAllowedKeyWords() { - for (const keyword of allowedKeywords) { - verify.completionListContains(keyword, keyword, /*documentation*/ undefined, "keyword"); - } -} - +const allowedKeywordCount = verify.allowedClassElementKeywords.length; const nonClassElementMarkers = [ "InsideMethod" ]; @@ -104,7 +89,7 @@ const onlyClassElementKeywordLocations = [ ]; for (const marker of onlyClassElementKeywordLocations) { goTo.marker(marker); - verifyAllowedKeyWords(); + verify.completionListContainsClassElementKeywords(); verify.completionListCount(allowedKeywordCount); } @@ -113,9 +98,8 @@ const classElementCompletionLocations = [ "classThatIsEmptyAndExtendingAnotherClass", "classThatHasAlreadyImplementedAnotherClassMethod", "classThatHasAlreadyImplementedAnotherClassMethodAfterMethod", - // TODO should we give completion for these keywords - //"classThatHasWrittenPublicKeyword", - //"classElementContainingStatic", + "classThatHasWrittenPublicKeyword", + "classElementContainingStatic", "classThatStartedWritingIdentifier", "propDeclarationWithoutSemicolon", "propDeclarationWithSemicolon", @@ -126,17 +110,30 @@ const classElementCompletionLocations = [ "methodImplementation", "accessorSignatureWithoutSemicolon", "accessorSignatureImplementation", - // TODO should we give completion for these keywords - //"classThatHasWrittenGetKeyword", - //"classThatHasWrittenSetKeyword", - //"classThatStartedWritingIdentifierOfGetAccessor", - //"classThatStartedWritingIdentifierOfSetAccessor", - //"classThatStartedWritingIdentifierAfterModifier", - //"classThatStartedWritingIdentifierAfterStaticModifier" + "classThatHasWrittenGetKeyword", + "classThatHasWrittenSetKeyword", + "classThatStartedWritingIdentifierOfGetAccessor", + "classThatStartedWritingIdentifierOfSetAccessor", + "classThatStartedWritingIdentifierAfterModifier", + "classThatStartedWritingIdentifierAfterStaticModifier" +]; + +const validMembersOfBase = [ + ["getValue", "(method) B.getValue(): number"], + ["protectedMethod", "(method) B.protectedMethod(): void"] +]; +const invalidMembersOfBase = [ + ["privateMethod", "(method) B.privateMethod(): void"], + ["staticMethod", "(method) B.staticMethod(): void"] ]; for (const marker of classElementCompletionLocations) { goTo.marker(marker); - verify.completionListContains("getValue", "(method) B.getValue(): number", /*documentation*/ undefined, "method"); - verifyAllowedKeyWords(); - verify.completionListCount(allowedKeywordCount + 1); + for (const [validMemberOfBaseSymbol, validMemberOfBaseText] of validMembersOfBase) { + verify.completionListContains(validMemberOfBaseSymbol, validMemberOfBaseText, /*documentation*/ undefined, "method"); + } + for (const [invalidMemberOfBaseSymbol, invalidMemberOfBaseText] of invalidMembersOfBase) { + verify.not.completionListContains(invalidMemberOfBaseSymbol, invalidMemberOfBaseText, /*documentation*/ undefined, "method"); + } + verify.completionListContainsClassElementKeywords(); + verify.completionListCount(allowedKeywordCount + validMembersOfBase.length); } \ No newline at end of file diff --git a/tests/cases/fourslash/completionListBuilderLocations_properties.ts b/tests/cases/fourslash/completionListBuilderLocations_properties.ts index 806d8c1de4f..2cdc3b7e7ba 100644 --- a/tests/cases/fourslash/completionListBuilderLocations_properties.ts +++ b/tests/cases/fourslash/completionListBuilderLocations_properties.ts @@ -10,4 +10,4 @@ //// public static a/*property2*/ ////} -goTo.eachMarker(() => verify.completionListIsEmpty()); +goTo.eachMarker(() => verify.completionListContainsClassElementKeywords()); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 374dfd33c7d..31d8b8b4ef2 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -133,11 +133,13 @@ declare namespace FourSlashInterface { class verifyNegatable { private negative; not: verifyNegatable; + allowedClassElementKeywords: string[]; constructor(negative?: boolean); completionListCount(expectedCount: number): void; completionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number): void; completionListItemsCountIsGreaterThan(count: number): void; completionListIsEmpty(): void; + completionListContainsClassElementKeywords(): void; completionListAllowsNewIdentifier(): void; signatureHelpPresent(): void; errorExistsBetweenMarkers(startMarker: string, endMarker: string): void; diff --git a/tests/cases/fourslash/referencesForClassMembersExtendingGenericClass.ts b/tests/cases/fourslash/referencesForClassMembersExtendingGenericClass.ts index 3e453663f63..448c2627531 100644 --- a/tests/cases/fourslash/referencesForClassMembersExtendingGenericClass.ts +++ b/tests/cases/fourslash/referencesForClassMembersExtendingGenericClass.ts @@ -26,7 +26,7 @@ const methods = ranges.get("method"); const [m0, m1, m2] = methods; verify.referenceGroups(m0, [{ definition: "(method) Base.method(a?: T, b?: U): this", ranges: methods }]); verify.referenceGroups(m1, [ - { definition: "(method) Base.method(): void", ranges: [m0] }, + { definition: "(method) Base.method(a?: T, b?: U): this", ranges: [m0] }, { definition: "(method) MyClass.method(): void", ranges: [m1, m2] } ]); verify.referenceGroups(m2, [ From fcb0f4617812dae7fdf2c7dd97a508e2535e3c3f Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 4 May 2017 17:44:34 -0700 Subject: [PATCH 11/77] Tune the completion list for static and private modifiers Do not show inherited members in completion for when writing private member Show only static inherited members when writing static member --- src/services/completions.ts | 39 +++++-- .../completionEntryForClassMembers.ts | 107 ++++++++++++------ 2 files changed, 107 insertions(+), 39 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index da59dab8828..cb4abb89b58 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -939,10 +939,31 @@ namespace ts.Completions { const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration); if (baseTypeNode) { - const baseType = typeChecker.getTypeAtLocation(baseTypeNode); - // List of property symbols of base type that are not private - symbols = filter(typeChecker.getPropertiesOfType(baseType), - baseProperty => !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); + const classElement = contextToken.parent; + let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement); + // If this is context token is not something we are editing now, consider if this would lead to be modifier + if (contextToken.kind === SyntaxKind.Identifier && !isCurrentlyEditingNode(contextToken)) { + switch (contextToken.getText()) { + case "private": + classElementModifierFlags = classElementModifierFlags | ModifierFlags.Private; + break; + case "static": + classElementModifierFlags = classElementModifierFlags | ModifierFlags.Static; + break; + } + } + + // No member list for private methods + if (!(classElementModifierFlags & ModifierFlags.Private)) { + const baseType = typeChecker.getTypeAtLocation(baseTypeNode); + const typeToGetPropertiesFrom = (classElementModifierFlags & ModifierFlags.Static) ? + typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, classLikeDeclaration) : + baseType; + + // List of property symbols of base type that are not private + symbols = filter(typeChecker.getPropertiesOfType(typeToGetPropertiesFrom), + baseProperty => baseProperty.getDeclarations() && !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); + } } return true; @@ -1248,7 +1269,7 @@ namespace ts.Completions { for (const element of namedImportsOrExports) { // If this is the current item we are editing right now, do not filter it out - if (element.getStart() <= position && position <= element.getEnd()) { + if (isCurrentlyEditingNode(element)) { continue; } @@ -1287,7 +1308,7 @@ namespace ts.Completions { } // If this is the current item we are editing right now, do not filter it out - if (m.getStart() <= position && position <= m.getEnd()) { + if (isCurrentlyEditingNode(m)) { continue; } @@ -1322,7 +1343,7 @@ namespace ts.Completions { const seenNames = createMap(); for (const attr of attributes) { // If this is the current item we are editing right now, do not filter it out - if (attr.getStart() <= position && position <= attr.getEnd()) { + if (isCurrentlyEditingNode(attr)) { continue; } @@ -1333,6 +1354,10 @@ namespace ts.Completions { return filter(symbols, a => !seenNames.get(a.name)); } + + function isCurrentlyEditingNode(node: Node): boolean { + return node.getStart() <= position && position <= node.getEnd(); + } } /** diff --git a/tests/cases/fourslash/completionEntryForClassMembers.ts b/tests/cases/fourslash/completionEntryForClassMembers.ts index c52977a0002..672046f62a7 100644 --- a/tests/cases/fourslash/completionEntryForClassMembers.ts +++ b/tests/cases/fourslash/completionEntryForClassMembers.ts @@ -23,12 +23,19 @@ ////class F extends B { //// public /*classThatHasWrittenPublicKeyword*/ ////} +////class F2 extends B { +//// private /*classThatHasWrittenPrivateKeyword*/ +////} ////class G extends B { //// static /*classElementContainingStatic*/ ////} +////class G2 extends B { +//// private static /*classElementContainingPrivateStatic*/ +////} ////class H extends B { //// prop/*classThatStartedWritingIdentifier*/ ////} +//////Class for location verification ////class I extends B { //// prop0: number //// /*propDeclarationWithoutSemicolon*/ @@ -68,38 +75,87 @@ ////class L extends B { //// public identi/*classThatStartedWritingIdentifierAfterModifier*/ ////} -////class L extends B { +////class L2 extends B { +//// private identi/*classThatStartedWritingIdentifierAfterPrivateModifier*/ +////} +////class M extends B { //// static identi/*classThatStartedWritingIdentifierAfterStaticModifier*/ ////} +////class M extends B { +//// private static identi/*classThatStartedWritingIdentifierAfterPrivateStaticModifier*/ +////} const allowedKeywordCount = verify.allowedClassElementKeywords.length; +type CompletionInfo = [string, string]; +type CompletionInfoVerifier = { validMembers: CompletionInfo[], invalidMembers: CompletionInfo[] }; + +function verifyClassElementLocations({ validMembers, invalidMembers }: CompletionInfoVerifier, classElementCompletionLocations: string[]) { + for (const marker of classElementCompletionLocations) { + goTo.marker(marker); + verifyCompletionInfo(validMembers, verify); + verifyCompletionInfo(invalidMembers, verify.not); + verify.completionListContainsClassElementKeywords(); + verify.completionListCount(allowedKeywordCount + validMembers.length); + } +} + +function verifyCompletionInfo(memberInfo: CompletionInfo[], verify: FourSlashInterface.verifyNegatable) { + for (const [symbol, text] of memberInfo) { + verify.completionListContains(symbol, text, /*documentation*/ undefined, "method"); + } +} + +const allMembersOfBase: CompletionInfo[] = [ + ["getValue", "(method) B.getValue(): number"], + ["protectedMethod", "(method) B.protectedMethod(): void"], + ["privateMethod", "(method) B.privateMethod(): void"], + ["staticMethod", "(method) B.staticMethod(): void"] +]; +function filterCompletionInfo(fn: (a: CompletionInfo) => boolean): CompletionInfoVerifier { + const validMembers: CompletionInfo[] = []; + const invalidMembers: CompletionInfo[] = []; + for (const member of allMembersOfBase) { + if (fn(member)) { + validMembers.push(member); + } + else { + invalidMembers.push(member); + } + } + return { validMembers, invalidMembers }; +} + + +const instanceMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "getValue" || a === "protectedMethod"); +const staticMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "staticMethod"); + +// Not a class element declaration location const nonClassElementMarkers = [ "InsideMethod" ]; for (const marker of nonClassElementMarkers) { goTo.marker(marker); - verify.not.completionListContains("getValue"); + verifyCompletionInfo(allMembersOfBase, verify.not); verify.not.completionListIsEmpty(); } -// Only keywords allowed at this position since they dont extend the class +// Only keywords allowed at this position since they dont extend the class or are private const onlyClassElementKeywordLocations = [ "abstractClass", - "classThatDoesNotExtendAnotherClass" + "classThatDoesNotExtendAnotherClass", + "classThatHasWrittenPrivateKeyword", + "classElementContainingPrivateStatic", + "classThatStartedWritingIdentifierAfterPrivateModifier", + "classThatStartedWritingIdentifierAfterPrivateStaticModifier" ]; -for (const marker of onlyClassElementKeywordLocations) { - goTo.marker(marker); - verify.completionListContainsClassElementKeywords(); - verify.completionListCount(allowedKeywordCount); -} +verifyClassElementLocations({ validMembers: [], invalidMembers: allMembersOfBase }, onlyClassElementKeywordLocations); -// Base members and class member keywords allowed -const classElementCompletionLocations = [ +// Instance base members and class member keywords allowed +const classInstanceElementLocations = [ "classThatIsEmptyAndExtendingAnotherClass", "classThatHasAlreadyImplementedAnotherClassMethod", "classThatHasAlreadyImplementedAnotherClassMethodAfterMethod", "classThatHasWrittenPublicKeyword", - "classElementContainingStatic", "classThatStartedWritingIdentifier", "propDeclarationWithoutSemicolon", "propDeclarationWithSemicolon", @@ -115,25 +171,12 @@ const classElementCompletionLocations = [ "classThatStartedWritingIdentifierOfGetAccessor", "classThatStartedWritingIdentifierOfSetAccessor", "classThatStartedWritingIdentifierAfterModifier", +]; +verifyClassElementLocations(instanceMemberInfo, classInstanceElementLocations); + +// Static Base members and class member keywords allowed +const staticClassLocations = [ + "classElementContainingStatic", "classThatStartedWritingIdentifierAfterStaticModifier" ]; - -const validMembersOfBase = [ - ["getValue", "(method) B.getValue(): number"], - ["protectedMethod", "(method) B.protectedMethod(): void"] -]; -const invalidMembersOfBase = [ - ["privateMethod", "(method) B.privateMethod(): void"], - ["staticMethod", "(method) B.staticMethod(): void"] -]; -for (const marker of classElementCompletionLocations) { - goTo.marker(marker); - for (const [validMemberOfBaseSymbol, validMemberOfBaseText] of validMembersOfBase) { - verify.completionListContains(validMemberOfBaseSymbol, validMemberOfBaseText, /*documentation*/ undefined, "method"); - } - for (const [invalidMemberOfBaseSymbol, invalidMemberOfBaseText] of invalidMembersOfBase) { - verify.not.completionListContains(invalidMemberOfBaseSymbol, invalidMemberOfBaseText, /*documentation*/ undefined, "method"); - } - verify.completionListContainsClassElementKeywords(); - verify.completionListCount(allowedKeywordCount + validMembersOfBase.length); -} \ No newline at end of file +verifyClassElementLocations(staticMemberInfo, staticClassLocations); \ No newline at end of file From d2bda47e80f94b60cc8971a55c39e86f1f6455ab Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 12:39:47 -0700 Subject: [PATCH 12/77] fix inTypeAlias logic --- src/compiler/checker.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6ee3377d88a..5e7ce03afe1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2297,7 +2297,6 @@ namespace ts { InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type // TODO: ??? InTypeAlias: boolean; // Writing type in type alias declaration - checkAlias: boolean; symbolStack: Symbol[] | undefined; } @@ -2310,7 +2309,6 @@ namespace ts { InElementType: false, InFirstTypeArgument: false, InTypeAlias: false, - checkAlias: true, symbolStack: undefined }; } @@ -2319,6 +2317,8 @@ namespace ts { const InElementType = context.InElementType; // TODO: why doesn't tts unset the flag? context.InElementType = false; + const inTypeAlias = context.InTypeAlias; + context.InTypeAlias = false; // TODO: should be assert? if (!type) { @@ -2406,12 +2406,11 @@ namespace ts { return createTypeReferenceNode(name, /*typeArguments*/ undefined); } - if (!context.InTypeAlias && context.checkAlias && type.aliasSymbol) { + if (!inTypeAlias && type.aliasSymbol) { const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context); const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false); return createTypeReferenceNode(name, typeArgumentNodes); } - context.checkAlias = false; if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; From a5bef216aea8b871f7f23a78b260a4d635cffb74 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 13:47:51 -0700 Subject: [PATCH 13/77] Use `symbolToName` instead of copying --- src/compiler/checker.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5e7ce03afe1..18a6c03c6d6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2287,7 +2287,7 @@ namespace ts { interface NodeBuilderContext { readonly enclosingDeclaration: Node | undefined; readonly flags: NodeBuilderFlags | undefined; - + // State encounteredError: boolean; inObjectTypeLiteral: boolean; @@ -2564,7 +2564,6 @@ namespace ts { return createTypeLiteralNode(members); } - function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { if (InElementType) { return true; @@ -2670,13 +2669,10 @@ namespace ts { return typeElements; } + // TODO: make logic mirror that of writeObjectLiteralType for (const propertySymbol of properties) { const propertyType = getTypeOfSymbol(propertySymbol); - const oldDeclaration = propertySymbol.declarations && propertySymbol.declarations[0] as TypeElement; - if (!oldDeclaration) { - return; - } - const propertyName = getDeepSynthesizedClone(oldDeclaration.name); + const propertyName = symbolToName(propertySymbol, /*expectsIdentifier*/ true, context); const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); @@ -2697,7 +2693,7 @@ namespace ts { propertyName, optionalToken, propertyTypeNode, - /*initializer*/undefined); + /*initializer*/ undefined); typeElements.push(propertySignature); } } @@ -2744,8 +2740,8 @@ namespace ts { const returnType = getReturnTypeOfSignature(signature); returnTypeNode = returnType && typeToTypeNodeHelper(returnType, context); } - if(context.flags & NodeBuilderFlags.SuppressAnyReturnType) { - if(returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) { + if (context.flags & NodeBuilderFlags.SuppressAnyReturnType) { + if (returnTypeNode && returnTypeNode.kind === SyntaxKind.AnyKeyword) { returnTypeNode = undefined; } } From b8bb0c22f3261a28d05e3d3aaae15d652d9d6087 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 14:47:57 -0700 Subject: [PATCH 14/77] emit type parameter's default --- src/compiler/emitter.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index eebbe302898..cd16074d572 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -814,6 +814,7 @@ namespace ts { function emitTypeParameter(node: TypeParameterDeclaration) { emit(node.name); emitWithPrefix(" extends ", node.constraint); + emitWithPrefix(" = ", node.default); } function emitParameter(node: ParameterDeclaration) { From a1d2049c29af925f3bc6d8ff09dee299d250edb2 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 14:48:20 -0700 Subject: [PATCH 15/77] convert type format flags --- src/compiler/checker.ts | 49 +++++++++++++++++++++++++++++++++++++++-- src/compiler/types.ts | 2 +- 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 18a6c03c6d6..6496b191eac 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2242,10 +2242,49 @@ namespace ts { return result; } + function typeFormatFlagsToNodeBuilderFlags(flags: TypeFormatFlags): NodeBuilderFlags { + let result = NodeBuilderFlags.None; + if (flags & TypeFormatFlags.WriteArrayAsGenericType) { + result |= NodeBuilderFlags.WriteArrayAsGenericType; + } + if (flags & TypeFormatFlags.UseTypeOfFunction) { + result |= NodeBuilderFlags.UseTypeOfFunction; + } + if (flags & TypeFormatFlags.NoTruncation) { + result |= NodeBuilderFlags.NoTruncation; + } + if (flags & TypeFormatFlags.WriteArrowStyleSignature) { + result |= NodeBuilderFlags.WriteArrowStyleSignature; + } + if (flags & TypeFormatFlags.WriteOwnNameForAnyLike) { + result |= NodeBuilderFlags.WriteOwnNameForAnyLike; + } + if (flags & TypeFormatFlags.WriteArrayAsGenericType) { + result |= NodeBuilderFlags.WriteArrayAsGenericType; + } + if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) { + result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature; + } + if (flags & TypeFormatFlags.UseFullyQualifiedType) { + result |= NodeBuilderFlags.UseFullyQualifiedType; + } + if (flags & TypeFormatFlags.UseTypeAliasValue) { + result |= NodeBuilderFlags.UseTypeAliasValue; + } + if (flags & TypeFormatFlags.SuppressAnyReturnType) { + result |= NodeBuilderFlags.SuppressAnyReturnType; + } + if (flags & TypeFormatFlags.AddUndefined) { + result |= NodeBuilderFlags.AddUndefined; + } + + return result; + } + function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { const str = oldTypeToString(type, enclosingDeclaration, flags); str; const str2 = oldTypeToString(type, enclosingDeclaration, flags); str2; - const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, NodeBuilderFlags.ignoreErrors); + const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, typeFormatFlagsToNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors, !!(flags & TypeFormatFlags.InTypeAlias)); Debug.assert(typeNode !== undefined, "should always get typenode?"); const newLine = NewLineKind.None; const options = { newLine, removeComments: true }; @@ -2264,8 +2303,9 @@ namespace ts { function createNodeBuilder() { return { - typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { + typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, inTypeAlias?: boolean) => { const context = createNodeBuilderContext(enclosingDeclaration, flags); + context.InTypeAlias = inTypeAlias; const resultingNode = typeToTypeNodeHelper(type, context); const result = context.encounteredError ? undefined : resultingNode; return result; @@ -2403,6 +2443,7 @@ namespace ts { if (type.flags & TypeFlags.TypeParameter) { const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. + // TODO: add constraint here? return createTypeReferenceNode(name, /*typeArguments*/ undefined); } @@ -2585,6 +2626,10 @@ namespace ts { function typeReferenceToTypeNode(type: TypeReference) { const typeArguments: Type[] = type.typeArguments || emptyArray; if (type.target === globalArrayType) { + if (context.flags & NodeBuilderFlags.WriteArrayAsGenericType) { + const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context); + return createTypeReferenceNode("Array", [typeArgumentNode]); + } context.InElementType = true; const elementType = typeToTypeNodeHelper(typeArguments[0], context); context.InElementType = false; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 17df0243a1b..48c4b2cd4ac 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2496,7 +2496,7 @@ namespace ts { /** Note that the resulting nodes cannot be checked. */ - typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; + typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, inTypeAlias?: boolean): TypeNode; /** Note that the resulting nodes cannot be checked. */ signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration; /** Note that the resulting nodes cannot be checked. */ From 818bfdcd88c798e2b6141e9f1cfd8082d7b9d710 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 15:08:02 -0700 Subject: [PATCH 16/77] accessibility check --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6496b191eac..5302f43bcc7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2443,11 +2443,11 @@ namespace ts { if (type.flags & TypeFlags.TypeParameter) { const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. - // TODO: add constraint here? return createTypeReferenceNode(name, /*typeArguments*/ undefined); } - if (!inTypeAlias && type.aliasSymbol) { + if (!inTypeAlias && type.aliasSymbol && + isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context); const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false); return createTypeReferenceNode(name, typeArgumentNodes); From e43377db55ff23c43b773cce787cbe3195dfc316 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 5 May 2017 17:12:58 -0700 Subject: [PATCH 17/77] Better qualified name handling --- src/compiler/checker.ts | 42 +++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5302f43bcc7..64adede088e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2278,7 +2278,7 @@ namespace ts { result |= NodeBuilderFlags.AddUndefined; } - return result; + return result; } function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { @@ -2325,7 +2325,7 @@ namespace ts { }; interface NodeBuilderContext { - readonly enclosingDeclaration: Node | undefined; + enclosingDeclaration: Node | undefined; readonly flags: NodeBuilderFlags | undefined; // State @@ -2382,7 +2382,7 @@ namespace ts { return createKeywordTypeNode(SyntaxKind.BooleanKeyword); } if (type.flags & TypeFlags.Enum) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { @@ -2396,7 +2396,7 @@ namespace ts { } if (type.flags & TypeFlags.EnumLiteral) { const parentSymbol = getParentOfSymbol(type.symbol); - const parentName = symbolToName(parentSymbol, /*expectsIdentifier*/ false, context); + const parentName = symbolToName(parentSymbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); const name = getNameOfSymbol(type.symbol, context); const enumLiteralName = createQualifiedName(parentName, name); return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined); @@ -2436,19 +2436,19 @@ namespace ts { } if (objectFlags & ObjectFlags.ClassOrInterface) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); // TODO(aozgaa): handle type arguments. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.TypeParameter) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, context); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (!inTypeAlias && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { - const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, context); + const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false); return createTypeReferenceNode(name, typeArgumentNodes); } @@ -2526,14 +2526,14 @@ namespace ts { if (symbol.flags & SymbolFlags.Class && !getBaseTypeVariableOfClass(symbol) || symbol.flags & (SymbolFlags.Enum | SymbolFlags.ValueModule) || shouldWriteTypeOfFunctionSymbol()) { - return createTypeQueryNodeFromSymbol(symbol); + return createTypeQueryNodeFromSymbol(symbol, SymbolFlags.Value); } else if (contains(context.symbolStack, symbol)) { // If type is an anonymous type literal in a type alias declaration, use type alias name const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags - const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false, context); + const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false, SymbolFlags.Type, context); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { @@ -2618,8 +2618,8 @@ namespace ts { return false; } - function createTypeQueryNodeFromSymbol(symbol: Symbol) { - const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, context); + function createTypeQueryNodeFromSymbol(symbol: Symbol, symbolFlags: SymbolFlags) { + const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, symbolFlags, context); return createTypeQueryNode(entityName); } @@ -2665,7 +2665,7 @@ namespace ts { // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { // inFirstTypeArgument??? - const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, context); + const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, SymbolFlags.Type, context); if (!qualifiedName) { qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); } @@ -2679,7 +2679,7 @@ namespace ts { } } let entityName: EntityName = undefined; - const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true, context); + const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true, SymbolFlags.Type, context); if (qualifiedName) { Debug.assert(!qualifiedName.right); qualifiedName.right = nameIdentifier; @@ -2717,7 +2717,10 @@ namespace ts { // TODO: make logic mirror that of writeObjectLiteralType for (const propertySymbol of properties) { const propertyType = getTypeOfSymbol(propertySymbol); - const propertyName = symbolToName(propertySymbol, /*expectsIdentifier*/ true, context); + const saveEnclosingDeclaration = context.enclosingDeclaration; + context.enclosingDeclaration = undefined; + const propertyName = symbolToName(propertySymbol, /*expectsIdentifier*/ true, SymbolFlags.Value, context); + context.enclosingDeclaration = saveEnclosingDeclaration; const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { const signatures = getSignaturesOfType(propertyType, SignatureKind.Call); @@ -2767,7 +2770,6 @@ namespace ts { } function signatureToSignatureDeclarationHelper(signature: Signature, kind: SyntaxKind, context: NodeBuilderContext): SignatureDeclaration { - const typeParameters = signature.typeParameters && signature.typeParameters.map(parameter => typeParameterToDeclaration(parameter, context)); const parameters = signature.parameters.map(parameter => symbolToParameterDeclaration(parameter, context)); if (signature.thisParameter) { @@ -2801,7 +2803,7 @@ namespace ts { const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); - const name = symbolToName(type.symbol, /*expectsIdentifier*/ true, context); + const name = symbolToName(type.symbol, /*expectsIdentifier*/ true, SymbolFlags.Type, context); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } @@ -2825,15 +2827,15 @@ namespace ts { // TODO: add meaning: SymbolFlags argument. // TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR. - function symbolToName(symbol: Symbol, expectsIdentifier: true, context: NodeBuilderContext): Identifier; - function symbolToName(symbol: Symbol, expectsIdentifier: false, context: NodeBuilderContext): EntityName; - function symbolToName(symbol: Symbol, expectsIdentifier: boolean, context: NodeBuilderContext): EntityName { + function symbolToName(symbol: Symbol, expectsIdentifier: true, meaning: SymbolFlags, context: NodeBuilderContext): Identifier; + function symbolToName(symbol: Symbol, expectsIdentifier: false, meaning: SymbolFlags, context: NodeBuilderContext): EntityName; + function symbolToName(symbol: Symbol, expectsIdentifier: boolean, meaning: SymbolFlags, context: NodeBuilderContext): EntityName { // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. let chain: Symbol[]; const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; if (!isTypeParameter && context.enclosingDeclaration) { - chain = getSymbolChain(symbol, SymbolFlags.None, /*endOfChain*/ true); + chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); Debug.assert(chain && chain.length > 0); } else { From 965b98f6cbaa7c23246a3bb59d5d707bd6b5164e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 8 May 2017 11:10:47 -0700 Subject: [PATCH 18/77] `UseFullyQualifiedType` --- src/compiler/checker.ts | 2 +- src/compiler/types.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 64adede088e..8a84fe42515 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2834,7 +2834,7 @@ namespace ts { // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. let chain: Symbol[]; const isTypeParameter = symbol.flags & SymbolFlags.TypeParameter; - if (!isTypeParameter && context.enclosingDeclaration) { + if (!isTypeParameter && (context.enclosingDeclaration || context.flags & NodeBuilderFlags.UseFullyQualifiedType)) { chain = getSymbolChain(symbol, meaning, /*endOfChain*/ true); Debug.assert(chain && chain.length > 0); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 48c4b2cd4ac..6c1861a5892 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2571,7 +2571,6 @@ namespace ts { WriteOwnNameForAnyLike = 1 << 4, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc) // TODO WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature - // TODO UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type) // TODO UseTypeAliasValue = 1 << 7, // Serialize the type instead of using type-alias. This is needed when we emit declaration file. From b00aea5c7cb5ada80bce995a34bc83eb394e1dad Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 8 May 2017 11:45:02 -0700 Subject: [PATCH 19/77] Parameter improvements --- src/compiler/checker.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8a84fe42515..1fae8206f49 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2812,16 +2812,26 @@ namespace ts { const parameterType = getTypeOfSymbol(parameterSymbol); const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); const name = getDeepSynthesizedClone(parameterDeclaration.name); + + let questionToken: Token | undefined; + let initializer: Expression | undefined; + if (isOptionalParameter(parameterDeclaration)) { + questionToken = createToken(SyntaxKind.QuestionToken); + } + else { // TODO(aozgaa): In the future, check initializer accessibility. + initializer = parameterDeclaration.initializer; + } const parameterNode = createParameter( parameterDeclaration.decorators, parameterDeclaration.modifiers, - parameterDeclaration.dotDotDotToken && createToken(SyntaxKind.DotDotDotToken), - // Clone name to remove trivia. + (parameterDeclaration ? isRestParameter(parameterDeclaration) : isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) ? + createToken(SyntaxKind.DotDotDotToken) : + undefined, name, - parameterDeclaration.questionToken && createToken(SyntaxKind.QuestionToken), + questionToken, parameterTypeNode, - parameterDeclaration.initializer); + initializer); return parameterNode; } From d9b68e92138faea04316356875bca8e9630f0042 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 8 May 2017 16:26:57 -0700 Subject: [PATCH 20/77] Type Literal and Binding Name formatting --- src/compiler/checker.ts | 25 ++++++++++++++++++++++--- src/compiler/emitter.ts | 9 +++++---- src/compiler/types.ts | 2 ++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1fae8206f49..658bad82f83 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2602,7 +2602,8 @@ namespace ts { context.inObjectTypeLiteral = true; const members = createTypeNodesFromResolvedType(resolved); context.inObjectTypeLiteral = saveInObjectTypeLiteral; - return createTypeLiteralNode(members); + const typeLiteralNode = createTypeLiteralNode(members); + return setEmitFlags(typeLiteralNode, EmitFlags.ToStringFormatting); } function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { @@ -2811,8 +2812,14 @@ namespace ts { const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); const parameterType = getTypeOfSymbol(parameterSymbol); const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); - const name = getDeepSynthesizedClone(parameterDeclaration.name); - + let name: BindingName; + if (parameterDeclaration.name.kind === SyntaxKind.Identifier) { + name = getSynthesizedClone(parameterDeclaration.name); + } + else { + Debug.assert(parameterDeclaration.name.kind === SyntaxKind.ArrayBindingPattern || parameterDeclaration.name.kind === SyntaxKind.ObjectBindingPattern); + name = cloneBindingName(parameterDeclaration.name); + } let questionToken: Token | undefined; let initializer: Expression | undefined; if (isOptionalParameter(parameterDeclaration)) { @@ -2833,6 +2840,18 @@ namespace ts { parameterTypeNode, initializer); return parameterNode; + + function cloneBindingName(node: BindingName): BindingName { + return elideInitializerAndSetEmitFlags(node); + function elideInitializerAndSetEmitFlags(node: Node): Node { + const visited = visitEachChild(node, elideInitializerAndSetEmitFlags, nullTransformationContext, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags); + const clone = nodeIsSynthesized(visited) ? visited : getSynthesizedClone(visited); + if (clone.kind === SyntaxKind.BindingElement) { + (clone).initializer = undefined; + } + return setEmitFlags(clone, EmitFlags.ToStringFormatting); + } + } } // TODO: add meaning: SymbolFlags argument. diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index cd16074d572..70c9688b60e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -956,7 +956,7 @@ namespace ts { function emitTypeLiteral(node: TypeLiteralNode) { write("{"); if (node.members.length > 0) { - emitList(node, node.members, ListFormat.SingleLineTypeLiteralMembers); + emitList(node, node.members, getEmitFlags(node) & EmitFlags.ToStringFormatting ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers); } write("}"); } @@ -1037,7 +1037,7 @@ namespace ts { } else { write("{"); - emitList(node, elements, ListFormat.ObjectBindingPatternElements); + emitList(node, elements, getEmitFlags(node) & EmitFlags.ToStringFormatting ? ListFormat.ObjectBindingPatternElements : ListFormat.ObjectBindingPatternElementsWithSpaceBetweenBraces); write("}"); } } @@ -2948,13 +2948,14 @@ namespace ts { // Precomputed Formats Modifiers = SingleLine | SpaceBetweenSiblings, HeritageClauses = SingleLine | SpaceBetweenSiblings, - SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented, // MultiLine | Indented, + SingleLineTypeLiteralMembers = SingleLine | SpaceBetweenBraces | SpaceBetweenSiblings | Indented, MultiLineTypeLiteralMembers = MultiLine | Indented, TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, - ObjectBindingPatternElements = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, + ObjectBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, + ObjectBindingPatternElementsWithSpaceBetweenBraces = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 6c1861a5892..67b73cdbda5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3943,6 +3943,7 @@ namespace ts { } export const enum EmitFlags { + None = 0, SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node. NoSubstitution = 1 << 2, // Disables further substitution of an expression. @@ -3969,6 +3970,7 @@ namespace ts { NoHoisting = 1 << 20, // Do not hoist this declaration in --module system HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration Iterator = 1 << 22, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. + ToStringFormatting = 1 << 23 } export interface EmitHelper { From 51c76ac35128adba8a760fc08092e0035a1b5cd5 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 8 May 2017 18:17:17 -0700 Subject: [PATCH 21/77] fix malformed nodes --- src/compiler/checker.ts | 6 +++--- src/compiler/factory.ts | 4 ++++ src/compiler/utilities.ts | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 658bad82f83..9ac1dc2d615 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2752,7 +2752,7 @@ namespace ts { function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); - const name = getNameFromIndexInfo(indexInfo); + const name = getNameFromIndexInfo(indexInfo) || "x"; const indexingParameter = createParameter( /*decorators*/ undefined, @@ -2830,8 +2830,8 @@ namespace ts { initializer = parameterDeclaration.initializer; } const parameterNode = createParameter( - parameterDeclaration.decorators, - parameterDeclaration.modifiers, + /*decorators*/ undefined, + cloneNodeArray(parameterDeclaration.modifiers), (parameterDeclaration ? isRestParameter(parameterDeclaration) : isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) ? createToken(SyntaxKind.DotDotDotToken) : undefined, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 172710f39aa..45b09892bb0 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -89,6 +89,10 @@ namespace ts { return nodeIsSynthesized(clone) ? clone : getSynthesizedClone(clone); } + export function cloneNodeArray(nodeArray: NodeArray) { + return nodeArray && nodeArray.map(getDeepSynthesizedClone); + } + // Literals export function createLiteral(value: string): StringLiteral; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index cd63ebfb690..baa83697fba 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -466,7 +466,7 @@ namespace ts { return getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } - export function getNameFromIndexInfo(info: IndexInfo) { + export function getNameFromIndexInfo(info: IndexInfo): string | undefined { return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined; } From ac68e8d6f12ec81eda832d1f67ee6ff438e58cd8 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 9 May 2017 11:09:20 -0700 Subject: [PATCH 22/77] initializer issues --- src/compiler/checker.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ac1dc2d615..9410402d372 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2810,7 +2810,10 @@ namespace ts { function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration { const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); - const parameterType = getTypeOfSymbol(parameterSymbol); + let parameterType = getTypeOfSymbol(parameterSymbol); + if (isRequiredInitializedParameter(parameterDeclaration)) { + parameterType = includeFalsyTypes(parameterType, TypeFlags.Undefined); + } const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); let name: BindingName; if (parameterDeclaration.name.kind === SyntaxKind.Identifier) { @@ -2820,25 +2823,19 @@ namespace ts { Debug.assert(parameterDeclaration.name.kind === SyntaxKind.ArrayBindingPattern || parameterDeclaration.name.kind === SyntaxKind.ObjectBindingPattern); name = cloneBindingName(parameterDeclaration.name); } - let questionToken: Token | undefined; - let initializer: Expression | undefined; - if (isOptionalParameter(parameterDeclaration)) { - questionToken = createToken(SyntaxKind.QuestionToken); - } - else { - // TODO(aozgaa): In the future, check initializer accessibility. - initializer = parameterDeclaration.initializer; - } + const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; + const dotDotDotToken = (parameterDeclaration ? isRestParameter(parameterDeclaration) : isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) ? + createToken(SyntaxKind.DotDotDotToken) : + undefined; + const parameterNode = createParameter( /*decorators*/ undefined, cloneNodeArray(parameterDeclaration.modifiers), - (parameterDeclaration ? isRestParameter(parameterDeclaration) : isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) ? - createToken(SyntaxKind.DotDotDotToken) : - undefined, + dotDotDotToken, name, questionToken, parameterTypeNode, - initializer); + /*initializer*/ undefined); return parameterNode; function cloneBindingName(node: BindingName): BindingName { From e0438db63eaadddf6cf1f8822fb588d4f26eaaff Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 9 May 2017 12:57:22 -0700 Subject: [PATCH 23/77] Parenthesize function types --- src/compiler/checker.ts | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9410402d372..439874cf987 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2359,6 +2359,8 @@ namespace ts { context.InElementType = false; const inTypeAlias = context.InTypeAlias; context.InTypeAlias = false; + const inFirstTypeArgument = context.InFirstTypeArgument; + context.InFirstTypeArgument = false; // TODO: should be assert? if (!type) { @@ -2367,8 +2369,6 @@ namespace ts { return undefined; } - - if (type.flags & TypeFlags.Any) { return createKeywordTypeNode(SyntaxKind.AnyKeyword); } @@ -2586,15 +2586,18 @@ namespace ts { if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const signature = resolved.callSignatures[0]; - const functionTypeNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); + const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); return shouldAddParenthesisAroundFunctionType(signature, context) ? - createParenthesizedTypeNode(functionTypeNode) : - functionTypeNode; + createParenthesizedTypeNode(signatureNode) : + signatureNode; } if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { const signature = resolved.constructSignatures[0]; - return signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context); + const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context); + return shouldAddParenthesisAroundFunctionType(signature, context) ? + createParenthesizedTypeNode(signatureNode) : + signatureNode; } } @@ -2610,7 +2613,7 @@ namespace ts { if (InElementType) { return true; } - else if (context.InFirstTypeArgument) { + else if (inFirstTypeArgument) { // Add parenthesis around function type for the first type argument to avoid ambiguity const typeParameters = callSignature.target && (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature) ? callSignature.target.typeParameters : callSignature.typeParameters; @@ -2690,7 +2693,14 @@ namespace ts { entityName = nameIdentifier; } const typeParameterCount = (type.target.typeParameters || emptyArray).length; - const typeArgumentNodes = some(typeArguments) ? mapToTypeNodeArray(typeArguments.slice(i, typeParameterCount - i), /*addInElementTypeFlag*/ false) : undefined; + + let typeArgumentNodes: TypeNode[] | undefined; + if (some(typeArguments)) { + const slice = typeArguments.slice(i, typeParameterCount - i); + context.InFirstTypeArgument = true; + typeArgumentNodes = mapToTypeNodeArray(slice, /*addInElementTypeFlag*/ false); + } + return createTypeReferenceNode(entityName, typeArgumentNodes); } } @@ -2851,7 +2861,6 @@ namespace ts { } } - // TODO: add meaning: SymbolFlags argument. // TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR. function symbolToName(symbol: Symbol, expectsIdentifier: true, meaning: SymbolFlags, context: NodeBuilderContext): Identifier; function symbolToName(symbol: Symbol, expectsIdentifier: false, meaning: SymbolFlags, context: NodeBuilderContext): EntityName; From 89f8cca488361b8385861792f13b87606802fbb8 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 9 May 2017 13:14:39 -0700 Subject: [PATCH 24/77] mapped type indent --- src/compiler/checker.ts | 3 ++- src/compiler/emitter.ts | 19 +++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 439874cf987..f06afe4425d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2516,7 +2516,8 @@ namespace ts { const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; - return createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); + const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); + return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); } function createAnonymousTypeNode(type: ObjectType): TypeNode { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 70c9688b60e..11cc83dc15a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1004,9 +1004,15 @@ namespace ts { } function emitMappedType(node: MappedTypeNode) { + const emitFlags = getEmitFlags(node); write("{"); - writeLine(); - increaseIndent(); + if (emitFlags & EmitFlags.SingleLine) { + write(" "); + } + else { + writeLine(); + increaseIndent(); + } writeIfPresent(node.readonlyToken, "readonly "); write("["); emit(node.typeParameter.name); @@ -1017,8 +1023,13 @@ namespace ts { write(": "); emit(node.type); write(";"); - writeLine(); - decreaseIndent(); + if (emitFlags & EmitFlags.SingleLine) { + write(" "); + } + else { + writeLine(); + decreaseIndent(); + } write("}"); } From ad58572edbcd761623a918851e9afa36a733a455 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 9 May 2017 13:50:14 -0700 Subject: [PATCH 25/77] trailing comma --- src/compiler/emitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 11cc83dc15a..582bc6ed7ab 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2965,7 +2965,7 @@ namespace ts { TupleTypeElements = CommaDelimited | SpaceBetweenSiblings | SingleLine | Indented, UnionTypeConstituents = BarDelimited | SpaceBetweenSiblings | SingleLine, IntersectionTypeConstituents = AmpersandDelimited | SpaceBetweenSiblings | SingleLine, - ObjectBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, + ObjectBindingPatternElements = SingleLine | CommaDelimited | SpaceBetweenSiblings, ObjectBindingPatternElementsWithSpaceBetweenBraces = SingleLine | AllowTrailingComma | SpaceBetweenBraces | CommaDelimited | SpaceBetweenSiblings, ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, From 41452a5b2199e17ceb440784a06fd18e60220d8d Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 9 May 2017 18:20:46 -0700 Subject: [PATCH 26/77] add typeArguments to identifier --- src/compiler/checker.ts | 59 ++++++++++++++++++++-------------------- src/compiler/emitter.ts | 1 + src/compiler/factory.ts | 9 +++++- src/compiler/types.ts | 1 + src/compiler/visitor.ts | 3 ++ src/services/services.ts | 1 + 6 files changed, 44 insertions(+), 30 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f06afe4425d..82567bc170d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2449,13 +2449,13 @@ namespace ts { if (!inTypeAlias && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); - const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, /*addInElementTypeFlag*/ false); + const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); return createTypeReferenceNode(name, typeArgumentNodes); } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; - const typeNodes = types && mapToTypeNodeArray(types, /*addInElementTypeFlag*/ true); + const typeNodes = types && mapToTypeNodeArray(types, context, /*addInElementTypeFlag*/ true, /*addInFirstTypeArgumentFlag*/ false); if (typeNodes && typeNodes.length > 0) { const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); return InElementType ? createParenthesizedTypeNode(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode; @@ -2492,20 +2492,6 @@ namespace ts { Debug.fail("Should be unreachable."); - function mapToTypeNodeArray(types: Type[], addInElementTypeFlag: boolean): TypeNode[] { - const result = []; - Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); - for (const type of types) { - context.InElementType = addInElementTypeFlag; - const typeNode = typeToTypeNodeHelper(type, context); - if (typeNode) { - result.push(typeNode); - } - } - Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); - return result; - } - function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); const typeParameter = getTypeParameterFromMappedType(type); @@ -2642,7 +2628,7 @@ namespace ts { } else if (type.target.objectFlags & ObjectFlags.Tuple) { if (typeArguments.length > 0) { - const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), /*addInElementTypeFlag*/ false); + const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ false); if (tupleConstituentNodes && tupleConstituentNodes.length > 0) { return createTupleTypeNode(tupleConstituentNodes); } @@ -2669,8 +2655,11 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - // inFirstTypeArgument??? + const qualifiedNamePartTypeArguments = typeArguments.slice(start,i); + const qualifiedNamePartTypeArgumentNodes = qualifiedNamePartTypeArguments && createNodeArray(mapToTypeNodeArray(qualifiedNamePartTypeArguments, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true)); const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, SymbolFlags.Type, context); + qualifiedNamePart.typeArguments = qualifiedNamePartTypeArgumentNodes; + if (!qualifiedName) { qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); } @@ -2699,7 +2688,7 @@ namespace ts { if (some(typeArguments)) { const slice = typeArguments.slice(i, typeParameterCount - i); context.InFirstTypeArgument = true; - typeArgumentNodes = mapToTypeNodeArray(slice, /*addInElementTypeFlag*/ false); + typeArgumentNodes = mapToTypeNodeArray(slice, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); } return createTypeReferenceNode(entityName, typeArgumentNodes); @@ -2761,6 +2750,24 @@ namespace ts { } } + function mapToTypeNodeArray(types: Type[], context: NodeBuilderContext, addInElementTypeFlag: boolean, addInFirstTypeArgumentFlag: boolean): TypeNode[] { + const result = []; + Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); + for (let i = 0; i < types.length; ++i) { + let type = types[i] + context.InElementType = addInElementTypeFlag; + if (i === 0) { + context.InFirstTypeArgument = addInFirstTypeArgumentFlag; + } + const typeNode = typeToTypeNodeHelper(type, context); + if (typeNode) { + result.push(typeNode); + } + } + Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); + return result; + } + function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const name = getNameFromIndexInfo(indexInfo) || "x"; @@ -2889,9 +2896,8 @@ namespace ts { Debug.assert(chain && 0 <= index && index < chain.length); // const parentIndex = index - 1; const symbol = chain[index]; - let typeParameterString = ""; + let typeParameterNodes: TypeNode[] | undefined; if (index > 0) { - const parentSymbol = chain[index - 1]; let typeParameters: TypeParameter[]; if (getCheckFlags(symbol) & CheckFlags.Instantiated) { @@ -2907,17 +2913,12 @@ namespace ts { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowTypeParameterInQualifiedName)) { context.encounteredError = true; } - const writer = getSingleLineStringWriter(); - const displayBuilder = getSymbolDisplayBuilder(); - displayBuilder.buildDisplayForTypeParametersAndDelimiters(typeParameters, writer, context.enclosingDeclaration, 0); - typeParameterString = writer.string(); - releaseStringWriter(writer); - + typeParameterNodes = typeParameters && mapToTypeNodeArray(typeParameters, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); } } + const symbolName = getNameOfSymbol(symbol, context); - const symbolNameWithTypeParameters = typeParameterString.length > 0 ? `${symbolName}<${typeParameterString}>` : symbolName; - const identifier = createIdentifier(symbolNameWithTypeParameters); + const identifier = createIdentifier(symbolName, typeParameterNodes); return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 582bc6ed7ab..b96e87ec55c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -780,6 +780,7 @@ namespace ts { function emitIdentifier(node: Identifier) { write(getTextOfNode(node, /*includeTrivia*/ false)); + emitTypeArguments(node, node.typeArguments); } // diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 45b09892bb0..510e4ec03de 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -136,15 +136,22 @@ namespace ts { // Identifiers - export function createIdentifier(text: string): Identifier { + export function createIdentifier(text: string, typeArguments?: TypeNode[]): Identifier { const node = createSynthesizedNode(SyntaxKind.Identifier); node.text = escapeIdentifier(text); node.originalKeywordKind = text ? stringToToken(text) : SyntaxKind.Unknown; node.autoGenerateKind = GeneratedIdentifierKind.None; node.autoGenerateId = 0; + node.typeArguments = asNodeArray(typeArguments); return node; } + export function updateIdentifier(node: Identifier, typeArguments: NodeArray | undefined): Identifier { + return node.typeArguments !== typeArguments + ? updateNode(createIdentifier(node.text, typeArguments), node) + : node; + } + let nextAutoGenerateId = 0; /** Create a unique temporary variable. */ diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 67b73cdbda5..ad89069c286 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -575,6 +575,7 @@ namespace ts { /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace + /*@internal*/ typeArguments: NodeArray; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. } // Transient identifier node (marked by id === -1) diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 1e196a925f8..fce43b622bb 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -219,6 +219,9 @@ namespace ts { } switch (node.kind) { + case SyntaxKind.Identifier: + return updateIdentifier(node, nodesVisitor((node).typeArguments, visitor, isTypeNode)); + case SyntaxKind.SemicolonClassElement: case SyntaxKind.EmptyStatement: case SyntaxKind.OmittedExpression: diff --git a/src/services/services.ts b/src/services/services.ts index 8f2cbb22379..faf27f4f2b0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -357,6 +357,7 @@ namespace ts { _incrementExpressionBrand: any; _unaryExpressionBrand: any; _expressionBrand: any; + typeArguments: any; constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) { super(pos, end); } From 3c8732fab1cdc524c1cf3a506bda871b09842199 Mon Sep 17 00:00:00 2001 From: Charles Pierce Date: Wed, 10 May 2017 17:26:47 -0700 Subject: [PATCH 27/77] Prevent Duplicate String Literal Completions --- src/services/completions.ts | 23 +++++++++++-------- .../fourslash/completionForStringLiteral12.ts | 10 ++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteral12.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 318c02683ff..948d7349419 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -221,11 +221,12 @@ namespace ts.Completions { function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined { const candidates: Signature[] = []; const entries: CompletionEntry[] = []; + const uniques = createMap(); typeChecker.getResolvedSignature(argumentInfo.invocation, candidates); for (const candidate of candidates) { - addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker); + addStringLiteralCompletionsFromType(typeChecker.getParameterType(candidate, argumentInfo.argumentIndex), entries, typeChecker, uniques); } if (entries.length) { @@ -258,7 +259,7 @@ namespace ts.Completions { return undefined; } - function addStringLiteralCompletionsFromType(type: Type, result: Push, typeChecker: TypeChecker): void { + function addStringLiteralCompletionsFromType(type: Type, result: Push, typeChecker: TypeChecker, uniques = createMap()): void { if (type && type.flags & TypeFlags.TypeParameter) { type = typeChecker.getBaseConstraintOfType(type); } @@ -267,16 +268,20 @@ namespace ts.Completions { } if (type.flags & TypeFlags.Union) { for (const t of (type).types) { - addStringLiteralCompletionsFromType(t, result, typeChecker); + addStringLiteralCompletionsFromType(t, result, typeChecker, uniques); } } else if (type.flags & TypeFlags.StringLiteral) { - result.push({ - name: (type).text, - kindModifiers: ScriptElementKindModifier.none, - kind: ScriptElementKind.variableElement, - sortText: "0" - }); + const name = (type).text; + if (!uniques.get(name)) { + uniques.set(name, name); + result.push({ + name, + kindModifiers: ScriptElementKindModifier.none, + kind: ScriptElementKind.variableElement, + sortText: "0" + }); + } } } diff --git a/tests/cases/fourslash/completionForStringLiteral12.ts b/tests/cases/fourslash/completionForStringLiteral12.ts new file mode 100644 index 00000000000..22bb2f00a43 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral12.ts @@ -0,0 +1,10 @@ +/// + +////function foo(x: "bla"): void; +////function foo(x: "bla"): void; +////function foo(x: string) {} +////foo("/**/") + +goTo.marker(); +verify.completionListContains("bla"); +verify.completionListCount(1); From ac6018aa955147838aacbdce9c4d0604a9b634be Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 10 May 2017 17:01:05 -0700 Subject: [PATCH 28/77] type arguments in qualified names --- src/compiler/checker.ts | 74 ++++++++++++++++++++++++++++++----------- src/compiler/types.ts | 12 +++---- 2 files changed, 60 insertions(+), 26 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 82567bc170d..ed5c7ea669e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2448,9 +2448,10 @@ namespace ts { if (!inTypeAlias && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { - const name = symbolToName(type.aliasSymbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); - const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); + const name = symbolToTypeReferenceName(type.aliasSymbol); + const typeArgumentNodes = toTypeArgumentNodes(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); + // return symbolToTypeReferenceIdentifier(type.aliasSymbol, type.aliasTypeArguments); } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { @@ -2614,6 +2615,13 @@ namespace ts { return createTypeQueryNode(entityName); } + function symbolToTypeReferenceName(symbol: Symbol) { + // Unnamed function expressions and arrow functions have reserved names that we don't want to display + const entityName = symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.name) ? symbolToName(symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context) : createIdentifier(""); + // TODO: assert no type args? + return entityName; + } + function typeReferenceToTypeNode(type: TypeReference) { const typeArguments: Type[] = type.typeArguments || emptyArray; if (type.target === globalArrayType) { @@ -2641,7 +2649,7 @@ namespace ts { else { const outerTypeParameters = type.target.outerTypeParameters; let i = 0; - let qualifiedName: QualifiedName | undefined = undefined; + let qualifiedName: QualifiedName | undefined; if (outerTypeParameters) { let inFirstTypeArgument = true; const length = outerTypeParameters.length; @@ -2655,46 +2663,68 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const qualifiedNamePartTypeArguments = typeArguments.slice(start,i); - const qualifiedNamePartTypeArgumentNodes = qualifiedNamePartTypeArguments && createNodeArray(mapToTypeNodeArray(qualifiedNamePartTypeArguments, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true)); - const qualifiedNamePart = symbolToName(parent, /*expectsIdentifier*/ true, SymbolFlags.Type, context); - qualifiedNamePart.typeArguments = qualifiedNamePartTypeArgumentNodes; + const typeArgumentNodes = createNodeArray(toTypeArgumentNodes(typeArguments.slice(start, i), context)); + const namePart = symbolToTypeReferenceName(parent); + (namePart.kind === SyntaxKind.Identifier ? namePart : namePart.right).typeArguments = typeArgumentNodes; - if (!qualifiedName) { - qualifiedName = createQualifiedName(qualifiedNamePart, /*right*/ undefined); + if (qualifiedName) { + Debug.assert(!qualifiedName.right); + qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, namePart); + qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); } else { - Debug.assert(!qualifiedName.right); - qualifiedName.right = qualifiedNamePart; - qualifiedName = createQualifiedName(qualifiedName, /*right*/ undefined); + qualifiedName = createQualifiedName(namePart, /*right*/ undefined); } } inFirstTypeArgument = false; } } + let entityName: EntityName = undefined; - const nameIdentifier = symbolToName(type.symbol, /*expectsIdentifier*/ true, SymbolFlags.Type, context); + const nameIdentifier = symbolToTypeReferenceName(type.symbol); if (qualifiedName) { Debug.assert(!qualifiedName.right); - qualifiedName.right = nameIdentifier; + qualifiedName = addToQualifiedNameMissingRightIdentifier(qualifiedName, nameIdentifier); entityName = qualifiedName; } else { entityName = nameIdentifier; } - const typeParameterCount = (type.target.typeParameters || emptyArray).length; let typeArgumentNodes: TypeNode[] | undefined; if (some(typeArguments)) { - const slice = typeArguments.slice(i, typeParameterCount - i); - context.InFirstTypeArgument = true; - typeArgumentNodes = mapToTypeNodeArray(slice, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); + const typeParameterCount = (type.target.typeParameters || emptyArray).length; + const slice = typeArguments && typeArguments.slice(i, typeParameterCount); + typeArgumentNodes = toTypeArgumentNodes(slice, context); + } + + if (typeArgumentNodes) { + const lastIdentifier = entityName.kind === SyntaxKind.Identifier ? entityName : entityName.right; + lastIdentifier.typeArguments = undefined; } return createTypeReferenceNode(entityName, typeArgumentNodes); } } + function addToQualifiedNameMissingRightIdentifier(left: QualifiedName, right: Identifier | QualifiedName) { + Debug.assert(left.right === undefined); + + if (right.kind === SyntaxKind.Identifier) { + left.right = right; + return left; + } + + let rightPart = right; + while (rightPart.left.kind !== SyntaxKind.Identifier) { + rightPart = rightPart.left; + } + + left.right = rightPart.left; + rightPart.left = left; + return right; + } + function createTypeNodesFromResolvedType(resolvedType: ResolvedType): TypeElement[] { const typeElements: TypeElement[] = []; for (const signature of resolvedType.callSignatures) { @@ -2754,7 +2784,7 @@ namespace ts { const result = []; Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); for (let i = 0; i < types.length; ++i) { - let type = types[i] + const type = types[i]; context.InElementType = addInElementTypeFlag; if (i === 0) { context.InFirstTypeArgument = addInFirstTypeArgumentFlag; @@ -2768,6 +2798,10 @@ namespace ts { return result; } + function toTypeArgumentNodes(typeArguments: Type[], context: NodeBuilderContext) { + return typeArguments && mapToTypeNodeArray(typeArguments, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); + } + function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const name = getNameFromIndexInfo(indexInfo) || "x"; @@ -2913,7 +2947,7 @@ namespace ts { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowTypeParameterInQualifiedName)) { context.encounteredError = true; } - typeParameterNodes = typeParameters && mapToTypeNodeArray(typeParameters, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); + typeParameterNodes = toTypeArgumentNodes(typeParameters, context); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ad89069c286..73d6067cb10 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -570,12 +570,12 @@ namespace ts { export interface Identifier extends PrimaryExpression { kind: SyntaxKind.Identifier; - text: string; // Text of identifier (with escapes converted to characters) - originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later - /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. - /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. - isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace - /*@internal*/ typeArguments: NodeArray; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. + text: string; // Text of identifier (with escapes converted to characters) + originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later + /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. + /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. + isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace + /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. } // Transient identifier node (marked by id === -1) From cff19ab3d3fc2cb8d405a9c435b364b5b81985c8 Mon Sep 17 00:00:00 2001 From: Charles Pierce Date: Thu, 11 May 2017 10:42:25 -0700 Subject: [PATCH 29/77] Switch map to be used as a set rather than a map --- src/services/completions.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 948d7349419..e850bfb4496 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -221,7 +221,7 @@ namespace ts.Completions { function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo, typeChecker: TypeChecker): CompletionInfo | undefined { const candidates: Signature[] = []; const entries: CompletionEntry[] = []; - const uniques = createMap(); + const uniques = createMap(); typeChecker.getResolvedSignature(argumentInfo.invocation, candidates); @@ -259,7 +259,7 @@ namespace ts.Completions { return undefined; } - function addStringLiteralCompletionsFromType(type: Type, result: Push, typeChecker: TypeChecker, uniques = createMap()): void { + function addStringLiteralCompletionsFromType(type: Type, result: Push, typeChecker: TypeChecker, uniques = createMap()): void { if (type && type.flags & TypeFlags.TypeParameter) { type = typeChecker.getBaseConstraintOfType(type); } @@ -273,8 +273,8 @@ namespace ts.Completions { } else if (type.flags & TypeFlags.StringLiteral) { const name = (type).text; - if (!uniques.get(name)) { - uniques.set(name, name); + if (!uniques.has(name)) { + uniques.set(name, true); result.push({ name, kindModifiers: ScriptElementKindModifier.none, From a8ad40f131c0459d6316ff872cd7e4909d36d71b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 11 May 2017 11:42:36 -0700 Subject: [PATCH 30/77] Add async to the list of class element completion keyword and refactoring according to feedback --- src/harness/fourslash.ts | 3 ++- src/services/completions.ts | 8 ++++---- tests/cases/fourslash/completionEntryForClassMembers.ts | 4 ++++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 74790400b98..57d0eb59ee2 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -3426,7 +3426,8 @@ namespace FourSlashInterface { "readonly", "get", "set", - "constructor" + "constructor", + "async" ]; constructor(protected state: FourSlash.TestState, private negative = false) { diff --git a/src/services/completions.ts b/src/services/completions.ts index cb4abb89b58..986ad0fe5ad 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -618,7 +618,8 @@ namespace ts.Completions { if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) { // cursor inside class declaration - return tryGetClassLikeCompletionSymbols(classLikeContainer); + getGetClassLikeCompletionSymbols(classLikeContainer); + return true; } if (jsxContainer = tryGetContainingJsxElement(contextToken)) { @@ -929,7 +930,7 @@ namespace ts.Completions { * * @returns true if 'symbols' was successfully populated; false otherwise. */ - function tryGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration): boolean { + function getGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration) { // We're looking up possible property names from parent type. isMemberCompletion = true; // Declaring new property/method/accessor @@ -965,8 +966,6 @@ namespace ts.Completions { baseProperty => baseProperty.getDeclarations() && !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); } } - - return true; } /** @@ -1431,6 +1430,7 @@ namespace ts.Completions { case SyntaxKind.ReadonlyKeyword: case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: + case SyntaxKind.AsyncKeyword: return true; } } diff --git a/tests/cases/fourslash/completionEntryForClassMembers.ts b/tests/cases/fourslash/completionEntryForClassMembers.ts index 672046f62a7..8cea37d3b1e 100644 --- a/tests/cases/fourslash/completionEntryForClassMembers.ts +++ b/tests/cases/fourslash/completionEntryForClassMembers.ts @@ -84,6 +84,9 @@ ////class M extends B { //// private static identi/*classThatStartedWritingIdentifierAfterPrivateStaticModifier*/ ////} +////class N extends B { +//// async /*classThatHasWrittenAsyncKeyword*/ +////} const allowedKeywordCount = verify.allowedClassElementKeywords.length; type CompletionInfo = [string, string]; @@ -171,6 +174,7 @@ const classInstanceElementLocations = [ "classThatStartedWritingIdentifierOfGetAccessor", "classThatStartedWritingIdentifierOfSetAccessor", "classThatStartedWritingIdentifierAfterModifier", + "classThatHasWrittenAsyncKeyword" ]; verifyClassElementLocations(instanceMemberInfo, classInstanceElementLocations); From 7c89ff7d77b41b64ac440eab34dddfc2c7e3f057 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 11 May 2017 13:39:37 -0700 Subject: [PATCH 31/77] Inline resolvedModuleFromResolved --- src/compiler/moduleNameResolver.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index acf01bac905..12a42b6899c 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -47,13 +47,11 @@ namespace ts { return resolved.path; } - /** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */ - function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull { - return { resolvedFileName: path, extension, isExternalLibraryImport }; - } - function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations { - return { resolvedModule: resolved && resolvedModuleFromResolved(resolved, isExternalLibraryImport), failedLookupLocations }; + return { + resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport }, + failedLookupLocations + }; } export function moduleHasNonRelativeName(moduleName: string): boolean { From d5f34da9dd913c201ba00c44b50806b2ca1a0261 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 14:07:33 -0700 Subject: [PATCH 32/77] Accept baselines and update test --- tests/baselines/reference/escapedIdentifiers.types | 2 +- .../reference/unicodeExtendedEscapesInStrings06_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings06_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings08_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings08_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings09_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings09_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings10_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings10_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings11_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings11_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings13_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings13_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings15_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings15_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings16_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings16_ES6.types | 2 +- .../fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/baselines/reference/escapedIdentifiers.types b/tests/baselines/reference/escapedIdentifiers.types index 6d48bb3392f..ef6c0983d39 100644 --- a/tests/baselines/reference/escapedIdentifiers.types +++ b/tests/baselines/reference/escapedIdentifiers.types @@ -220,7 +220,7 @@ class testClass { >testClass : testClass public func(arg1: number, arg\u0032: string, arg\u0033: boolean, arg4: number) { ->func : (arg1: number, arg\u0032: string, arg\u0033: boolean, arg4: number) => void +>func : (arg1: number, arg2: string, arg3: boolean, arg4: number) => void >arg1 : number >arg\u0032 : string >arg\u0033 : boolean diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types index 0c742aaf7bd..e6942df5d1e 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types @@ -3,5 +3,5 @@ // 1. Assert: 0 ≤ cp ≤ 0x10FFFF. var x = "\u{10FFFF}"; >x : string ->"\u{10FFFF}" : "􏿿" +>"\u{10FFFF}" : "\uDBFF\uDFFF" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types index 6b98c0c98a7..82cc1fa622b 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types @@ -3,5 +3,5 @@ // 1. Assert: 0 ≤ cp ≤ 0x10FFFF. var x = "\u{10FFFF}"; >x : string ->"\u{10FFFF}" : "􏿿" +>"\u{10FFFF}" : "\uDBFF\uDFFF" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types index 0007b758817..9c316cca313 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types @@ -4,5 +4,5 @@ // (FFFF == 65535) var x = "\u{FFFF}"; >x : string ->"\u{FFFF}" : "￿" +>"\u{FFFF}" : "\uFFFF" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types index aedd2f72ef9..a5982a77c67 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types @@ -4,5 +4,5 @@ // (FFFF == 65535) var x = "\u{FFFF}"; >x : string ->"\u{FFFF}" : "￿" +>"\u{FFFF}" : "\uFFFF" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types index daf8a776e8a..7508721f141 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types @@ -4,5 +4,5 @@ // (10000 == 65536) var x = "\u{10000}"; >x : string ->"\u{10000}" : "𐀀" +>"\u{10000}" : "\uD800\uDC00" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types index 0963f552c96..5f05276ed33 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types @@ -4,5 +4,5 @@ // (10000 == 65536) var x = "\u{10000}"; >x : string ->"\u{10000}" : "𐀀" +>"\u{10000}" : "\uD800\uDC00" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types index a4977300e65..a5422cc7a92 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{D800}"; >x : string ->"\u{D800}" : "�" +>"\u{D800}" : "\uD800" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types index 6d6d0112f08..2fcdd0547ee 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{D800}"; >x : string ->"\u{D800}" : "�" +>"\u{D800}" : "\uD800" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types index 74e6817c85a..b26387baaec 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{DC00}"; >x : string ->"\u{DC00}" : "�" +>"\u{DC00}" : "\uDC00" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types index c1b3f372dab..cd010529996 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{DC00}"; >x : string ->"\u{DC00}" : "�" +>"\u{DC00}" : "\uDC00" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types index 224e81adec5..68eb2fe8de1 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings13_ES5.ts === var x = "\u{DDDDD}"; >x : string ->"\u{DDDDD}" : "󝷝" +>"\u{DDDDD}" : "\uDB37\uDDDD" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types index e69b2af435c..4ad8d2e3f3d 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings13_ES6.ts === var x = "\u{DDDDD}"; >x : string ->"\u{DDDDD}" : "󝷝" +>"\u{DDDDD}" : "\uDB37\uDDDD" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types index 1973bb3790b..8da56115188 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings15_ES5.ts === var x = "\u{abcd}\u{ef12}\u{3456}\u{7890}"; >x : string ->"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "ꯍ㑖碐" +>"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types index 16ae5177f51..613d91afae2 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings15_ES6.ts === var x = "\u{abcd}\u{ef12}\u{3456}\u{7890}"; >x : string ->"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "ꯍ㑖碐" +>"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types index 1ae9d9ef3d5..1b29651e2d7 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings16_ES5.ts === var x = "\u{ABCD}\u{EF12}\u{3456}\u{7890}"; >x : string ->"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "ꯍ㑖碐" +>"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types index a4abc5bdeae..addf7dedff4 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings16_ES6.ts === var x = "\u{ABCD}\u{EF12}\u{3456}\u{7890}"; >x : string ->"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "ꯍ㑖碐" +>"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts index eab4c80c786..f7dfd5e1787 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts @@ -5,7 +5,7 @@ //// class C implements I {[| |]} verify.rangeAfterCodeFix(` - test(a: MyType): void { + test(a: [string, number]): void { throw new Error("Method not implemented."); } `); From 588c4eca427fe598147b17c23b8c47637a5fdac8 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 11 May 2017 13:59:06 -0700 Subject: [PATCH 33/77] Filter out existing members of the class from the completion list --- src/services/completions.ts | 53 +++++++++++++++++-- .../completionEntryForClassMembers.ts | 33 ++++++++++-- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 986ad0fe5ad..6be420c0919 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -927,8 +927,6 @@ namespace ts.Completions { /** * Aggregates relevant symbols for completion in class declaration * Relevant symbols are stored in the captured 'symbols' variable. - * - * @returns true if 'symbols' was successfully populated; false otherwise. */ function getGetClassLikeCompletionSymbols(classLikeDeclaration: ClassLikeDeclaration) { // We're looking up possible property names from parent type. @@ -961,9 +959,8 @@ namespace ts.Completions { typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, classLikeDeclaration) : baseType; - // List of property symbols of base type that are not private - symbols = filter(typeChecker.getPropertiesOfType(typeToGetPropertiesFrom), - baseProperty => baseProperty.getDeclarations() && !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); + // List of property symbols of base type that are not private and already implemented + symbols = filterClassMembersList(typeChecker.getPropertiesOfType(typeToGetPropertiesFrom), classLikeDeclaration.members, classElementModifierFlags); } } } @@ -1332,6 +1329,52 @@ namespace ts.Completions { return filter(contextualMemberSymbols, m => !existingMemberNames.get(m.name)); } + /** + * Filters out completion suggestions for class elements. + * + * @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags + */ + function filterClassMembersList(baseSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] { + const existingMemberNames = createMap(); + for (const m of existingMembers) { + // Ignore omitted expressions for missing members + if (m.kind !== SyntaxKind.PropertyDeclaration && + m.kind !== SyntaxKind.MethodDeclaration && + m.kind !== SyntaxKind.GetAccessor && + m.kind !== SyntaxKind.SetAccessor) { + continue; + } + + // If this is the current item we are editing right now, do not filter it out + if (isCurrentlyEditingNode(m)) { + continue; + } + + // Dont filter member even if the name matches if it is declared private in the list + if (hasModifier(m, ModifierFlags.Private)) { + continue; + } + + // do not filter it out if the static presence doesnt match + const mIsStatic = hasModifier(m, ModifierFlags.Static); + const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static); + if ((mIsStatic && currentElementIsStatic) || + (!mIsStatic && currentElementIsStatic)) { + continue; + } + + const existingName = getPropertyNameForPropertyNameNode(m.name); + if (existingName) { + existingMemberNames.set(existingName, true); + } + } + + return filter(baseSymbols, baseProperty => + !existingMemberNames.get(baseProperty.name) && + baseProperty.getDeclarations() && + !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); + } + /** * Filters out completion suggestions from 'symbols' according to existing JSX attributes. * diff --git a/tests/cases/fourslash/completionEntryForClassMembers.ts b/tests/cases/fourslash/completionEntryForClassMembers.ts index 8cea37d3b1e..b242ca7ed78 100644 --- a/tests/cases/fourslash/completionEntryForClassMembers.ts +++ b/tests/cases/fourslash/completionEntryForClassMembers.ts @@ -17,6 +17,19 @@ //// } //// /*classThatHasAlreadyImplementedAnotherClassMethodAfterMethod*/ ////} +////class D1 extends B { +//// /*classThatHasDifferentMethodThanBase*/ +//// getValue1() { +//// return 10; +//// } +//// /*classThatHasDifferentMethodThanBaseAfterMethod*/ +////} +////class D2 extends B { +//// /*classThatHasAlreadyImplementedAnotherClassProtectedMethod*/ +//// protectedMethod() { +//// } +//// /*classThatHasDifferentMethodThanBaseAfterProtectedMethod*/ +////} ////class E { //// /*classThatDoesNotExtendAnotherClass*/ ////} @@ -131,6 +144,8 @@ function filterCompletionInfo(fn: (a: CompletionInfo) => boolean): CompletionInf const instanceMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "getValue" || a === "protectedMethod"); const staticMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "staticMethod"); +const instanceWithoutProtectedMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "getValue"); +const instanceWithoutPublicMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "protectedMethod"); // Not a class element declaration location const nonClassElementMarkers = [ @@ -156,8 +171,8 @@ verifyClassElementLocations({ validMembers: [], invalidMembers: allMembersOfBase // Instance base members and class member keywords allowed const classInstanceElementLocations = [ "classThatIsEmptyAndExtendingAnotherClass", - "classThatHasAlreadyImplementedAnotherClassMethod", - "classThatHasAlreadyImplementedAnotherClassMethodAfterMethod", + "classThatHasDifferentMethodThanBase", + "classThatHasDifferentMethodThanBaseAfterMethod", "classThatHasWrittenPublicKeyword", "classThatStartedWritingIdentifier", "propDeclarationWithoutSemicolon", @@ -183,4 +198,16 @@ const staticClassLocations = [ "classElementContainingStatic", "classThatStartedWritingIdentifierAfterStaticModifier" ]; -verifyClassElementLocations(staticMemberInfo, staticClassLocations); \ No newline at end of file +verifyClassElementLocations(staticMemberInfo, staticClassLocations); + +const classInstanceElementWithoutPublicMethodLocations = [ + "classThatHasAlreadyImplementedAnotherClassMethod", + "classThatHasAlreadyImplementedAnotherClassMethodAfterMethod", +]; +verifyClassElementLocations(instanceWithoutPublicMemberInfo, classInstanceElementWithoutPublicMethodLocations); + +const classInstanceElementWithoutProtectedMethodLocations = [ + "classThatHasAlreadyImplementedAnotherClassProtectedMethod", + "classThatHasDifferentMethodThanBaseAfterProtectedMethod", +]; +verifyClassElementLocations(instanceWithoutProtectedMemberInfo, classInstanceElementWithoutProtectedMethodLocations); \ No newline at end of file From d6fa91edcd1cf3bf277958881dc1e5f81e8a183a Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 11 May 2017 15:48:17 -0700 Subject: [PATCH 34/77] goToDefinition: Skip default and `=` imports --- src/services/goToDefinition.ts | 39 ++++++++++++------- .../cases/fourslash/goToDefinitionImports.ts | 26 +++++++++++++ 2 files changed, 51 insertions(+), 14 deletions(-) create mode 100644 tests/cases/fourslash/goToDefinitionImports.ts diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 07b1e4448bd..946924bcf87 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -50,20 +50,8 @@ namespace ts.GoToDefinition { // get the aliased symbol instead. This allows for goto def on an import e.g. // import {A, B} from "mod"; // to jump to the implementation directly. - if (symbol.flags & SymbolFlags.Alias) { - const declaration = symbol.declarations[0]; - - // Go to the original declaration for cases: - // - // (1) when the aliased symbol was declared in the location(parent). - // (2) when the aliased symbol is originating from a named import. - // - if (node.kind === SyntaxKind.Identifier && - (node.parent === declaration || - (declaration.kind === SyntaxKind.ImportSpecifier && declaration.parent && declaration.parent.kind === SyntaxKind.NamedImports))) { - - symbol = typeChecker.getAliasedSymbol(symbol); - } + if (symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { + symbol = typeChecker.getAliasedSymbol(symbol); } // Because name in short-hand property assignment has two different meanings: property name and property value, @@ -136,6 +124,29 @@ namespace ts.GoToDefinition { return getDefinitionFromSymbol(typeChecker, type.symbol, node); } + // Go to the original declaration for cases: + // + // (1) when the aliased symbol was declared in the location(parent). + // (2) when the aliased symbol is originating from an import. + // + function shouldSkipAlias(node: Node, declaration: Node): boolean { + if (node.kind !== SyntaxKind.Identifier) { + return false; + } + if (node.parent === declaration) { + return true; + } + switch (declaration.kind) { + case SyntaxKind.ImportClause: + case SyntaxKind.ImportEqualsDeclaration: + return true; + case SyntaxKind.ImportSpecifier: + return declaration.parent.kind === SyntaxKind.NamedImports; + default: + return false; + } + } + function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] { const result: DefinitionInfo[] = []; const declarations = symbol.getDeclarations(); diff --git a/tests/cases/fourslash/goToDefinitionImports.ts b/tests/cases/fourslash/goToDefinitionImports.ts new file mode 100644 index 00000000000..4fdaedb5c3d --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionImports.ts @@ -0,0 +1,26 @@ +/// + +// @Filename: /a.ts +////export default function /*fDef*/f() {} +////export const /*xDef*/x = 0; + +// @Filename: /b.ts +/////*bDef*/declare const b: number; +////export = b; + +// @Filename: /b.ts +////import f, { x } from "./a"; +////import * as /*aDef*/a from "./a"; +////import b = require("./b"); +/////*fUse*/f; +/////*xUse*/x; +/////*aUse*/a; +/////*bUse*/b; + +verify.goToDefinition({ + aUse: "aDef", // Namespace import isn't "skipped" + fUse: "fDef", + xUse: "xDef", + bUse: "bDef", +}); + From 181ff8615064edeb5fdf33121973c1d0ec07dc50 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 May 2017 16:30:43 -0700 Subject: [PATCH 35/77] getApparentType for each constituent of props type of the targetAttributesType --- src/compiler/checker.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 861279b2225..ce89fb72b52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13548,6 +13548,17 @@ namespace ts { return _jsxElementChildrenPropertyName; } + function createIntersectionOfApparentTypeOfJsxPropsType(propsType: Type): Type { + if (propsType && propsType.flags & TypeFlags.Intersection) { + const propsApprentType: Type[] = []; + for (const t of (propsType).types) { + propsApprentType.push(getApparentType(t)); + } + return getIntersectionType(propsApprentType); + } + return propsType; + } + /** * Get JSX attributes type by trying to resolve openingLikeElement as a stateless function component. * Return only attributes type of successfully resolved call signature. @@ -13568,6 +13579,7 @@ namespace ts { if (callSignature !== unknownSignature) { const callReturnType = callSignature && getReturnTypeOfSignature(callSignature); let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0])); + paramType = createIntersectionOfApparentTypeOfJsxPropsType(paramType); if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { // Intersect in JSX.IntrinsicAttributes if it exists const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes); @@ -13605,7 +13617,8 @@ namespace ts { let allMatchingAttributesType: Type; for (const candidate of candidatesOutArray) { const callReturnType = getReturnTypeOfSignature(candidate); - const paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0])); + let paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0])); + paramType = createIntersectionOfApparentTypeOfJsxPropsType(paramType); if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { let shouldBeCandidate = true; for (const attribute of openingLikeElement.attributes.properties) { From bce7ddb5c555725e7b3a23d9042d194fca86e82b Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 May 2017 16:30:56 -0700 Subject: [PATCH 36/77] Add tests and update lib tests file --- .../jsx/tsxGenericAttributesType1.tsx | 18 ++++++++++++++++++ .../jsx/tsxGenericAttributesType2.tsx | 10 ++++++++++ .../jsx/tsxGenericAttributesType3.tsx | 17 +++++++++++++++++ .../jsx/tsxGenericAttributesType4.tsx | 18 ++++++++++++++++++ tests/lib/react.d.ts | 2 +- 5 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType1.tsx create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType2.tsx create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType3.tsx create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType4.tsx diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType1.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType1.tsx new file mode 100644 index 00000000000..e6b7fc18ef7 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType1.tsx @@ -0,0 +1,18 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +const decorator = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; + +const decorator2 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; + +const decorator3 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType2.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType2.tsx new file mode 100644 index 00000000000..48acd55546f --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType2.tsx @@ -0,0 +1,10 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +const decorator4 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType3.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType3.tsx new file mode 100644 index 00000000000..b683c5e7970 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType3.tsx @@ -0,0 +1,17 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + render() { + return ; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType4.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType4.tsx new file mode 100644 index 00000000000..b207f2b4398 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType4.tsx @@ -0,0 +1,18 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + render() { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + } +} \ No newline at end of file diff --git a/tests/lib/react.d.ts b/tests/lib/react.d.ts index 1d7b787b999..7f03899eb6c 100644 --- a/tests/lib/react.d.ts +++ b/tests/lib/react.d.ts @@ -197,7 +197,7 @@ declare namespace __React { type SFC

= StatelessComponent

; interface StatelessComponent

{ - (props: P, context?: any): ReactElement; + (props: P & { children?: ReactNode }, context?: any): ReactElement; propTypes?: ValidationMap

; contextTypes?: ValidationMap; defaultProps?: P; From 705771d8746aaf5eee67883a1d282776d54dc183 Mon Sep 17 00:00:00 2001 From: Kanchalai Tanglertsampan Date: Thu, 11 May 2017 16:31:40 -0700 Subject: [PATCH 37/77] Update baselines --- .../reference/tsxGenericAttributesType1.js | 28 +++++++ .../tsxGenericAttributesType1.symbols | 66 ++++++++++++++++ .../reference/tsxGenericAttributesType1.types | 77 +++++++++++++++++++ .../tsxGenericAttributesType2.errors.txt | 11 +++ .../reference/tsxGenericAttributesType2.js | 14 ++++ .../reference/tsxGenericAttributesType3.js | 48 ++++++++++++ .../tsxGenericAttributesType3.symbols | 41 ++++++++++ .../reference/tsxGenericAttributesType3.types | 43 +++++++++++ .../tsxGenericAttributesType4.errors.txt | 19 +++++ .../reference/tsxGenericAttributesType4.js | 50 ++++++++++++ 10 files changed, 397 insertions(+) create mode 100644 tests/baselines/reference/tsxGenericAttributesType1.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType1.symbols create mode 100644 tests/baselines/reference/tsxGenericAttributesType1.types create mode 100644 tests/baselines/reference/tsxGenericAttributesType2.errors.txt create mode 100644 tests/baselines/reference/tsxGenericAttributesType2.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType3.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType3.symbols create mode 100644 tests/baselines/reference/tsxGenericAttributesType3.types create mode 100644 tests/baselines/reference/tsxGenericAttributesType4.errors.txt create mode 100644 tests/baselines/reference/tsxGenericAttributesType4.js diff --git a/tests/baselines/reference/tsxGenericAttributesType1.js b/tests/baselines/reference/tsxGenericAttributesType1.js new file mode 100644 index 00000000000..5d3d388396a --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType1.js @@ -0,0 +1,28 @@ +//// [file.tsx] +import React = require('react'); + +const decorator = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; + +const decorator2 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; + +const decorator3 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; + +//// [file.jsx] +"use strict"; +exports.__esModule = true; +var React = require("react"); +var decorator = function (Component) { + return function (props) { return ; }; +}; +var decorator2 = function (Component) { + return function (props) { return ; }; +}; +var decorator3 = function (Component) { + return function (props) { return ; }; +}; diff --git a/tests/baselines/reference/tsxGenericAttributesType1.symbols b/tests/baselines/reference/tsxGenericAttributesType1.symbols new file mode 100644 index 00000000000..473de9b5c72 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType1.symbols @@ -0,0 +1,66 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +const decorator = function (Component: React.StatelessComponent): React.StatelessComponent { +>decorator : Symbol(decorator, Decl(file.tsx, 2, 5)) +>T : Symbol(T, Decl(file.tsx, 2, 28)) +>Component : Symbol(Component, Decl(file.tsx, 2, 31)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40)) +>T : Symbol(T, Decl(file.tsx, 2, 28)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40)) +>T : Symbol(T, Decl(file.tsx, 2, 28)) + + return (props) => +>props : Symbol(props, Decl(file.tsx, 3, 12)) +>Component : Symbol(Component, Decl(file.tsx, 2, 31)) +>props : Symbol(props, Decl(file.tsx, 3, 12)) +>Component : Symbol(Component, Decl(file.tsx, 2, 31)) + +}; + +const decorator2 = function (Component: React.StatelessComponent): React.StatelessComponent { +>decorator2 : Symbol(decorator2, Decl(file.tsx, 6, 5)) +>T : Symbol(T, Decl(file.tsx, 6, 29)) +>x : Symbol(x, Decl(file.tsx, 6, 40)) +>Component : Symbol(Component, Decl(file.tsx, 6, 54)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40)) +>T : Symbol(T, Decl(file.tsx, 6, 29)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40)) +>T : Symbol(T, Decl(file.tsx, 6, 29)) + + return (props) => +>props : Symbol(props, Decl(file.tsx, 7, 12)) +>Component : Symbol(Component, Decl(file.tsx, 6, 54)) +>props : Symbol(props, Decl(file.tsx, 7, 12)) +>x : Symbol(x, Decl(file.tsx, 7, 43)) +>Component : Symbol(Component, Decl(file.tsx, 6, 54)) + +}; + +const decorator3 = function (Component: React.StatelessComponent): React.StatelessComponent { +>decorator3 : Symbol(decorator3, Decl(file.tsx, 10, 5)) +>T : Symbol(T, Decl(file.tsx, 10, 29)) +>x : Symbol(x, Decl(file.tsx, 10, 40)) +>U : Symbol(U, Decl(file.tsx, 10, 53)) +>x : Symbol(x, Decl(file.tsx, 10, 65)) +>Component : Symbol(Component, Decl(file.tsx, 10, 80)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40)) +>T : Symbol(T, Decl(file.tsx, 10, 29)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>StatelessComponent : Symbol(React.StatelessComponent, Decl(react.d.ts, 197, 40)) +>T : Symbol(T, Decl(file.tsx, 10, 29)) + + return (props) => +>props : Symbol(props, Decl(file.tsx, 11, 12)) +>Component : Symbol(Component, Decl(file.tsx, 10, 80)) +>x : Symbol(x, Decl(file.tsx, 11, 32)) +>props : Symbol(props, Decl(file.tsx, 11, 12)) +>Component : Symbol(Component, Decl(file.tsx, 10, 80)) + +}; diff --git a/tests/baselines/reference/tsxGenericAttributesType1.types b/tests/baselines/reference/tsxGenericAttributesType1.types new file mode 100644 index 00000000000..50be358302e --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType1.types @@ -0,0 +1,77 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : typeof React + +const decorator = function (Component: React.StatelessComponent): React.StatelessComponent { +>decorator : (Component: React.StatelessComponent) => React.StatelessComponent +>function (Component: React.StatelessComponent): React.StatelessComponent { return (props) => } : (Component: React.StatelessComponent) => React.StatelessComponent +>T : T +>Component : React.StatelessComponent +>React : any +>StatelessComponent : React.StatelessComponent

+>T : T +>React : any +>StatelessComponent : React.StatelessComponent

+>T : T + + return (props) => +>(props) => : (props: T & { children?: React.ReactNode; }) => JSX.Element +>props : T & { children?: React.ReactNode; } +> : JSX.Element +>Component : React.StatelessComponent +>props : T & { children?: React.ReactNode; } +>Component : React.StatelessComponent + +}; + +const decorator2 = function (Component: React.StatelessComponent): React.StatelessComponent { +>decorator2 : (Component: React.StatelessComponent) => React.StatelessComponent +>function (Component: React.StatelessComponent): React.StatelessComponent { return (props) => } : (Component: React.StatelessComponent) => React.StatelessComponent +>T : T +>x : number +>Component : React.StatelessComponent +>React : any +>StatelessComponent : React.StatelessComponent

+>T : T +>React : any +>StatelessComponent : React.StatelessComponent

+>T : T + + return (props) => +>(props) => : (props: T & { children?: React.ReactNode; }) => JSX.Element +>props : T & { children?: React.ReactNode; } +> : JSX.Element +>Component : React.StatelessComponent +>props : T & { children?: React.ReactNode; } +>x : number +>2 : 2 +>Component : React.StatelessComponent + +}; + +const decorator3 = function (Component: React.StatelessComponent): React.StatelessComponent { +>decorator3 : (Component: React.StatelessComponent) => React.StatelessComponent +>function (Component: React.StatelessComponent): React.StatelessComponent { return (props) => } : (Component: React.StatelessComponent) => React.StatelessComponent +>T : T +>x : number +>U : U +>x : number +>Component : React.StatelessComponent +>React : any +>StatelessComponent : React.StatelessComponent

+>T : T +>React : any +>StatelessComponent : React.StatelessComponent

+>T : T + + return (props) => +>(props) => : (props: T & { children?: React.ReactNode; }) => JSX.Element +>props : T & { children?: React.ReactNode; } +> : JSX.Element +>Component : React.StatelessComponent +>x : number +>2 : 2 +>props : T & { children?: React.ReactNode; } +>Component : React.StatelessComponent + +}; diff --git a/tests/baselines/reference/tsxGenericAttributesType2.errors.txt b/tests/baselines/reference/tsxGenericAttributesType2.errors.txt new file mode 100644 index 00000000000..cbd926c9b69 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType2.errors.txt @@ -0,0 +1,11 @@ +tests/cases/conformance/jsx/file.tsx(4,45): error TS2339: Property 'y' does not exist on type 'IntrinsicAttributes & { x: number; } & { children?: ReactNode; }'. + + +==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== + import React = require('react'); + + const decorator4 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => + ~~~~~~~~~~ +!!! error TS2339: Property 'y' does not exist on type 'IntrinsicAttributes & { x: number; } & { children?: ReactNode; }'. + }; \ No newline at end of file diff --git a/tests/baselines/reference/tsxGenericAttributesType2.js b/tests/baselines/reference/tsxGenericAttributesType2.js new file mode 100644 index 00000000000..7b46d08c2c4 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType2.js @@ -0,0 +1,14 @@ +//// [file.tsx] +import React = require('react'); + +const decorator4 = function (Component: React.StatelessComponent): React.StatelessComponent { + return (props) => +}; + +//// [file.jsx] +"use strict"; +exports.__esModule = true; +var React = require("react"); +var decorator4 = function (Component) { + return function (props) { return ; }; +}; diff --git a/tests/baselines/reference/tsxGenericAttributesType3.js b/tests/baselines/reference/tsxGenericAttributesType3.js new file mode 100644 index 00000000000..51491d99aa2 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType3.js @@ -0,0 +1,48 @@ +//// [file.tsx] +import React = require('react'); + +class B1 extends React.Component { + render() { + return

hi
; + } +} +class B extends React.Component { + render() { + return ; + } +} + +//// [file.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var React = require("react"); +var B1 = (function (_super) { + __extends(B1, _super); + function B1() { + return _super !== null && _super.apply(this, arguments) || this; + } + B1.prototype.render = function () { + return
hi
; + }; + return B1; +}(React.Component)); +var B = (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + B.prototype.render = function () { + return ; + }; + return B; +}(React.Component)); diff --git a/tests/baselines/reference/tsxGenericAttributesType3.symbols b/tests/baselines/reference/tsxGenericAttributesType3.symbols new file mode 100644 index 00000000000..8c235257e8d --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType3.symbols @@ -0,0 +1,41 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +class B1 extends React.Component { +>B1 : Symbol(B1, Decl(file.tsx, 0, 32)) +>T : Symbol(T, Decl(file.tsx, 2, 9)) +>x : Symbol(x, Decl(file.tsx, 2, 20)) +>x : Symbol(x, Decl(file.tsx, 2, 36)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>T : Symbol(T, Decl(file.tsx, 2, 9)) + + render() { +>render : Symbol(B1.render, Decl(file.tsx, 2, 82)) + + return
hi
; +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2399, 45)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2399, 45)) + } +} +class B extends React.Component { +>B : Symbol(B, Decl(file.tsx, 6, 1)) +>U : Symbol(U, Decl(file.tsx, 7, 8)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>U : Symbol(U, Decl(file.tsx, 7, 8)) + + render() { +>render : Symbol(B.render, Decl(file.tsx, 7, 43)) + + return ; +>B1 : Symbol(B1, Decl(file.tsx, 0, 32)) +>this.props : Symbol(React.Component.props, Decl(react.d.ts, 166, 37)) +>this : Symbol(B, Decl(file.tsx, 6, 1)) +>props : Symbol(React.Component.props, Decl(react.d.ts, 166, 37)) +>x : Symbol(x, Decl(file.tsx, 9, 34)) + } +} diff --git a/tests/baselines/reference/tsxGenericAttributesType3.types b/tests/baselines/reference/tsxGenericAttributesType3.types new file mode 100644 index 00000000000..72674ac5bf7 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType3.types @@ -0,0 +1,43 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : typeof React + +class B1 extends React.Component { +>B1 : B1 +>T : T +>x : string +>x : string +>React.Component : React.Component +>React : typeof React +>Component : typeof React.Component +>T : T + + render() { +>render : () => JSX.Element + + return
hi
; +>
hi
: JSX.Element +>div : any +>div : any + } +} +class B extends React.Component { +>B : B +>U : U +>React.Component : React.Component +>React : typeof React +>Component : typeof React.Component +>U : U + + render() { +>render : () => JSX.Element + + return ; +> : JSX.Element +>B1 : typeof B1 +>this.props : U & { children?: React.ReactNode; } +>this : this +>props : U & { children?: React.ReactNode; } +>x : string + } +} diff --git a/tests/baselines/reference/tsxGenericAttributesType4.errors.txt b/tests/baselines/reference/tsxGenericAttributesType4.errors.txt new file mode 100644 index 00000000000..ee91f938b9f --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType4.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/jsx/file.tsx(11,36): error TS2339: Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes> & { children?: ReactNode; }'. + + +==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== + import React = require('react'); + + class B1 extends React.Component { + render() { + return
hi
; + } + } + class B extends React.Component { + render() { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + ~~~~~~ +!!! error TS2339: Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes> & { children?: ReactNode; }'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/tsxGenericAttributesType4.js b/tests/baselines/reference/tsxGenericAttributesType4.js new file mode 100644 index 00000000000..a00d03cd61b --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType4.js @@ -0,0 +1,50 @@ +//// [file.tsx] +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + render() { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + } +} + +//// [file.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var React = require("react"); +var B1 = (function (_super) { + __extends(B1, _super); + function B1() { + return _super !== null && _super.apply(this, arguments) || this; + } + B1.prototype.render = function () { + return
hi
; + }; + return B1; +}(React.Component)); +var B = (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + B.prototype.render = function () { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + }; + return B; +}(React.Component)); From a5e93c427ba9f1987b8b84f31a20deb1af5bf35e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 16:40:14 -0700 Subject: [PATCH 38/77] remove souceFile checks --- src/compiler/checker.ts | 4 ++-- src/compiler/emitter.ts | 17 +++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ed5c7ea669e..2e45a17a207 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2289,9 +2289,9 @@ namespace ts { const newLine = NewLineKind.None; const options = { newLine, removeComments: true }; const writer = createTextWriter(""); - // writer.writeLine = noop; const printer = createPrinter(options, writer); - printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ undefined, writer); + const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); + printer.writeNode(EmitHint.Unspecified, typeNode, /*sourceFile*/ sourceFile, writer); const result = writer.getText(); const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index b96e87ec55c..073c49df559 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -153,7 +153,7 @@ namespace ts { for (let i = 0; i < numNodes; i++) { const currentNode = bundle ? bundle.sourceFiles[i] : node; const sourceFile = isSourceFile(currentNode) ? currentNode : currentSourceFile; - const shouldSkip = compilerOptions.noEmitHelpers || (sourceFile && getExternalHelpersModuleName(sourceFile) !== undefined); + const shouldSkip = compilerOptions.noEmitHelpers || getExternalHelpersModuleName(sourceFile) !== undefined; const shouldBundle = isSourceFile(currentNode) && !isOwnFileEmit; const helpers = getEmitHelpers(currentNode); if (helpers) { @@ -234,6 +234,11 @@ namespace ts { writeBundle }; + /** + * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. + */ + function printNode(hint: EmitHint, node: TypeNode, sourceFile: undefined): string; + function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined): string { switch (hint) { case EmitHint.SourceFile: @@ -1112,7 +1117,7 @@ namespace ts { function emitPropertyAccessExpression(node: PropertyAccessExpression) { let indentBeforeDot = false; let indentAfterDot = false; - if (currentSourceFile && !(getEmitFlags(node) & EmitFlags.NoIndentation)) { + if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) { const dotRangeStart = node.expression.end; const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1; const dotToken = { kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd }; @@ -2520,7 +2525,7 @@ namespace ts { const firstChild = children[0]; if (firstChild === undefined) { - return !(currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile)); + return !(rangeIsOnSingleLine(parentNode, currentSourceFile)); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { return synthesizedNodeStartsOnNewLine(firstChild, format); @@ -2546,7 +2551,7 @@ namespace ts { return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); } else { - return !(currentSourceFile && rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile)); + return !(rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile)); } } else { @@ -2565,13 +2570,13 @@ namespace ts { const lastChild = lastOrUndefined(children); if (lastChild === undefined) { - return !(currentSourceFile && rangeIsOnSingleLine(parentNode, currentSourceFile)); + return !(rangeIsOnSingleLine(parentNode, currentSourceFile)); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { return synthesizedNodeStartsOnNewLine(lastChild, format); } else { - return !(currentSourceFile && rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile)); + return !(rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile)); } } else { From 82109d3d1c1f4d31bad310521e5375875e2db4e0 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 17:39:27 -0700 Subject: [PATCH 39/77] cleanup --- src/compiler/checker.ts | 61 ++++++++++++----------------------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2e45a17a207..f6930b01aca 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2229,19 +2229,7 @@ namespace ts { return result; } - function oldTypeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const writer = getSingleLineStringWriter(); - getSymbolDisplayBuilder().buildTypeDisplay(type, writer, enclosingDeclaration, flags); - let result = writer.string(); - releaseStringWriter(writer); - - const maxLength = compilerOptions.noErrorTruncation || flags & TypeFormatFlags.NoTruncation ? undefined : 100; - if (maxLength && result.length >= maxLength) { - result = result.substr(0, maxLength - "...".length) + "..."; - } - return result; - } - + function typeFormatFlagsToNodeBuilderFlags(flags: TypeFormatFlags): NodeBuilderFlags { let result = NodeBuilderFlags.None; if (flags & TypeFormatFlags.WriteArrayAsGenericType) { @@ -2282,8 +2270,6 @@ namespace ts { } function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const str = oldTypeToString(type, enclosingDeclaration, flags); str; - const str2 = oldTypeToString(type, enclosingDeclaration, flags); str2; const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, typeFormatFlagsToNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors, !!(flags & TypeFormatFlags.InTypeAlias)); Debug.assert(typeNode !== undefined, "should always get typenode?"); const newLine = NewLineKind.None; @@ -2331,11 +2317,8 @@ namespace ts { // State encounteredError: boolean; inObjectTypeLiteral: boolean; - // TODO: needed for part of parens handling InElementType: boolean; // Writing an array or union element type - // TODO: ??? - InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type - // TODO: ??? + InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type InTypeAlias: boolean; // Writing type in type alias declaration symbolStack: Symbol[] | undefined; } @@ -2354,18 +2337,15 @@ namespace ts { } function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { - const InElementType = context.InElementType; - // TODO: why doesn't tts unset the flag? + const inElementType = context.InElementType; context.InElementType = false; const inTypeAlias = context.InTypeAlias; context.InTypeAlias = false; const inFirstTypeArgument = context.InFirstTypeArgument; context.InFirstTypeArgument = false; - // TODO: should be assert? if (!type) { context.encounteredError = true; - // TODO(aozgaa): should we return implict any (undefined) or explicit any (keywordtypenode)? return undefined; } @@ -2382,7 +2362,7 @@ namespace ts { return createKeywordTypeNode(SyntaxKind.BooleanKeyword); } if (type.flags & TypeFlags.Enum) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); + const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { @@ -2396,7 +2376,7 @@ namespace ts { } if (type.flags & TypeFlags.EnumLiteral) { const parentSymbol = getParentOfSymbol(type.symbol); - const parentName = symbolToName(parentSymbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); + const parentName = symbolToName(parentSymbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); const name = getNameOfSymbol(type.symbol, context); const enumLiteralName = createQualifiedName(parentName, name); return createTypeReferenceNode(enumLiteralName, /*typeArguments*/ undefined); @@ -2436,12 +2416,11 @@ namespace ts { } if (objectFlags & ObjectFlags.ClassOrInterface) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); - // TODO(aozgaa): handle type arguments. + const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & TypeFlags.TypeParameter) { - const name = symbolToName(type.symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context); + const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } @@ -2451,7 +2430,6 @@ namespace ts { const name = symbolToTypeReferenceName(type.aliasSymbol); const typeArgumentNodes = toTypeArgumentNodes(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); - // return symbolToTypeReferenceIdentifier(type.aliasSymbol, type.aliasTypeArguments); } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { @@ -2459,7 +2437,7 @@ namespace ts { const typeNodes = types && mapToTypeNodeArray(types, context, /*addInElementTypeFlag*/ true, /*addInFirstTypeArgumentFlag*/ false); if (typeNodes && typeNodes.length > 0) { const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); - return InElementType ? createParenthesizedTypeNode(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode; + return inElementType ? createParenthesizedTypeNode(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode; } else { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) { @@ -2521,7 +2499,7 @@ namespace ts { const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags - const entityName = symbolToName(typeAlias, /*expectsIdentifier*/ false, SymbolFlags.Type, context); + const entityName = symbolToName(typeAlias, context, SymbolFlags.Type, /*expectsIdentifier*/ false ); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { @@ -2598,7 +2576,7 @@ namespace ts { } function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { - if (InElementType) { + if (inElementType) { return true; } else if (inFirstTypeArgument) { @@ -2611,14 +2589,13 @@ namespace ts { } function createTypeQueryNodeFromSymbol(symbol: Symbol, symbolFlags: SymbolFlags) { - const entityName = symbolToName(symbol, /*expectsIdentifier*/ false, symbolFlags, context); + const entityName = symbolToName(symbol, context, symbolFlags, /*expectsIdentifier*/ false); return createTypeQueryNode(entityName); } function symbolToTypeReferenceName(symbol: Symbol) { // Unnamed function expressions and arrow functions have reserved names that we don't want to display - const entityName = symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.name) ? symbolToName(symbol, /*expectsIdentifier*/ false, SymbolFlags.Type, context) : createIdentifier(""); - // TODO: assert no type args? + const entityName = symbol.flags & SymbolFlags.Class || !isReservedMemberName(symbol.name) ? symbolToName(symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier(""); return entityName; } @@ -2651,7 +2628,6 @@ namespace ts { let i = 0; let qualifiedName: QualifiedName | undefined; if (outerTypeParameters) { - let inFirstTypeArgument = true; const length = outerTypeParameters.length; while (i < length) { // Find group of type arguments for type parameters with the same declaring container. @@ -2676,7 +2652,6 @@ namespace ts { qualifiedName = createQualifiedName(namePart, /*right*/ undefined); } } - inFirstTypeArgument = false; } } @@ -2745,12 +2720,11 @@ namespace ts { return typeElements; } - // TODO: make logic mirror that of writeObjectLiteralType for (const propertySymbol of properties) { const propertyType = getTypeOfSymbol(propertySymbol); const saveEnclosingDeclaration = context.enclosingDeclaration; context.enclosingDeclaration = undefined; - const propertyName = symbolToName(propertySymbol, /*expectsIdentifier*/ true, SymbolFlags.Value, context); + const propertyName = symbolToName(propertySymbol, context, SymbolFlags.Value, /*expectsIdentifier*/ true); context.enclosingDeclaration = saveEnclosingDeclaration; const optionalToken = propertySymbol.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined; if (propertySymbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && !getPropertiesOfObjectType(propertyType).length) { @@ -2856,7 +2830,7 @@ namespace ts { const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); - const name = symbolToName(type.symbol, /*expectsIdentifier*/ true, SymbolFlags.Type, context); + const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } @@ -2903,10 +2877,9 @@ namespace ts { } } - // TODO: add SymbolFormatFlags?? Yes to add outer type parameters. Defer UseOnlyExternalAliasing until a separate symbolbuilder PR. - function symbolToName(symbol: Symbol, expectsIdentifier: true, meaning: SymbolFlags, context: NodeBuilderContext): Identifier; - function symbolToName(symbol: Symbol, expectsIdentifier: false, meaning: SymbolFlags, context: NodeBuilderContext): EntityName; - function symbolToName(symbol: Symbol, expectsIdentifier: boolean, meaning: SymbolFlags, context: NodeBuilderContext): EntityName { + function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: true): Identifier; + function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: false): EntityName; + function symbolToName(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, expectsIdentifier: boolean): EntityName { // Try to get qualified name if the symbol is not a type parameter and there is an enclosing declaration. let chain: Symbol[]; From c099938f61291e777fb796af20dff13eb3de1e10 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 18:18:35 -0700 Subject: [PATCH 40/77] move state to flags --- src/compiler/checker.ts | 61 ++++++++++++++++++----------------------- src/compiler/types.ts | 8 ++++++ 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f6930b01aca..b5000a7af99 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2229,7 +2229,7 @@ namespace ts { return result; } - + function typeFormatFlagsToNodeBuilderFlags(flags: TypeFormatFlags): NodeBuilderFlags { let result = NodeBuilderFlags.None; if (flags & TypeFormatFlags.WriteArrayAsGenericType) { @@ -2270,7 +2270,7 @@ namespace ts { } function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, typeFormatFlagsToNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors, !!(flags & TypeFormatFlags.InTypeAlias)); + const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, typeFormatFlagsToNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors); Debug.assert(typeNode !== undefined, "should always get typenode?"); const newLine = NewLineKind.None; const options = { newLine, removeComments: true }; @@ -2289,9 +2289,8 @@ namespace ts { function createNodeBuilder() { return { - typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, inTypeAlias?: boolean) => { + typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags) => { const context = createNodeBuilderContext(enclosingDeclaration, flags); - context.InTypeAlias = inTypeAlias; const resultingNode = typeToTypeNodeHelper(type, context); const result = context.encounteredError ? undefined : resultingNode; return result; @@ -2312,14 +2311,10 @@ namespace ts { interface NodeBuilderContext { enclosingDeclaration: Node | undefined; - readonly flags: NodeBuilderFlags | undefined; + flags: NodeBuilderFlags | undefined; // State encounteredError: boolean; - inObjectTypeLiteral: boolean; - InElementType: boolean; // Writing an array or union element type - InFirstTypeArgument: boolean; // Writing first type argument of the instantiated type - InTypeAlias: boolean; // Writing type in type alias declaration symbolStack: Symbol[] | undefined; } @@ -2328,21 +2323,15 @@ namespace ts { enclosingDeclaration, flags, encounteredError: false, - inObjectTypeLiteral: false, - InElementType: false, - InFirstTypeArgument: false, - InTypeAlias: false, symbolStack: undefined }; } function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { - const inElementType = context.InElementType; - context.InElementType = false; - const inTypeAlias = context.InTypeAlias; - context.InTypeAlias = false; - const inFirstTypeArgument = context.InFirstTypeArgument; - context.InFirstTypeArgument = false; + const inElementType = context.flags & NodeBuilderFlags.InElementType; + const inFirstTypeArgument = context.flags & NodeBuilderFlags.InFirstTypeArgument; + const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; + context.flags &= ~(NodeBuilderFlags.StateClearingFlags); if (!type) { context.encounteredError = true; @@ -2400,7 +2389,7 @@ namespace ts { return createKeywordTypeNode(SyntaxKind.ObjectKeyword); } if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { - if (context.inObjectTypeLiteral) { + if (context.flags & NodeBuilderFlags.inObjectTypeLiteral) { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowThisInObjectLiteral)) { context.encounteredError = true; } @@ -2455,16 +2444,16 @@ namespace ts { if (type.flags & TypeFlags.Index) { const indexedType = (type).type; - context.InElementType = true; + context.flags |= NodeBuilderFlags.InElementType; const indexTypeNode = typeToTypeNodeHelper(indexedType, context); - Debug.assert(context.InElementType === false); + Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); return createTypeOperatorNode(indexTypeNode); } if (type.flags & TypeFlags.IndexedAccess) { - context.InElementType = true; + context.flags |= NodeBuilderFlags.InElementType; const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); - Debug.assert(context.InElementType === false); + Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } @@ -2567,10 +2556,10 @@ namespace ts { } } - const saveInObjectTypeLiteral = context.inObjectTypeLiteral; - context.inObjectTypeLiteral = true; + const savedFlags = context.flags; + context.flags |= NodeBuilderFlags.inObjectTypeLiteral; const members = createTypeNodesFromResolvedType(resolved); - context.inObjectTypeLiteral = saveInObjectTypeLiteral; + context.flags = savedFlags; const typeLiteralNode = createTypeLiteralNode(members); return setEmitFlags(typeLiteralNode, EmitFlags.ToStringFormatting); } @@ -2606,9 +2595,11 @@ namespace ts { const typeArgumentNode = typeToTypeNodeHelper(typeArguments[0], context); return createTypeReferenceNode("Array", [typeArgumentNode]); } - context.InElementType = true; + + context.flags |= NodeBuilderFlags.InElementType; const elementType = typeToTypeNodeHelper(typeArguments[0], context); - context.InElementType = false; + Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); + return createArrayTypeNode(elementType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { @@ -2756,19 +2747,21 @@ namespace ts { function mapToTypeNodeArray(types: Type[], context: NodeBuilderContext, addInElementTypeFlag: boolean, addInFirstTypeArgumentFlag: boolean): TypeNode[] { const result = []; - Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); + Debug.assert(!(context.flags & NodeBuilderFlags.InElementType), "should be unset at the beginning of the helper"); for (let i = 0; i < types.length; ++i) { const type = types[i]; - context.InElementType = addInElementTypeFlag; - if (i === 0) { - context.InFirstTypeArgument = addInFirstTypeArgumentFlag; + if (addInElementTypeFlag) { + context.flags |= NodeBuilderFlags.InElementType; + } + if (i === 0 && addInFirstTypeArgumentFlag) { + context.flags |= NodeBuilderFlags.InFirstTypeArgument; } const typeNode = typeToTypeNodeHelper(type, context); if (typeNode) { result.push(typeNode); } } - Debug.assert(context.InElementType === false, "should be unset at the beginning of the helper"); + Debug.assert(!(context.flags & NodeBuilderFlags.InElementType), "should be unset at the end of the helper"); return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 73d6067cb10..e23bba7b238 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2589,6 +2589,14 @@ namespace ts { ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple, + // State + inObjectTypeLiteral = 1 << 20, + InElementType = 1 << 21, // Writing an array or union element type + InFirstTypeArgument = 1 << 22, // Writing first type argument of the instantiated type + InTypeAlias = 1 << 23, // Writing type in type alias declaration + + /** Flags that should not be passed on to sub-nodes of the current node being built. */ + StateClearingFlags = InElementType | InFirstTypeArgument | InTypeAlias } export interface SymbolDisplayBuilder { From 0432257949b617e043bbc45f7bab2a1ba24eadd9 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 18:39:40 -0700 Subject: [PATCH 41/77] use EmitFlags.SingleLine --- src/compiler/checker.ts | 4 ++-- src/compiler/emitter.ts | 4 ++-- src/compiler/types.ts | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b5000a7af99..a5e1787cfd0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2561,7 +2561,7 @@ namespace ts { const members = createTypeNodesFromResolvedType(resolved); context.flags = savedFlags; const typeLiteralNode = createTypeLiteralNode(members); - return setEmitFlags(typeLiteralNode, EmitFlags.ToStringFormatting); + return setEmitFlags(typeLiteralNode, EmitFlags.SingleLine); } function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { @@ -2865,7 +2865,7 @@ namespace ts { if (clone.kind === SyntaxKind.BindingElement) { (clone).initializer = undefined; } - return setEmitFlags(clone, EmitFlags.ToStringFormatting); + return setEmitFlags(clone, EmitFlags.SingleLine); } } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 073c49df559..59e3c9df96c 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -962,7 +962,7 @@ namespace ts { function emitTypeLiteral(node: TypeLiteralNode) { write("{"); if (node.members.length > 0) { - emitList(node, node.members, getEmitFlags(node) & EmitFlags.ToStringFormatting ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers); + emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers); } write("}"); } @@ -1054,7 +1054,7 @@ namespace ts { } else { write("{"); - emitList(node, elements, getEmitFlags(node) & EmitFlags.ToStringFormatting ? ListFormat.ObjectBindingPatternElements : ListFormat.ObjectBindingPatternElementsWithSpaceBetweenBraces); + emitList(node, elements, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.ObjectBindingPatternElements : ListFormat.ObjectBindingPatternElementsWithSpaceBetweenBraces); write("}"); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e23bba7b238..9191ad844f7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3979,7 +3979,6 @@ namespace ts { NoHoisting = 1 << 20, // Do not hoist this declaration in --module system HasEndOfDeclarationMarker = 1 << 21, // Declaration has an associated NotEmittedStatement to mark the end of the declaration Iterator = 1 << 22, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. - ToStringFormatting = 1 << 23 } export interface EmitHelper { From 7d48deec340e5b9d0096fad3b5e8751d7e5b68c3 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 18:44:26 -0700 Subject: [PATCH 42/77] handle todo --- src/compiler/checker.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a5e1787cfd0..fa81541d517 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2728,7 +2728,6 @@ namespace ts { } } else { - // TODO(aozgaa): should we create a node with explicit or implict any? const propertyTypeNode = propertyType ? typeToTypeNodeHelper(propertyType, context) : createKeywordTypeNode(SyntaxKind.AnyKeyword); const modifiers = isReadonlySymbol(propertySymbol) ? [createToken(SyntaxKind.ReadonlyKeyword)] : undefined; From 3640abd0a72f4a3018a9014d97df88c7f900d342 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 11 May 2017 18:53:57 -0700 Subject: [PATCH 43/77] Remove unused flags --- src/compiler/checker.ts | 28 ++++++---------------------- src/compiler/types.ts | 14 +------------- src/compiler/visitor.ts | 1 - 3 files changed, 7 insertions(+), 36 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fa81541d517..729fbdcaa52 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2232,20 +2232,17 @@ namespace ts { function typeFormatFlagsToNodeBuilderFlags(flags: TypeFormatFlags): NodeBuilderFlags { let result = NodeBuilderFlags.None; - if (flags & TypeFormatFlags.WriteArrayAsGenericType) { - result |= NodeBuilderFlags.WriteArrayAsGenericType; - } - if (flags & TypeFormatFlags.UseTypeOfFunction) { - result |= NodeBuilderFlags.UseTypeOfFunction; + if (flags === TypeFormatFlags.None) { + return result; } if (flags & TypeFormatFlags.NoTruncation) { result |= NodeBuilderFlags.NoTruncation; } - if (flags & TypeFormatFlags.WriteArrowStyleSignature) { - result |= NodeBuilderFlags.WriteArrowStyleSignature; + if (flags & TypeFormatFlags.UseFullyQualifiedType) { + result |= NodeBuilderFlags.UseFullyQualifiedType; } - if (flags & TypeFormatFlags.WriteOwnNameForAnyLike) { - result |= NodeBuilderFlags.WriteOwnNameForAnyLike; + if (flags & TypeFormatFlags.SuppressAnyReturnType) { + result |= NodeBuilderFlags.SuppressAnyReturnType; } if (flags & TypeFormatFlags.WriteArrayAsGenericType) { result |= NodeBuilderFlags.WriteArrayAsGenericType; @@ -2253,18 +2250,6 @@ namespace ts { if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) { result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature; } - if (flags & TypeFormatFlags.UseFullyQualifiedType) { - result |= NodeBuilderFlags.UseFullyQualifiedType; - } - if (flags & TypeFormatFlags.UseTypeAliasValue) { - result |= NodeBuilderFlags.UseTypeAliasValue; - } - if (flags & TypeFormatFlags.SuppressAnyReturnType) { - result |= NodeBuilderFlags.SuppressAnyReturnType; - } - if (flags & TypeFormatFlags.AddUndefined) { - result |= NodeBuilderFlags.AddUndefined; - } return result; } @@ -2893,7 +2878,6 @@ namespace ts { function createEntityNameFromSymbolChain(chain: Symbol[], index: number): EntityName { Debug.assert(chain && 0 <= index && index < chain.length); - // const parentIndex = index - 1; const symbol = chain[index]; let typeParameterNodes: TypeNode[] | undefined; if (index > 0) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9191ad844f7..49f1e9a3382 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2497,7 +2497,7 @@ namespace ts { /** Note that the resulting nodes cannot be checked. */ - typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, inTypeAlias?: boolean): TypeNode; + typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; /** Note that the resulting nodes cannot be checked. */ signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration; /** Note that the resulting nodes cannot be checked. */ @@ -2562,22 +2562,10 @@ namespace ts { None = 0, // Options NoTruncation = 1 << 0, // Don't truncate result - // TODO: part of emit. WriteArrayAsGenericType = 1 << 1, // Write Array instead T[] - // TODO: part of emit. - UseTypeOfFunction = 1 << 2, // Write typeof instead of function type literal - // TODO: part of emit. - WriteArrowStyleSignature = 1 << 3, // Write arrow style signature - // TODO: turn it into a failing type reference? - WriteOwnNameForAnyLike = 1 << 4, // Write symbol's own name instead of 'any' for any like types (eg. unknown, __resolving__ etc) - // TODO WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type) - // TODO - UseTypeAliasValue = 1 << 7, // Serialize the type instead of using type-alias. This is needed when we emit declaration file. SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. - // TODO - AddUndefined = 1 << 9, // Add undefined to types of initialized, non-optional parameters // Error handling allowThisInObjectLiteral = 1 << 10, diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index fce43b622bb..ce7908b885e 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -359,7 +359,6 @@ namespace ts { case SyntaxKind.PropertySignature: return updatePropertySignature((node), - // TODO: tokenVisitor or visitor for a nodearray of tokens? nodesVisitor((node).modifiers, visitor, isToken), visitNode((node).name, visitor, isPropertyName), visitNode((node).questionToken, tokenVisitor, isToken), From 84f419b533cd3382573320be4b5796a1a0eef144 Mon Sep 17 00:00:00 2001 From: Yui T Date: Thu, 11 May 2017 22:40:21 -0700 Subject: [PATCH 44/77] getApparentType of the propsType --- src/compiler/checker.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ce89fb72b52..03302ec5f32 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13548,13 +13548,16 @@ namespace ts { return _jsxElementChildrenPropertyName; } - function createIntersectionOfApparentTypeOfJsxPropsType(propsType: Type): Type { - if (propsType && propsType.flags & TypeFlags.Intersection) { - const propsApprentType: Type[] = []; - for (const t of (propsType).types) { - propsApprentType.push(getApparentType(t)); + function getApparentTypeOfJsxPropsType(propsType: Type): Type { + if (propsType) { + if (propsType.flags & TypeFlags.Intersection) { + const propsApprentType: Type[] = []; + for (const t of (propsType).types) { + propsApprentType.push(getApparentType(t)); + } + return getIntersectionType(propsApprentType); } - return getIntersectionType(propsApprentType); + return getApparentType(propsType); } return propsType; } @@ -13579,7 +13582,7 @@ namespace ts { if (callSignature !== unknownSignature) { const callReturnType = callSignature && getReturnTypeOfSignature(callSignature); let paramType = callReturnType && (callSignature.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(callSignature.parameters[0])); - paramType = createIntersectionOfApparentTypeOfJsxPropsType(paramType); + paramType = getApparentTypeOfJsxPropsType(paramType); if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { // Intersect in JSX.IntrinsicAttributes if it exists const intrinsicAttributes = getJsxType(JsxNames.IntrinsicAttributes); @@ -13618,7 +13621,7 @@ namespace ts { for (const candidate of candidatesOutArray) { const callReturnType = getReturnTypeOfSignature(candidate); let paramType = callReturnType && (candidate.parameters.length === 0 ? emptyObjectType : getTypeOfSymbol(candidate.parameters[0])); - paramType = createIntersectionOfApparentTypeOfJsxPropsType(paramType); + paramType = getApparentTypeOfJsxPropsType(paramType); if (callReturnType && isTypeAssignableTo(callReturnType, jsxStatelessElementType)) { let shouldBeCandidate = true; for (const attribute of openingLikeElement.attributes.properties) { From 8d09085800f131f0a5e70ed0fd2c8cb81d5afdc5 Mon Sep 17 00:00:00 2001 From: Yui T Date: Thu, 11 May 2017 22:40:34 -0700 Subject: [PATCH 45/77] Add more tests and update baselines --- .../tsxGenericAttributesType5.errors.txt | 20 ++++++++ .../reference/tsxGenericAttributesType5.js | 51 +++++++++++++++++++ .../reference/tsxGenericAttributesType6.js | 49 ++++++++++++++++++ .../tsxGenericAttributesType6.symbols | 45 ++++++++++++++++ .../reference/tsxGenericAttributesType6.types | 47 +++++++++++++++++ .../reference/tsxGenericAttributesType7.js | 22 ++++++++ .../tsxGenericAttributesType7.symbols | 35 +++++++++++++ .../reference/tsxGenericAttributesType7.types | 39 ++++++++++++++ .../reference/tsxGenericAttributesType8.js | 22 ++++++++ .../tsxGenericAttributesType8.symbols | 34 +++++++++++++ .../reference/tsxGenericAttributesType8.types | 38 ++++++++++++++ .../tsxStatelessFunctionComponents3.types | 6 +-- .../jsx/tsxGenericAttributesType5.tsx | 19 +++++++ .../jsx/tsxGenericAttributesType6.tsx | 18 +++++++ .../jsx/tsxGenericAttributesType7.tsx | 15 ++++++ .../jsx/tsxGenericAttributesType8.tsx | 15 ++++++ 16 files changed, 472 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/tsxGenericAttributesType5.errors.txt create mode 100644 tests/baselines/reference/tsxGenericAttributesType5.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType6.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType6.symbols create mode 100644 tests/baselines/reference/tsxGenericAttributesType6.types create mode 100644 tests/baselines/reference/tsxGenericAttributesType7.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType7.symbols create mode 100644 tests/baselines/reference/tsxGenericAttributesType7.types create mode 100644 tests/baselines/reference/tsxGenericAttributesType8.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType8.symbols create mode 100644 tests/baselines/reference/tsxGenericAttributesType8.types create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType5.tsx create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType6.tsx create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType7.tsx create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType8.tsx diff --git a/tests/baselines/reference/tsxGenericAttributesType5.errors.txt b/tests/baselines/reference/tsxGenericAttributesType5.errors.txt new file mode 100644 index 00000000000..83f07326f92 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType5.errors.txt @@ -0,0 +1,20 @@ +tests/cases/conformance/jsx/file.tsx(12,36): error TS2339: Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes> & { children?: ReactNode; }'. + + +==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== + import React = require('react'); + + class B1 extends React.Component { + render() { + return
hi
; + } + } + class B extends React.Component { + props: U; + render() { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + ~~~~~~ +!!! error TS2339: Property 'x' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes> & { children?: ReactNode; }'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/tsxGenericAttributesType5.js b/tests/baselines/reference/tsxGenericAttributesType5.js new file mode 100644 index 00000000000..cb535417dd6 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType5.js @@ -0,0 +1,51 @@ +//// [file.tsx] +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + props: U; + render() { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + } +} + +//// [file.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var React = require("react"); +var B1 = (function (_super) { + __extends(B1, _super); + function B1() { + return _super !== null && _super.apply(this, arguments) || this; + } + B1.prototype.render = function () { + return
hi
; + }; + return B1; +}(React.Component)); +var B = (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + B.prototype.render = function () { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + }; + return B; +}(React.Component)); diff --git a/tests/baselines/reference/tsxGenericAttributesType6.js b/tests/baselines/reference/tsxGenericAttributesType6.js new file mode 100644 index 00000000000..84cab3e1f94 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType6.js @@ -0,0 +1,49 @@ +//// [file.tsx] +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + props: U; + render() { + return ; + } +} + +//// [file.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var React = require("react"); +var B1 = (function (_super) { + __extends(B1, _super); + function B1() { + return _super !== null && _super.apply(this, arguments) || this; + } + B1.prototype.render = function () { + return
hi
; + }; + return B1; +}(React.Component)); +var B = (function (_super) { + __extends(B, _super); + function B() { + return _super !== null && _super.apply(this, arguments) || this; + } + B.prototype.render = function () { + return ; + }; + return B; +}(React.Component)); diff --git a/tests/baselines/reference/tsxGenericAttributesType6.symbols b/tests/baselines/reference/tsxGenericAttributesType6.symbols new file mode 100644 index 00000000000..27c8ed2d6dc --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType6.symbols @@ -0,0 +1,45 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +class B1 extends React.Component { +>B1 : Symbol(B1, Decl(file.tsx, 0, 32)) +>T : Symbol(T, Decl(file.tsx, 2, 9)) +>x : Symbol(x, Decl(file.tsx, 2, 20)) +>x : Symbol(x, Decl(file.tsx, 2, 36)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>T : Symbol(T, Decl(file.tsx, 2, 9)) + + render() { +>render : Symbol(B1.render, Decl(file.tsx, 2, 82)) + + return
hi
; +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2399, 45)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(react.d.ts, 2399, 45)) + } +} +class B extends React.Component { +>B : Symbol(B, Decl(file.tsx, 6, 1)) +>U : Symbol(U, Decl(file.tsx, 7, 8)) +>React.Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>Component : Symbol(React.Component, Decl(react.d.ts, 158, 55)) +>U : Symbol(U, Decl(file.tsx, 7, 8)) + + props: U; +>props : Symbol(B.props, Decl(file.tsx, 7, 43)) +>U : Symbol(U, Decl(file.tsx, 7, 8)) + + render() { +>render : Symbol(B.render, Decl(file.tsx, 8, 13)) + + return ; +>B1 : Symbol(B1, Decl(file.tsx, 0, 32)) +>this.props : Symbol(B.props, Decl(file.tsx, 7, 43)) +>this : Symbol(B, Decl(file.tsx, 6, 1)) +>props : Symbol(B.props, Decl(file.tsx, 7, 43)) +>x : Symbol(x, Decl(file.tsx, 10, 34)) + } +} diff --git a/tests/baselines/reference/tsxGenericAttributesType6.types b/tests/baselines/reference/tsxGenericAttributesType6.types new file mode 100644 index 00000000000..662b6299dad --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType6.types @@ -0,0 +1,47 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : typeof React + +class B1 extends React.Component { +>B1 : B1 +>T : T +>x : string +>x : string +>React.Component : React.Component +>React : typeof React +>Component : typeof React.Component +>T : T + + render() { +>render : () => JSX.Element + + return
hi
; +>
hi
: JSX.Element +>div : any +>div : any + } +} +class B extends React.Component { +>B : B +>U : U +>React.Component : React.Component +>React : typeof React +>Component : typeof React.Component +>U : U + + props: U; +>props : U +>U : U + + render() { +>render : () => JSX.Element + + return ; +> : JSX.Element +>B1 : typeof B1 +>this.props : U +>this : this +>props : U +>x : string + } +} diff --git a/tests/baselines/reference/tsxGenericAttributesType7.js b/tests/baselines/reference/tsxGenericAttributesType7.js new file mode 100644 index 00000000000..4e254f25e55 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType7.js @@ -0,0 +1,22 @@ +//// [file.tsx] +import React = require('react'); + +declare function Component(props: T) : JSX.Element; +const decorator = function (props: U) { + return ; +} + +const decorator1 = function (props: U) { + return ; +} + +//// [file.jsx] +"use strict"; +exports.__esModule = true; +var React = require("react"); +var decorator = function (props) { + return ; +}; +var decorator1 = function (props) { + return ; +}; diff --git a/tests/baselines/reference/tsxGenericAttributesType7.symbols b/tests/baselines/reference/tsxGenericAttributesType7.symbols new file mode 100644 index 00000000000..e31042bd843 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType7.symbols @@ -0,0 +1,35 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +declare function Component(props: T) : JSX.Element; +>Component : Symbol(Component, Decl(file.tsx, 0, 32)) +>T : Symbol(T, Decl(file.tsx, 2, 27)) +>props : Symbol(props, Decl(file.tsx, 2, 30)) +>T : Symbol(T, Decl(file.tsx, 2, 27)) +>JSX : Symbol(JSX, Decl(react.d.ts, 2352, 1)) +>Element : Symbol(JSX.Element, Decl(react.d.ts, 2355, 27)) + +const decorator = function (props: U) { +>decorator : Symbol(decorator, Decl(file.tsx, 3, 5)) +>U : Symbol(U, Decl(file.tsx, 3, 28)) +>props : Symbol(props, Decl(file.tsx, 3, 31)) +>U : Symbol(U, Decl(file.tsx, 3, 28)) + + return ; +>Component : Symbol(Component, Decl(file.tsx, 0, 32)) +>props : Symbol(props, Decl(file.tsx, 3, 31)) +} + +const decorator1 = function (props: U) { +>decorator1 : Symbol(decorator1, Decl(file.tsx, 7, 5)) +>U : Symbol(U, Decl(file.tsx, 7, 29)) +>x : Symbol(x, Decl(file.tsx, 7, 40)) +>props : Symbol(props, Decl(file.tsx, 7, 52)) +>U : Symbol(U, Decl(file.tsx, 7, 29)) + + return ; +>Component : Symbol(Component, Decl(file.tsx, 0, 32)) +>props : Symbol(props, Decl(file.tsx, 7, 52)) +>x : Symbol(x, Decl(file.tsx, 8, 32)) +} diff --git a/tests/baselines/reference/tsxGenericAttributesType7.types b/tests/baselines/reference/tsxGenericAttributesType7.types new file mode 100644 index 00000000000..58d853e4881 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType7.types @@ -0,0 +1,39 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : typeof React + +declare function Component(props: T) : JSX.Element; +>Component : (props: T) => JSX.Element +>T : T +>props : T +>T : T +>JSX : any +>Element : JSX.Element + +const decorator = function (props: U) { +>decorator : (props: U) => JSX.Element +>function (props: U) { return ;} : (props: U) => JSX.Element +>U : U +>props : U +>U : U + + return ; +> : JSX.Element +>Component : (props: T) => JSX.Element +>props : U +} + +const decorator1 = function (props: U) { +>decorator1 : (props: U) => JSX.Element +>function (props: U) { return ;} : (props: U) => JSX.Element +>U : U +>x : string +>props : U +>U : U + + return ; +> : JSX.Element +>Component : (props: T) => JSX.Element +>props : U +>x : string +} diff --git a/tests/baselines/reference/tsxGenericAttributesType8.js b/tests/baselines/reference/tsxGenericAttributesType8.js new file mode 100644 index 00000000000..435bf425c6a --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType8.js @@ -0,0 +1,22 @@ +//// [file.tsx] +import React = require('react'); + +declare function Component(props: T) : JSX.Element; +const decorator = function (props: U) { + return ; +} + +const decorator1 = function (props: U) { + return ; +} + +//// [file.jsx] +"use strict"; +exports.__esModule = true; +var React = require("react"); +var decorator = function (props) { + return ; +}; +var decorator1 = function (props) { + return ; +}; diff --git a/tests/baselines/reference/tsxGenericAttributesType8.symbols b/tests/baselines/reference/tsxGenericAttributesType8.symbols new file mode 100644 index 00000000000..516dbe9c322 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType8.symbols @@ -0,0 +1,34 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +declare function Component(props: T) : JSX.Element; +>Component : Symbol(Component, Decl(file.tsx, 0, 32)) +>T : Symbol(T, Decl(file.tsx, 2, 27)) +>props : Symbol(props, Decl(file.tsx, 2, 30)) +>T : Symbol(T, Decl(file.tsx, 2, 27)) +>JSX : Symbol(JSX, Decl(react.d.ts, 2352, 1)) +>Element : Symbol(JSX.Element, Decl(react.d.ts, 2355, 27)) + +const decorator = function (props: U) { +>decorator : Symbol(decorator, Decl(file.tsx, 3, 5)) +>U : Symbol(U, Decl(file.tsx, 3, 28)) +>props : Symbol(props, Decl(file.tsx, 3, 31)) +>U : Symbol(U, Decl(file.tsx, 3, 28)) + + return ; +>Component : Symbol(Component, Decl(file.tsx, 0, 32)) +>props : Symbol(props, Decl(file.tsx, 3, 31)) +} + +const decorator1 = function (props: U) { +>decorator1 : Symbol(decorator1, Decl(file.tsx, 7, 5)) +>U : Symbol(U, Decl(file.tsx, 7, 29)) +>x : Symbol(x, Decl(file.tsx, 7, 40)) +>props : Symbol(props, Decl(file.tsx, 7, 52)) +>U : Symbol(U, Decl(file.tsx, 7, 29)) + + return ; +>Component : Symbol(Component, Decl(file.tsx, 0, 32)) +>props : Symbol(props, Decl(file.tsx, 7, 52)) +} diff --git a/tests/baselines/reference/tsxGenericAttributesType8.types b/tests/baselines/reference/tsxGenericAttributesType8.types new file mode 100644 index 00000000000..b2a62a575ff --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType8.types @@ -0,0 +1,38 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : typeof React + +declare function Component(props: T) : JSX.Element; +>Component : (props: T) => JSX.Element +>T : T +>props : T +>T : T +>JSX : any +>Element : JSX.Element + +const decorator = function (props: U) { +>decorator : (props: U) => JSX.Element +>function (props: U) { return ;} : (props: U) => JSX.Element +>U : U +>props : U +>U : U + + return ; +> : JSX.Element +>Component : (props: T) => JSX.Element +>props : U +} + +const decorator1 = function (props: U) { +>decorator1 : (props: U) => JSX.Element +>function (props: U) { return ;} : (props: U) => JSX.Element +>U : U +>x : string +>props : U +>U : U + + return ; +> : JSX.Element +>Component : (props: T) => JSX.Element +>props : U +} diff --git a/tests/baselines/reference/tsxStatelessFunctionComponents3.types b/tests/baselines/reference/tsxStatelessFunctionComponents3.types index 714781058eb..879f8c75cdf 100644 --- a/tests/baselines/reference/tsxStatelessFunctionComponents3.types +++ b/tests/baselines/reference/tsxStatelessFunctionComponents3.types @@ -21,8 +21,8 @@ var MainMenu: React.StatelessComponent<{}> = (props) => (
>MainMenu : React.StatelessComponent<{}> >React : any >StatelessComponent : React.StatelessComponent

->(props) => (

Main Menu

) : (props: {}) => JSX.Element ->props : {} +>(props) => (

Main Menu

) : (props: { children?: React.ReactNode; }) => JSX.Element +>props : { children?: React.ReactNode; } >(

Main Menu

) : JSX.Element >

Main Menu

: JSX.Element >div : any @@ -40,7 +40,7 @@ var App: React.StatelessComponent<{ children }> = ({children}) => ( >React : any >StatelessComponent : React.StatelessComponent

>children : any ->({children}) => (

) : ({children}: { children: any; }) => JSX.Element +>({children}) => (
) : ({children}: { children: any; } & { children?: React.ReactNode; }) => JSX.Element >children : any >(
) : JSX.Element diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType5.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType5.tsx new file mode 100644 index 00000000000..d0215da7397 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType5.tsx @@ -0,0 +1,19 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + props: U; + render() { + // Should be an ok but as of 2.3.3 this will be an error as we will instantiate B1.props to be empty object + return ; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType6.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType6.tsx new file mode 100644 index 00000000000..d70df8a0cf7 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType6.tsx @@ -0,0 +1,18 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +class B1 extends React.Component { + render() { + return
hi
; + } +} +class B extends React.Component { + props: U; + render() { + return ; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType7.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType7.tsx new file mode 100644 index 00000000000..3044fda23df --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType7.tsx @@ -0,0 +1,15 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +declare function Component(props: T) : JSX.Element; +const decorator = function (props: U) { + return ; +} + +const decorator1 = function (props: U) { + return ; +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType8.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType8.tsx new file mode 100644 index 00000000000..b1d3a7445c8 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType8.tsx @@ -0,0 +1,15 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +declare function Component(props: T) : JSX.Element; +const decorator = function (props: U) { + return ; +} + +const decorator1 = function (props: U) { + return ; +} \ No newline at end of file From f45f7579fc33617b9595a441823dce22e2c0c32a Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 11 May 2017 23:51:20 -0700 Subject: [PATCH 46/77] Adds CommaList to avoid large deeply nested comma expressions --- src/compiler/emitter.ts | 8 ++++++++ src/compiler/factory.ts | 16 +++++++++++++++- src/compiler/parser.ts | 2 ++ src/compiler/types.ts | 6 ++++++ src/compiler/utilities.ts | 4 ++++ src/compiler/visitor.ts | 8 ++++++++ 6 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 669c9f5dd36..c0d8cae861f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -733,6 +733,9 @@ namespace ts { // Transformation nodes case SyntaxKind.PartiallyEmittedExpression: return emitPartiallyEmittedExpression(node); + + case SyntaxKind.CommaList: + return emitCommaList(node); } } @@ -2101,6 +2104,10 @@ namespace ts { emitExpression(node.expression); } + function emitCommaList(node: CommaList) { + emitExpressionList(node, node.elements, ListFormat.CommaListElements); + } + /** * Emits any prologue directives at the start of a Statement list, returning the * number of prologue directives written to the output. @@ -2951,6 +2958,7 @@ namespace ts { ArrayBindingPatternElements = SingleLine | AllowTrailingComma | CommaDelimited | SpaceBetweenSiblings, ObjectLiteralExpressionProperties = PreserveLines | CommaDelimited | SpaceBetweenSiblings | SpaceBetweenBraces | Indented | Braces, ArrayLiteralExpressionElements = PreserveLines | CommaDelimited | SpaceBetweenSiblings | AllowTrailingComma | Indented | SquareBrackets, + CommaListElements = CommaDelimited | SpaceBetweenSiblings | SingleLine, CallExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis, NewExpressionArguments = CommaDelimited | SpaceBetweenSiblings | SingleLine | Parenthesis | OptionalIfUndefined, TemplateExpressionSpans = SingleLine | NoInterveningComments, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 2b781b6dbb3..7f84ffd534f 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2077,6 +2077,18 @@ namespace ts { return node; } + export function createCommaList(elements: Expression[]) { + const node = createSynthesizedNode(SyntaxKind.CommaList); + node.elements = createNodeArray(elements); + return node; + } + + export function updateCommaList(node: CommaList, elements: Expression[]) { + return node.elements !== elements + ? updateNode(createCommaList(elements), node) + : node; + } + export function createBundle(sourceFiles: SourceFile[]) { const node = createNode(SyntaxKind.Bundle); node.sourceFiles = sourceFiles; @@ -2865,7 +2877,9 @@ namespace ts { } export function inlineExpressions(expressions: Expression[]) { - return reduceLeft(expressions, createComma); + return expressions.length > 10 + ? createCommaList(expressions) + : reduceLeft(expressions, createComma); } export function createExpressionFromEntityName(node: EntityName | Expression): Expression { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index cc3fb24bdb8..1e8f7a1c904 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -362,6 +362,8 @@ namespace ts { return visitNode(cbNode, (node).expression); case SyntaxKind.MissingDeclaration: return visitNodes(cbNodes, node.decorators); + case SyntaxKind.CommaList: + return visitNodes(cbNodes, (node).elements); case SyntaxKind.JsxElement: return visitNode(cbNode, (node).openingElement) || diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a36378ade1d..5ad89bc469e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -391,6 +391,7 @@ namespace ts { PartiallyEmittedExpression, MergeDeclarationMarker, EndOfDeclarationMarker, + CommaList, // Enum value count Count, @@ -1603,6 +1604,11 @@ namespace ts { kind: SyntaxKind.EndOfDeclarationMarker; } + export interface CommaList extends Expression { + kind: SyntaxKind.CommaList; + elements: NodeArray; + } + /** * Marks the beginning of a merged transformed declaration. */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 95b76286c59..ae54f9d3e76 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2327,6 +2327,9 @@ namespace ts { case SyntaxKind.SpreadElement: return 1; + case SyntaxKind.CommaList: + return 0; + default: return -1; } @@ -3915,6 +3918,7 @@ namespace ts { || kind === SyntaxKind.SpreadElement || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression + || kind === SyntaxKind.CommaList || isUnaryExpressionKind(kind); } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index e434dadeaf7..7788c313139 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -876,6 +876,10 @@ namespace ts { return updatePartiallyEmittedExpression(node, visitNode((node).expression, visitor, isExpression)); + case SyntaxKind.CommaList: + return updateCommaList(node, + nodesVisitor((node).elements, visitor, isExpression)); + default: // No need to visit nodes with no children. return node; @@ -1389,6 +1393,10 @@ namespace ts { result = reduceNode((node).expression, cbNode, result); break; + case SyntaxKind.CommaList: + result = reduceNodes((node).elements, cbNodes, result); + break; + default: break; } From 22cf036ed91e638220520bb5ff5b167ee723180c Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Fri, 12 May 2017 09:57:39 -0700 Subject: [PATCH 47/77] Clean up naming, add documentation, flatten (some) nested commas --- src/compiler/core.ts | 29 +++++++++++++++++++++++++++++ src/compiler/emitter.ts | 6 +++--- src/compiler/factory.ts | 22 ++++++++++++++++++---- src/compiler/parser.ts | 4 ++-- src/compiler/types.ts | 9 ++++++--- src/compiler/utilities.ts | 4 ++-- src/compiler/visitor.ts | 10 +++++----- 7 files changed, 65 insertions(+), 19 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 321cc06f765..5e4afce98c8 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -490,6 +490,35 @@ namespace ts { return result; } + /** + * Maps an array. If the mapped value is an array, it is spread into the result. + * Avoids allocation if all elements map to themselves. + * + * @param array The array to map. + * @param mapfn The callback used to map the result into one or more values. + */ + export function sameFlatMap(array: T[], mapfn: (x: T, i: number) => T | T[]): T[] { + let result: T[]; + if (array) { + for (let i = 0; i < array.length; i++) { + const item = array[i]; + const mapped = mapfn(item, i); + if (result || item !== mapped || isArray(mapped)) { + if (!result) { + result = array.slice(0, i); + } + if (isArray(mapped)) { + addRange(result, mapped); + } + else { + result.push(mapped); + } + } + } + } + return result || array; + } + /** * Computes the first matching span of elements and returns a tuple of the first span * and the remaining elements. diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index c0d8cae861f..76423e45bd9 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -734,8 +734,8 @@ namespace ts { case SyntaxKind.PartiallyEmittedExpression: return emitPartiallyEmittedExpression(node); - case SyntaxKind.CommaList: - return emitCommaList(node); + case SyntaxKind.CommaListExpression: + return emitCommaList(node); } } @@ -2104,7 +2104,7 @@ namespace ts { emitExpression(node.expression); } - function emitCommaList(node: CommaList) { + function emitCommaList(node: CommaListExpression) { emitExpressionList(node, node.elements, ListFormat.CommaListElements); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 7f84ffd534f..42f6a1267bc 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2077,13 +2077,25 @@ namespace ts { return node; } - export function createCommaList(elements: Expression[]) { - const node = createSynthesizedNode(SyntaxKind.CommaList); - node.elements = createNodeArray(elements); + function flattenCommaElements(node: Expression): Expression | Expression[] { + if (nodeIsSynthesized(node) && !isParseTreeNode(node) && !node.original && !node.emitNode && !node.id) { + if (node.kind === SyntaxKind.CommaListExpression) { + return (node).elements; + } + if (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.CommaToken) { + return [node.left, node.right]; + } + } return node; } - export function updateCommaList(node: CommaList, elements: Expression[]) { + export function createCommaList(elements: Expression[]) { + const node = createSynthesizedNode(SyntaxKind.CommaListExpression); + node.elements = createNodeArray(sameFlatMap(elements, flattenCommaElements)); + return node; + } + + export function updateCommaList(node: CommaListExpression, elements: Expression[]) { return node.elements !== elements ? updateNode(createCommaList(elements), node) : node; @@ -2877,6 +2889,8 @@ namespace ts { } export function inlineExpressions(expressions: Expression[]) { + // Avoid deeply nested comma expressions as traversing them during emit can result in "Maximum call + // stack size exceeded" errors. return expressions.length > 10 ? createCommaList(expressions) : reduceLeft(expressions, createComma); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1e8f7a1c904..dd189784a17 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -362,8 +362,8 @@ namespace ts { return visitNode(cbNode, (node).expression); case SyntaxKind.MissingDeclaration: return visitNodes(cbNodes, node.decorators); - case SyntaxKind.CommaList: - return visitNodes(cbNodes, (node).elements); + case SyntaxKind.CommaListExpression: + return visitNodes(cbNodes, (node).elements); case SyntaxKind.JsxElement: return visitNode(cbNode, (node).openingElement) || diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5ad89bc469e..f38ec9abd9c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -389,9 +389,9 @@ namespace ts { // Transformation nodes NotEmittedStatement, PartiallyEmittedExpression, + CommaListExpression, MergeDeclarationMarker, EndOfDeclarationMarker, - CommaList, // Enum value count Count, @@ -1604,8 +1604,11 @@ namespace ts { kind: SyntaxKind.EndOfDeclarationMarker; } - export interface CommaList extends Expression { - kind: SyntaxKind.CommaList; + /** + * A list of comma-seperated expressions. This node is only created by transformations. + */ + export interface CommaListExpression extends Expression { + kind: SyntaxKind.CommaListExpression; elements: NodeArray; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ae54f9d3e76..1a981a0844f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2327,7 +2327,7 @@ namespace ts { case SyntaxKind.SpreadElement: return 1; - case SyntaxKind.CommaList: + case SyntaxKind.CommaListExpression: return 0; default: @@ -3918,7 +3918,7 @@ namespace ts { || kind === SyntaxKind.SpreadElement || kind === SyntaxKind.AsExpression || kind === SyntaxKind.OmittedExpression - || kind === SyntaxKind.CommaList + || kind === SyntaxKind.CommaListExpression || isUnaryExpressionKind(kind); } diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index 7788c313139..517854e21ce 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -876,9 +876,9 @@ namespace ts { return updatePartiallyEmittedExpression(node, visitNode((node).expression, visitor, isExpression)); - case SyntaxKind.CommaList: - return updateCommaList(node, - nodesVisitor((node).elements, visitor, isExpression)); + case SyntaxKind.CommaListExpression: + return updateCommaList(node, + nodesVisitor((node).elements, visitor, isExpression)); default: // No need to visit nodes with no children. @@ -1393,8 +1393,8 @@ namespace ts { result = reduceNode((node).expression, cbNode, result); break; - case SyntaxKind.CommaList: - result = reduceNodes((node).elements, cbNodes, result); + case SyntaxKind.CommaListExpression: + result = reduceNodes((node).elements, cbNodes, result); break; default: From aaf6b83cb573c33c838f85a05d5029135a620b7c Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 12 May 2017 07:32:00 -0700 Subject: [PATCH 48/77] Don't goto aliased symbol with no declarations; and update tests --- src/services/goToDefinition.ts | 5 ++++- tests/cases/fourslash/ambientShorthandGotoDefinition.ts | 4 ++-- tests/cases/fourslash/goToDefinition_untypedModule.ts | 6 +++--- tests/cases/fourslash/quickInfoMeaning.ts | 8 ++++---- .../fourslash/tsxGoToDefinitionClassInDifferentFile.ts | 4 ++-- tests/cases/fourslash/untypedModuleImport.ts | 2 +- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 946924bcf87..d0901de9a47 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -51,7 +51,10 @@ namespace ts.GoToDefinition { // import {A, B} from "mod"; // to jump to the implementation directly. if (symbol.flags & SymbolFlags.Alias && shouldSkipAlias(node, symbol.declarations[0])) { - symbol = typeChecker.getAliasedSymbol(symbol); + const aliased = typeChecker.getAliasedSymbol(symbol); + if (aliased.declarations) { + symbol = aliased; + } } // Because name in short-hand property assignment has two different meanings: property name and property value, diff --git a/tests/cases/fourslash/ambientShorthandGotoDefinition.ts b/tests/cases/fourslash/ambientShorthandGotoDefinition.ts index 410fc932cec..74e348beb74 100644 --- a/tests/cases/fourslash/ambientShorthandGotoDefinition.ts +++ b/tests/cases/fourslash/ambientShorthandGotoDefinition.ts @@ -12,7 +12,7 @@ verify.quickInfoAt("useFoo", "import foo"); verify.goToDefinition({ - useFoo: "importFoo", + useFoo: "module", importFoo: "module" }); @@ -27,6 +27,6 @@ verify.goToDefinition({ verify.quickInfoAt("useBang", "import bang = require(\"jquery\")"); verify.goToDefinition({ - useBang: "importBang", + useBang: "module", importBang: "module" }); diff --git a/tests/cases/fourslash/goToDefinition_untypedModule.ts b/tests/cases/fourslash/goToDefinition_untypedModule.ts index b4571438434..fe29f7104cf 100644 --- a/tests/cases/fourslash/goToDefinition_untypedModule.ts +++ b/tests/cases/fourslash/goToDefinition_untypedModule.ts @@ -4,7 +4,7 @@ ////not read // @Filename: /a.ts -////import { f } from "foo"; -/////**/f(); +////import { /*def*/f } from "foo"; +/////*use*/f(); -verify.goToDefinition("", []); +verify.goToDefinition("use", "def"); diff --git a/tests/cases/fourslash/quickInfoMeaning.ts b/tests/cases/fourslash/quickInfoMeaning.ts index a3b9f988e1d..2c7aa5a0ccd 100644 --- a/tests/cases/fourslash/quickInfoMeaning.ts +++ b/tests/cases/fourslash/quickInfoMeaning.ts @@ -8,13 +8,13 @@ // @Filename: foo.d.ts ////declare const /*foo_value_declaration*/foo: number; ////declare module "foo_module" { -//// interface I { x: number; y: number } +//// interface /*foo_type_declaration*/I { x: number; y: number } //// export = I; ////} // @Filename: foo_user.ts /////// -////import /*foo_type_declaration*/foo = require("foo_module"); +////import foo = require("foo_module"); ////const x = foo/*foo_value*/; ////const i: foo/*foo_type*/ = { x: 1, y: 2 }; @@ -39,13 +39,13 @@ verify.goToDefinitionIs("foo_type_declaration"); // @Filename: bar.d.ts ////declare interface /*bar_type_declaration*/bar { x: number; y: number } ////declare module "bar_module" { -//// const x: number; +//// const /*bar_value_declaration*/x: number; //// export = x; ////} // @Filename: bar_user.ts /////// -////import /*bar_value_declaration*/bar = require("bar_module"); +////import bar = require("bar_module"); ////const x = bar/*bar_value*/; ////const i: bar/*bar_type*/ = { x: 1, y: 2 }; diff --git a/tests/cases/fourslash/tsxGoToDefinitionClassInDifferentFile.ts b/tests/cases/fourslash/tsxGoToDefinitionClassInDifferentFile.ts index f217e4096fc..7aa1c11fdc3 100644 --- a/tests/cases/fourslash/tsxGoToDefinitionClassInDifferentFile.ts +++ b/tests/cases/fourslash/tsxGoToDefinitionClassInDifferentFile.ts @@ -3,10 +3,10 @@ // @jsx: preserve // @Filename: C.tsx -////export default class C {} +////export default class /*def*/C {} // @Filename: a.tsx -////import /*def*/C from "./C"; +////import C from "./C"; ////const foo = ; verify.noErrors(); diff --git a/tests/cases/fourslash/untypedModuleImport.ts b/tests/cases/fourslash/untypedModuleImport.ts index 4dac6ac76bb..5501cec4792 100644 --- a/tests/cases/fourslash/untypedModuleImport.ts +++ b/tests/cases/fourslash/untypedModuleImport.ts @@ -17,6 +17,6 @@ const [r0, r1, r2] = test.ranges(); verify.singleReferenceGroup('"foo"', [r1]); goTo.marker("foo"); -verify.goToDefinitionIs([]); +verify.goToDefinitionIs("foo"); verify.quickInfoIs("import foo"); verify.singleReferenceGroup("import foo", [r0, r2]); From 8907c70a86843a3bc0f17c0cd815da017cafb950 Mon Sep 17 00:00:00 2001 From: Yui T Date: Fri, 12 May 2017 11:54:23 -0700 Subject: [PATCH 49/77] Address PR --- 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 03302ec5f32..14e789f17ae 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13549,17 +13549,17 @@ namespace ts { } function getApparentTypeOfJsxPropsType(propsType: Type): Type { - if (propsType) { - if (propsType.flags & TypeFlags.Intersection) { - const propsApprentType: Type[] = []; - for (const t of (propsType).types) { - propsApprentType.push(getApparentType(t)); - } - return getIntersectionType(propsApprentType); - } - return getApparentType(propsType); + if (!propsType) { + return undefined; } - return propsType; + if (propsType.flags & TypeFlags.Intersection) { + const propsApparentType: Type[] = []; + for (const t of (propsType).types) { + propsApparentType.push(getApparentType(t)); + } + return getIntersectionType(propsApparentType); + } + return getApparentType(propsType); } /** From 99ea9c730f415ed150693f42acbd5f769b3ee012 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 11 May 2017 13:59:06 -0700 Subject: [PATCH 50/77] Add the members of interfaces that need to be implemented to class element completion --- src/services/completions.ts | 45 +- .../completionEntryForClassMembers.ts | 45 +- .../completionEntryForClassMembers2.ts | 456 ++++++++++++++++++ 3 files changed, 533 insertions(+), 13 deletions(-) create mode 100644 tests/cases/fourslash/completionEntryForClassMembers2.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 6be420c0919..1d7d27f92f4 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -937,7 +937,8 @@ namespace ts.Completions { hasFilteredClassMemberKeywords = true; const baseTypeNode = getClassExtendsHeritageClauseElement(classLikeDeclaration); - if (baseTypeNode) { + const implementsTypeNodes = getClassImplementsHeritageClauseElements(classLikeDeclaration); + if (baseTypeNode || implementsTypeNodes) { const classElement = contextToken.parent; let classElementModifierFlags = isClassElement(classElement) && getModifierFlags(classElement); // If this is context token is not something we are editing now, consider if this would lead to be modifier @@ -954,13 +955,27 @@ namespace ts.Completions { // No member list for private methods if (!(classElementModifierFlags & ModifierFlags.Private)) { - const baseType = typeChecker.getTypeAtLocation(baseTypeNode); - const typeToGetPropertiesFrom = (classElementModifierFlags & ModifierFlags.Static) ? - typeChecker.getTypeOfSymbolAtLocation(baseType.symbol, classLikeDeclaration) : - baseType; + let baseClassTypeToGetPropertiesFrom: Type; + if (baseTypeNode) { + baseClassTypeToGetPropertiesFrom = typeChecker.getTypeAtLocation(baseTypeNode); + if (classElementModifierFlags & ModifierFlags.Static) { + // Use static class to get property symbols from + baseClassTypeToGetPropertiesFrom = typeChecker.getTypeOfSymbolAtLocation( + baseClassTypeToGetPropertiesFrom.symbol, classLikeDeclaration); + } + } + const implementedInterfaceTypePropertySymbols = (classElementModifierFlags & ModifierFlags.Static) ? + undefined : + flatMap(implementsTypeNodes, typeNode => typeChecker.getPropertiesOfType(typeChecker.getTypeAtLocation(typeNode))); // List of property symbols of base type that are not private and already implemented - symbols = filterClassMembersList(typeChecker.getPropertiesOfType(typeToGetPropertiesFrom), classLikeDeclaration.members, classElementModifierFlags); + symbols = filterClassMembersList( + baseClassTypeToGetPropertiesFrom ? + typeChecker.getPropertiesOfType(baseClassTypeToGetPropertiesFrom) : + undefined, + implementedInterfaceTypePropertySymbols, + classLikeDeclaration.members, + classElementModifierFlags); } } } @@ -1334,7 +1349,7 @@ namespace ts.Completions { * * @returns Symbols to be suggested in an class element depending on existing memebers and symbol flags */ - function filterClassMembersList(baseSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] { + function filterClassMembersList(baseSymbols: Symbol[], implementingTypeSymbols: Symbol[], existingMembers: ClassElement[], currentClassElementModifierFlags: ModifierFlags): Symbol[] { const existingMemberNames = createMap(); for (const m of existingMembers) { // Ignore omitted expressions for missing members @@ -1358,7 +1373,7 @@ namespace ts.Completions { // do not filter it out if the static presence doesnt match const mIsStatic = hasModifier(m, ModifierFlags.Static); const currentElementIsStatic = !!(currentClassElementModifierFlags & ModifierFlags.Static); - if ((mIsStatic && currentElementIsStatic) || + if ((mIsStatic && !currentElementIsStatic) || (!mIsStatic && currentElementIsStatic)) { continue; } @@ -1369,10 +1384,16 @@ namespace ts.Completions { } } - return filter(baseSymbols, baseProperty => - !existingMemberNames.get(baseProperty.name) && - baseProperty.getDeclarations() && - !(getDeclarationModifierFlagsFromSymbol(baseProperty) & ModifierFlags.Private)); + return concatenate( + filter(baseSymbols, baseProperty => isValidProperty(baseProperty, ModifierFlags.Private)), + filter(implementingTypeSymbols, implementingProperty => isValidProperty(implementingProperty, ModifierFlags.NonPublicAccessibilityModifier)) + ); + + function isValidProperty(propertySymbol: Symbol, inValidModifierFlags: ModifierFlags) { + return !existingMemberNames.get(propertySymbol.name) && + propertySymbol.getDeclarations() && + !(getDeclarationModifierFlagsFromSymbol(propertySymbol) & inValidModifierFlags); + } } /** diff --git a/tests/cases/fourslash/completionEntryForClassMembers.ts b/tests/cases/fourslash/completionEntryForClassMembers.ts index b242ca7ed78..527611b85bf 100644 --- a/tests/cases/fourslash/completionEntryForClassMembers.ts +++ b/tests/cases/fourslash/completionEntryForClassMembers.ts @@ -30,6 +30,18 @@ //// } //// /*classThatHasDifferentMethodThanBaseAfterProtectedMethod*/ ////} +////class D3 extends D1 { +//// /*classThatExtendsClassExtendingAnotherClass*/ +////} +////class D4 extends D1 { +//// static /*classThatExtendsClassExtendingAnotherClassAndTypesStatic*/ +////} +////class D5 extends D2 { +//// /*classThatExtendsClassExtendingAnotherClassWithOverridingMember*/ +////} +////class D6 extends D2 { +//// static /*classThatExtendsClassExtendingAnotherClassWithOverridingMemberAndTypesStatic*/ +////} ////class E { //// /*classThatDoesNotExtendAnotherClass*/ ////} @@ -127,6 +139,12 @@ const allMembersOfBase: CompletionInfo[] = [ ["privateMethod", "(method) B.privateMethod(): void"], ["staticMethod", "(method) B.staticMethod(): void"] ]; +const publicCompletionInfoOfD1: CompletionInfo[] = [ + ["getValue1", "(method) D1.getValue1(): number"] +]; +const publicCompletionInfoOfD2: CompletionInfo[] = [ + ["protectedMethod", "(method) D2.protectedMethod(): void"] +]; function filterCompletionInfo(fn: (a: CompletionInfo) => boolean): CompletionInfoVerifier { const validMembers: CompletionInfo[] = []; const invalidMembers: CompletionInfo[] = []; @@ -147,6 +165,19 @@ const staticMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "st const instanceWithoutProtectedMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "getValue"); const instanceWithoutPublicMemberInfo = filterCompletionInfo(([a]: CompletionInfo) => a === "protectedMethod"); +const instanceMemberInfoD1: CompletionInfoVerifier = { + validMembers: instanceMemberInfo.validMembers.concat(publicCompletionInfoOfD1), + invalidMembers: instanceMemberInfo.invalidMembers +}; +const instanceMemberInfoD2: CompletionInfoVerifier = { + validMembers: instanceWithoutProtectedMemberInfo.validMembers.concat(publicCompletionInfoOfD2), + invalidMembers: instanceWithoutProtectedMemberInfo.invalidMembers +}; +const staticMemberInfoDn: CompletionInfoVerifier = { + validMembers: staticMemberInfo.validMembers, + invalidMembers: staticMemberInfo.invalidMembers.concat(publicCompletionInfoOfD1, publicCompletionInfoOfD2) +}; + // Not a class element declaration location const nonClassElementMarkers = [ "InsideMethod" @@ -210,4 +241,16 @@ const classInstanceElementWithoutProtectedMethodLocations = [ "classThatHasAlreadyImplementedAnotherClassProtectedMethod", "classThatHasDifferentMethodThanBaseAfterProtectedMethod", ]; -verifyClassElementLocations(instanceWithoutProtectedMemberInfo, classInstanceElementWithoutProtectedMethodLocations); \ No newline at end of file +verifyClassElementLocations(instanceWithoutProtectedMemberInfo, classInstanceElementWithoutProtectedMethodLocations); + +// instance memebers in D1 and base class are shown +verifyClassElementLocations(instanceMemberInfoD1, ["classThatExtendsClassExtendingAnotherClass"]); + +// instance memebers in D2 and base class are shown +verifyClassElementLocations(instanceMemberInfoD2, ["classThatExtendsClassExtendingAnotherClassWithOverridingMember"]); + +// static base members and class member keywords allowed +verifyClassElementLocations(staticMemberInfoDn, [ + "classThatExtendsClassExtendingAnotherClassAndTypesStatic", + "classThatExtendsClassExtendingAnotherClassWithOverridingMemberAndTypesStatic" +]); \ No newline at end of file diff --git a/tests/cases/fourslash/completionEntryForClassMembers2.ts b/tests/cases/fourslash/completionEntryForClassMembers2.ts new file mode 100644 index 00000000000..539702196a3 --- /dev/null +++ b/tests/cases/fourslash/completionEntryForClassMembers2.ts @@ -0,0 +1,456 @@ +/// + +////interface I { +//// methodOfInterface(): number; +////} +////interface I2 { +//// methodOfInterface2(): number; +////} +////interface I3 { +//// getValue(): string; +//// method(): string; +////} +////interface I4 { +//// staticMethod(): void; +//// method(): string; +////} +////class B0 { +//// private privateMethod() { } +//// protected protectedMethod() { } +//// static staticMethod() { } +//// getValue(): string | boolean { return "hello"; } +//// private privateMethod1() { } +//// protected protectedMethod1() { } +//// static staticMethod1() { } +//// getValue1(): string | boolean { return "hello"; } +////} +////interface I5 extends B0 { +//// methodOfInterface5(): number; +////} +////interface I6 extends B0 { +//// methodOfInterface6(): number; +//// staticMethod(): void; +////} +////interface I7 extends I { +//// methodOfInterface7(): number; +////} +////class B { +//// private privateMethod() { } +//// protected protectedMethod() { } +//// static staticMethod() { } +//// getValue(): string | boolean { return "hello"; } +////} +////class C0 implements I, I2 { +//// /*implementsIAndI2*/ +////} +////class C00 implements I, I2 { +//// static /*implementsIAndI2AndWritingStatic*/ +////} +////class C001 implements I, I2 { +//// methodOfInterface/*implementsIAndI2AndWritingMethodNameOfI*/ +////} +////class C extends B implements I, I2 { +//// /*extendsBAndImplementsIAndI2*/ +////} +////class C1 extends B implements I, I2 { +//// static /*extendsBAndImplementsIAndI2AndWritingStatic*/ +////} +////class D extends B implements I, I2 { +//// /*extendsBAndImplementsIAndI2WithMethodFromB*/ +//// protected protectedMethod() { +//// return "protected"; +//// } +////} +////class E extends B implements I, I2 { +//// /*extendsBAndImplementsIAndI2WithMethodFromI*/ +//// methodOfInterface() { +//// return 1; +//// } +////} +////class F extends B implements I, I2 { +//// /*extendsBAndImplementsIAndI2WithMethodFromBAndI*/ +//// protected protectedMethod() { +//// return "protected" +//// } +//// methodOfInterface() { +//// return 1; +//// } +////} +////class F2 extends B implements I, I2 { +//// protected protectedMethod() { +//// return "protected" +//// } +//// methodOfInterface() { +//// return 1; +//// } +//// static /*extendsBAndImplementsIAndI2WithMethodFromBAndIAndTypesStatic*/ +////} +////class G extends B implements I3 { +//// /*extendsBAndImplementsI3WithSameNameMembers*/ +////} +////class H extends B implements I3 { +//// /*extendsBAndImplementsI3WithSameNameMembersAndHasImplementedTheMember*/ +//// getValue() { +//// return "hello"; +//// } +////} +////class J extends B0 implements I4 { +//// /*extendsB0ThatExtendsAndImplementsI4WithStaticMethod*/ +////} +////class L extends B0 implements I4 { +//// /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedAnotherMethod*/ +//// staticMethod2() { +//// return "hello"; +//// } +////} +////class K extends B0 implements I4 { +//// /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethod*/ +//// staticMethod() { +//// return "hello"; +//// } +////} +////class M extends B0 implements I4 { +//// /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsStatic*/ +//// static staticMethod() { +//// return "hello"; +//// } +////} +////class N extends B0 implements I4 { +//// /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsBoth*/ +//// staticMethod() { +//// return "hello"; +//// } +//// static staticMethod() { +//// return "hello"; +//// } +////} +////class J1 extends B0 implements I4 { +//// static /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodWritingStatic*/ +////} +////class L1 extends B0 implements I4 { +//// staticMethod2() { +//// return "hello"; +//// } +//// static /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedAnotherMethodWritingStatic*/ +////} +////class K1 extends B0 implements I4 { +//// staticMethod() { +//// return "hello"; +//// } +//// static /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodWritingStatic*/ +////} +////class M1 extends B0 implements I4 { +//// static staticMethod() { +//// return "hello"; +//// } +//// static /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsStaticWritingStatic*/ +////} +////class N1 extends B0 implements I4 { +//// staticMethod() { +//// return "hello"; +//// } +//// static staticMethod() { +//// return "hello"; +//// } +//// static /*extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsBothWritingStatic*/ +////} +////class O implements I7 { +//// /*implementsI7whichExtendsI*/ +////} +////class P implements I7, I { +//// /*implementsI7whichExtendsIAndAlsoImplementsI*/ +////} +////class Q implements I, I7 { +//// /*implementsIAndAlsoImplementsI7whichExtendsI*/ +////} +////class R implements I5 { +//// /*implementsI5ThatExtendsB0*/ +////} +////class S implements I6 { +//// /*implementsI6ThatExtendsB0AndHasStaticMethodOfB0*/ +////} +////class T extends B0 implements I5 { +//// /*extendsB0AndImplementsI5ThatExtendsB0*/ +////} +////class U extends B0 implements I6 { +//// /*extendsB0AndImplementsI6ThatExtendsB0AndHasStaticMethodOfB0*/ +////} +////class R1 implements I5 { +//// static /*implementsI5ThatExtendsB0TypesStatic*/ +////} +////class S1 implements I6 { +//// static /*implementsI6ThatExtendsB0AndHasStaticMethodOfB0TypesStatic*/ +////} +////class T1 extends B0 implements I5 { +//// static /*extendsB0AndImplementsI5ThatExtendsB0TypesStatic*/ +////} +////class U1 extends B0 implements I6 { +//// static /*extendsB0AndImplementsI6ThatExtendsB0AndHasStaticMethodOfB0TypesStatic*/ +////} + +const allowedKeywordCount = verify.allowedClassElementKeywords.length; +type CompletionInfo = [string, string]; +type CompletionInfoVerifier = { validMembers: CompletionInfo[], invalidMembers: CompletionInfo[] }; + +function verifyClassElementLocations({ validMembers, invalidMembers }: CompletionInfoVerifier, classElementCompletionLocations: string[]) { + for (const marker of classElementCompletionLocations) { + goTo.marker(marker); + verifyCompletionInfo(validMembers, verify); + verifyCompletionInfo(invalidMembers, verify.not); + verify.completionListContainsClassElementKeywords(); + verify.completionListCount(allowedKeywordCount + validMembers.length); + } +} + +function verifyCompletionInfo(memberInfo: CompletionInfo[], verify: FourSlashInterface.verifyNegatable) { + for (const [symbol, text] of memberInfo) { + verify.completionListContains(symbol, text, /*documentation*/ undefined, "method"); + } +} + +const validInstanceMembersOfBaseClassB: CompletionInfo[] = [ + ["getValue", "(method) B.getValue(): string | boolean"], + ["protectedMethod", "(method) B.protectedMethod(): void"], +]; +const validStaticMembersOfBaseClassB: CompletionInfo[] = [ + ["staticMethod", "(method) B.staticMethod(): void"] +]; +const privateMembersOfBaseClassB: CompletionInfo[] = [ + ["privateMethod", "(method) B.privateMethod(): void"], +]; +const protectedPropertiesOfBaseClassB0: CompletionInfo[] = [ + ["protectedMethod", "(method) B0.protectedMethod(): void"], + ["protectedMethod1", "(method) B0.protectedMethod1(): void"], +]; +const publicPropertiesOfBaseClassB0: CompletionInfo[] = [ + ["getValue", "(method) B0.getValue(): string | boolean"], + ["getValue1", "(method) B0.getValue1(): string | boolean"], +]; +const validInstanceMembersOfBaseClassB0: CompletionInfo[] = protectedPropertiesOfBaseClassB0.concat(publicPropertiesOfBaseClassB0); +const validStaticMembersOfBaseClassB0: CompletionInfo[] = [ + ["staticMethod", "(method) B0.staticMethod(): void"], + ["staticMethod1", "(method) B0.staticMethod1(): void"] +]; +const privateMembersOfBaseClassB0: CompletionInfo[] = [ + ["privateMethod", "(method) B0.privateMethod(): void"], + ["privateMethod1", "(method) B0.privateMethod1(): void"], +]; +const membersOfI: CompletionInfo[] = [ + ["methodOfInterface", "(method) I.methodOfInterface(): number"], +]; +const membersOfI2: CompletionInfo[] = [ + ["methodOfInterface2", "(method) I2.methodOfInterface2(): number"], +]; +const membersOfI3: CompletionInfo[] = [ + ["getValue", "(method) I3.getValue(): string"], + ["method", "(method) I3.method(): string"], +]; +const membersOfI4: CompletionInfo[] = [ + ["staticMethod", "(method) I4.staticMethod(): void"], + ["method", "(method) I4.method(): string"], +]; +const membersOfI5: CompletionInfo[] = publicPropertiesOfBaseClassB0.concat([ + ["methodOfInterface5", "(method) I5.methodOfInterface5(): number"] +]); +const membersOfI6: CompletionInfo[] = publicPropertiesOfBaseClassB0.concat([ + ["staticMethod", "(method) I6.staticMethod(): void"], + ["methodOfInterface6", "(method) I6.methodOfInterface6(): number"] +]); +const membersOfI7: CompletionInfo[] = membersOfI.concat([ + ["methodOfInterface7", "(method) I7.methodOfInterface7(): number"] +]); + +function getCompletionInfoVerifier( + validMembers: CompletionInfo[], + invalidMembers: CompletionInfo[], + arrayToDistribute: CompletionInfo[], + isValidDistributionCriteria: (v: CompletionInfo) => boolean): CompletionInfoVerifier { + if (arrayToDistribute) { + validMembers = validMembers.concat(arrayToDistribute.filter(isValidDistributionCriteria)); + invalidMembers = invalidMembers.concat(arrayToDistribute.filter(v => !isValidDistributionCriteria(v))); + } + return { + validMembers, + invalidMembers + } +} + +const noMembers: CompletionInfo[] = []; +const membersOfIAndI2 = membersOfI.concat(membersOfI2); +const invalidMembersOfBAtInstanceLocation = privateMembersOfBaseClassB.concat(validStaticMembersOfBaseClassB); + +// members of I and I2 +verifyClassElementLocations({ validMembers: membersOfIAndI2, invalidMembers: noMembers }, [ + "implementsIAndI2", + "implementsIAndI2AndWritingMethodNameOfI" +]); + +// Static location so no members of I and I2 +verifyClassElementLocations({ validMembers: noMembers, invalidMembers: membersOfIAndI2 }, + ["implementsIAndI2AndWritingStatic"]); + +const allInstanceBAndIAndI2 = membersOfIAndI2.concat(validInstanceMembersOfBaseClassB); +// members of instance B, I and I2 +verifyClassElementLocations({ + validMembers: allInstanceBAndIAndI2, + invalidMembers: invalidMembersOfBAtInstanceLocation +}, ["extendsBAndImplementsIAndI2"]); + +// static location so only static members of B and no members of instance B, I and I2 +verifyClassElementLocations({ + validMembers: validStaticMembersOfBaseClassB, + invalidMembers: privateMembersOfBaseClassB.concat(allInstanceBAndIAndI2) +}, [ + "extendsBAndImplementsIAndI2AndWritingStatic", + "extendsBAndImplementsIAndI2WithMethodFromBAndIAndTypesStatic" + ]); + +// instance members of B without protectedMethod, I and I2 +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ membersOfIAndI2, + /*invalidMembers*/ invalidMembersOfBAtInstanceLocation, + /*arrayToDistribute*/ validInstanceMembersOfBaseClassB, + value => value[0] !== "protectedMethod"), + ["extendsBAndImplementsIAndI2WithMethodFromB"]); + +// instance members of B, members of T without methodOfInterface and I2 +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ membersOfI2.concat(validInstanceMembersOfBaseClassB), + /*invalidMembers*/ invalidMembersOfBAtInstanceLocation, + /*arrayToDistribute*/ membersOfI, + value => value[0] !== "methodOfInterface"), + ["extendsBAndImplementsIAndI2WithMethodFromI"]); + +// instance members of B without protectedMethod, members of T without methodOfInterface and I2 +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ membersOfI2, + /*invalidMembers*/ invalidMembersOfBAtInstanceLocation, + /*arrayToDistribute*/ membersOfI.concat(validInstanceMembersOfBaseClassB), + value => value[0] !== "methodOfInterface" && value[0] !== "protectedMethod"), + ["extendsBAndImplementsIAndI2WithMethodFromBAndI"]); + +// members of B and members of I3 that are not same as name of method in B +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ validInstanceMembersOfBaseClassB, + /*invalidMembers*/ invalidMembersOfBAtInstanceLocation, + /*arrayToDistribute*/ membersOfI3, + value => value[0] !== "getValue"), + ["extendsBAndImplementsI3WithSameNameMembers"]); + +// members of B (without getValue since its implemented) and members of I3 that are not same as name of method in B +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ noMembers, + /*invalidMembers*/ invalidMembersOfBAtInstanceLocation, + /*arrayToDistribute*/ membersOfI3.concat(validInstanceMembersOfBaseClassB), + value => value[0] !== "getValue"), + ["extendsBAndImplementsI3WithSameNameMembersAndHasImplementedTheMember"]); + +const invalidMembersOfB0AtInstanceSide = privateMembersOfBaseClassB0.concat(validStaticMembersOfBaseClassB0); +const invalidMembersOfB0AtStaticSide = privateMembersOfBaseClassB0.concat(validInstanceMembersOfBaseClassB0); +// members of B0 and members of I4 +verifyClassElementLocations({ + validMembers: validInstanceMembersOfBaseClassB0.concat(membersOfI4), + invalidMembers: invalidMembersOfB0AtInstanceSide +}, [ + "extendsB0ThatExtendsAndImplementsI4WithStaticMethod", + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedAnotherMethod", + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsStatic" + ]); + +// members of B0 and members of I4 that are not staticMethod +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ validInstanceMembersOfBaseClassB0, + /*invalidMembers*/ invalidMembersOfB0AtInstanceSide, + /*arrayToDistribute*/ membersOfI4, + value => value[0] !== "staticMethod" + ), [ + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethod", + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsBoth" + ]); + +// static members of B0 +verifyClassElementLocations({ + validMembers: validStaticMembersOfBaseClassB0, + invalidMembers: invalidMembersOfB0AtStaticSide.concat(membersOfI4) +}, [ + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodWritingStatic", + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedAnotherMethodWritingStatic", + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodWritingStatic" + ]); + +// static members of B0 without staticMethod +verifyClassElementLocations( + getCompletionInfoVerifier( + /*validMembers*/ noMembers, + /*invalidMembers*/ invalidMembersOfB0AtStaticSide.concat(membersOfI4), + /*arrayToDistribute*/ validStaticMembersOfBaseClassB0, + value => value[0] !== "staticMethod" + ), [ + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsStaticWritingStatic", + "extendsB0ThatExtendsAndImplementsI4WithStaticMethodAndImplementedThatMethodAsBothWritingStatic" + ]); + +// members of I7 extends I +verifyClassElementLocations({ validMembers: membersOfI7, invalidMembers: noMembers }, [ + "implementsI7whichExtendsI", + "implementsI7whichExtendsIAndAlsoImplementsI", + "implementsIAndAlsoImplementsI7whichExtendsI" +]); + +const invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0 = invalidMembersOfB0AtInstanceSide + .concat(protectedPropertiesOfBaseClassB0); +// members of I5 extends B0 +verifyClassElementLocations({ + validMembers: membersOfI5, + invalidMembers: invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0 +}, [ + "implementsI5ThatExtendsB0", + ]); + +// members of I6 extends B0 +verifyClassElementLocations({ + validMembers: membersOfI6, + invalidMembers: invalidMembersOfB0AtInstanceSideFromInterfaceExtendingB0 +}, [ + "implementsI6ThatExtendsB0AndHasStaticMethodOfB0", + ]); + +// members of B0 and I5 that extends B0 +verifyClassElementLocations({ + validMembers: membersOfI5.concat(protectedPropertiesOfBaseClassB0), + invalidMembers: invalidMembersOfB0AtInstanceSide +}, [ + "extendsB0AndImplementsI5ThatExtendsB0" + ]); + +// members of B0 and I6 that extends B0 +verifyClassElementLocations({ + validMembers: membersOfI6.concat(protectedPropertiesOfBaseClassB0), + invalidMembers: invalidMembersOfB0AtInstanceSide +}, [ + "extendsB0AndImplementsI6ThatExtendsB0AndHasStaticMethodOfB0" + ]); + +// nothing on static side as these do not extend any other class +verifyClassElementLocations({ + validMembers: [], + invalidMembers: membersOfI5.concat(membersOfI6, invalidMembersOfB0AtStaticSide) +}, [ + "implementsI5ThatExtendsB0TypesStatic", + "implementsI6ThatExtendsB0AndHasStaticMethodOfB0TypesStatic" + ]); + +// statics of base B but nothing from instance side +verifyClassElementLocations({ + validMembers: validStaticMembersOfBaseClassB0, + invalidMembers: membersOfI5.concat(membersOfI6, invalidMembersOfB0AtStaticSide) +}, [ + "extendsB0AndImplementsI5ThatExtendsB0TypesStatic", + "extendsB0AndImplementsI6ThatExtendsB0AndHasStaticMethodOfB0TypesStatic" + ]); \ No newline at end of file From 0588f8b3800097513c3029c8b8f20ffba443c5c1 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 12 May 2017 13:25:15 -0700 Subject: [PATCH 51/77] cleanup --- src/compiler/checker.ts | 46 +++++++++++++---------------------------- src/compiler/factory.ts | 9 -------- 2 files changed, 14 insertions(+), 41 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3ce73b3283e..3d040bd284e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2436,24 +2436,17 @@ namespace ts { Debug.assert(!!(type.flags & TypeFlags.Object)); return typeReferenceToTypeNode(type); } - if (objectFlags & ObjectFlags.ClassOrInterface) { - Debug.assert(!!(type.flags & TypeFlags.Object)); - const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); - return createTypeReferenceNode(name, /*typeArguments*/ undefined); - } - if (type.flags & TypeFlags.TypeParameter) { + if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } - if (!inTypeAlias && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const name = symbolToTypeReferenceName(type.aliasSymbol); const typeArgumentNodes = toTypeArgumentNodes(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); } - if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; const typeNodes = types && mapToTypeNodeArray(types, context, /*addInElementTypeFlag*/ true, /*addInFirstTypeArgumentFlag*/ false); @@ -2468,13 +2461,11 @@ namespace ts { return undefined; } } - if (objectFlags & (ObjectFlags.Anonymous | ObjectFlags.Mapped)) { Debug.assert(!!(type.flags & TypeFlags.Object)); // The type is an object literal type. return createAnonymousTypeNode(type); } - if (type.flags & TypeFlags.Index) { const indexedType = (type).type; context.flags |= NodeBuilderFlags.InElementType; @@ -2482,7 +2473,6 @@ namespace ts { Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); return createTypeOperatorNode(indexTypeNode); } - if (type.flags & TypeFlags.IndexedAccess) { context.flags |= NodeBuilderFlags.InElementType; const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); @@ -2495,13 +2485,10 @@ namespace ts { function createMappedTypeNodeFromType(type: MappedType) { Debug.assert(!!(type.flags & TypeFlags.Object)); - const typeParameter = getTypeParameterFromMappedType(type); - const typeParameterNode = typeParameterToDeclaration(typeParameter, context); - - const templateType = getTemplateTypeFromMappedType(type); - const templateTypeNode = typeToTypeNodeHelper(templateType, context); const readonlyToken = type.declaration && type.declaration.readonlyToken ? createToken(SyntaxKind.ReadonlyKeyword) : undefined; const questionToken = type.declaration && type.declaration.questionToken ? createToken(SyntaxKind.QuestionToken) : undefined; + const typeParameterNode = typeParameterToDeclaration(getTypeParameterFromMappedType(type), context); + const templateTypeNode = typeToTypeNodeHelper(getTemplateTypeFromMappedType(type), context); const mappedTypeNode = createMappedTypeNode(readonlyToken, typeParameterNode, questionToken, templateTypeNode); return setEmitFlags(mappedTypeNode, EmitFlags.SingleLine); @@ -2521,7 +2508,7 @@ namespace ts { const typeAlias = getTypeAliasForTypeLiteral(type); if (typeAlias) { // The specified symbol flags need to be reinterpreted as type flags - const entityName = symbolToName(typeAlias, context, SymbolFlags.Type, /*expectsIdentifier*/ false ); + const entityName = symbolToName(typeAlias, context, SymbolFlags.Type, /*expectsIdentifier*/ false); return createTypeReferenceNode(entityName, /*typeArguments*/ undefined); } else { @@ -2802,8 +2789,8 @@ namespace ts { } function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { - const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const name = getNameFromIndexInfo(indexInfo) || "x"; + const indexerTypeNode = createKeywordTypeNode(kind === IndexKind.String ? SyntaxKind.StringKeyword : SyntaxKind.NumberKeyword); const indexingParameter = createParameter( /*decorators*/ undefined, @@ -2851,37 +2838,32 @@ namespace ts { } function typeParameterToDeclaration(type: TypeParameter, context: NodeBuilderContext): TypeParameterDeclaration { + const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); const constraint = getConstraintFromTypeParameter(type); const constraintNode = constraint && typeToTypeNodeHelper(constraint, context); const defaultParameter = getDefaultFromTypeParameter(type); const defaultParameterNode = defaultParameter && typeToTypeNodeHelper(defaultParameter, context); - const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ true); return createTypeParameterDeclaration(name, constraintNode, defaultParameterNode); } function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext): ParameterDeclaration { const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); + const modifiers = parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone); + const dotDotDotToken = isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined; + const name = parameterDeclaration.name.kind === SyntaxKind.Identifier ? + getSynthesizedClone(parameterDeclaration.name) : + cloneBindingName(parameterDeclaration.name); + const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; + let parameterType = getTypeOfSymbol(parameterSymbol); if (isRequiredInitializedParameter(parameterDeclaration)) { parameterType = includeFalsyTypes(parameterType, TypeFlags.Undefined); } const parameterTypeNode = typeToTypeNodeHelper(parameterType, context); - let name: BindingName; - if (parameterDeclaration.name.kind === SyntaxKind.Identifier) { - name = getSynthesizedClone(parameterDeclaration.name); - } - else { - Debug.assert(parameterDeclaration.name.kind === SyntaxKind.ArrayBindingPattern || parameterDeclaration.name.kind === SyntaxKind.ObjectBindingPattern); - name = cloneBindingName(parameterDeclaration.name); - } - const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; - const dotDotDotToken = (parameterDeclaration ? isRestParameter(parameterDeclaration) : isTransientSymbol(parameterSymbol) && parameterSymbol.isRestParameter) ? - createToken(SyntaxKind.DotDotDotToken) : - undefined; const parameterNode = createParameter( /*decorators*/ undefined, - cloneNodeArray(parameterDeclaration.modifiers), + modifiers, dotDotDotToken, name, questionToken, diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 230eb678ee5..97ee45aedcc 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -84,15 +84,6 @@ namespace ts { return clone; } - export function getDeepSynthesizedClone(node: T | undefined): T { - const clone = visitEachChild(node, getDeepSynthesizedClone, nullTransformationContext, /*nodeVisitor*/ undefined, getSynthesizedClone); - return nodeIsSynthesized(clone) ? clone : getSynthesizedClone(clone); - } - - export function cloneNodeArray(nodeArray: NodeArray) { - return nodeArray && nodeArray.map(getDeepSynthesizedClone); - } - // Literals export function createLiteral(value: string): StringLiteral; From ca748d6c02fb7cbd208d6b2ad9aaa43e4b1b8c66 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Fri, 12 May 2017 14:48:13 -0700 Subject: [PATCH 52/77] Add insertSpaceAfterTypeAssertion to the server protocol --- src/server/protocol.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 428b69027c7..c4c9ecece2b 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -2267,6 +2267,7 @@ namespace ts.server.protocol { insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean; insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean; insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean; + insertSpaceAfterTypeAssertion?: boolean; insertSpaceBeforeFunctionParenthesis?: boolean; placeOpenBraceOnNewLineForFunctions?: boolean; placeOpenBraceOnNewLineForControlBlocks?: boolean; From 4240f6f5d9fedf3134f82ca23e1e9f7d74cc7422 Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Fri, 12 May 2017 16:17:32 -0700 Subject: [PATCH 53/77] Extract diagnostics formatter used by --pretty This allows compilers other than tsc.js to display nice in-context diagnostics --- src/compiler/program.ts | 95 +++++++++++++++++++++++++++++++++++++++++ src/compiler/tsc.ts | 87 +------------------------------------ 2 files changed, 96 insertions(+), 86 deletions(-) diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 7893fee651e..8233ab4802e 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -241,6 +241,101 @@ namespace ts { return output; } + const redForegroundEscapeSequence = "\u001b[91m"; + const yellowForegroundEscapeSequence = "\u001b[93m"; + const blueForegroundEscapeSequence = "\u001b[93m"; + const gutterStyleSequence = "\u001b[100;30m"; + const gutterSeparator = " "; + const resetEscapeSequence = "\u001b[0m"; + const ellipsis = "..."; + function getCategoryFormat(category: DiagnosticCategory): string { + switch (category) { + case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence; + case DiagnosticCategory.Error: return redForegroundEscapeSequence; + case DiagnosticCategory.Message: return blueForegroundEscapeSequence; + } + } + + function formatAndReset(text: string, formatStyle: string) { + return formatStyle + text + resetEscapeSequence; + } + + function padLeft(s: string, length: number) { + while (s.length < length) { + s = " " + s; + } + return s; + } + + export function formatDiagnosticsWithColorAndContext(diagnostics: Diagnostic[], host: FormatDiagnosticsHost): string { + let output = ""; + for (const diagnostic of diagnostics) { + if (diagnostic.file) { + const { start, length, file } = diagnostic; + const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start); + const { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length); + const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line; + const relativeFileName = host ? convertToRelativePath(file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)) : file.fileName; + + const hasMoreThanFiveLines = (lastLine - firstLine) >= 4; + let gutterWidth = (lastLine + 1 + "").length; + if (hasMoreThanFiveLines) { + gutterWidth = Math.max(ellipsis.length, gutterWidth); + } + + output += sys.newLine; + for (let i = firstLine; i <= lastLine; i++) { + // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines, + // so we'll skip ahead to the second-to-last line. + if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) { + output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine; + i = lastLine - 1; + } + + const lineStart = getPositionOfLineAndCharacter(file, i, 0); + const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length; + let lineContent = file.text.slice(lineStart, lineEnd); + lineContent = lineContent.replace(/\s+$/g, ""); // trim from end + lineContent = lineContent.replace("\t", " "); // convert tabs to single spaces + + // Output the gutter and the actual contents of the line. + output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator; + output += lineContent + sys.newLine; + + // Output the gutter and the error span for the line using tildes. + output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator; + output += redForegroundEscapeSequence; + if (i === firstLine) { + // If we're on the last line, then limit it to the last character of the last line. + // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position. + const lastCharForLine = i === lastLine ? lastLineChar : undefined; + + output += lineContent.slice(0, firstLineChar).replace(/\S/g, " "); + output += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~"); + } + else if (i === lastLine) { + output += lineContent.slice(0, lastLineChar).replace(/./g, "~"); + } + else { + // Squiggle the entire line. + output += lineContent.replace(/./g, "~"); + } + output += resetEscapeSequence; + + output += sys.newLine; + } + + output += sys.newLine; + output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; + } + + const categoryColor = getCategoryFormat(diagnostic.category); + const category = DiagnosticCategory[diagnostic.category].toLowerCase(); + output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`; + } + return output; + } + export function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string { if (typeof messageText === "string") { return messageText; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 8981da65ba3..834c7326e22 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -60,93 +60,8 @@ namespace ts { sys.write(ts.formatDiagnostics([diagnostic], host)); } - const redForegroundEscapeSequence = "\u001b[91m"; - const yellowForegroundEscapeSequence = "\u001b[93m"; - const blueForegroundEscapeSequence = "\u001b[93m"; - const gutterStyleSequence = "\u001b[100;30m"; - const gutterSeparator = " "; - const resetEscapeSequence = "\u001b[0m"; - const ellipsis = "..."; - function getCategoryFormat(category: DiagnosticCategory): string { - switch (category) { - case DiagnosticCategory.Warning: return yellowForegroundEscapeSequence; - case DiagnosticCategory.Error: return redForegroundEscapeSequence; - case DiagnosticCategory.Message: return blueForegroundEscapeSequence; - } - } - - function formatAndReset(text: string, formatStyle: string) { - return formatStyle + text + resetEscapeSequence; - } - function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost): void { - let output = ""; - - if (diagnostic.file) { - const { start, length, file } = diagnostic; - const { line: firstLine, character: firstLineChar } = getLineAndCharacterOfPosition(file, start); - const { line: lastLine, character: lastLineChar } = getLineAndCharacterOfPosition(file, start + length); - const lastLineInFile = getLineAndCharacterOfPosition(file, file.text.length).line; - const relativeFileName = host ? convertToRelativePath(file.fileName, host.getCurrentDirectory(), fileName => host.getCanonicalFileName(fileName)) : file.fileName; - - const hasMoreThanFiveLines = (lastLine - firstLine) >= 4; - let gutterWidth = (lastLine + 1 + "").length; - if (hasMoreThanFiveLines) { - gutterWidth = Math.max(ellipsis.length, gutterWidth); - } - - output += sys.newLine; - for (let i = firstLine; i <= lastLine; i++) { - // If the error spans over 5 lines, we'll only show the first 2 and last 2 lines, - // so we'll skip ahead to the second-to-last line. - if (hasMoreThanFiveLines && firstLine + 1 < i && i < lastLine - 1) { - output += formatAndReset(padLeft(ellipsis, gutterWidth), gutterStyleSequence) + gutterSeparator + sys.newLine; - i = lastLine - 1; - } - - const lineStart = getPositionOfLineAndCharacter(file, i, 0); - const lineEnd = i < lastLineInFile ? getPositionOfLineAndCharacter(file, i + 1, 0) : file.text.length; - let lineContent = file.text.slice(lineStart, lineEnd); - lineContent = lineContent.replace(/\s+$/g, ""); // trim from end - lineContent = lineContent.replace("\t", " "); // convert tabs to single spaces - - // Output the gutter and the actual contents of the line. - output += formatAndReset(padLeft(i + 1 + "", gutterWidth), gutterStyleSequence) + gutterSeparator; - output += lineContent + sys.newLine; - - // Output the gutter and the error span for the line using tildes. - output += formatAndReset(padLeft("", gutterWidth), gutterStyleSequence) + gutterSeparator; - output += redForegroundEscapeSequence; - if (i === firstLine) { - // If we're on the last line, then limit it to the last character of the last line. - // Otherwise, we'll just squiggle the rest of the line, giving 'slice' no end position. - const lastCharForLine = i === lastLine ? lastLineChar : undefined; - - output += lineContent.slice(0, firstLineChar).replace(/\S/g, " "); - output += lineContent.slice(firstLineChar, lastCharForLine).replace(/./g, "~"); - } - else if (i === lastLine) { - output += lineContent.slice(0, lastLineChar).replace(/./g, "~"); - } - else { - // Squiggle the entire line. - output += lineContent.replace(/./g, "~"); - } - output += resetEscapeSequence; - - output += sys.newLine; - } - - output += sys.newLine; - output += `${ relativeFileName }(${ firstLine + 1 },${ firstLineChar + 1 }): `; - } - - const categoryColor = getCategoryFormat(diagnostic.category); - const category = DiagnosticCategory[diagnostic.category].toLowerCase(); - output += `${ formatAndReset(category, categoryColor) } TS${ diagnostic.code }: ${ flattenDiagnosticMessageText(diagnostic.messageText, sys.newLine) }`; - output += sys.newLine + sys.newLine; - - sys.write(output); + sys.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + sys.newLine + sys.newLine); } function reportWatchDiagnostic(diagnostic: Diagnostic) { From a32bc985bfeca89c32f0d7c10e83563dc115ef6e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 12 May 2017 16:27:35 -0700 Subject: [PATCH 54/77] respond to comments --- src/compiler/checker.ts | 65 +++++++++++++++++++-------------------- src/compiler/emitter.ts | 21 +++++++------ src/compiler/factory.ts | 60 +++++++++++++++++++----------------- src/compiler/types.ts | 28 ++++++++--------- src/compiler/utilities.ts | 2 -- src/compiler/visitor.ts | 12 ++++++++ src/services/services.ts | 2 +- 7 files changed, 101 insertions(+), 89 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3d040bd284e..a4c5f401321 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2278,35 +2278,10 @@ namespace ts { return result; } - function typeFormatFlagsToNodeBuilderFlags(flags: TypeFormatFlags): NodeBuilderFlags { - let result = NodeBuilderFlags.None; - if (flags === TypeFormatFlags.None) { - return result; - } - if (flags & TypeFormatFlags.NoTruncation) { - result |= NodeBuilderFlags.NoTruncation; - } - if (flags & TypeFormatFlags.UseFullyQualifiedType) { - result |= NodeBuilderFlags.UseFullyQualifiedType; - } - if (flags & TypeFormatFlags.SuppressAnyReturnType) { - result |= NodeBuilderFlags.SuppressAnyReturnType; - } - if (flags & TypeFormatFlags.WriteArrayAsGenericType) { - result |= NodeBuilderFlags.WriteArrayAsGenericType; - } - if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) { - result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature; - } - - return result; - } - function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, typeFormatFlagsToNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors); + const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors); Debug.assert(typeNode !== undefined, "should always get typenode?"); - const newLine = NewLineKind.None; - const options = { newLine, removeComments: true }; + const options = { removeComments: true }; const writer = createTextWriter(""); const printer = createPrinter(options, writer); const sourceFile = enclosingDeclaration && getSourceFileOfNode(enclosingDeclaration); @@ -2318,6 +2293,30 @@ namespace ts { return result.substr(0, maxLength - "...".length) + "..."; } return result; + + function toNodeBuilderFlags(flags?: TypeFormatFlags): NodeBuilderFlags { + let result = NodeBuilderFlags.None; + if (!flags) { + return result; + } + if (flags & TypeFormatFlags.NoTruncation) { + result |= NodeBuilderFlags.NoTruncation; + } + if (flags & TypeFormatFlags.UseFullyQualifiedType) { + result |= NodeBuilderFlags.UseFullyQualifiedType; + } + if (flags & TypeFormatFlags.SuppressAnyReturnType) { + result |= NodeBuilderFlags.SuppressAnyReturnType; + } + if (flags & TypeFormatFlags.WriteArrayAsGenericType) { + result |= NodeBuilderFlags.WriteArrayAsGenericType; + } + if (flags & TypeFormatFlags.WriteTypeArgumentsOfSignature) { + result |= NodeBuilderFlags.WriteTypeArgumentsOfSignature; + } + + return result; + } } function createNodeBuilder() { @@ -2423,7 +2422,7 @@ namespace ts { } if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { if (context.flags & NodeBuilderFlags.inObjectTypeLiteral) { - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowThisInObjectLiteral)) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) { context.encounteredError = true; } } @@ -2455,7 +2454,7 @@ namespace ts { return inElementType ? createParenthesizedType(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode; } else { - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyUnionOrIntersection)) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) { context.encounteredError = true; } return undefined; @@ -2629,7 +2628,7 @@ namespace ts { return createTupleTypeNode(tupleConstituentNodes); } } - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowEmptyTuple)) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyTuple)) { context.encounteredError = true; } return undefined; @@ -2901,7 +2900,7 @@ namespace ts { if (expectsIdentifier && chain.length !== 1 && !context.encounteredError - && !(context.flags & NodeBuilderFlags.allowQualifedNameInPlaceOfIdentifier)) { + && !(context.flags & NodeBuilderFlags.AllowQualifedNameInPlaceOfIdentifier)) { context.encounteredError = true; } return createEntityNameFromSymbolChain(chain, chain.length - 1); @@ -2923,7 +2922,7 @@ namespace ts { } } if (typeParameters && typeParameters.length > 0) { - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowTypeParameterInQualifiedName)) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowTypeParameterInQualifiedName)) { context.encounteredError = true; } typeParameterNodes = toTypeArgumentNodes(typeParameters, context); @@ -2981,7 +2980,7 @@ namespace ts { if (declaration.parent && declaration.parent.kind === SyntaxKind.VariableDeclaration) { return declarationNameToString((declaration.parent).name); } - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.allowAnonymousIdentifier)) { + if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowAnonymousIdentifier)) { context.encounteredError = true; } switch (declaration.kind) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d943e91b497..30e5ba1a091 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -234,12 +234,7 @@ namespace ts { writeBundle }; - /** - * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. - */ - function printNode(hint: EmitHint, node: TypeNode, sourceFile: undefined): string; - function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; - function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined): string { + function printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string { switch (hint) { case EmitHint.SourceFile: Debug.assert(isSourceFile(node), "Expected a SourceFile node."); @@ -269,6 +264,11 @@ namespace ts { return endPrint(); } + /** + * If `sourceFile` is `undefined`, `node` must be a synthesized `TypeNode`. + */ + function writeNode(hint: EmitHint, node: TypeNode, sourceFile: undefined, output: EmitTextWriter): void; + function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile, output: EmitTextWriter): void; function writeNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined, output: EmitTextWriter) { const previousWriter = writer; setWriter(output); @@ -961,6 +961,7 @@ namespace ts { function emitTypeLiteral(node: TypeLiteralNode) { write("{"); + // TODO: fix added indentation so we can remove this check. if (node.members.length > 0) { emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers); } @@ -2525,7 +2526,7 @@ namespace ts { const firstChild = children[0]; if (firstChild === undefined) { - return !(rangeIsOnSingleLine(parentNode, currentSourceFile)); + return !rangeIsOnSingleLine(parentNode, currentSourceFile); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(firstChild)) { return synthesizedNodeStartsOnNewLine(firstChild, format); @@ -2551,7 +2552,7 @@ namespace ts { return synthesizedNodeStartsOnNewLine(previousNode, format) || synthesizedNodeStartsOnNewLine(nextNode, format); } else { - return !(rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile)); + return !rangeEndIsOnSameLineAsRangeStart(previousNode, nextNode, currentSourceFile); } } else { @@ -2570,13 +2571,13 @@ namespace ts { const lastChild = lastOrUndefined(children); if (lastChild === undefined) { - return !(rangeIsOnSingleLine(parentNode, currentSourceFile)); + return !rangeIsOnSingleLine(parentNode, currentSourceFile); } else if (positionIsSynthesized(parentNode.pos) || nodeIsSynthesized(lastChild)) { return synthesizedNodeStartsOnNewLine(lastChild, format); } else { - return !(rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile)); + return !rangeEndPositionsAreOnSameLine(parentNode, lastChild, currentSourceFile); } } else { diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 97ee45aedcc..45aa14bdd2b 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -2,26 +2,6 @@ /// namespace ts { - export const nullTransformationContext: TransformationContext = { - enableEmitNotification: noop, - enableSubstitution: noop, - endLexicalEnvironment: () => undefined, - getCompilerOptions: notImplemented, - getEmitHost: notImplemented, - getEmitResolver: notImplemented, - hoistFunctionDeclaration: noop, - hoistVariableDeclaration: noop, - isEmitNotificationEnabled: notImplemented, - isSubstitutionEnabled: notImplemented, - onEmitNode: noop, - onSubstituteNode: notImplemented, - readEmitHelpers: notImplemented, - requestEmitHelper: noop, - resumeLexicalEnvironment: noop, - startLexicalEnvironment: noop, - suspendLexicalEnvironment: noop - }; - function createSynthesizedNode(kind: SyntaxKind): Node { const node = createNode(kind, -1, -1); node.flags |= NodeFlags.Synthesized; @@ -127,13 +107,18 @@ namespace ts { // Identifiers + export function createIdentifier(text: string): Identifier; + /* @internal */ + export function createIdentifier(text: string, typeArguments: TypeNode[]): Identifier; export function createIdentifier(text: string, typeArguments?: TypeNode[]): Identifier { const node = createSynthesizedNode(SyntaxKind.Identifier); node.text = escapeIdentifier(text); node.originalKeywordKind = text ? stringToToken(text) : SyntaxKind.Unknown; node.autoGenerateKind = GeneratedIdentifierKind.None; node.autoGenerateId = 0; - node.typeArguments = asNodeArray(typeArguments); + if (typeArguments) { + node.typeArguments = createNodeArray(typeArguments); + } return node; } @@ -299,13 +284,13 @@ namespace ts { // Type Elements export function createPropertySignature(modifiers: Modifier[] | undefined, name: PropertyName | string, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined): PropertySignature { - const propertySignature = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature; - propertySignature.modifiers = asNodeArray(modifiers); - propertySignature.name = asName(name); - propertySignature.questionToken = questionToken; - propertySignature.type = type; - propertySignature.initializer = initializer; - return propertySignature; + const node = createSynthesizedNode(SyntaxKind.PropertySignature) as PropertySignature; + node.modifiers = asNodeArray(modifiers); + node.name = asName(name); + node.questionToken = questionToken; + node.type = type; + node.initializer = initializer; + return node; } export function updatePropertySignature(node: PropertySignature, modifiers: Modifier[] | undefined, name: PropertyName, questionToken: QuestionToken | undefined, type: TypeNode | undefined, initializer: Expression | undefined) { @@ -2494,6 +2479,25 @@ namespace ts { /* @internal */ namespace ts { + export const nullTransformationContext: TransformationContext = { + enableEmitNotification: noop, + enableSubstitution: noop, + endLexicalEnvironment: () => undefined, + getCompilerOptions: notImplemented, + getEmitHost: notImplemented, + getEmitResolver: notImplemented, + hoistFunctionDeclaration: noop, + hoistVariableDeclaration: noop, + isEmitNotificationEnabled: notImplemented, + isSubstitutionEnabled: notImplemented, + onEmitNode: noop, + onSubstituteNode: notImplemented, + readEmitHelpers: notImplemented, + requestEmitHelper: noop, + resumeLexicalEnvironment: noop, + startLexicalEnvironment: noop, + suspendLexicalEnvironment: noop + }; // Compound nodes diff --git a/src/compiler/types.ts b/src/compiler/types.ts index df89120e441..e7e6f9ad1af 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -576,11 +576,11 @@ namespace ts { * If the identifier begins with two underscores, this will begin with three. */ text: string; - originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later + originalKeywordKind?: SyntaxKind; // Original syntaxKind which get set so that we can report an error later /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. - /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. - isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace - /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. + /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. + isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace + /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. } // Transient identifier node (marked by id === -1) @@ -2592,14 +2592,14 @@ namespace ts { SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. // Error handling - allowThisInObjectLiteral = 1 << 10, - allowQualifedNameInPlaceOfIdentifier = 1 << 11, - allowTypeParameterInQualifiedName = 1 << 12, - allowAnonymousIdentifier = 1 << 13, - allowEmptyUnionOrIntersection = 1 << 14, - allowEmptyTuple = 1 << 15, + AllowThisInObjectLiteral = 1 << 10, + AllowQualifedNameInPlaceOfIdentifier = 1 << 11, + AllowTypeParameterInQualifiedName = 1 << 12, + AllowAnonymousIdentifier = 1 << 13, + AllowEmptyUnionOrIntersection = 1 << 14, + AllowEmptyTuple = 1 << 15, - ignoreErrors = allowThisInObjectLiteral | allowQualifedNameInPlaceOfIdentifier | allowTypeParameterInQualifiedName | allowAnonymousIdentifier | allowEmptyUnionOrIntersection | allowEmptyTuple, + ignoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowTypeParameterInQualifiedName | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple, // State inObjectTypeLiteral = 1 << 20, @@ -3538,8 +3538,7 @@ namespace ts { export const enum NewLineKind { CarriageReturnLineFeed = 0, - LineFeed = 1, - None = 2 + LineFeed = 1 } export interface LineAndCharacter { @@ -3964,7 +3963,6 @@ namespace ts { } export const enum EmitFlags { - None = 0, SingleLine = 1 << 0, // The contents of this node should be emitted on a single line. AdviseOnEmitNode = 1 << 1, // The printer should invoke the onEmitNode callback when printing this node. NoSubstitution = 1 << 2, // Disables further substitution of an expression. @@ -4195,7 +4193,7 @@ namespace ts { * the identifiers of the source file are used when generating unique names to avoid * collisions. */ - printNode(hint: EmitHint, node: Node, sourceFile: SourceFile | undefined): string; + printNode(hint: EmitHint, node: Node, sourceFile: SourceFile): string; /** * Prints a source file as-is, without any emit transformations. */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index be6b8ccee13..825465085c8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3253,8 +3253,6 @@ namespace ts { const lineFeed = "\n"; export function getNewLineCharacter(options: CompilerOptions | PrinterOptions): string { switch (options.newLine) { - case NewLineKind.None: - return ""; case NewLineKind.CarriageReturnLineFeed: return carriageReturnLineFeed; case NewLineKind.LineFeed: diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index c9c3a3c48fa..c62af0da794 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -254,6 +254,7 @@ namespace ts { case SyntaxKind.Decorator: return updateDecorator(node, visitNode((node).expression, visitor, isExpression)); + // Type elements case SyntaxKind.PropertySignature: @@ -970,6 +971,15 @@ namespace ts { break; // Type member + + case SyntaxKind.PropertySignature: + result = reduceNodes((node).modifiers, cbNodes, result); + result = reduceNode((node).name, cbNode, result); + result = reduceNode((node).questionToken, cbNode, result); + result = reduceNode((node).type, cbNode, result); + result = reduceNode((node).initializer, cbNode, result); + break; + case SyntaxKind.PropertyDeclaration: result = reduceNodes((node).decorators, cbNodes, result); result = reduceNodes((node).modifiers, cbNodes, result); @@ -978,6 +988,8 @@ namespace ts { result = reduceNode((node).initializer, cbNode, result); break; + case SyntaxKind.PropertySignature: + case SyntaxKind.MethodDeclaration: result = reduceNodes((node).decorators, cbNodes, result); result = reduceNodes((node).modifiers, cbNodes, result); diff --git a/src/services/services.ts b/src/services/services.ts index 43d875cbf7d..ccc9f66e51a 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -360,7 +360,7 @@ namespace ts { _incrementExpressionBrand: any; _unaryExpressionBrand: any; _expressionBrand: any; - typeArguments: any; + /*@internal*/typeArguments: NodeArray; constructor(_kind: SyntaxKind.Identifier, pos: number, end: number) { super(pos, end); } From 9e9054bd2b112acfaa310fa7ac7cfb0209ff7c16 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 12 May 2017 17:42:10 -0700 Subject: [PATCH 55/77] handle todo --- src/compiler/checker.ts | 2 +- src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 12 ++---------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a4c5f401321..a5295df7b8b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2555,7 +2555,7 @@ namespace ts { const resolved = resolveStructuredTypeMembers(type); if (!resolved.properties.length && !resolved.stringIndexInfo && !resolved.numberIndexInfo) { if (!resolved.callSignatures.length && !resolved.constructSignatures.length) { - return createTypeLiteralNode(/*members*/ undefined); + return setEmitFlags(createTypeLiteralNode(/*members*/ undefined), EmitFlags.SingleLine); } if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 30e5ba1a091..8ec03e90c77 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -961,7 +961,7 @@ namespace ts { function emitTypeLiteral(node: TypeLiteralNode) { write("{"); - // TODO: fix added indentation so we can remove this check. + // If the literal is empty, do not add spaces between braces. if (node.members.length > 0) { emitList(node, node.members, getEmitFlags(node) & EmitFlags.SingleLine ? ListFormat.SingleLineTypeLiteralMembers : ListFormat.MultiLineTypeLiteralMembers); } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 45aa14bdd2b..d236ef99591 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3294,16 +3294,6 @@ namespace ts { return statements; } - export function parenthesizeConditionalHead(condition: Expression) { - const conditionalPrecedence = getOperatorPrecedence(SyntaxKind.ConditionalExpression, SyntaxKind.QuestionToken); - const emittedCondition = skipPartiallyEmittedExpressions(condition); - const conditionPrecedence = getExpressionPrecedence(emittedCondition); - if (compareValues(conditionPrecedence, conditionalPrecedence) === Comparison.LessThan) { - return createParen(condition); - } - return condition; - } - /** * Wraps the operand to a BinaryExpression in parentheses if they are needed to preserve the intended * order of operations. @@ -3605,6 +3595,8 @@ namespace ts { return expression; } + function parenthesizeElementTypeMembers() {} + /** * Clones a series of not-emitted expressions with a new inner expression. * From 6fd86b47f9f374aeb8022f8a58cad877e503affb Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Fri, 12 May 2017 17:51:14 -0700 Subject: [PATCH 56/77] temp --- src/compiler/factory.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index d236ef99591..094ad8cb8cf 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3595,7 +3595,18 @@ namespace ts { return expression; } - function parenthesizeElementTypeMembers() {} + function parenthesizeElementTypeMember(member: TypeNode) { + switch (member.kind) { + case SyntaxKind.UnionType: + case SyntaxKind.IntersectionType: + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return createParenthesizedType(member); + } + } + function parenthesizeElementTypeMembers(members: NodeArray) { + return createNodeArray(members.map(parenthesizeElementTypeMember)); + } /** * Clones a series of not-emitted expressions with a new inner expression. From 8af5910555460f1b96e7a13d289df78f0808d09b Mon Sep 17 00:00:00 2001 From: Dick van den Brink Date: Mon, 15 May 2017 14:55:17 +0200 Subject: [PATCH 57/77] Only report used before declaration errors on regular enums const enums don't generate code and the values are inlined so now error is necessary --- 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 376bdc13c88..d6f56a69b9a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1280,7 +1280,7 @@ namespace ts { else if (result.flags & SymbolFlags.Class) { error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration))); } - else if (result.flags & SymbolFlags.Enum) { + else if (result.flags & SymbolFlags.RegularEnum) { error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration))); } } From 0cd2245c9ad125b0860c72c6058183ce84823ed0 Mon Sep 17 00:00:00 2001 From: Dick van den Brink Date: Mon, 15 May 2017 14:57:24 +0200 Subject: [PATCH 58/77] Accept baselines for const enum changes --- .../baselines/reference/enumUsedBeforeDeclaration.errors.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/baselines/reference/enumUsedBeforeDeclaration.errors.txt b/tests/baselines/reference/enumUsedBeforeDeclaration.errors.txt index aea5366389b..64e1830b521 100644 --- a/tests/baselines/reference/enumUsedBeforeDeclaration.errors.txt +++ b/tests/baselines/reference/enumUsedBeforeDeclaration.errors.txt @@ -1,14 +1,11 @@ tests/cases/compiler/enumUsedBeforeDeclaration.ts(1,18): error TS2450: Enum 'Color' used before its declaration. -tests/cases/compiler/enumUsedBeforeDeclaration.ts(2,24): error TS2450: Enum 'ConstColor' used before its declaration. -==== tests/cases/compiler/enumUsedBeforeDeclaration.ts (2 errors) ==== +==== tests/cases/compiler/enumUsedBeforeDeclaration.ts (1 errors) ==== const v: Color = Color.Green; ~~~~~ !!! error TS2450: Enum 'Color' used before its declaration. const v2: ConstColor = ConstColor.Green; - ~~~~~~~~~~ -!!! error TS2450: Enum 'ConstColor' used before its declaration. enum Color { Red, Green, Blue } const enum ConstColor { Red, Green, Blue } From 5eb2bd08acb4bf42e0aeb75f30dd90d83a4182fb Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 May 2017 08:24:29 -0700 Subject: [PATCH 59/77] findAllReferences: In `export default foo`, symbol name is `foo` --- src/compiler/utilities.ts | 4 ++++ src/services/importTracker.ts | 9 +++++---- .../fourslash/findAllRefsForDefaultExport04.ts | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsForDefaultExport04.ts diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 58a1822a24d..5046b44d094 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4032,6 +4032,10 @@ namespace ts { return node.kind === SyntaxKind.ExportSpecifier; } + export function isExportAssignment(node: Node): node is ExportAssignment { + return node.kind === SyntaxKind.ExportAssignment; + } + export function isModuleOrEnumDeclaration(node: Node): node is ModuleDeclaration | EnumDeclaration { return node.kind === SyntaxKind.ModuleDeclaration || node.kind === SyntaxKind.EnumDeclaration; } diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index df50f3ec31c..c540c44620a 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -526,17 +526,18 @@ namespace ts.FindAllReferences { return isExternalModuleSymbol(exportingModuleSymbol) ? { exportingModuleSymbol, exportKind } : undefined; } - function symbolName(symbol: Symbol): string { + function symbolName(symbol: Symbol): string | undefined { if (symbol.name !== "default") { return symbol.name; } - const name = forEach(symbol.declarations, decl => { + return forEach(symbol.declarations, decl => { + if (isExportAssignment(decl)) { + return isIdentifier(decl.expression) ? decl.expression.text : undefined; + } const name = getNameOfDeclaration(decl); return name && name.kind === SyntaxKind.Identifier && name.text; }); - Debug.assert(!!name); - return name; } /** If at an export specifier, go to the symbol it refers to. */ diff --git a/tests/cases/fourslash/findAllRefsForDefaultExport04.ts b/tests/cases/fourslash/findAllRefsForDefaultExport04.ts new file mode 100644 index 00000000000..c8fdb0a6149 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsForDefaultExport04.ts @@ -0,0 +1,16 @@ +/// + +// @Filename: /a.ts +////const [|{| "isWriteAccess": true, "isDefinition": true |}a|] = 0; +////export default [|a|]; + +// @Filename: /b.ts +////import [|{| "isWriteAccess": true, "isDefinition": true |}a|] from "./a"; +////[|a|]; + +const [r0, r1, r2, r3] = test.ranges(); +verify.referenceGroups([r0, r1], [ + { definition: "const a: 0", ranges: [r0, r1] }, + { definition: "import a", ranges: [r2, r3] } +]); +verify.singleReferenceGroup("import a", [r2, r3]); From 20a4e32becf40823d86cdd76e32b5291e41315df Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 15 May 2017 10:18:34 -0700 Subject: [PATCH 60/77] parenthesization in factory --- src/compiler/checker.ts | 72 ++++++++++----------------------------- src/compiler/factory.ts | 29 ++++++++++++---- src/compiler/types.ts | 9 ++--- src/compiler/utilities.ts | 10 ++++++ 4 files changed, 52 insertions(+), 68 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a5295df7b8b..a7ca6769082 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2279,7 +2279,7 @@ namespace ts { } function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors); + const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); Debug.assert(typeNode !== undefined, "should always get typenode?"); const options = { removeComments: true }; const writer = createTextWriter(""); @@ -2360,10 +2360,8 @@ namespace ts { } function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { - const inElementType = context.flags & NodeBuilderFlags.InElementType; - const inFirstTypeArgument = context.flags & NodeBuilderFlags.InFirstTypeArgument; const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; - context.flags &= ~(NodeBuilderFlags.StateClearingFlags); + context.flags &= ~(NodeBuilderFlags.InTypeAlias); if (!type) { context.encounteredError = true; @@ -2443,15 +2441,15 @@ namespace ts { if (!inTypeAlias && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const name = symbolToTypeReferenceName(type.aliasSymbol); - const typeArgumentNodes = toTypeArgumentNodes(type.aliasTypeArguments, context); + const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; - const typeNodes = types && mapToTypeNodeArray(types, context, /*addInElementTypeFlag*/ true, /*addInFirstTypeArgumentFlag*/ false); + const typeNodes = types && mapToTypeNodeArray(types, context); if (typeNodes && typeNodes.length > 0) { const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); - return inElementType ? createParenthesizedType(unionOrIntersectionTypeNode) : unionOrIntersectionTypeNode; + return unionOrIntersectionTypeNode; } else { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowEmptyUnionOrIntersection)) { @@ -2467,15 +2465,11 @@ namespace ts { } if (type.flags & TypeFlags.Index) { const indexedType = (type).type; - context.flags |= NodeBuilderFlags.InElementType; const indexTypeNode = typeToTypeNodeHelper(indexedType, context); - Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); return createTypeOperatorNode(indexTypeNode); } if (type.flags & TypeFlags.IndexedAccess) { - context.flags |= NodeBuilderFlags.InElementType; const objectTypeNode = typeToTypeNodeHelper((type).objectType, context); - Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); const indexTypeNode = typeToTypeNodeHelper((type).indexType, context); return createIndexedAccessTypeNode(objectTypeNode, indexTypeNode); } @@ -2561,17 +2555,14 @@ namespace ts { if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const signature = resolved.callSignatures[0]; const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.FunctionType, context); - return shouldAddParenthesisAroundFunctionType(signature, context) ? - createParenthesizedType(signatureNode) : - signatureNode; + return signatureNode; } + if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { const signature = resolved.constructSignatures[0]; const signatureNode = signatureToSignatureDeclarationHelper(signature, SyntaxKind.ConstructorType, context); - return shouldAddParenthesisAroundFunctionType(signature, context) ? - createParenthesizedType(signatureNode) : - signatureNode; + return signatureNode; } } @@ -2583,19 +2574,6 @@ namespace ts { return setEmitFlags(typeLiteralNode, EmitFlags.SingleLine); } - function shouldAddParenthesisAroundFunctionType(callSignature: Signature, context: NodeBuilderContext) { - if (inElementType) { - return true; - } - else if (inFirstTypeArgument) { - // Add parenthesis around function type for the first type argument to avoid ambiguity - const typeParameters = callSignature.target && (context.flags & NodeBuilderFlags.WriteTypeArgumentsOfSignature) ? - callSignature.target.typeParameters : callSignature.typeParameters; - return typeParameters && typeParameters.length !== 0; - } - return false; - } - function createTypeQueryNodeFromSymbol(symbol: Symbol, symbolFlags: SymbolFlags) { const entityName = symbolToName(symbol, context, symbolFlags, /*expectsIdentifier*/ false); return createTypeQueryNode(entityName); @@ -2615,15 +2593,13 @@ namespace ts { return createTypeReferenceNode("Array", [typeArgumentNode]); } - context.flags |= NodeBuilderFlags.InElementType; const elementType = typeToTypeNodeHelper(typeArguments[0], context); - Debug.assert(!(context.flags & NodeBuilderFlags.InElementType)); - return createArrayTypeNode(elementType); } else if (type.target.objectFlags & ObjectFlags.Tuple) { if (typeArguments.length > 0) { - const tupleConstituentNodes = mapToTypeNodeArray(typeArguments.slice(0, getTypeReferenceArity(type)), context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ false); + const slice = typeArguments.slice(0, getTypeReferenceArity(type)); + const tupleConstituentNodes = slice && mapToTypeNodeArray(slice, context); if (tupleConstituentNodes && tupleConstituentNodes.length > 0) { return createTupleTypeNode(tupleConstituentNodes); } @@ -2649,7 +2625,8 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const typeArgumentNodes = createNodeArray(toTypeArgumentNodes(typeArguments.slice(start, i), context)); + const slice = typeArguments.slice(start, i); + const typeArgumentNodes = slice && createNodeArray(mapToTypeNodeArray(slice, context)); const namePart = symbolToTypeReferenceName(parent); (namePart.kind === SyntaxKind.Identifier ? namePart : namePart.right).typeArguments = typeArgumentNodes; @@ -2680,7 +2657,7 @@ namespace ts { if (some(typeArguments)) { const typeParameterCount = (type.target.typeParameters || emptyArray).length; const slice = typeArguments && typeArguments.slice(i, typeParameterCount); - typeArgumentNodes = toTypeArgumentNodes(slice, context); + typeArgumentNodes = slice && mapToTypeNodeArray(slice, context); } if (typeArgumentNodes) { @@ -2763,28 +2740,17 @@ namespace ts { } } - function mapToTypeNodeArray(types: Type[], context: NodeBuilderContext, addInElementTypeFlag: boolean, addInFirstTypeArgumentFlag: boolean): TypeNode[] { + function mapToTypeNodeArray(types: Type[], context: NodeBuilderContext): TypeNode[] { const result = []; - Debug.assert(!(context.flags & NodeBuilderFlags.InElementType), "should be unset at the beginning of the helper"); for (let i = 0; i < types.length; ++i) { const type = types[i]; - if (addInElementTypeFlag) { - context.flags |= NodeBuilderFlags.InElementType; - } - if (i === 0 && addInFirstTypeArgumentFlag) { - context.flags |= NodeBuilderFlags.InFirstTypeArgument; - } const typeNode = typeToTypeNodeHelper(type, context); if (typeNode) { result.push(typeNode); } } - Debug.assert(!(context.flags & NodeBuilderFlags.InElementType), "should be unset at the end of the helper"); - return result; - } - function toTypeArgumentNodes(typeArguments: Type[], context: NodeBuilderContext) { - return typeArguments && mapToTypeNodeArray(typeArguments, context, /*addInElementTypeFlag*/ false, /*addInFirstTypeArgumentFlag*/ true); + return result; } function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { @@ -2909,7 +2875,7 @@ namespace ts { Debug.assert(chain && 0 <= index && index < chain.length); const symbol = chain[index]; let typeParameterNodes: TypeNode[] | undefined; - if (index > 0) { + if (context.flags & NodeBuilderFlags.WriteTypeParametersInQualifiedName && index > 0) { const parentSymbol = chain[index - 1]; let typeParameters: TypeParameter[]; if (getCheckFlags(symbol) & CheckFlags.Instantiated) { @@ -2921,11 +2887,9 @@ namespace ts { typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol); } } + if (typeParameters && typeParameters.length > 0) { - if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowTypeParameterInQualifiedName)) { - context.encounteredError = true; - } - typeParameterNodes = toTypeArgumentNodes(typeParameters, context); + typeParameterNodes = mapToTypeNodeArray(typeParameters, context); } } diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 094ad8cb8cf..18e71f94ee3 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -506,7 +506,7 @@ namespace ts { export function createTypeReferenceNode(typeName: string | EntityName, typeArguments: TypeNode[] | undefined) { const node = createSynthesizedNode(SyntaxKind.TypeReference) as TypeReferenceNode; node.typeName = asName(typeName); - node.typeArguments = asNodeArray(typeArguments); + node.typeArguments = typeArguments && parenthesizeTypeParameters(typeArguments); return node; } @@ -559,7 +559,7 @@ namespace ts { export function createArrayTypeNode(elementType: TypeNode) { const node = createSynthesizedNode(SyntaxKind.ArrayType) as ArrayTypeNode; - node.elementType = elementType; + node.elementType = parenthesizeElementTypeMember(elementType); return node; } @@ -599,7 +599,7 @@ namespace ts { export function createUnionOrIntersectionTypeNode(kind: SyntaxKind.UnionType | SyntaxKind.IntersectionType, types: TypeNode[]) { const node = createSynthesizedNode(kind) as UnionTypeNode | IntersectionTypeNode; - node.types = createNodeArray(types); + node.types = parenthesizeElementTypeMembers(types); return node; } @@ -628,7 +628,7 @@ namespace ts { export function createTypeOperatorNode(type: TypeNode) { const node = createSynthesizedNode(SyntaxKind.TypeOperator) as TypeOperatorNode; node.operator = SyntaxKind.KeyOfKeyword; - node.type = type; + node.type = parenthesizeElementTypeMember(type); return node; } @@ -638,7 +638,7 @@ namespace ts { export function createIndexedAccessTypeNode(objectType: TypeNode, indexType: TypeNode) { const node = createSynthesizedNode(SyntaxKind.IndexedAccessType) as IndexedAccessTypeNode; - node.objectType = objectType; + node.objectType = parenthesizeElementTypeMember(objectType); node.indexType = indexType; return node; } @@ -3595,7 +3595,7 @@ namespace ts { return expression; } - function parenthesizeElementTypeMember(member: TypeNode) { + export function parenthesizeElementTypeMember(member: TypeNode) { switch (member.kind) { case SyntaxKind.UnionType: case SyntaxKind.IntersectionType: @@ -3603,11 +3603,26 @@ namespace ts { case SyntaxKind.ConstructorType: return createParenthesizedType(member); } + return member; } - function parenthesizeElementTypeMembers(members: NodeArray) { + + export function parenthesizeElementTypeMembers(members: TypeNode[]) { + // TODO: does this lose `originalNode` ptr? return createNodeArray(members.map(parenthesizeElementTypeMember)); } + export function parenthesizeTypeParameters(typeParameters: TypeNode[]) { + if (typeParameters && typeParameters.length > 0) { + const nodeArray = createNodeArray(typeParameters); + const firstEntry = nodeArray[0]; + if (isFunctionOrConstructor(firstEntry) && firstEntry.typeParameters) { + nodeArray[0] = createParenthesizedType(firstEntry); + } + + return nodeArray; + } + } + /** * Clones a series of not-emitted expressions with a new inner expression. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e7e6f9ad1af..cf204d158a1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2590,25 +2590,20 @@ namespace ts { WriteTypeArgumentsOfSignature = 1 << 5, // Write the type arguments instead of type parameters of the signature UseFullyQualifiedType = 1 << 6, // Write out the fully qualified type name (eg. Module.Type, instead of Type) SuppressAnyReturnType = 1 << 8, // If the return type is any-like, don't offer a return type. + WriteTypeParametersInQualifiedName = 1 << 9, // Error handling AllowThisInObjectLiteral = 1 << 10, AllowQualifedNameInPlaceOfIdentifier = 1 << 11, - AllowTypeParameterInQualifiedName = 1 << 12, AllowAnonymousIdentifier = 1 << 13, AllowEmptyUnionOrIntersection = 1 << 14, AllowEmptyTuple = 1 << 15, - ignoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowTypeParameterInQualifiedName | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple, + ignoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple, // State inObjectTypeLiteral = 1 << 20, - InElementType = 1 << 21, // Writing an array or union element type - InFirstTypeArgument = 1 << 22, // Writing first type argument of the instantiated type InTypeAlias = 1 << 23, // Writing type in type alias declaration - - /** Flags that should not be passed on to sub-nodes of the current node being built. */ - StateClearingFlags = InElementType | InFirstTypeArgument | InTypeAlias } export interface SymbolDisplayBuilder { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 825465085c8..80daf383639 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -881,6 +881,16 @@ namespace ts { return false; } + export function isFunctionOrConstructor(node: Node): node is FunctionTypeNode | ConstructorTypeNode { + switch (node.kind) { + case SyntaxKind.FunctionType: + case SyntaxKind.ConstructorType: + return true; + } + + return false; + } + export function introducesArgumentsExoticObject(node: Node) { switch (node.kind) { case SyntaxKind.MethodDeclaration: From d8f29a5fe8b918c2a48e7b745117de3c66b5a827 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 15 May 2017 12:42:28 -0700 Subject: [PATCH 61/77] update tests --- ...assImplementInterfaceMemberNestedTypeAlias.ts | 16 ++++++++++++++++ ...eFixClassImplementInterfaceMemberTypeAlias.ts | 4 ++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts new file mode 100644 index 00000000000..5aa5f1a4e51 --- /dev/null +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts @@ -0,0 +1,16 @@ +/// + +//// type Either = { val: T } | Error; +//// interface I { +//// x: Either>; +//// foo(x: Either>): void; +//// } +//// class C implements I {[| |]} + +verify.rangeAfterCodeFix(` + x: Either>; + foo(x: Either>): void { + throw new Error("Method not implemented."); + } +`); + diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts index f7dfd5e1787..44981302a1a 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts @@ -1,12 +1,12 @@ /// //// type MyType = [string, number]; -//// interface I { test(a: MyType): void; } +//// interface I { x: MyType; test(a: MyType): void; } //// class C implements I {[| |]} verify.rangeAfterCodeFix(` + x: [string, number]; test(a: [string, number]): void { throw new Error("Method not implemented."); } `); - From 4d028a81eeb8909f2be3a89fa14e8291199b656c Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 15 May 2017 15:10:52 -0700 Subject: [PATCH 62/77] getTokenAtPositionWorker: Remove duplicate loop --- src/services/utilities.ts | 48 ++++++++++++--------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4ae9204f7ed..e452d18de48 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -651,44 +651,26 @@ namespace ts { return current; } - if (includeJsDocComment) { - const jsDocChildren = ts.filter(current.getChildren(), isJSDocNode); - for (const jsDocChild of jsDocChildren) { - const start = allowPositionInLeadingTrivia ? jsDocChild.getFullStart() : jsDocChild.getStart(sourceFile, includeJsDocComment); - if (start <= position) { - const end = jsDocChild.getEnd(); - if (position < end || (position === end && jsDocChild.kind === SyntaxKind.EndOfFileToken)) { - current = jsDocChild; - continue outer; - } - else if (includeItemAtEndPosition && end === position) { - const previousToken = findPrecedingToken(position, sourceFile, jsDocChild); - if (previousToken && includeItemAtEndPosition(previousToken)) { - return previousToken; - } - } - } - } - } - // find the child that contains 'position' for (const child of current.getChildren()) { - // all jsDocComment nodes were already visited - if (isJSDocNode(child)) { + if (isJSDocNode(child) && !includeJsDocComment) { continue; } + const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment); - if (start <= position) { - const end = child.getEnd(); - if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) { - current = child; - continue outer; - } - else if (includeItemAtEndPosition && end === position) { - const previousToken = findPrecedingToken(position, sourceFile, child); - if (previousToken && includeItemAtEndPosition(previousToken)) { - return previousToken; - } + if (start > position) { + break; + } + + const end = child.getEnd(); + if (position < end || (position === end && child.kind === SyntaxKind.EndOfFileToken)) { + current = child; + continue outer; + } + else if (includeItemAtEndPosition && end === position) { + const previousToken = findPrecedingToken(position, sourceFile, child); + if (previousToken && includeItemAtEndPosition(previousToken)) { + return previousToken; } } } From 05d2f2d5d9c88a28f86d48a6d8ee49f5cdf28a3b Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 15 May 2017 15:28:55 -0700 Subject: [PATCH 63/77] Fix issue with throw in async delegator. --- src/compiler/transformers/esnext.ts | 4 ++-- .../emitter.asyncGenerators.classMethods.es2015.js | 8 ++++---- .../reference/emitter.asyncGenerators.classMethods.es5.js | 8 ++++---- ...emitter.asyncGenerators.functionDeclarations.es2015.js | 8 ++++---- .../emitter.asyncGenerators.functionDeclarations.es5.js | 8 ++++---- .../emitter.asyncGenerators.functionExpressions.es2015.js | 8 ++++---- .../emitter.asyncGenerators.functionExpressions.es5.js | 8 ++++---- ...emitter.asyncGenerators.objectLiteralMethods.es2015.js | 8 ++++---- .../emitter.asyncGenerators.objectLiteralMethods.es5.js | 8 ++++---- 9 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index fef1b009ba3..81ebd0b8f5b 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -929,8 +929,8 @@ namespace ts { text: ` var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; ` }; diff --git a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js index 5f1b97b208c..d91571f93ad 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es2015.js @@ -128,8 +128,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -169,8 +169,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; class C5 { f() { diff --git a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js index 1fa1b19e36f..cfd7c9725e4 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.classMethods.es5.js @@ -264,8 +264,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -354,8 +354,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js index 98e8291ac6a..4ca8322da9f 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es2015.js @@ -91,8 +91,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -130,8 +130,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; function f5() { return __asyncGenerator(this, arguments, function* f5_1() { diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js index f842a2c813a..410a25d2e16 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionDeclarations.es5.js @@ -218,8 +218,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -303,8 +303,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js index d491c0465f0..602fd47d38b 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es2015.js @@ -91,8 +91,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -130,8 +130,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; const f5 = function () { return __asyncGenerator(this, arguments, function* () { diff --git a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js index 779d9c5340e..45d17737cef 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.functionExpressions.es5.js @@ -218,8 +218,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -303,8 +303,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; diff --git a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js index 22ffff01eec..760931a4342 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js +++ b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es2015.js @@ -111,8 +111,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -152,8 +152,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; const o5 = { f() { diff --git a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js index 46f3a1efac2..b5069bcea2c 100644 --- a/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js +++ b/tests/baselines/reference/emitter.asyncGenerators.objectLiteralMethods.es5.js @@ -238,8 +238,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); @@ -325,8 +325,8 @@ var __asyncValues = (this && this.__asyncIterator) || function (o) { }; var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i, p; - return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.iterator] = function () { return this; }, i; - function verb(n) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : v; }; } + return i = {}, verb("next"), verb("throw", function (e) { throw e; }), verb("return"), i[Symbol.iterator] = function () { return this; }, i; + function verb(n, f) { if (o[n]) i[n] = function (v) { return (p = !p) ? { value: __await(o[n](v)), done: n === "return" } : f ? f(v) : v; }; } }; var __values = (this && this.__values) || function (o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; From e9ccb1664263c88702cf8223ce3dd73f7dfd4751 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 15 May 2017 15:32:14 -0700 Subject: [PATCH 64/77] Eliminate redundant exploration in type inference --- src/compiler/checker.ts | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d6f56a69b9a..26cdab1e599 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10096,21 +10096,29 @@ namespace ts { inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget); } + function getSymbolForInference(type: Type) { + // Exclude the static side of classes since it shares its symbol with the instance side which leads + // to false positives. + return type.flags & TypeFlags.Object && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class) ? type.symbol : undefined; + } + function inferTypes(typeVariables: TypeVariable[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) { - let sourceStack: Type[]; - let targetStack: Type[]; + let stack: Type[]; let depth = 0; let inferiority = 0; const visited = createMap(); inferFromTypes(originalSource, originalTarget); - function isInProcess(source: Type, target: Type) { - for (let i = 0; i < depth; i++) { - if (source === sourceStack[i] && target === targetStack[i]) { - return true; + function isInstantiationInProcess(type: Type) { + const symbol = getSymbolForInference(type); + if (symbol) { + for (let i = 0; i < depth; i++) { + const t = stack[i]; + if (getSymbolForInference(t) === symbol) { + return true; + } } } - return false; } function inferFromTypes(source: Type, target: Type) { @@ -10240,10 +10248,10 @@ namespace ts { else { source = getApparentType(source); if (source.flags & TypeFlags.Object) { - if (isInProcess(source, target)) { - return; - } - if (isDeeplyNestedType(source, sourceStack, depth) && isDeeplyNestedType(target, targetStack, depth)) { + // If we are already processing another target type with the same associated symbol (such as + // an instantiation of the same generic type), we do not explore this target as it would yield + // no further inferences. + if (isInstantiationInProcess(target)) { return; } const key = source.id + "," + target.id; @@ -10251,12 +10259,7 @@ namespace ts { return; } visited.set(key, true); - if (depth === 0) { - sourceStack = []; - targetStack = []; - } - sourceStack[depth] = source; - targetStack[depth] = target; + (stack || (stack = []))[depth] = target; depth++; inferFromObjectTypes(source, target); depth--; From d51e467238db9229ca0620607067c083aee07cc3 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Mon, 15 May 2017 15:46:50 -0700 Subject: [PATCH 65/77] Fix #15463: use intersection types to emulate spread in generic react components (#15851) * Fix #15463: use intersection types to emulate spread in generic react components * Fix lint errors * reverse condition --- src/compiler/checker.ts | 18 ++++++-- .../tsxAttributeResolution5.errors.txt | 24 ++++++----- .../reference/tsxGenericAttributesType9.js | 40 ++++++++++++++++++ .../tsxGenericAttributesType9.symbols | 37 +++++++++++++++++ .../reference/tsxGenericAttributesType9.types | 41 +++++++++++++++++++ ...ionComponentsWithTypeArguments2.errors.txt | 18 ++++---- ...ionComponentsWithTypeArguments4.errors.txt | 18 +++++--- ...ionComponentsWithTypeArguments5.errors.txt | 12 +++++- .../jsx/tsxGenericAttributesType9.tsx | 16 ++++++++ tests/cases/fourslash/tsxQuickInfo6.ts | 2 +- tests/cases/fourslash/tsxQuickInfo7.ts | 4 +- 11 files changed, 197 insertions(+), 33 deletions(-) create mode 100644 tests/baselines/reference/tsxGenericAttributesType9.js create mode 100644 tests/baselines/reference/tsxGenericAttributesType9.symbols create mode 100644 tests/baselines/reference/tsxGenericAttributesType9.types create mode 100644 tests/cases/conformance/jsx/tsxGenericAttributesType9.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d6f56a69b9a..a2a31e3a197 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13314,6 +13314,7 @@ namespace ts { let spread: Type = emptyObjectType; let attributesArray: Symbol[] = []; let hasSpreadAnyType = false; + let typeToIntersect: Type; let explicitlySpecifyChildrenAttribute = false; const jsxChildrenPropertyName = getJsxElementChildrenPropertyname(); @@ -13345,11 +13346,16 @@ namespace ts { attributesArray = []; attributesTable = createMap(); } - const exprType = getApparentType(checkExpression(attributeDecl.expression)); + const exprType = checkExpression(attributeDecl.expression); if (isTypeAny(exprType)) { hasSpreadAnyType = true; } - spread = getSpreadType(spread, exprType); + if (isValidSpreadType(exprType)) { + spread = getSpreadType(spread, exprType); + } + else { + typeToIntersect = typeToIntersect ? getIntersectionType([typeToIntersect, exprType]) : exprType; + } } } @@ -13404,7 +13410,13 @@ namespace ts { } } - return hasSpreadAnyType ? anyType : createJsxAttributesType(attributes.symbol, attributesTable); + if (hasSpreadAnyType) { + return anyType; + } + + const attributeType = createJsxAttributesType(attributes.symbol, attributesTable); + return typeToIntersect && attributesTable.size ? getIntersectionType([typeToIntersect, attributeType]) : + typeToIntersect ? typeToIntersect : attributeType; /** * Create anonymous type from given attributes symbol table. diff --git a/tests/baselines/reference/tsxAttributeResolution5.errors.txt b/tests/baselines/reference/tsxAttributeResolution5.errors.txt index 0c6be5f35d0..f5af99d6f07 100644 --- a/tests/baselines/reference/tsxAttributeResolution5.errors.txt +++ b/tests/baselines/reference/tsxAttributeResolution5.errors.txt @@ -1,8 +1,10 @@ -tests/cases/conformance/jsx/file.tsx(21,16): error TS2322: Type '{ x: number; }' is not assignable to type 'Attribs1'. - Types of property 'x' are incompatible. - Type 'number' is not assignable to type 'string'. -tests/cases/conformance/jsx/file.tsx(25,16): error TS2322: Type '{ y: string; }' is not assignable to type 'Attribs1'. - Property 'x' is missing in type '{ y: string; }'. +tests/cases/conformance/jsx/file.tsx(21,16): error TS2322: Type 'T' is not assignable to type 'Attribs1'. + Type '{ x: number; }' is not assignable to type 'Attribs1'. + Types of property 'x' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsx/file.tsx(25,16): error TS2322: Type 'T' is not assignable to type 'Attribs1'. + Type '{ y: string; }' is not assignable to type 'Attribs1'. + Property 'x' is missing in type '{ y: string; }'. tests/cases/conformance/jsx/file.tsx(29,8): error TS2322: Type '{}' is not assignable to type 'Attribs1'. Property 'x' is missing in type '{}'. @@ -30,16 +32,18 @@ tests/cases/conformance/jsx/file.tsx(29,8): error TS2322: Type '{}' is not assig function make2 (obj: T) { return ; // Error (x is number, not string) ~~~~~~~~ -!!! error TS2322: Type '{ x: number; }' is not assignable to type 'Attribs1'. -!!! error TS2322: Types of property 'x' are incompatible. -!!! error TS2322: Type 'number' is not assignable to type 'string'. +!!! error TS2322: Type 'T' is not assignable to type 'Attribs1'. +!!! error TS2322: Type '{ x: number; }' is not assignable to type 'Attribs1'. +!!! error TS2322: Types of property 'x' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. } function make3 (obj: T) { return ; // Error, missing x ~~~~~~~~ -!!! error TS2322: Type '{ y: string; }' is not assignable to type 'Attribs1'. -!!! error TS2322: Property 'x' is missing in type '{ y: string; }'. +!!! error TS2322: Type 'T' is not assignable to type 'Attribs1'. +!!! error TS2322: Type '{ y: string; }' is not assignable to type 'Attribs1'. +!!! error TS2322: Property 'x' is missing in type '{ y: string; }'. } diff --git a/tests/baselines/reference/tsxGenericAttributesType9.js b/tests/baselines/reference/tsxGenericAttributesType9.js new file mode 100644 index 00000000000..b7b90736ad1 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType9.js @@ -0,0 +1,40 @@ +//// [file.tsx] +import React = require('react'); + +export function makeP

(Ctor: React.ComponentClass

): React.ComponentClass

{ + return class extends React.PureComponent { + public render(): JSX.Element { + return ( + + ); + } + }; +} + +//// [file.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var React = require("react"); +function makeP(Ctor) { + return (function (_super) { + __extends(class_1, _super); + function class_1() { + return _super !== null && _super.apply(this, arguments) || this; + } + class_1.prototype.render = function () { + return (); + }; + return class_1; + }(React.PureComponent)); +} +exports.makeP = makeP; diff --git a/tests/baselines/reference/tsxGenericAttributesType9.symbols b/tests/baselines/reference/tsxGenericAttributesType9.symbols new file mode 100644 index 00000000000..f4df2e79408 --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType9.symbols @@ -0,0 +1,37 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : Symbol(React, Decl(file.tsx, 0, 0)) + +export function makeP

(Ctor: React.ComponentClass

): React.ComponentClass

{ +>makeP : Symbol(makeP, Decl(file.tsx, 0, 32)) +>P : Symbol(P, Decl(file.tsx, 2, 22)) +>Ctor : Symbol(Ctor, Decl(file.tsx, 2, 25)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>ComponentClass : Symbol(React.ComponentClass, Decl(react.d.ts, 204, 5)) +>P : Symbol(P, Decl(file.tsx, 2, 22)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>ComponentClass : Symbol(React.ComponentClass, Decl(react.d.ts, 204, 5)) +>P : Symbol(P, Decl(file.tsx, 2, 22)) + + return class extends React.PureComponent { +>React.PureComponent : Symbol(React.PureComponent, Decl(react.d.ts, 179, 5)) +>React : Symbol(React, Decl(file.tsx, 0, 0)) +>PureComponent : Symbol(React.PureComponent, Decl(react.d.ts, 179, 5)) +>P : Symbol(P, Decl(file.tsx, 2, 22)) + + public render(): JSX.Element { +>render : Symbol((Anonymous class).render, Decl(file.tsx, 3, 52)) +>JSX : Symbol(JSX, Decl(react.d.ts, 2352, 1)) +>Element : Symbol(JSX.Element, Decl(react.d.ts, 2355, 27)) + + return ( + +>Ctor : Symbol(Ctor, Decl(file.tsx, 2, 25)) +>this.props : Symbol(React.Component.props, Decl(react.d.ts, 166, 37)) +>this : Symbol((Anonymous class), Decl(file.tsx, 3, 7)) +>props : Symbol(React.Component.props, Decl(react.d.ts, 166, 37)) + + ); + } + }; +} diff --git a/tests/baselines/reference/tsxGenericAttributesType9.types b/tests/baselines/reference/tsxGenericAttributesType9.types new file mode 100644 index 00000000000..a1d7efc49be --- /dev/null +++ b/tests/baselines/reference/tsxGenericAttributesType9.types @@ -0,0 +1,41 @@ +=== tests/cases/conformance/jsx/file.tsx === +import React = require('react'); +>React : typeof React + +export function makeP

(Ctor: React.ComponentClass

): React.ComponentClass

{ +>makeP :

(Ctor: React.ComponentClass

) => React.ComponentClass

+>P : P +>Ctor : React.ComponentClass

+>React : any +>ComponentClass : React.ComponentClass

+>P : P +>React : any +>ComponentClass : React.ComponentClass

+>P : P + + return class extends React.PureComponent { +>class extends React.PureComponent { public render(): JSX.Element { return ( ); } } : typeof (Anonymous class) +>React.PureComponent : React.PureComponent +>React : typeof React +>PureComponent : typeof React.PureComponent +>P : P + + public render(): JSX.Element { +>render : () => JSX.Element +>JSX : any +>Element : JSX.Element + + return ( +>( ) : JSX.Element + + +> : JSX.Element +>Ctor : React.ComponentClass

+>this.props : P & { children?: React.ReactNode; } +>this : this +>props : P & { children?: React.ReactNode; } + + ); + } + }; +} diff --git a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments2.errors.txt b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments2.errors.txt index c8ff457711c..7e940116887 100644 --- a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments2.errors.txt +++ b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments2.errors.txt @@ -1,10 +1,9 @@ -tests/cases/conformance/jsx/file.tsx(8,34): error TS2322: Type '{ ignore-prop: 10; prop: number; }' is not assignable to type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'. - Type '{ ignore-prop: 10; prop: number; }' is not assignable to type '{ prop: number; "ignore-prop": string; }'. +tests/cases/conformance/jsx/file.tsx(8,34): error TS2322: Type 'T & { ignore-prop: 10; }' is not assignable to type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'. + Type 'T & { ignore-prop: 10; }' is not assignable to type '{ prop: number; "ignore-prop": string; }'. Types of property '"ignore-prop"' are incompatible. Type '10' is not assignable to type 'string'. -tests/cases/conformance/jsx/file.tsx(13,34): error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & { prop: {}; "ignore-prop": string; }'. - Type '{}' is not assignable to type '{ prop: {}; "ignore-prop": string; }'. - Property 'prop' is missing in type '{}'. +tests/cases/conformance/jsx/file.tsx(13,34): error TS2322: Type 'T' is not assignable to type 'IntrinsicAttributes & { prop: {}; "ignore-prop": string; }'. + Type 'T' is not assignable to type '{ prop: {}; "ignore-prop": string; }'. tests/cases/conformance/jsx/file.tsx(20,19): error TS2322: Type '{ func: (a: number, b: string) => void; }' is not assignable to type 'IntrinsicAttributes & { func: (arg: number) => void; }'. Type '{ func: (a: number, b: string) => void; }' is not assignable to type '{ func: (arg: number) => void; }'. Types of property 'func' are incompatible. @@ -25,8 +24,8 @@ tests/cases/conformance/jsx/file.tsx(31,10): error TS2453: The type argument for function Bar(arg: T) { let a1 = ; ~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2322: Type '{ ignore-prop: 10; prop: number; }' is not assignable to type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'. -!!! error TS2322: Type '{ ignore-prop: 10; prop: number; }' is not assignable to type '{ prop: number; "ignore-prop": string; }'. +!!! error TS2322: Type 'T & { ignore-prop: 10; }' is not assignable to type 'IntrinsicAttributes & { prop: number; "ignore-prop": string; }'. +!!! error TS2322: Type 'T & { ignore-prop: 10; }' is not assignable to type '{ prop: number; "ignore-prop": string; }'. !!! error TS2322: Types of property '"ignore-prop"' are incompatible. !!! error TS2322: Type '10' is not assignable to type 'string'. } @@ -35,9 +34,8 @@ tests/cases/conformance/jsx/file.tsx(31,10): error TS2453: The type argument for function Baz(arg: T) { let a0 = ~~~~~~~~ -!!! error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & { prop: {}; "ignore-prop": string; }'. -!!! error TS2322: Type '{}' is not assignable to type '{ prop: {}; "ignore-prop": string; }'. -!!! error TS2322: Property 'prop' is missing in type '{}'. +!!! error TS2322: Type 'T' is not assignable to type 'IntrinsicAttributes & { prop: {}; "ignore-prop": string; }'. +!!! error TS2322: Type 'T' is not assignable to type '{ prop: {}; "ignore-prop": string; }'. } declare function Link(l: {func: (arg: U)=>void}): JSX.Element; diff --git a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt index d9b50ef31c6..311cfaed86f 100644 --- a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt +++ b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments4.errors.txt @@ -1,9 +1,12 @@ tests/cases/conformance/jsx/file.tsx(9,33): error TS2322: Type '{ a: number; }' is not assignable to type 'IntrinsicAttributes & { b: {}; a: number; }'. Type '{ a: number; }' is not assignable to type '{ b: {}; a: number; }'. Property 'b' is missing in type '{ a: number; }'. -tests/cases/conformance/jsx/file.tsx(10,33): error TS2322: Type '{ b: number; }' is not assignable to type 'IntrinsicAttributes & { b: number; a: {}; }'. - Type '{ b: number; }' is not assignable to type '{ b: number; a: {}; }'. - Property 'a' is missing in type '{ b: number; }'. +tests/cases/conformance/jsx/file.tsx(10,33): error TS2322: Type 'T' is not assignable to type 'IntrinsicAttributes & { b: number; a: {}; }'. + Type '{ b: number; }' is not assignable to type 'IntrinsicAttributes & { b: number; a: {}; }'. + Type '{ b: number; }' is not assignable to type '{ b: number; a: {}; }'. + Type 'T' is not assignable to type '{ b: number; a: {}; }'. + Type '{ b: number; }' is not assignable to type '{ b: number; a: {}; }'. + Property 'a' is missing in type '{ b: number; }'. ==== tests/cases/conformance/jsx/file.tsx (2 errors) ==== @@ -22,7 +25,10 @@ tests/cases/conformance/jsx/file.tsx(10,33): error TS2322: Type '{ b: number; }' !!! error TS2322: Property 'b' is missing in type '{ a: number; }'. let a2 = // missing a ~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2322: Type '{ b: number; }' is not assignable to type 'IntrinsicAttributes & { b: number; a: {}; }'. -!!! error TS2322: Type '{ b: number; }' is not assignable to type '{ b: number; a: {}; }'. -!!! error TS2322: Property 'a' is missing in type '{ b: number; }'. +!!! error TS2322: Type 'T' is not assignable to type 'IntrinsicAttributes & { b: number; a: {}; }'. +!!! error TS2322: Type '{ b: number; }' is not assignable to type 'IntrinsicAttributes & { b: number; a: {}; }'. +!!! error TS2322: Type '{ b: number; }' is not assignable to type '{ b: number; a: {}; }'. +!!! error TS2322: Type 'T' is not assignable to type '{ b: number; a: {}; }'. +!!! error TS2322: Type '{ b: number; }' is not assignable to type '{ b: number; a: {}; }'. +!!! error TS2322: Property 'a' is missing in type '{ b: number; }'. } \ No newline at end of file diff --git a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments5.errors.txt b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments5.errors.txt index b92375797e7..57a2ae556f7 100644 --- a/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments5.errors.txt +++ b/tests/baselines/reference/tsxStatelessFunctionComponentsWithTypeArguments5.errors.txt @@ -1,7 +1,11 @@ +tests/cases/conformance/jsx/file.tsx(15,14): error TS2605: JSX element type 'Element' is not a constructor function for JSX elements. + Property 'render' is missing in type 'Element'. +tests/cases/conformance/jsx/file.tsx(15,15): error TS2453: The type argument for type parameter 'U' cannot be inferred from the usage. Consider specifying the type arguments explicitly. + Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate '"hello"'. tests/cases/conformance/jsx/file.tsx(16,42): error TS2339: Property 'prop1' does not exist on type 'IntrinsicAttributes & { prop: number; }'. -==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== +==== tests/cases/conformance/jsx/file.tsx (3 errors) ==== import React = require('react') declare function Component(l: U): JSX.Element; @@ -17,6 +21,12 @@ tests/cases/conformance/jsx/file.tsx(16,42): error TS2339: Property 'prop1' does let a1 = ; // U is number let a2 = ; // U is number let a3 = ; // U is "hello" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2605: JSX element type 'Element' is not a constructor function for JSX elements. +!!! error TS2605: Property 'render' is missing in type 'Element'. + ~~~~~~~~~~~~~~~~~ +!!! error TS2453: The type argument for type parameter 'U' cannot be inferred from the usage. Consider specifying the type arguments explicitly. +!!! error TS2453: Type argument candidate 'number' is not a valid type argument because it is not a supertype of candidate '"hello"'. let a4 = ; // U is "hello" ~~~~~~~~~~~~~ !!! error TS2339: Property 'prop1' does not exist on type 'IntrinsicAttributes & { prop: number; }'. diff --git a/tests/cases/conformance/jsx/tsxGenericAttributesType9.tsx b/tests/cases/conformance/jsx/tsxGenericAttributesType9.tsx new file mode 100644 index 00000000000..a9466a43983 --- /dev/null +++ b/tests/cases/conformance/jsx/tsxGenericAttributesType9.tsx @@ -0,0 +1,16 @@ +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @libFiles: react.d.ts,lib.d.ts + +import React = require('react'); + +export function makeP

(Ctor: React.ComponentClass

): React.ComponentClass

{ + return class extends React.PureComponent { + public render(): JSX.Element { + return ( + + ); + } + }; +} \ No newline at end of file diff --git a/tests/cases/fourslash/tsxQuickInfo6.ts b/tests/cases/fourslash/tsxQuickInfo6.ts index 88d9435e801..2b65dc7667a 100644 --- a/tests/cases/fourslash/tsxQuickInfo6.ts +++ b/tests/cases/fourslash/tsxQuickInfo6.ts @@ -15,5 +15,5 @@ verify.quickInfos({ 1: "function ComponentSpecific(l: {\n prop: number;\n}): any", - 2: "function ComponentSpecific<\"hello\">(l: {\n prop: \"hello\";\n}): any" + 2: "function ComponentSpecific(l: {\n prop: U;\n}): any" }); diff --git a/tests/cases/fourslash/tsxQuickInfo7.ts b/tests/cases/fourslash/tsxQuickInfo7.ts index 3e66fb655f1..cf08aa53e98 100644 --- a/tests/cases/fourslash/tsxQuickInfo7.ts +++ b/tests/cases/fourslash/tsxQuickInfo7.ts @@ -24,6 +24,6 @@ verify.quickInfos({ 3: "function OverloadComponent(attr: {\n b: string;\n a: boolean;\n}): any (+2 overloads)", 4: "function OverloadComponent(attr: {\n b: number;\n a?: string;\n \"ignore-prop\": boolean;\n}): any (+2 overloads)", 5: "function OverloadComponent(): any (+2 overloads)", - 6: "function OverloadComponent(attr: {\n b: number;\n a: boolean;\n}): any (+2 overloads)", - 7: "function OverloadComponent(attr: {\n b: string;\n a: boolean;\n}): any (+2 overloads)" + 6: "function OverloadComponent(): any (+2 overloads)", + 7: "function OverloadComponent(): any (+2 overloads)", }); From bba41a8a0c9740ff57eed37ea281c4a6b046c21b Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 15 May 2017 18:42:49 -0700 Subject: [PATCH 66/77] avoid ascii escaping literal text --- src/compiler/checker.ts | 8 +++++--- src/compiler/emitter.ts | 4 +++- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 19 +++++++++---------- tests/cases/compiler/unicodeStringLiteral.ts | 1 + ...ImplementInterfaceIndexSignaturesString.ts | 4 ++-- ...mplementInterfaceInheritsAbstractMethod.ts | 4 ++-- ...ssImplementInterfaceMultipleImplements2.ts | 4 ++-- 8 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 tests/cases/compiler/unicodeStringLiteral.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a7ca6769082..d9b3c52b73d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2385,7 +2385,7 @@ namespace ts { return createTypeReferenceNode(name, /*typeArguments*/ undefined); } if (type.flags & (TypeFlags.StringLiteral)) { - return createLiteralTypeNode((createLiteral((type).text))); + return createLiteralTypeNode(setEmitFlags(createLiteral((type).text), EmitFlags.NoAsciiEscaping)); } if (type.flags & (TypeFlags.NumberLiteral)) { return createLiteralTypeNode((createNumericLiteral((type).text))); @@ -2783,7 +2783,9 @@ namespace ts { let returnTypeNode: TypeNode; if (signature.typePredicate) { const typePredicate = signature.typePredicate; - const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? createIdentifier((typePredicate).parameterName) : createThisTypeNode(); + const parameterName = typePredicate.kind === TypePredicateKind.Identifier ? + setEmitFlags(createIdentifier((typePredicate).parameterName), EmitFlags.NoAsciiEscaping) : + createThisTypeNode(); const typeNode = typeToTypeNodeHelper(typePredicate.type, context); returnTypeNode = createTypePredicateNode(parameterName, typeNode); } @@ -2894,7 +2896,7 @@ namespace ts { } const symbolName = getNameOfSymbol(symbol, context); - const identifier = createIdentifier(symbolName, typeParameterNodes); + const identifier = setEmitFlags(createIdentifier(symbolName, typeParameterNodes), EmitFlags.NoAsciiEscaping); return index > 0 ? createQualifiedName(createEntityNameFromSymbolChain(chain, index - 1), identifier) : identifier; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 8ec03e90c77..826b636e71e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2653,7 +2653,9 @@ namespace ts { if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { const textSourceNode = (node).textSourceNode; if (isIdentifier(textSourceNode)) { - return "\"" + escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode))) + "\""; + return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? + `"${escapeString(getTextOfNode(textSourceNode))}"` : + `"${escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode)))}"`; } else { return getLiteralTextOfNode(textSourceNode); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cf204d158a1..4bc97c56c00 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3985,6 +3985,7 @@ namespace ts { NoHoisting = 1 << 21, // Do not hoist this declaration in --module system HasEndOfDeclarationMarker = 1 << 22, // Declaration has an associated NotEmittedStatement to mark the end of the declaration Iterator = 1 << 23, // The expression to a `yield*` should be treated as an Iterator when down-leveling, not an Iterable. + NoAsciiEscaping = 1 << 24, // When synthesizing nodes that lack an original node or textSourceNode, we want to write the text on the node with ASCII escaping substitutions. } export interface EmitHelper { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 80daf383639..9dac18eec89 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -328,19 +328,22 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, node); } + const escapeText = getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? + (text: string) => escapeString(text) : + (text: string) => escapeNonAsciiCharacters(escapeString(text)); // If we can't reach the original source text, use the canonical form if it's a number, - // or an escaped quoted form of the original text if it's string-like. + // or a (possibly escaped) quoted form of the original text if it's string-like. switch (node.kind) { case SyntaxKind.StringLiteral: - return getQuotedEscapedLiteralText('"', node.text, '"'); + return '"' + escapeText(node.text) + '"'; case SyntaxKind.NoSubstitutionTemplateLiteral: - return getQuotedEscapedLiteralText("`", node.text, "`"); + return "`" + escapeText(node.text) + "`"; case SyntaxKind.TemplateHead: - return getQuotedEscapedLiteralText("`", node.text, "${"); + return "`" + escapeText(node.text) + "${"; case SyntaxKind.TemplateMiddle: - return getQuotedEscapedLiteralText("}", node.text, "${"); + return "}" + escapeText(node.text) + "${"; case SyntaxKind.TemplateTail: - return getQuotedEscapedLiteralText("}", node.text, "`"); + return "}" + escapeText(node.text) + "`"; case SyntaxKind.NumericLiteral: return node.text; } @@ -348,10 +351,6 @@ namespace ts { Debug.fail(`Literal kind '${node.kind}' not accounted for.`); } - function getQuotedEscapedLiteralText(leftQuote: string, text: string, rightQuote: string) { - return leftQuote + escapeNonAsciiCharacters(escapeString(text)) + rightQuote; - } - // Add an extra underscore to identifiers that start with two underscores to avoid issues with magic names like '__proto__' export function escapeIdentifier(identifier: string): string { return identifier.length >= 2 && identifier.charCodeAt(0) === CharacterCodes._ && identifier.charCodeAt(1) === CharacterCodes._ ? "_" + identifier : identifier; diff --git a/tests/cases/compiler/unicodeStringLiteral.ts b/tests/cases/compiler/unicodeStringLiteral.ts new file mode 100644 index 00000000000..fa2e1283309 --- /dev/null +++ b/tests/cases/compiler/unicodeStringLiteral.ts @@ -0,0 +1 @@ +var ੳ = "Ü­ਲĭ"; \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts index 4b595c6eda7..9d42faada36 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts @@ -1,11 +1,11 @@ /// //// interface I { -//// [x: string]: X; +//// [Ƚ: string]: X; //// } //// //// class C implements I {[| |]} verify.rangeAfterCodeFix(` - [x: string]: number; + [Ƚ: string]: number; `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts index c141592823a..7bb230a2dfb 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -2,12 +2,12 @@ //// abstract class C1 { } //// abstract class C2 { -//// abstract f1(); +//// abstract fA(); //// } //// interface I1 extends C1, C2 { } //// class C3 implements I1 {[| |]} -verify.rangeAfterCodeFix(`f1(){ +verify.rangeAfterCodeFix(`fA(){ throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts index e5100b88f6c..946b6495c5f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleImplements2.ts @@ -4,7 +4,7 @@ //// x: number; //// } //// interface I2 { -//// y: number; +//// y: "𣋝ઢȴ¬⏊"; //// } //// //// class C implements I1,I2 {[| @@ -12,7 +12,7 @@ //// } verify.rangeAfterCodeFix(` -y: number; +y: "𣋝ઢȴ¬⏊"; `); verify.not.codeFixAvailable(); From a9ab30c068e3096a6586980d5c3dd89ab63f1851 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 15 May 2017 18:43:06 -0700 Subject: [PATCH 67/77] revert baselines --- .../reference/unicodeExtendedEscapesInStrings06_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings06_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings08_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings08_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings09_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings09_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings10_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings10_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings11_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings11_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings13_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings13_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings15_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings15_ES6.types | 2 +- .../reference/unicodeExtendedEscapesInStrings16_ES5.types | 2 +- .../reference/unicodeExtendedEscapesInStrings16_ES6.types | 2 +- tests/baselines/reference/unicodeStringLiteral.js | 5 +++++ tests/baselines/reference/unicodeStringLiteral.symbols | 4 ++++ tests/baselines/reference/unicodeStringLiteral.types | 5 +++++ 19 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 tests/baselines/reference/unicodeStringLiteral.js create mode 100644 tests/baselines/reference/unicodeStringLiteral.symbols create mode 100644 tests/baselines/reference/unicodeStringLiteral.types diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types index e6942df5d1e..0c742aaf7bd 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES5.types @@ -3,5 +3,5 @@ // 1. Assert: 0 ≤ cp ≤ 0x10FFFF. var x = "\u{10FFFF}"; >x : string ->"\u{10FFFF}" : "\uDBFF\uDFFF" +>"\u{10FFFF}" : "􏿿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types index 82cc1fa622b..6b98c0c98a7 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings06_ES6.types @@ -3,5 +3,5 @@ // 1. Assert: 0 ≤ cp ≤ 0x10FFFF. var x = "\u{10FFFF}"; >x : string ->"\u{10FFFF}" : "\uDBFF\uDFFF" +>"\u{10FFFF}" : "􏿿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types index 9c316cca313..0007b758817 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES5.types @@ -4,5 +4,5 @@ // (FFFF == 65535) var x = "\u{FFFF}"; >x : string ->"\u{FFFF}" : "\uFFFF" +>"\u{FFFF}" : "￿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types index a5982a77c67..aedd2f72ef9 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings08_ES6.types @@ -4,5 +4,5 @@ // (FFFF == 65535) var x = "\u{FFFF}"; >x : string ->"\u{FFFF}" : "\uFFFF" +>"\u{FFFF}" : "￿" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types index 7508721f141..daf8a776e8a 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES5.types @@ -4,5 +4,5 @@ // (10000 == 65536) var x = "\u{10000}"; >x : string ->"\u{10000}" : "\uD800\uDC00" +>"\u{10000}" : "𐀀" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types index 5f05276ed33..0963f552c96 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings09_ES6.types @@ -4,5 +4,5 @@ // (10000 == 65536) var x = "\u{10000}"; >x : string ->"\u{10000}" : "\uD800\uDC00" +>"\u{10000}" : "𐀀" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types index a5422cc7a92..a4977300e65 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES5.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{D800}"; >x : string ->"\u{D800}" : "\uD800" +>"\u{D800}" : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types index 2fcdd0547ee..6d6d0112f08 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings10_ES6.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{D800}"; >x : string ->"\u{D800}" : "\uD800" +>"\u{D800}" : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types index b26387baaec..74e6817c85a 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES5.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{DC00}"; >x : string ->"\u{DC00}" : "\uDC00" +>"\u{DC00}" : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types index cd010529996..c1b3f372dab 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings11_ES6.types @@ -5,5 +5,5 @@ // this is a useful edge-case test. var x = "\u{DC00}"; >x : string ->"\u{DC00}" : "\uDC00" +>"\u{DC00}" : "�" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types index 68eb2fe8de1..224e81adec5 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings13_ES5.ts === var x = "\u{DDDDD}"; >x : string ->"\u{DDDDD}" : "\uDB37\uDDDD" +>"\u{DDDDD}" : "󝷝" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types index 4ad8d2e3f3d..e69b2af435c 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings13_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings13_ES6.ts === var x = "\u{DDDDD}"; >x : string ->"\u{DDDDD}" : "\uDB37\uDDDD" +>"\u{DDDDD}" : "󝷝" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types index 8da56115188..1973bb3790b 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings15_ES5.ts === var x = "\u{abcd}\u{ef12}\u{3456}\u{7890}"; >x : string ->"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" +>"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types index 613d91afae2..16ae5177f51 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings15_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings15_ES6.ts === var x = "\u{abcd}\u{ef12}\u{3456}\u{7890}"; >x : string ->"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" +>"\u{abcd}\u{ef12}\u{3456}\u{7890}" : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types index 1b29651e2d7..1ae9d9ef3d5 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES5.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings16_ES5.ts === var x = "\u{ABCD}\u{EF12}\u{3456}\u{7890}"; >x : string ->"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" +>"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types index addf7dedff4..a4abc5bdeae 100644 --- a/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types +++ b/tests/baselines/reference/unicodeExtendedEscapesInStrings16_ES6.types @@ -1,5 +1,5 @@ === tests/cases/conformance/es6/unicodeExtendedEscapes/unicodeExtendedEscapesInStrings16_ES6.ts === var x = "\u{ABCD}\u{EF12}\u{3456}\u{7890}"; >x : string ->"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "\uABCD\uEF12\u3456\u7890" +>"\u{ABCD}\u{EF12}\u{3456}\u{7890}" : "ꯍ㑖碐" diff --git a/tests/baselines/reference/unicodeStringLiteral.js b/tests/baselines/reference/unicodeStringLiteral.js new file mode 100644 index 00000000000..c69f7aa7e99 --- /dev/null +++ b/tests/baselines/reference/unicodeStringLiteral.js @@ -0,0 +1,5 @@ +//// [unicodeStringLiteral.ts] +var ੳ = "Ü­ਲĭ"; + +//// [unicodeStringLiteral.js] +var ੳ = "Ü­ਲĭ"; diff --git a/tests/baselines/reference/unicodeStringLiteral.symbols b/tests/baselines/reference/unicodeStringLiteral.symbols new file mode 100644 index 00000000000..f48111c0608 --- /dev/null +++ b/tests/baselines/reference/unicodeStringLiteral.symbols @@ -0,0 +1,4 @@ +=== tests/cases/compiler/unicodeStringLiteral.ts === +var ੳ = "Ü­ਲĭ"; +>ੳ : Symbol(ੳ, Decl(unicodeStringLiteral.ts, 0, 3)) + diff --git a/tests/baselines/reference/unicodeStringLiteral.types b/tests/baselines/reference/unicodeStringLiteral.types new file mode 100644 index 00000000000..e98f5987ed3 --- /dev/null +++ b/tests/baselines/reference/unicodeStringLiteral.types @@ -0,0 +1,5 @@ +=== tests/cases/compiler/unicodeStringLiteral.ts === +var ੳ = "Ü­ਲĭ"; +>ੳ : string +>"Ü­ਲĭ" : "Ü­ਲ\u000Eĭ" + From b2176c1872ca67fe7a97491639a34942e5c35a65 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Mon, 15 May 2017 19:11:36 -0700 Subject: [PATCH 68/77] no ascii escaping in param names --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d9b3c52b73d..0f2e5e9e0ff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2818,7 +2818,7 @@ namespace ts { const modifiers = parameterDeclaration.modifiers && parameterDeclaration.modifiers.map(getSynthesizedClone); const dotDotDotToken = isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined; const name = parameterDeclaration.name.kind === SyntaxKind.Identifier ? - getSynthesizedClone(parameterDeclaration.name) : + setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : cloneBindingName(parameterDeclaration.name); const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; @@ -2846,7 +2846,7 @@ namespace ts { if (clone.kind === SyntaxKind.BindingElement) { (clone).initializer = undefined; } - return setEmitFlags(clone, EmitFlags.SingleLine); + return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping); } } } From 2d3c7bae24926ba1ef63722bab813c39bc81b2bf Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 May 2017 07:10:38 -0700 Subject: [PATCH 69/77] break -> continue --- src/services/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e452d18de48..ed28daf0197 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -659,7 +659,7 @@ namespace ts { const start = allowPositionInLeadingTrivia ? child.getFullStart() : child.getStart(sourceFile, includeJsDocComment); if (start > position) { - break; + continue; } const end = child.getEnd(); From ed1a6c10e2281d8c51b53d60e6957a03289d4a73 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 16 May 2017 09:12:32 -0700 Subject: [PATCH 70/77] Address CR feedback + defer creation of visited map --- src/compiler/checker.ts | 53 ++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 26cdab1e599..602125091ab 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10096,31 +10096,12 @@ namespace ts { inferTypes(context.signature.typeParameters, context.inferences, originalSource, originalTarget); } - function getSymbolForInference(type: Type) { - // Exclude the static side of classes since it shares its symbol with the instance side which leads - // to false positives. - return type.flags & TypeFlags.Object && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class) ? type.symbol : undefined; - } - function inferTypes(typeVariables: TypeVariable[], typeInferences: TypeInferences[], originalSource: Type, originalTarget: Type) { - let stack: Type[]; - let depth = 0; + let symbolStack: Symbol[]; + let visited: Map; let inferiority = 0; - const visited = createMap(); inferFromTypes(originalSource, originalTarget); - function isInstantiationInProcess(type: Type) { - const symbol = getSymbolForInference(type); - if (symbol) { - for (let i = 0; i < depth; i++) { - const t = stack[i]; - if (getSymbolForInference(t) === symbol) { - return true; - } - } - } - } - function inferFromTypes(source: Type, target: Type) { if (!couldContainTypeVariables(target)) { return; @@ -10248,21 +10229,29 @@ namespace ts { else { source = getApparentType(source); if (source.flags & TypeFlags.Object) { + const key = source.id + "," + target.id; + if (visited && visited.get(key)) { + return; + } + (visited || (visited = createMap())).set(key, true); // If we are already processing another target type with the same associated symbol (such as // an instantiation of the same generic type), we do not explore this target as it would yield - // no further inferences. - if (isInstantiationInProcess(target)) { - return; + // no further inferences. We exclude the static side of classes from this check since it shares + // its symbol with the instance side which would lead to false positives. + const isNonConstructorObject = target.flags & TypeFlags.Object && + !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class); + const symbol = isNonConstructorObject ? target.symbol : undefined; + if (symbol) { + if (contains(symbolStack, symbol)) { + return; + } + (symbolStack || (symbolStack = [])).push(symbol); + inferFromObjectTypes(source, target); + symbolStack.pop(); } - const key = source.id + "," + target.id; - if (visited.get(key)) { - return; + else { + inferFromObjectTypes(source, target); } - visited.set(key, true); - (stack || (stack = []))[depth] = target; - depth++; - inferFromObjectTypes(source, target); - depth--; } } } From ffa21fe271d09eb964dad6715c272965294be981 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 May 2017 09:38:26 -0700 Subject: [PATCH 71/77] getDeclarationOfKind: Improve type safety --- src/compiler/checker.ts | 28 ++++++++++++++-------------- src/compiler/utilities.ts | 4 ++-- src/services/findAllReferences.ts | 4 ++-- src/services/symbolDisplay.ts | 4 ++-- 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a2a31e3a197..5c37ff2c6ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2731,7 +2731,7 @@ namespace ts { } function symbolToParameterDeclaration(parameterSymbol: Symbol): ParameterDeclaration { - const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); + const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter); const parameterType = getTypeOfSymbol(parameterSymbol); const parameterTypeNode = typeToTypeNodeHelper(parameterType); // TODO(aozgaa): In the future, check initializer accessibility. @@ -4100,7 +4100,7 @@ namespace ts { const func = declaration.parent; // For a parameter of a set accessor, use the type of the get accessor if one is present if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) { - const getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); + const getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); if (getter) { const getterSignature = getSignatureFromDeclaration(getter); const thisParameter = getAccessorThisParameter(func as AccessorDeclaration); @@ -4389,8 +4389,8 @@ namespace ts { function getTypeOfAccessors(symbol: Symbol): Type { const links = getSymbolLinks(symbol); if (!links.type) { - const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); - const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); + const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); + const setter = getDeclarationOfKind(symbol, SyntaxKind.SetAccessor); if (getter && getter.flags & NodeFlags.JavaScriptFile) { const jsDocType = getTypeForDeclarationFromJSDocComment(getter); @@ -4439,7 +4439,7 @@ namespace ts { if (!popTypeResolution()) { type = anyType; if (noImplicitAny) { - const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); + const getter = getDeclarationOfKind(symbol, SyntaxKind.GetAccessor); error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol)); } } @@ -4916,7 +4916,7 @@ namespace ts { return unknownType; } - let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); + let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); let type: Type; if (declaration) { if (declaration.jsDocTypeLiteral) { @@ -4927,7 +4927,7 @@ namespace ts { } } else { - declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); + declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); type = getTypeFromTypeNode(declaration.type); } @@ -6220,7 +6220,7 @@ namespace ts { !hasDynamicName(declaration) && (!hasThisParameter || !thisParameter)) { const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - const other = getDeclarationOfKind(declaration.symbol, otherKind); + const other = getDeclarationOfKind(declaration.symbol, otherKind); if (other) { thisParameter = getAnnotatedAccessorThisParameter(other); } @@ -6263,7 +6263,7 @@ namespace ts { // TypeScript 1.0 spec (April 2014): // If only one accessor includes a type annotation, the other behaves as if it had the same type annotation. if (declaration.kind === SyntaxKind.GetAccessor && !hasDynamicName(declaration)) { - const setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); + const setter = getDeclarationOfKind(declaration.symbol, SyntaxKind.SetAccessor); return getAnnotatedAccessorType(setter); } @@ -6476,7 +6476,7 @@ namespace ts { } function getConstraintDeclaration(type: TypeParameter) { - return (getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter)).constraint; + return getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter).constraint; } function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type { @@ -12567,7 +12567,7 @@ namespace ts { // corresponding set accessor has a type annotation, return statements in the function are contextually typed if (functionDecl.type || functionDecl.kind === SyntaxKind.Constructor || - functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(functionDecl.symbol, SyntaxKind.SetAccessor))) { + functionDecl.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(functionDecl.symbol, SyntaxKind.SetAccessor))) { return getReturnTypeOfSignature(getSignatureFromDeclaration(functionDecl)); } @@ -18110,7 +18110,7 @@ namespace ts { // TypeScript 1.0 spec (April 2014): 8.4.3 // Accessors for the same member name must specify the same accessibility. const otherKind = node.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - const otherAccessor = getDeclarationOfKind(node.symbol, otherKind); + const otherAccessor = getDeclarationOfKind(node.symbol, otherKind); if (otherAccessor) { if ((getModifierFlags(node) & ModifierFlags.AccessibilityModifier) !== (getModifierFlags(otherAccessor) & ModifierFlags.AccessibilityModifier)) { error(node.name, Diagnostics.Getter_and_setter_accessors_do_not_agree_in_visibility); @@ -20242,7 +20242,7 @@ namespace ts { } function isGetAccessorWithAnnotatedSetAccessor(node: FunctionLikeDeclaration) { - return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor))); + return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor))); } function isUnwrappedReturnTypeVoidOrAny(func: FunctionLikeDeclaration, returnType: Type): boolean { @@ -20930,7 +20930,7 @@ namespace ts { checkTypeParameterListsIdentical(symbol); // Only check this symbol once - const firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); + const firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); if (node === firstInterfaceDecl) { const type = getDeclaredTypeOfSymbol(symbol); const typeWithThis = getTypeWithThisArgument(type); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5046b44d094..98b22605ad7 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11,12 +11,12 @@ namespace ts { isTypeReferenceDirective?: boolean; } - export function getDeclarationOfKind(symbol: Symbol, kind: SyntaxKind): Declaration { + export function getDeclarationOfKind(symbol: Symbol, kind: T["kind"]): T { const declarations = symbol.declarations; if (declarations) { for (const declaration of declarations) { if (declaration.kind === kind) { - return declaration; + return declaration as T; } } } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 395c282f6d5..d9deaef9e56 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -550,7 +550,7 @@ namespace ts.FindAllReferences.Core { } function isObjectBindingPatternElementWithoutPropertyName(symbol: Symbol): boolean { - const bindingElement = getDeclarationOfKind(symbol, SyntaxKind.BindingElement); + const bindingElement = getDeclarationOfKind(symbol, SyntaxKind.BindingElement); return bindingElement && bindingElement.parent.kind === SyntaxKind.ObjectBindingPattern && !bindingElement.propertyName; @@ -558,7 +558,7 @@ namespace ts.FindAllReferences.Core { function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined { if (isObjectBindingPatternElementWithoutPropertyName(symbol)) { - const bindingElement = getDeclarationOfKind(symbol, SyntaxKind.BindingElement); + const bindingElement = getDeclarationOfKind(symbol, SyntaxKind.BindingElement); const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent); return typeOfPattern && checker.getPropertyOfType(typeOfPattern, (bindingElement.name).text); } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 4268a52c419..5e9d115b33e 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -275,7 +275,7 @@ namespace ts.SymbolDisplay { } if (symbolFlags & SymbolFlags.Module) { addNewLineIfDisplayPartsExist(); - const declaration = getDeclarationOfKind(symbol, SyntaxKind.ModuleDeclaration); + const declaration = getDeclarationOfKind(symbol, SyntaxKind.ModuleDeclaration); const isNamespace = declaration && declaration.name && declaration.name.kind === SyntaxKind.Identifier; displayParts.push(keywordPart(isNamespace ? SyntaxKind.NamespaceKeyword : SyntaxKind.ModuleKeyword)); displayParts.push(spacePart()); @@ -296,7 +296,7 @@ namespace ts.SymbolDisplay { } else { // Method/function type parameter - let declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter); + let declaration: Node = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter); Debug.assert(declaration !== undefined); declaration = declaration.parent; From badfcbfb43f594710020481662fa2ae680ddfecd Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 May 2017 09:45:01 -0700 Subject: [PATCH 72/77] findAncestor: Add generic overload for use with type predicates --- src/compiler/checker.ts | 4 ++-- src/compiler/core.ts | 2 ++ src/compiler/utilities.ts | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a22c841450..5b816656183 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1300,7 +1300,7 @@ namespace ts { return node; } - return findAncestor(node, n => n.kind === SyntaxKind.ImportDeclaration) as ImportDeclaration; + return findAncestor(node, isImportDeclaration); } } @@ -22638,7 +22638,7 @@ namespace ts { const symbolIsUmdExport = symbolFile !== referenceFile; return symbolIsUmdExport ? undefined : symbolFile; } - return findAncestor(node.parent, n => isModuleOrEnumDeclaration(n) && getSymbolOfNode(n) === parentSymbol) as ModuleDeclaration | EnumDeclaration; + return findAncestor(node.parent, (n): n is ModuleDeclaration | EnumDeclaration => isModuleOrEnumDeclaration(n) && getSymbolOfNode(n) === parentSymbol); } } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 5e4afce98c8..60553fdab01 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -230,6 +230,8 @@ namespace ts { * If no such value is found, it applies the callback until the parent pointer is undefined or the callback returns "quit" * At that point findAncestor returns undefined. */ + export function findAncestor(node: Node, callback: (element: Node) => element is T): T | undefined; + export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node | undefined; export function findAncestor(node: Node, callback: (element: Node) => boolean | "quit"): Node { while (node) { const result = callback(node); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 5046b44d094..43a144f9045 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4010,6 +4010,10 @@ namespace ts { return node.kind === SyntaxKind.ImportEqualsDeclaration; } + export function isImportDeclaration(node: Node): node is ImportDeclaration { + return node.kind === SyntaxKind.ImportDeclaration; + } + export function isImportClause(node: Node): node is ImportClause { return node.kind === SyntaxKind.ImportClause; } From ea42644c8f5d50633237438cc37637c8b4e4e4e1 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 May 2017 10:25:46 -0700 Subject: [PATCH 73/77] Replace unnecessary `let` with two `const`s --- src/services/symbolDisplay.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 5e9d115b33e..71dc7b78133 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -296,9 +296,9 @@ namespace ts.SymbolDisplay { } else { // Method/function type parameter - let declaration: Node = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter); - Debug.assert(declaration !== undefined); - declaration = declaration.parent; + const decl = getDeclarationOfKind(symbol, SyntaxKind.TypeParameter); + Debug.assert(decl !== undefined); + const declaration = decl.parent; if (declaration) { if (isFunctionLikeKind(declaration.kind)) { From 56d04429b31e2f440589c026de64e7722b471e1c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 16 May 2017 11:30:30 -0700 Subject: [PATCH 74/77] Add target and lib references for tsc-instrumented I think they were previously provided implicitly. --- Gulpfile.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Gulpfile.ts b/Gulpfile.ts index 32fbf2c43e0..03046789fe1 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -935,7 +935,7 @@ gulp.task(loggedIOJsPath, /*help*/ false, [], (done) => { const temp = path.join(builtLocalDirectory, "temp"); mkdirP(temp, (err) => { if (err) { console.error(err); done(err); process.exit(1); } - exec(host, [LKGCompiler, "--types --outdir", temp, loggedIOpath], () => { + exec(host, [LKGCompiler, "--types", "--target es5", "--lib es5", "--outdir", temp, loggedIOpath], () => { fs.renameSync(path.join(temp, "/harness/loggedIO.js"), loggedIOJsPath); del(temp).then(() => done(), done); }, done); @@ -946,7 +946,13 @@ const instrumenterPath = path.join(harnessDirectory, "instrumenter.ts"); const instrumenterJsPath = path.join(builtLocalDirectory, "instrumenter.js"); gulp.task(instrumenterJsPath, /*help*/ false, [servicesFile], () => { const settings: tsc.Settings = getCompilerSettings({ - outFile: instrumenterJsPath + outFile: instrumenterJsPath, + target: "es5", + lib: [ + "es6", + "dom", + "scripthost" + ] }, /*useBuiltCompiler*/ true); return gulp.src(instrumenterPath) .pipe(newer(instrumenterJsPath)) From 5fb77a09011facdac31bd9cf5fb84f734026adac Mon Sep 17 00:00:00 2001 From: Klaus Meinhardt Date: Tue, 16 May 2017 23:13:58 +0200 Subject: [PATCH 75/77] Update return types of APIs (#15887) * Update types.ts * Update types in parser.ts and scanner.ts --- src/compiler/parser.ts | 8 ++++---- src/compiler/scanner.ts | 13 +++++++------ src/compiler/types.ts | 30 +++++++++++++++--------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index dd189784a17..2d9806bcbf4 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -23,19 +23,19 @@ namespace ts { } } - function visitNode(cbNode: (node: Node) => T, node: Node): T { + function visitNode(cbNode: (node: Node) => T, node: Node): T | undefined { if (node) { return cbNode(node); } } - function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]) { + function visitNodeArray(cbNodes: (nodes: Node[]) => T, nodes: Node[]): T | undefined { if (nodes) { return cbNodes(nodes); } } - function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]) { + function visitEachNode(cbNode: (node: Node) => T, nodes: Node[]): T | undefined { if (nodes) { for (const node of nodes) { const result = cbNode(node); @@ -50,7 +50,7 @@ namespace ts { // stored in properties. If a 'cbNodes' callback is specified, it is invoked for embedded arrays; otherwise, // embedded arrays are flattened and the 'cbNode' callback is invoked for each element. If a callback returns // a truthy value, iteration stops and that value is returned. Otherwise, undefined is returned. - export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T { + export function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T | undefined { if (!node) { return; } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 659c2f416ab..d27bbf879fe 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -712,11 +712,11 @@ namespace ts { return accumulator; } - export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) { + export function forEachLeadingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ false, cb, state); } - export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T) { + export function forEachTrailingCommentRange(text: string, pos: number, cb: (pos: number, end: number, kind: CommentKind, hasTrailingNewLine: boolean, state: T) => U, state?: T): U | undefined { return iterateCommentRanges(/*reduce*/ false, text, pos, /*trailing*/ true, cb, state); } @@ -746,10 +746,11 @@ namespace ts { } /** Optionally, get the shebang */ - export function getShebang(text: string): string { - return shebangTriviaRegex.test(text) - ? shebangTriviaRegex.exec(text)[0] - : undefined; + export function getShebang(text: string): string | undefined { + const match = shebangTriviaRegex.exec(text); + if (match) { + return match[0]; + } } export function isIdentifierStart(ch: number, languageVersion: ScriptTarget): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 67d80a92ce2..955bb0622dd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2527,11 +2527,11 @@ namespace ts { indexInfoToIndexSignatureDeclaration(indexInfo: IndexInfo, kind: IndexKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): IndexSignatureDeclaration; getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; - getSymbolAtLocation(node: Node): Symbol; + getSymbolAtLocation(node: Node): Symbol | undefined; getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[]; - getShorthandAssignmentValueSymbol(location: Node): Symbol; - getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol; - getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol; + getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined; + getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined; + getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined; getTypeAtLocation(node: Node): Type; getTypeFromTypeNode(node: TypeNode): Type; signatureToString(signature: Signature, enclosingDeclaration?: Node, flags?: TypeFormatFlags, kind?: SignatureKind): string; @@ -2542,15 +2542,15 @@ namespace ts { getAugmentedPropertiesOfType(type: Type): Symbol[]; getRootSymbols(symbol: Symbol): Symbol[]; getContextualType(node: Expression): Type | undefined; - getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature; - getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature; - isImplementationOfOverload(node: FunctionLikeDeclaration): boolean; + getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[]): Signature | undefined; + getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature | undefined; + isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined; isUndefinedSymbol(symbol: Symbol): boolean; isArgumentsSymbol(symbol: Symbol): boolean; isUnknownSymbol(symbol: Symbol): boolean; /* @internal */ getMergedSymbol(symbol: Symbol): Symbol; - getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; + getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number | undefined; isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; /** Follow all aliases to get the original symbol. */ getAliasedSymbol(symbol: Symbol): Symbol; @@ -2560,7 +2560,7 @@ namespace ts { /** Unlike `getExportsOfModule`, this includes properties of an `export =` value. */ /* @internal */ getExportsAndPropertiesOfModule(moduleSymbol: Symbol): Symbol[]; - getAllAttributesTypeFromJsxOpeningLikeElement(elementNode: JsxOpeningLikeElement): Type; + getAllAttributesTypeFromJsxOpeningLikeElement(elementNode: JsxOpeningLikeElement): Type | undefined; getJsxIntrinsicTagNames(): Symbol[]; isOptionalParameter(node: ParameterDeclaration): boolean; getAmbientModules(): Symbol[]; @@ -2568,10 +2568,10 @@ namespace ts { tryGetMemberInModuleExports(memberName: string, moduleSymbol: Symbol): Symbol | undefined; getApparentType(type: Type): Type; getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined; - getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string; - /* @internal */ getBaseConstraintOfType(type: Type): Type; + getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined; + /* @internal */ getBaseConstraintOfType(type: Type): Type | undefined; - /* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol; + /* @internal */ tryFindAmbientModuleWithoutAugmentations(moduleName: string): Symbol | undefined; // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; @@ -2753,7 +2753,7 @@ namespace ts { getNodeCheckFlags(node: Node): NodeCheckFlags; isDeclarationVisible(node: Declaration): boolean; collectLinkedAliases(node: Identifier): Node[]; - isImplementationOfOverload(node: FunctionLikeDeclaration): boolean; + isImplementationOfOverload(node: FunctionLikeDeclaration): boolean | undefined; isRequiredInitializedParameter(node: ParameterDeclaration): boolean; writeTypeOfDeclaration(declaration: AccessorDeclaration | VariableLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; @@ -3135,7 +3135,7 @@ namespace ts { */ export interface TypeReference extends ObjectType { target: GenericType; // Type reference target - typeArguments: Type[]; // Type reference type arguments (undefined if none) + typeArguments?: Type[]; // Type reference type arguments (undefined if none) } // Generic class and interface types @@ -3265,7 +3265,7 @@ namespace ts { export interface Signature { declaration: SignatureDeclaration; // Originating declaration - typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) + typeParameters?: TypeParameter[]; // Type parameters (undefined if non-generic) parameters: Symbol[]; // Parameters /* @internal */ thisParameter?: Symbol; // symbol of this-type parameter From 0defde71850fe75b4ee3ef7e5386fd1d4d374fc6 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 16 May 2017 15:04:33 -0700 Subject: [PATCH 76/77] Services utilities: Combine `isInsideComment` with `isInComment` --- src/services/completions.ts | 2 +- src/services/services.ts | 4 +- src/services/utilities.ts | 84 ++++++++++++------------------------- 3 files changed, 30 insertions(+), 60 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 5bc2a268617..d80eaa9a7ed 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -359,7 +359,7 @@ namespace ts.Completions { start = timestamp(); // Completion not allowed inside comments, bail out if this is the case - const insideComment = isInsideComment(sourceFile, currentToken, position); + const insideComment = isInComment(sourceFile, position, currentToken); log("getCompletionData: Is inside comment: " + (timestamp() - start)); if (insideComment) { diff --git a/src/services/services.ts b/src/services/services.ts index 1b4678b5dbc..d35f1e7b8a5 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1853,8 +1853,8 @@ namespace ts { // OK, we have found a match in the file. This is only an acceptable match if // it is contained within a comment. - const token = getTokenAtPosition(sourceFile, matchPosition); - if (!isInsideComment(sourceFile, token, matchPosition)) { + + if (!isInComment(sourceFile, matchPosition)) { continue; } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 4ae9204f7ed..8a9976e00bb 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -256,37 +256,6 @@ namespace ts { getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node; } - /** Returns true if the position is within a comment */ - export function isInsideComment(sourceFile: SourceFile, token: Node, position: number): boolean { - // The position has to be: 1. in the leading trivia (before token.getStart()), and 2. within a comment - return position <= token.getStart(sourceFile) && - (isInsideCommentRange(getTrailingCommentRanges(sourceFile.text, token.getFullStart())) || - isInsideCommentRange(getLeadingCommentRanges(sourceFile.text, token.getFullStart()))); - - function isInsideCommentRange(comments: CommentRange[]): boolean { - return forEach(comments, comment => { - // either we are 1. completely inside the comment, or 2. at the end of the comment - if (comment.pos < position && position < comment.end) { - return true; - } - else if (position === comment.end) { - const text = sourceFile.text; - const width = comment.end - comment.pos; - // is single line comment or just /* - if (width <= 2 || text.charCodeAt(comment.pos + 1) === CharacterCodes.slash) { - return true; - } - else { - // is unterminated multi-line comment - return !(text.charCodeAt(comment.end - 1) === CharacterCodes.slash && - text.charCodeAt(comment.end - 2) === CharacterCodes.asterisk); - } - } - return false; - }); - } - } - export function getContainerNode(node: Node): Declaration { while (true) { node = node.parent; @@ -834,10 +803,6 @@ namespace ts { return false; } - export function isInComment(sourceFile: SourceFile, position: number) { - return isInCommentHelper(sourceFile, position, /*predicate*/ undefined); - } - /** * returns true if the position is in between the open and close elements of an JSX expression. */ @@ -883,15 +848,26 @@ namespace ts { } /** - * Returns true if the cursor at position in sourceFile is within a comment that additionally - * satisfies predicate, and false otherwise. + * Returns true if the cursor at position in sourceFile is within a comment. + * + * @param tokenAtPosition Must equal `getTokenAtPosition(sourceFile, position) + * @param predicate Additional predicate to test on the comment range. */ - export function isInCommentHelper(sourceFile: SourceFile, position: number, predicate?: (c: CommentRange) => boolean): boolean { - const token = getTokenAtPosition(sourceFile, position); + export function isInComment(sourceFile: SourceFile, position: number, tokenAtPosition = getTokenAtPosition(sourceFile, position), predicate?: (c: CommentRange) => boolean): boolean { + return position <= tokenAtPosition.getStart(sourceFile) && + (isInCommentRange(getLeadingCommentRanges(sourceFile.text, tokenAtPosition.pos)) || + isInCommentRange(getTrailingCommentRanges(sourceFile.text, tokenAtPosition.pos))); - if (token && position <= token.getStart(sourceFile)) { - const commentRanges = getLeadingCommentRanges(sourceFile.text, token.pos); + function isInCommentRange(commentRanges: CommentRange[]): boolean { + return forEach(commentRanges, c => isPositionInCommentRange(c, position, sourceFile.text) && (!predicate || predicate(c))); + } + } + function isPositionInCommentRange({ pos, end, kind }: ts.CommentRange, position: number, text: string): boolean { + if (pos < position && position < end) { + return true; + } + else if (position === end) { // The end marker of a single-line comment does not include the newline character. // In the following case, we are inside a comment (^ denotes the cursor position): // @@ -902,15 +878,13 @@ namespace ts { // /* asdf */^ // // Internally, we represent the end of the comment at the newline and closing '/', respectively. - return predicate ? - forEach(commentRanges, c => c.pos < position && - (c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end) && - predicate(c)) : - forEach(commentRanges, c => c.pos < position && - (c.kind === SyntaxKind.SingleLineCommentTrivia ? position <= c.end : position < c.end)); + return kind === SyntaxKind.SingleLineCommentTrivia || + // true for unterminated multi-line comment + !(text.charCodeAt(end - 1) === CharacterCodes.slash && text.charCodeAt(end - 2) === CharacterCodes.asterisk); + } + else { + return false; } - - return false; } export function hasDocComment(sourceFile: SourceFile, position: number) { @@ -1093,21 +1067,17 @@ namespace ts { } export function isInReferenceComment(sourceFile: SourceFile, position: number): boolean { - return isInCommentHelper(sourceFile, position, isReferenceComment); - - function isReferenceComment(c: CommentRange): boolean { + return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => { const commentText = sourceFile.text.substring(c.pos, c.end); return tripleSlashDirectivePrefixRegex.test(commentText); - } + }); } export function isInNonReferenceComment(sourceFile: SourceFile, position: number): boolean { - return isInCommentHelper(sourceFile, position, isNonReferenceComment); - - function isNonReferenceComment(c: CommentRange): boolean { + return isInComment(sourceFile, position, /*tokenAtPosition*/ undefined, c => { const commentText = sourceFile.text.substring(c.pos, c.end); return !tripleSlashDirectivePrefixRegex.test(commentText); - } + }); } export function createTextSpanFromNode(node: Node, sourceFile?: SourceFile): TextSpan { From 0820f692705e4b1118b0c9a8f57783073f80f8e6 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 16 May 2017 15:28:32 -0700 Subject: [PATCH 77/77] respond to comments --- src/compiler/checker.ts | 48 +++++++++++++++++++-------------------- src/compiler/emitter.ts | 2 +- src/compiler/factory.ts | 15 ++++++------ src/compiler/types.ts | 6 ++--- src/compiler/utilities.ts | 10 ++++---- src/compiler/visitor.ts | 2 -- 6 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0f2e5e9e0ff..e426a7bdbaf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2279,7 +2279,7 @@ namespace ts { } function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string { - const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.ignoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); + const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName); Debug.assert(typeNode !== undefined, "should always get typenode?"); const options = { removeComments: true }; const writer = createTextWriter(""); @@ -2361,7 +2361,7 @@ namespace ts { function typeToTypeNodeHelper(type: Type, context: NodeBuilderContext): TypeNode { const inTypeAlias = context.flags & NodeBuilderFlags.InTypeAlias; - context.flags &= ~(NodeBuilderFlags.InTypeAlias); + context.flags &= ~NodeBuilderFlags.InTypeAlias; if (!type) { context.encounteredError = true; @@ -2419,7 +2419,7 @@ namespace ts { return createKeywordTypeNode(SyntaxKind.ObjectKeyword); } if (type.flags & TypeFlags.TypeParameter && (type as TypeParameter).isThisType) { - if (context.flags & NodeBuilderFlags.inObjectTypeLiteral) { + if (context.flags & NodeBuilderFlags.InObjectTypeLiteral) { if (!context.encounteredError && !(context.flags & NodeBuilderFlags.AllowThisInObjectLiteral)) { context.encounteredError = true; } @@ -2441,12 +2441,12 @@ namespace ts { if (!inTypeAlias && type.aliasSymbol && isSymbolAccessible(type.aliasSymbol, context.enclosingDeclaration, SymbolFlags.Type, /*shouldComputeAliasesToMakeVisible*/ false).accessibility === SymbolAccessibility.Accessible) { const name = symbolToTypeReferenceName(type.aliasSymbol); - const typeArgumentNodes = type.aliasTypeArguments && mapToTypeNodeArray(type.aliasTypeArguments, context); + const typeArgumentNodes = mapToTypeNodes(type.aliasTypeArguments, context); return createTypeReferenceNode(name, typeArgumentNodes); } if (type.flags & (TypeFlags.Union | TypeFlags.Intersection)) { const types = type.flags & TypeFlags.Union ? formatUnionTypes((type).types) : (type).types; - const typeNodes = types && mapToTypeNodeArray(types, context); + const typeNodes = mapToTypeNodes(types, context); if (typeNodes && typeNodes.length > 0) { const unionOrIntersectionTypeNode = createUnionOrIntersectionTypeNode(type.flags & TypeFlags.Union ? SyntaxKind.UnionType : SyntaxKind.IntersectionType, typeNodes); return unionOrIntersectionTypeNode; @@ -2567,7 +2567,7 @@ namespace ts { } const savedFlags = context.flags; - context.flags |= NodeBuilderFlags.inObjectTypeLiteral; + context.flags |= NodeBuilderFlags.InObjectTypeLiteral; const members = createTypeNodesFromResolvedType(resolved); context.flags = savedFlags; const typeLiteralNode = createTypeLiteralNode(members); @@ -2598,8 +2598,7 @@ namespace ts { } else if (type.target.objectFlags & ObjectFlags.Tuple) { if (typeArguments.length > 0) { - const slice = typeArguments.slice(0, getTypeReferenceArity(type)); - const tupleConstituentNodes = slice && mapToTypeNodeArray(slice, context); + const tupleConstituentNodes = mapToTypeNodes(typeArguments.slice(0, getTypeReferenceArity(type)), context); if (tupleConstituentNodes && tupleConstituentNodes.length > 0) { return createTupleTypeNode(tupleConstituentNodes); } @@ -2625,8 +2624,8 @@ namespace ts { // When type parameters are their own type arguments for the whole group (i.e. we have // the default outer type arguments), we don't show the group. if (!rangeEquals(outerTypeParameters, typeArguments, start, i)) { - const slice = typeArguments.slice(start, i); - const typeArgumentNodes = slice && createNodeArray(mapToTypeNodeArray(slice, context)); + const typeArgumentSlice = mapToTypeNodes(typeArguments.slice(start, i), context); + const typeArgumentNodes = typeArgumentSlice && createNodeArray(typeArgumentSlice); const namePart = symbolToTypeReferenceName(parent); (namePart.kind === SyntaxKind.Identifier ? namePart : namePart.right).typeArguments = typeArgumentNodes; @@ -2654,10 +2653,9 @@ namespace ts { } let typeArgumentNodes: TypeNode[] | undefined; - if (some(typeArguments)) { + if (typeArguments.length > 0) { const typeParameterCount = (type.target.typeParameters || emptyArray).length; - const slice = typeArguments && typeArguments.slice(i, typeParameterCount); - typeArgumentNodes = slice && mapToTypeNodeArray(slice, context); + typeArgumentNodes = mapToTypeNodes(typeArguments.slice(i, typeParameterCount), context); } if (typeArgumentNodes) { @@ -2740,17 +2738,19 @@ namespace ts { } } - function mapToTypeNodeArray(types: Type[], context: NodeBuilderContext): TypeNode[] { - const result = []; - for (let i = 0; i < types.length; ++i) { - const type = types[i]; - const typeNode = typeToTypeNodeHelper(type, context); - if (typeNode) { - result.push(typeNode); + function mapToTypeNodes(types: Type[], context: NodeBuilderContext): TypeNode[] { + if (some(types)) { + const result = []; + for (let i = 0; i < types.length; ++i) { + const type = types[i]; + const typeNode = typeToTypeNodeHelper(type, context); + if (typeNode) { + result.push(typeNode); + } } - } - return result; + return result; + } } function indexInfoToIndexSignatureDeclarationHelper(indexInfo: IndexInfo, kind: IndexKind, context: NodeBuilderContext): IndexSignatureDeclaration { @@ -2890,9 +2890,7 @@ namespace ts { } } - if (typeParameters && typeParameters.length > 0) { - typeParameterNodes = mapToTypeNodeArray(typeParameters, context); - } + typeParameterNodes = mapToTypeNodes(typeParameters, context); } const symbolName = getNameOfSymbol(symbol, context); diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 826b636e71e..7917480975e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2655,7 +2655,7 @@ namespace ts { if (isIdentifier(textSourceNode)) { return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? `"${escapeString(getTextOfNode(textSourceNode))}"` : - `"${escapeNonAsciiCharacters(escapeString(getTextOfNode(textSourceNode)))}"`; + `"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`; } else { return getLiteralTextOfNode(textSourceNode); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 18e71f94ee3..1a5f1fbac5f 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -3607,16 +3607,17 @@ namespace ts { } export function parenthesizeElementTypeMembers(members: TypeNode[]) { - // TODO: does this lose `originalNode` ptr? - return createNodeArray(members.map(parenthesizeElementTypeMember)); + return createNodeArray(sameMap(members, parenthesizeElementTypeMember)); } export function parenthesizeTypeParameters(typeParameters: TypeNode[]) { - if (typeParameters && typeParameters.length > 0) { - const nodeArray = createNodeArray(typeParameters); - const firstEntry = nodeArray[0]; - if (isFunctionOrConstructor(firstEntry) && firstEntry.typeParameters) { - nodeArray[0] = createParenthesizedType(firstEntry); + if (some(typeParameters)) { + const nodeArray = createNodeArray() as NodeArray; + for (let i = 0; i < typeParameters.length; ++i) { + const entry = typeParameters[i]; + nodeArray.push(i === 0 && isFunctionOrConstructorTypeNode(entry) && entry.typeParameters ? + createParenthesizedType(entry) : + entry); } return nodeArray; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4bc97c56c00..3ac3caa1216 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -580,7 +580,7 @@ namespace ts { /*@internal*/ autoGenerateKind?: GeneratedIdentifierKind; // Specifies whether to auto-generate the text for an identifier. /*@internal*/ autoGenerateId?: number; // Ensures unique generated identifiers get unique names, but clones get the same name. isInJSDocNamespace?: boolean; // if the node is a member in a JSDoc namespace - /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes.Though not syntactically valid, used in emitting diagnostics. + /*@internal*/ typeArguments?: NodeArray; // Only defined on synthesized nodes. Though not syntactically valid, used in emitting diagnostics. } // Transient identifier node (marked by id === -1) @@ -2599,10 +2599,10 @@ namespace ts { AllowEmptyUnionOrIntersection = 1 << 14, AllowEmptyTuple = 1 << 15, - ignoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple, + IgnoreErrors = AllowThisInObjectLiteral | AllowQualifedNameInPlaceOfIdentifier | AllowAnonymousIdentifier | AllowEmptyUnionOrIntersection | AllowEmptyTuple, // State - inObjectTypeLiteral = 1 << 20, + InObjectTypeLiteral = 1 << 20, InTypeAlias = 1 << 23, // Writing type in type alias declaration } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9dac18eec89..c8e481bc2e9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -328,9 +328,8 @@ namespace ts { return getSourceTextOfNodeFromSourceFile(sourceFile, node); } - const escapeText = getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? - (text: string) => escapeString(text) : - (text: string) => escapeNonAsciiCharacters(escapeString(text)); + const escapeText = getEmitFlags(node) & EmitFlags.NoAsciiEscaping ? escapeString : escapeNonAsciiString; + // If we can't reach the original source text, use the canonical form if it's a number, // or a (possibly escaped) quoted form of the original text if it's string-like. switch (node.kind) { @@ -880,7 +879,7 @@ namespace ts { return false; } - export function isFunctionOrConstructor(node: Node): node is FunctionTypeNode | ConstructorTypeNode { + export function isFunctionOrConstructorTypeNode(node: Node): node is FunctionTypeNode | ConstructorTypeNode { switch (node.kind) { case SyntaxKind.FunctionType: case SyntaxKind.ConstructorType: @@ -2468,7 +2467,8 @@ namespace ts { } const nonAsciiCharacters = /[^\u0000-\u007F]/g; - export function escapeNonAsciiCharacters(s: string): string { + export function escapeNonAsciiString(s: string): string { + s = escapeString(s); // Replace non-ASCII characters with '\uNNNN' escapes if any exist. // Otherwise just return the original string. return nonAsciiCharacters.test(s) ? diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index c62af0da794..f8fe92c184c 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -988,8 +988,6 @@ namespace ts { result = reduceNode((node).initializer, cbNode, result); break; - case SyntaxKind.PropertySignature: - case SyntaxKind.MethodDeclaration: result = reduceNodes((node).decorators, cbNodes, result); result = reduceNodes((node).modifiers, cbNodes, result);