From 12f5dd85d71b6ffddec54d74a6875dd0b82bdd8e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Sep 2017 06:33:47 -0700 Subject: [PATCH 01/56] Introduce --strictFunctionTypes mode --- src/compiler/checker.ts | 82 +++++++++++++++++++++++----- src/compiler/commandLineParser.ts | 7 +++ src/compiler/diagnosticMessages.json | 4 ++ src/compiler/types.ts | 10 ++++ 4 files changed, 90 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 62d44d160a7..33d8b9249da 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -66,6 +66,7 @@ namespace ts { const noUnusedIdentifiers = !!compilerOptions.noUnusedLocals || !!compilerOptions.noUnusedParameters; const allowSyntheticDefaultImports = typeof compilerOptions.allowSyntheticDefaultImports !== "undefined" ? compilerOptions.allowSyntheticDefaultImports : modulekind === ModuleKind.System; const strictNullChecks = compilerOptions.strictNullChecks === undefined ? compilerOptions.strict : compilerOptions.strictNullChecks; + const strictFunctionTypes = compilerOptions.strictFunctionTypes === undefined ? compilerOptions.strict : compilerOptions.strictFunctionTypes; const noImplicitAny = compilerOptions.noImplicitAny === undefined ? compilerOptions.strict : compilerOptions.noImplicitAny; const noImplicitThis = compilerOptions.noImplicitThis === undefined ? compilerOptions.strict : compilerOptions.noImplicitThis; @@ -2517,7 +2518,7 @@ namespace ts { return typeReferenceToTypeNode(type); } if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) { - const name = symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false); + const name = type.symbol ? symbolToName(type.symbol, context, SymbolFlags.Type, /*expectsIdentifier*/ false) : createIdentifier("?"); // Ignore constraint/default when creating a usage (as opposed to declaration) of a type parameter. return createTypeReferenceNode(name, /*typeArguments*/ undefined); } @@ -6729,7 +6730,7 @@ namespace ts { } function getConstraintDeclaration(type: TypeParameter) { - return getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter).constraint; + return type.symbol && getDeclarationOfKind(type.symbol, SyntaxKind.TypeParameter).constraint; } function getConstraintFromTypeParameter(typeParameter: TypeParameter): Type { @@ -8541,6 +8542,9 @@ namespace ts { source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } + const targetKind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; + const strictVariance = strictFunctionTypes && targetKind !== SyntaxKind.MethodDeclaration && targetKind !== SyntaxKind.MethodSignature; + let result = Ternary.True; const sourceThisType = getThisTypeOfSignature(source); @@ -8548,7 +8552,7 @@ namespace ts { const targetThisType = getThisTypeOfSignature(target); if (targetThisType) { // void sources are assignable to anything. - const related = compareTypes(sourceThisType, targetThisType, /*reportErrors*/ false) + const related = !strictVariance && compareTypes(sourceThisType, targetThisType, /*reportErrors*/ false) || compareTypes(targetThisType, sourceThisType, reportErrors); if (!related) { if (reportErrors) { @@ -8582,7 +8586,7 @@ namespace ts { (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable); const related = callbacks ? compareSignaturesRelated(targetSig, sourceSig, /*checkAsCallback*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) : - !checkAsCallback && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); + !checkAsCallback && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors); if (!related) { if (reportErrors) { errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, @@ -9194,7 +9198,7 @@ namespace ts { return result; } - function typeArgumentsRelatedTo(source: TypeReference, target: TypeReference, reportErrors: boolean): Ternary { + function typeArgumentsRelatedTo(source: TypeReference, target: TypeReference, variances: Variance[], reportErrors: boolean): Ternary { const sources = source.typeArguments || emptyArray; const targets = target.typeArguments || emptyArray; if (sources.length !== targets.length && relation === identityRelation) { @@ -9203,11 +9207,34 @@ namespace ts { const length = sources.length <= targets.length ? sources.length : targets.length; let result = Ternary.True; for (let i = 0; i < length; i++) { - const related = isRelatedTo(sources[i], targets[i], reportErrors); - if (!related) { - return Ternary.False; + const variance = i < variances.length ? variances[i] : Variance.Covariant; + if (variance !== Variance.Omnivariant) { + const s = sources[i]; + const t = targets[i]; + let related = Ternary.True; + if (variance === Variance.Covariant) { + related = isRelatedTo(s, t, reportErrors); + } + else if (variance === Variance.Contravariant) { + related = isRelatedTo(t, s, reportErrors); + } + else if (variance === Variance.Bivariant) { + related = isRelatedTo(t, s, /*reportErrors*/ false); + if (!related) { + related = isRelatedTo(s, t, reportErrors); + } + } + else { + related = isRelatedTo(s, t, reportErrors); + if (related) { + related &= isRelatedTo(t, s, reportErrors); + } + } + if (!related) { + return Ternary.False; + } + result &= related; } - result &= related; } return result; } @@ -9371,9 +9398,15 @@ namespace ts { } else { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { - // We have type references to same target type, see if relationship holds for all type arguments - if (result = typeArgumentsRelatedTo(source, target, reportErrors)) { - return result; + const variances = getVariances((source).target); + if (variances) { + // We have type references to same target type, see if relationship holds for all type arguments + if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { + return result; + } + if (variances !== emptyArray) { + return Ternary.False; + } } } // Even if relationship doesn't hold for unions, intersections, or generic type references, @@ -9785,6 +9818,29 @@ namespace ts { } } + function getVarianceType(type: GenericType, source: TypeParameter, target: Type) { + return createTypeReference(type, map(type.typeParameters, t => t === source ? target: t)); + } + + function getVariances(type: GenericType) { + const typeParameters = type.typeParameters || emptyArray; + let variances = type.variances; + if (!variances) { + variances = type.variances = []; + for (const tp of typeParameters) { + const superType = getVarianceType(type, tp, stringType); + const subType = getVarianceType(type, tp, emptyStringType); + let variance = (isTypeAssignableTo(subType, superType) ? Variance.Covariant : 0) | + (isTypeAssignableTo(superType, subType) ? Variance.Contravariant : 0); + if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, numberType), superType)) { + variance = Variance.Omnivariant; + } + variances.push(variance); + } + } + return variances.length === typeParameters.length ? variances : emptyArray; + } + function isUnconstrainedTypeParameter(type: Type) { return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(type); } @@ -18856,7 +18912,7 @@ namespace ts { const typeArgument = typeArguments[i]; result = result && checkTypeAssignableTo( typeArgument, - getTypeWithThisArgument(instantiateType(constraint, mapper), typeArgument), + instantiateType(constraint, mapper), typeArgumentNodes[i], Diagnostics.Type_0_does_not_satisfy_the_constraint_1); } diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 54e5ee1d01d..b63442bc89a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -269,6 +269,13 @@ namespace ts { category: Diagnostics.Strict_Type_Checking_Options, description: Diagnostics.Enable_strict_null_checks }, + { + name: "strictFunctionTypes", + type: "boolean", + showInSimplifiedHelpView: true, + category: Diagnostics.Strict_Type_Checking_Options, + description: Diagnostics.Enable_strict_checking_of_function_types + }, { name: "noImplicitThis", type: "boolean", diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8a76ddcda9a..3a9857e09ea 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3318,6 +3318,10 @@ "category": "Message", "code": 6185 }, + "Enable strict checking of function types.": { + "category": "Message", + "code": 6186 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5347d7caaf0..cddfcee6413 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3347,6 +3347,7 @@ namespace ts { export interface GenericType extends InterfaceType, TypeReference { /* @internal */ instantiations: Map; // Generic instantiation cache + variances?: Variance[]; } export interface UnionOrIntersectionType extends Type { @@ -3440,6 +3441,14 @@ namespace ts { resolvedIndexType: IndexType; } + export const enum Variance { + Invariant = 0, + Covariant = 1, + Contravariant = 2, + Bivariant = Covariant | Contravariant, + Omnivariant = 4 + } + // Type parameters (TypeFlags.TypeParameter) export interface TypeParameter extends TypeVariable { /** Retrieve using getConstraintFromTypeParameter */ @@ -3707,6 +3716,7 @@ namespace ts { sourceMap?: boolean; sourceRoot?: string; strict?: boolean; + strictFunctionTypes?: boolean; // Always combine with strict property strictNullChecks?: boolean; // Always combine with strict property /* @internal */ stripInternal?: boolean; suppressExcessPropertyErrors?: boolean; From f8ff7f73651ac87c50d5a0c6ff16125011be3b6c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Sep 2017 13:36:46 -0700 Subject: [PATCH 02/56] Use dedicated marker types for variance determination --- src/compiler/checker.ts | 16 +++++++++++----- src/compiler/types.ts | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 33d8b9249da..f98e7bde2ad 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -282,6 +282,10 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); + const markerSuperType = createType(TypeFlags.MarkerType); + const markerSubType = createType(TypeFlags.MarkerType); + const markerOtherType = createType(TypeFlags.MarkerType); + const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); @@ -8944,6 +8948,10 @@ namespace ts { if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; + if (source.flags & TypeFlags.MarkerType && target.flags & TypeFlags.MarkerType) { + return source === markerSubType && target === markerSuperType ? Ternary.True : Ternary.False; + } + if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { @@ -9367,8 +9375,6 @@ namespace ts { if (!constraint || constraint.flags & TypeFlags.Any) { constraint = emptyObjectType; } - // The constraint may need to be further instantiated with its 'this' type. - constraint = getTypeWithThisArgument(constraint, source); // Report constraint errors only if the constraint is not the empty object type const reportConstraintErrors = reportErrors && constraint !== emptyObjectType; if (result = isRelatedTo(constraint, target, reportConstraintErrors)) { @@ -9828,11 +9834,11 @@ namespace ts { if (!variances) { variances = type.variances = []; for (const tp of typeParameters) { - const superType = getVarianceType(type, tp, stringType); - const subType = getVarianceType(type, tp, emptyStringType); + const superType = getVarianceType(type, tp, markerSuperType); + const subType = getVarianceType(type, tp, markerSubType); let variance = (isTypeAssignableTo(subType, superType) ? Variance.Covariant : 0) | (isTypeAssignableTo(superType, subType) ? Variance.Contravariant : 0); - if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, numberType), superType)) { + if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, markerOtherType), superType)) { variance = Variance.Omnivariant; } variances.push(variance); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cddfcee6413..a837722cc89 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3215,6 +3215,7 @@ namespace ts { NonPrimitive = 1 << 24, // intrinsic object type /* @internal */ JsxAttributes = 1 << 25, // Jsx attributes type + MarkerType = 1 << 26, // Marker type used for variance probing /* @internal */ Nullable = Undefined | Null, From 670d7113dac4624285e305ddb51ba85b72f7236e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Sep 2017 13:56:59 -0700 Subject: [PATCH 03/56] Add quick path for computing array variance as it is already known --- src/compiler/checker.ts | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f98e7bde2ad..65c95344c67 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9832,19 +9832,26 @@ namespace ts { const typeParameters = type.typeParameters || emptyArray; let variances = type.variances; if (!variances) { - variances = type.variances = []; - for (const tp of typeParameters) { - const superType = getVarianceType(type, tp, markerSuperType); - const subType = getVarianceType(type, tp, markerSubType); - let variance = (isTypeAssignableTo(subType, superType) ? Variance.Covariant : 0) | - (isTypeAssignableTo(superType, subType) ? Variance.Contravariant : 0); - if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, markerOtherType), superType)) { - variance = Variance.Omnivariant; - } - variances.push(variance); + if (type === globalArrayType || type === globalReadonlyArrayType) { + variances = [Variance.Covariant]; } + else { + type.variances = emptyArray; + variances = []; + for (const tp of typeParameters) { + const superType = getVarianceType(type, tp, markerSuperType); + const subType = getVarianceType(type, tp, markerSubType); + let variance = (isTypeAssignableTo(subType, superType) ? Variance.Covariant : 0) | + (isTypeAssignableTo(superType, subType) ? Variance.Contravariant : 0); + if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, markerOtherType), superType)) { + variance = Variance.Omnivariant; + } + variances.push(variance); + } + } + type.variances = variances; } - return variances.length === typeParameters.length ? variances : emptyArray; + return variances; } function isUnconstrainedTypeParameter(type: Type) { From a0fa69ff6c8ffc2ee533407f57ea53d61a28de8c Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 18 Sep 2017 17:31:54 -0700 Subject: [PATCH 04/56] Handle contravariance in type inference --- src/compiler/checker.ts | 30 ++++++++++++++++++++++++++---- src/compiler/types.ts | 7 ++++--- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65c95344c67..ae461119e8b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10129,6 +10129,10 @@ namespace ts { getUnionType(types, /*subtypeReduction*/ true); } + function getCommonSubtype(types: Type[]) { + return reduceLeft(types, (s, t) => isTypeSubtypeOf(t, s) ? t : s); + } + function isArrayType(type: Type): boolean { return getObjectFlags(type) & ObjectFlags.Reference && (type).target === globalArrayType; } @@ -10655,8 +10659,14 @@ namespace ts { const sourceTypes = (source).typeArguments || emptyArray; const targetTypes = (target).typeArguments || emptyArray; const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; + const variances = strictFunctionTypes ? getVariances((source).target) : undefined; for (let i = 0; i < count; i++) { - inferFromTypes(sourceTypes[i], targetTypes[i]); + if (variances && i < variances.length && variances[i] === Variance.Contravariant) { + inferFromContravariantTypes(sourceTypes[i], targetTypes[i]); + } + else { + inferFromTypes(sourceTypes[i], targetTypes[i]); + } } } else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) { @@ -10727,6 +10737,17 @@ namespace ts { } } + function inferFromContravariantTypes(source: Type, target: Type) { + if (strictFunctionTypes) { + priority ^= InferencePriority.Contravariant; + inferFromTypes(source, target); + priority ^= InferencePriority.Contravariant; + } + else { + inferFromTypes(source, target); + } + } + function getInferenceInfoForType(type: Type) { if (type.flags & TypeFlags.TypeVariable) { for (const inference of inferences) { @@ -10804,7 +10825,7 @@ namespace ts { } function inferFromSignature(source: Signature, target: Signature) { - forEachMatchingParameterType(source, target, inferFromTypes); + forEachMatchingParameterType(source, target, inferFromContravariantTypes); if (source.typePredicate && target.typePredicate && source.typePredicate.kind === target.typePredicate.kind) { inferFromTypes(source.typePredicate.type, target.typePredicate.type); @@ -10879,8 +10900,9 @@ namespace ts { const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) : inference.candidates; // Infer widened union or supertype, or the unknown type for no common supertype. We infer union types // for inferences coming from return types in order to avoid common supertype failures. - const unionOrSuperType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? - getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates); + const unionOrSuperType = inference.priority & InferencePriority.Contravariant ? getCommonSubtype(baseCandidates) : + context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, /*subtypeReduction*/ true) : + getCommonSupertype(baseCandidates); inferredType = getWidenedType(unionOrSuperType); } else if (context.flags & InferenceFlags.NoDefault) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a837722cc89..67974774ab5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3532,9 +3532,10 @@ namespace ts { } export const enum InferencePriority { - NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type - MappedType = 1 << 1, // Reverse inference for mapped type - ReturnType = 1 << 2, // Inference made from return type of generic function + Contravariant = 1 << 0, // Contravariant inference + NakedTypeVariable = 1 << 1, // Naked type variable in union or intersection type + MappedType = 1 << 2, // Reverse inference for mapped type + ReturnType = 1 << 3, // Inference made from return type of generic function } export interface InferenceInfo { From b58e0fba0ca692a8c1eee06a1967a9f445dcdbdc Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Sep 2017 10:11:18 -0700 Subject: [PATCH 05/56] Add comments --- src/compiler/checker.ts | 65 ++++++++++++++++++++++++++++++----------- src/compiler/types.ts | 22 +++++++------- 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ae461119e8b..8db4466cb86 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9215,7 +9215,11 @@ namespace ts { const length = sources.length <= targets.length ? sources.length : targets.length; let result = Ternary.True; for (let i = 0; i < length; i++) { + // When variance information isn't available we default to covariance. This happens + // in the process of computing variance information for recursive types and when + // comparing 'this' type arguments. const variance = i < variances.length ? variances[i] : Variance.Covariant; + // We simply ignore omnivariant type arguments (because they're never witnessed). if (variance !== Variance.Omnivariant) { const s = sources[i]; const t = targets[i]; @@ -9227,12 +9231,19 @@ namespace ts { related = isRelatedTo(t, s, reportErrors); } else if (variance === Variance.Bivariant) { + // In the bivariant case we first compare contravariantly without reporting + // errors. Then, if that doesn't succeed, we compare covariantly with error + // reporting. Thus, error elaboration will be based on the the covariant check, + // which is generally easier to reason about. related = isRelatedTo(t, s, /*reportErrors*/ false); if (!related) { related = isRelatedTo(s, t, reportErrors); } } else { + // In the invariant case we first compare covariantly, and only when that + // succeeds do we proceed to compare contravariantly. Thus, error elaboration + // will typically be based on the covariant check. related = isRelatedTo(s, t, reportErrors); if (related) { related &= isRelatedTo(t, s, reportErrors); @@ -9404,15 +9415,18 @@ namespace ts { } else { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { + // We have type references to the same generic type. Obtain the variance information for the + // type parameters and relate the type arguments accordingly. const variances = getVariances((source).target); - if (variances) { - // We have type references to same target type, see if relationship holds for all type arguments - if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { - return result; - } - if (variances !== emptyArray) { - return Ternary.False; - } + if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { + return result; + } + // The type arguments did not relate appropriately, but it may be because getVariances was + // invoked recursively and returned emptyArray (in which case typeArgumentsRelatedTo defaults + // to covariance for all type arguments). In that case we need to contine with a structural + // comparison. Otherwise, we know for certain the instantiations aren't related. + if (variances !== emptyArray) { + return Ternary.False; } } // Even if relationship doesn't hold for unions, intersections, or generic type references, @@ -9828,22 +9842,37 @@ namespace ts { return createTypeReference(type, map(type.typeParameters, t => t === source ? target: t)); } + // Return an array containing the variance of each type parameter. The variance is effectively + // a digest of the type comparisons that occur for each type argument when instantiations of the + // generic type are structurally compared. We infer the variance information by comparing + // instantiations of the generic type for type arguments with known relations. Note that the + // function returns the emptyArray singleton to signal that it has been invoked recursively for + // the given generic type. function getVariances(type: GenericType) { const typeParameters = type.typeParameters || emptyArray; let variances = type.variances; if (!variances) { if (type === globalArrayType || type === globalReadonlyArrayType) { + // Arrays are known to be covariant, no need to spend time computing this variances = [Variance.Covariant]; } else { + // The emptyArray singleton is used to signal a recursive invocation. type.variances = emptyArray; variances = []; for (const tp of typeParameters) { - const superType = getVarianceType(type, tp, markerSuperType); - const subType = getVarianceType(type, tp, markerSubType); - let variance = (isTypeAssignableTo(subType, superType) ? Variance.Covariant : 0) | - (isTypeAssignableTo(superType, subType) ? Variance.Contravariant : 0); - if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, markerOtherType), superType)) { + // We first compare instantiations where the type parameter is replaced with + // marker types that have a known subtype relationship. From this we can infer + // invariance, covariance, contravariance or bivariance. + const typeWithSuper = getVarianceType(type, tp, markerSuperType); + const typeWithSub = getVarianceType(type, tp, markerSubType); + let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : 0) | + (isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : 0); + // If the instantiations appear to be related bivariantly, it may be because the + // type parameter is omnivariant (i.e. it isn't witnessed anywhere in the generic + // type). To determine this we compare instantiations where the type parameter is + // replaced with marker types that are known to be unrelated. + if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, markerOtherType), typeWithSuper)) { variance = Variance.Omnivariant; } variances.push(variance); @@ -10129,6 +10158,7 @@ namespace ts { getUnionType(types, /*subtypeReduction*/ true); } + // Return the leftmost type for which no type to the right is a subtype. function getCommonSubtype(types: Type[]) { return reduceLeft(types, (s, t) => isTypeSubtypeOf(t, s) ? t : s); } @@ -10898,12 +10928,13 @@ namespace ts { !hasPrimitiveConstraint(inference.typeParameter) && (inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter)); const baseCandidates = widenLiteralTypes ? sameMap(inference.candidates, getWidenedLiteralType) : inference.candidates; - // Infer widened union or supertype, or the unknown type for no common supertype. We infer union types - // for inferences coming from return types in order to avoid common supertype failures. - const unionOrSuperType = inference.priority & InferencePriority.Contravariant ? getCommonSubtype(baseCandidates) : + // If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if + // union types were requested or if all inferences were made from the return type position, infer a + // union type. Otherwise, infer a common supertype. + const unwidenedType = inference.priority & InferencePriority.Contravariant ? getCommonSubtype(baseCandidates) : context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates); - inferredType = getWidenedType(unionOrSuperType); + inferredType = getWidenedType(unwidenedType); } else if (context.flags & InferenceFlags.NoDefault) { // We use silentNeverType as the wildcard that signals no inferences. diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 67974774ab5..3f07716fd1b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3344,11 +3344,19 @@ namespace ts { typeArguments?: Type[]; // Type reference type arguments (undefined if none) } + export const enum Variance { + Invariant = 0, // Both covariant and contravariant + Covariant = 1, // Covariant + Contravariant = 2, // Contravariant + Bivariant = 3, // Either covariant or contravariant + Omnivariant = 4 // Unwitnessed type parameter + } + // Generic class and interface types export interface GenericType extends InterfaceType, TypeReference { /* @internal */ - instantiations: Map; // Generic instantiation cache - variances?: Variance[]; + instantiations: Map; // Generic instantiation cache + variances?: Variance[]; // Variance of each type parameter } export interface UnionOrIntersectionType extends Type { @@ -3442,14 +3450,6 @@ namespace ts { resolvedIndexType: IndexType; } - export const enum Variance { - Invariant = 0, - Covariant = 1, - Contravariant = 2, - Bivariant = Covariant | Contravariant, - Omnivariant = 4 - } - // Type parameters (TypeFlags.TypeParameter) export interface TypeParameter extends TypeVariable { /** Retrieve using getConstraintFromTypeParameter */ @@ -3532,7 +3532,7 @@ namespace ts { } export const enum InferencePriority { - Contravariant = 1 << 0, // Contravariant inference + Contravariant = 1 << 0, // Inference from contravariant position NakedTypeVariable = 1 << 1, // Naked type variable in union or intersection type MappedType = 1 << 2, // Reverse inference for mapped type ReturnType = 1 << 3, // Inference made from return type of generic function From 84f7afd29e3082d5279594aee745e68f958ebc88 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Sep 2017 13:29:03 -0700 Subject: [PATCH 06/56] Handle special case of 'void' type arguments for covariant type parameters --- src/compiler/checker.ts | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8db4466cb86..4bacb88ef4a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9422,10 +9422,13 @@ namespace ts { return result; } // The type arguments did not relate appropriately, but it may be because getVariances was - // invoked recursively and returned emptyArray (in which case typeArgumentsRelatedTo defaults - // to covariance for all type arguments). In that case we need to contine with a structural - // comparison. Otherwise, we know for certain the instantiations aren't related. - if (variances !== emptyArray) { + // invoked recursively and returned emptyArray (in which case typeArgumentsRelatedTo defaulted + // to covariance for all type arguments). It might also be the case that the target type has a + // 'void' type argument for a covariant type parameter that is only used in return positions + // within the generic type (in which case any type argument is permitted on the source side). + // In those cases we proceed with a structural comparison. Otherwise, we know for certain the + // instantiations aren't related and we can return here. + if (variances !== emptyArray && !hasCovariantVoidArgument(target, variances)) { return Ternary.False; } } @@ -9839,7 +9842,7 @@ namespace ts { } function getVarianceType(type: GenericType, source: TypeParameter, target: Type) { - return createTypeReference(type, map(type.typeParameters, t => t === source ? target: t)); + return createTypeReference(type, map(type.typeParameters, t => t === source ? target : t)); } // Return an array containing the variance of each type parameter. The variance is effectively @@ -9848,7 +9851,7 @@ namespace ts { // instantiations of the generic type for type arguments with known relations. Note that the // function returns the emptyArray singleton to signal that it has been invoked recursively for // the given generic type. - function getVariances(type: GenericType) { + function getVariances(type: GenericType): Variance[] { const typeParameters = type.typeParameters || emptyArray; let variances = type.variances; if (!variances) { @@ -9883,6 +9886,17 @@ namespace ts { return variances; } + // Return true if the given type reference has a 'void' type argument for a covariant type parameter. + // See comment at call in recursiveTypeRelatedTo for when this case matters. + function hasCovariantVoidArgument(type: TypeReference, variances: Variance[]): boolean { + for (let i = 0; i < variances.length; i++) { + if (variances[i] === Variance.Covariant && type.typeArguments[i].flags & TypeFlags.Void) { + return true; + } + } + return false; + } + function isUnconstrainedTypeParameter(type: Type) { return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(type); } From 54eadef408884239025aa70c16d933e0c285d924 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Sep 2017 13:30:18 -0700 Subject: [PATCH 07/56] Accept new baselines --- tests/baselines/reference/fuzzy.errors.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/baselines/reference/fuzzy.errors.txt b/tests/baselines/reference/fuzzy.errors.txt index c3f05fd7f2f..b32f225a33e 100644 --- a/tests/baselines/reference/fuzzy.errors.txt +++ b/tests/baselines/reference/fuzzy.errors.txt @@ -4,6 +4,7 @@ tests/cases/compiler/fuzzy.ts(21,13): error TS2322: Type '{ anything: number; on Types of property 'oneI' are incompatible. Type 'this' is not assignable to type 'I'. Type 'C' is not assignable to type 'I'. + Property 'alsoWorks' is missing in type 'C'. tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' cannot be converted to type 'R'. Property 'anything' is missing in type '{ oneI: this; }'. @@ -38,6 +39,7 @@ tests/cases/compiler/fuzzy.ts(25,20): error TS2352: Type '{ oneI: this; }' canno !!! error TS2322: Types of property 'oneI' are incompatible. !!! error TS2322: Type 'this' is not assignable to type 'I'. !!! error TS2322: Type 'C' is not assignable to type 'I'. +!!! error TS2322: Property 'alsoWorks' is missing in type 'C'. } worksToo():R { From 44cc8c5ffe53d0fd034275fe4f6ebe809d58db43 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Sep 2017 14:23:01 -0700 Subject: [PATCH 08/56] Use methods in dom.generated.d.ts to opt out of strict checks --- src/lib/dom.generated.d.ts | 214 ++++++++++++++++++------------------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index d4ee81e4974..93bfb982955 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -4230,25 +4230,25 @@ interface HTMLBodyElement extends HTMLElement { bgProperties: string; link: any; noWrap: boolean; - onafterprint: (this: HTMLBodyElement, ev: Event) => any; - onbeforeprint: (this: HTMLBodyElement, ev: Event) => any; - onbeforeunload: (this: HTMLBodyElement, ev: BeforeUnloadEvent) => any; - onblur: (this: HTMLBodyElement, ev: FocusEvent) => any; - onerror: (this: HTMLBodyElement, ev: ErrorEvent) => any; - onfocus: (this: HTMLBodyElement, ev: FocusEvent) => any; - onhashchange: (this: HTMLBodyElement, ev: HashChangeEvent) => any; - onload: (this: HTMLBodyElement, ev: Event) => any; - onmessage: (this: HTMLBodyElement, ev: MessageEvent) => any; - onoffline: (this: HTMLBodyElement, ev: Event) => any; - ononline: (this: HTMLBodyElement, ev: Event) => any; - onorientationchange: (this: HTMLBodyElement, ev: Event) => any; - onpagehide: (this: HTMLBodyElement, ev: PageTransitionEvent) => any; - onpageshow: (this: HTMLBodyElement, ev: PageTransitionEvent) => any; - onpopstate: (this: HTMLBodyElement, ev: PopStateEvent) => any; - onresize: (this: HTMLBodyElement, ev: UIEvent) => any; - onscroll: (this: HTMLBodyElement, ev: UIEvent) => any; - onstorage: (this: HTMLBodyElement, ev: StorageEvent) => any; - onunload: (this: HTMLBodyElement, ev: Event) => any; + onafterprint(this: HTMLBodyElement, ev: Event): any; + onbeforeprint(this: HTMLBodyElement, ev: Event): any; + onbeforeunload(this: HTMLBodyElement, ev: BeforeUnloadEvent): any; + onblur(this: HTMLBodyElement, ev: FocusEvent): any; + onerror(this: HTMLBodyElement, ev: ErrorEvent): any; + onfocus(this: HTMLBodyElement, ev: FocusEvent): any; + onhashchange(this: HTMLBodyElement, ev: HashChangeEvent): any; + onload(this: HTMLBodyElement, ev: Event): any; + onmessage(this: HTMLBodyElement, ev: MessageEvent): any; + onoffline(this: HTMLBodyElement, ev: Event): any; + ononline(this: HTMLBodyElement, ev: Event): any; + onorientationchange(this: HTMLBodyElement, ev: Event): any; + onpagehide(this: HTMLBodyElement, ev: PageTransitionEvent): any; + onpageshow(this: HTMLBodyElement, ev: PageTransitionEvent): any; + onpopstate(this: HTMLBodyElement, ev: PopStateEvent): any; + onresize(this: HTMLBodyElement, ev: UIEvent): any; + onscroll(this: HTMLBodyElement, ev: UIEvent): any; + onstorage(this: HTMLBodyElement, ev: StorageEvent): any; + onunload(this: HTMLBodyElement, ev: Event): any; text: any; vLink: any; addEventListener(type: K, listener: (this: HTMLBodyElement, ev: HTMLBodyElementEventMap[K]) => any, useCapture?: boolean): void; @@ -4565,73 +4565,73 @@ interface HTMLElement extends Element { readonly offsetParent: Element; readonly offsetTop: number; readonly offsetWidth: number; - onabort: (this: HTMLElement, ev: UIEvent) => any; - onactivate: (this: HTMLElement, ev: UIEvent) => any; - onbeforeactivate: (this: HTMLElement, ev: UIEvent) => any; - onbeforecopy: (this: HTMLElement, ev: ClipboardEvent) => any; - onbeforecut: (this: HTMLElement, ev: ClipboardEvent) => any; - onbeforedeactivate: (this: HTMLElement, ev: UIEvent) => any; - onbeforepaste: (this: HTMLElement, ev: ClipboardEvent) => any; - onblur: (this: HTMLElement, ev: FocusEvent) => any; - oncanplay: (this: HTMLElement, ev: Event) => any; - oncanplaythrough: (this: HTMLElement, ev: Event) => any; - onchange: (this: HTMLElement, ev: Event) => any; - onclick: (this: HTMLElement, ev: MouseEvent) => any; - oncontextmenu: (this: HTMLElement, ev: PointerEvent) => any; - oncopy: (this: HTMLElement, ev: ClipboardEvent) => any; - oncuechange: (this: HTMLElement, ev: Event) => any; - oncut: (this: HTMLElement, ev: ClipboardEvent) => any; - ondblclick: (this: HTMLElement, ev: MouseEvent) => any; - ondeactivate: (this: HTMLElement, ev: UIEvent) => any; - ondrag: (this: HTMLElement, ev: DragEvent) => any; - ondragend: (this: HTMLElement, ev: DragEvent) => any; - ondragenter: (this: HTMLElement, ev: DragEvent) => any; - ondragleave: (this: HTMLElement, ev: DragEvent) => any; - ondragover: (this: HTMLElement, ev: DragEvent) => any; - ondragstart: (this: HTMLElement, ev: DragEvent) => any; - ondrop: (this: HTMLElement, ev: DragEvent) => any; - ondurationchange: (this: HTMLElement, ev: Event) => any; - onemptied: (this: HTMLElement, ev: Event) => any; - onended: (this: HTMLElement, ev: MediaStreamErrorEvent) => any; - onerror: (this: HTMLElement, ev: ErrorEvent) => any; - onfocus: (this: HTMLElement, ev: FocusEvent) => any; - oninput: (this: HTMLElement, ev: Event) => any; - oninvalid: (this: HTMLElement, ev: Event) => any; - onkeydown: (this: HTMLElement, ev: KeyboardEvent) => any; - onkeypress: (this: HTMLElement, ev: KeyboardEvent) => any; - onkeyup: (this: HTMLElement, ev: KeyboardEvent) => any; - onload: (this: HTMLElement, ev: Event) => any; - onloadeddata: (this: HTMLElement, ev: Event) => any; - onloadedmetadata: (this: HTMLElement, ev: Event) => any; - onloadstart: (this: HTMLElement, ev: Event) => any; - onmousedown: (this: HTMLElement, ev: MouseEvent) => any; - onmouseenter: (this: HTMLElement, ev: MouseEvent) => any; - onmouseleave: (this: HTMLElement, ev: MouseEvent) => any; - onmousemove: (this: HTMLElement, ev: MouseEvent) => any; - onmouseout: (this: HTMLElement, ev: MouseEvent) => any; - onmouseover: (this: HTMLElement, ev: MouseEvent) => any; - onmouseup: (this: HTMLElement, ev: MouseEvent) => any; - onmousewheel: (this: HTMLElement, ev: WheelEvent) => any; - onmscontentzoom: (this: HTMLElement, ev: UIEvent) => any; - onmsmanipulationstatechanged: (this: HTMLElement, ev: MSManipulationEvent) => any; - onpaste: (this: HTMLElement, ev: ClipboardEvent) => any; - onpause: (this: HTMLElement, ev: Event) => any; - onplay: (this: HTMLElement, ev: Event) => any; - onplaying: (this: HTMLElement, ev: Event) => any; - onprogress: (this: HTMLElement, ev: ProgressEvent) => any; - onratechange: (this: HTMLElement, ev: Event) => any; - onreset: (this: HTMLElement, ev: Event) => any; - onscroll: (this: HTMLElement, ev: UIEvent) => any; - onseeked: (this: HTMLElement, ev: Event) => any; - onseeking: (this: HTMLElement, ev: Event) => any; - onselect: (this: HTMLElement, ev: UIEvent) => any; - onselectstart: (this: HTMLElement, ev: Event) => any; - onstalled: (this: HTMLElement, ev: Event) => any; - onsubmit: (this: HTMLElement, ev: Event) => any; - onsuspend: (this: HTMLElement, ev: Event) => any; - ontimeupdate: (this: HTMLElement, ev: Event) => any; - onvolumechange: (this: HTMLElement, ev: Event) => any; - onwaiting: (this: HTMLElement, ev: Event) => any; + onabort(this: HTMLElement, ev: UIEvent): any; + onactivate(this: HTMLElement, ev: UIEvent): any; + onbeforeactivate(this: HTMLElement, ev: UIEvent): any; + onbeforecopy(this: HTMLElement, ev: ClipboardEvent): any; + onbeforecut(this: HTMLElement, ev: ClipboardEvent): any; + onbeforedeactivate(this: HTMLElement, ev: UIEvent): any; + onbeforepaste(this: HTMLElement, ev: ClipboardEvent): any; + onblur(this: HTMLElement, ev: FocusEvent): any; + oncanplay(this: HTMLElement, ev: Event): any; + oncanplaythrough(this: HTMLElement, ev: Event): any; + onchange(this: HTMLElement, ev: Event): any; + onclick(this: HTMLElement, ev: MouseEvent): any; + oncontextmenu(this: HTMLElement, ev: PointerEvent): any; + oncopy(this: HTMLElement, ev: ClipboardEvent): any; + oncuechange(this: HTMLElement, ev: Event): any; + oncut(this: HTMLElement, ev: ClipboardEvent): any; + ondblclick(this: HTMLElement, ev: MouseEvent): any; + ondeactivate(this: HTMLElement, ev: UIEvent): any; + ondrag(this: HTMLElement, ev: DragEvent): any; + ondragend(this: HTMLElement, ev: DragEvent): any; + ondragenter(this: HTMLElement, ev: DragEvent): any; + ondragleave(this: HTMLElement, ev: DragEvent): any; + ondragover(this: HTMLElement, ev: DragEvent): any; + ondragstart(this: HTMLElement, ev: DragEvent): any; + ondrop(this: HTMLElement, ev: DragEvent): any; + ondurationchange(this: HTMLElement, ev: Event): any; + onemptied(this: HTMLElement, ev: Event): any; + onended(this: HTMLElement, ev: MediaStreamErrorEvent): any; + onerror(this: HTMLElement, ev: ErrorEvent): any; + onfocus(this: HTMLElement, ev: FocusEvent): any; + oninput(this: HTMLElement, ev: Event): any; + oninvalid(this: HTMLElement, ev: Event): any; + onkeydown(this: HTMLElement, ev: KeyboardEvent): any; + onkeypress(this: HTMLElement, ev: KeyboardEvent): any; + onkeyup(this: HTMLElement, ev: KeyboardEvent): any; + onload(this: HTMLElement, ev: Event): any; + onloadeddata(this: HTMLElement, ev: Event): any; + onloadedmetadata(this: HTMLElement, ev: Event): any; + onloadstart(this: HTMLElement, ev: Event): any; + onmousedown(this: HTMLElement, ev: MouseEvent): any; + onmouseenter(this: HTMLElement, ev: MouseEvent): any; + onmouseleave(this: HTMLElement, ev: MouseEvent): any; + onmousemove(this: HTMLElement, ev: MouseEvent): any; + onmouseout(this: HTMLElement, ev: MouseEvent): any; + onmouseover(this: HTMLElement, ev: MouseEvent): any; + onmouseup(this: HTMLElement, ev: MouseEvent): any; + onmousewheel(this: HTMLElement, ev: WheelEvent): any; + onmscontentzoom(this: HTMLElement, ev: UIEvent): any; + onmsmanipulationstatechanged(this: HTMLElement, ev: MSManipulationEvent): any; + onpaste(this: HTMLElement, ev: ClipboardEvent): any; + onpause(this: HTMLElement, ev: Event): any; + onplay(this: HTMLElement, ev: Event): any; + onplaying(this: HTMLElement, ev: Event): any; + onprogress(this: HTMLElement, ev: ProgressEvent): any; + onratechange(this: HTMLElement, ev: Event): any; + onreset(this: HTMLElement, ev: Event): any; + onscroll(this: HTMLElement, ev: UIEvent): any; + onseeked(this: HTMLElement, ev: Event): any; + onseeking(this: HTMLElement, ev: Event): any; + onselect(this: HTMLElement, ev: UIEvent): any; + onselectstart(this: HTMLElement, ev: Event): any; + onstalled(this: HTMLElement, ev: Event): any; + onsubmit(this: HTMLElement, ev: Event): any; + onsuspend(this: HTMLElement, ev: Event): any; + ontimeupdate(this: HTMLElement, ev: Event): any; + onvolumechange(this: HTMLElement, ev: Event): any; + onwaiting(this: HTMLElement, ev: Event): any; outerText: string; spellcheck: boolean; readonly style: CSSStyleDeclaration; @@ -4904,7 +4904,7 @@ interface HTMLFrameElement extends HTMLElement, GetSVGDocument { /** * Raised when the object has been completely received from the server. */ - onload: (this: HTMLFrameElement, ev: Event) => any; + onload(this: HTMLFrameElement, ev: Event): any; /** * Sets or retrieves whether the frame can be scrolled. */ @@ -4967,31 +4967,31 @@ interface HTMLFrameSetElement extends HTMLElement { */ frameSpacing: any; name: string; - onafterprint: (this: HTMLFrameSetElement, ev: Event) => any; - onbeforeprint: (this: HTMLFrameSetElement, ev: Event) => any; - onbeforeunload: (this: HTMLFrameSetElement, ev: BeforeUnloadEvent) => any; + onafterprint(this: HTMLFrameSetElement, ev: Event): any; + onbeforeprint(this: HTMLFrameSetElement, ev: Event): any; + onbeforeunload(this: HTMLFrameSetElement, ev: BeforeUnloadEvent): any; /** * Fires when the object loses the input focus. */ - onblur: (this: HTMLFrameSetElement, ev: FocusEvent) => any; - onerror: (this: HTMLFrameSetElement, ev: ErrorEvent) => any; + onblur(this: HTMLFrameSetElement, ev: FocusEvent): any; + onerror(this: HTMLFrameSetElement, ev: ErrorEvent): any; /** * Fires when the object receives focus. */ - onfocus: (this: HTMLFrameSetElement, ev: FocusEvent) => any; - onhashchange: (this: HTMLFrameSetElement, ev: HashChangeEvent) => any; - onload: (this: HTMLFrameSetElement, ev: Event) => any; - onmessage: (this: HTMLFrameSetElement, ev: MessageEvent) => any; - onoffline: (this: HTMLFrameSetElement, ev: Event) => any; - ononline: (this: HTMLFrameSetElement, ev: Event) => any; - onorientationchange: (this: HTMLFrameSetElement, ev: Event) => any; - onpagehide: (this: HTMLFrameSetElement, ev: PageTransitionEvent) => any; - onpageshow: (this: HTMLFrameSetElement, ev: PageTransitionEvent) => any; - onpopstate: (this: HTMLFrameSetElement, ev: PopStateEvent) => any; - onresize: (this: HTMLFrameSetElement, ev: UIEvent) => any; - onscroll: (this: HTMLFrameSetElement, ev: UIEvent) => any; - onstorage: (this: HTMLFrameSetElement, ev: StorageEvent) => any; - onunload: (this: HTMLFrameSetElement, ev: Event) => any; + onfocus(this: HTMLFrameSetElement, ev: FocusEvent): any; + onhashchange(this: HTMLFrameSetElement, ev: HashChangeEvent): any; + onload(this: HTMLFrameSetElement, ev: Event): any; + onmessage(this: HTMLFrameSetElement, ev: MessageEvent): any; + onoffline(this: HTMLFrameSetElement, ev: Event): any; + ononline(this: HTMLFrameSetElement, ev: Event): any; + onorientationchange(this: HTMLFrameSetElement, ev: Event): any; + onpagehide(this: HTMLFrameSetElement, ev: PageTransitionEvent): any; + onpageshow(this: HTMLFrameSetElement, ev: PageTransitionEvent): any; + onpopstate(this: HTMLFrameSetElement, ev: PopStateEvent): any; + onresize(this: HTMLFrameSetElement, ev: UIEvent): any; + onscroll(this: HTMLFrameSetElement, ev: UIEvent): any; + onstorage(this: HTMLFrameSetElement, ev: StorageEvent): any; + onunload(this: HTMLFrameSetElement, ev: Event): any; /** * Sets or retrieves the frame heights of the object. */ @@ -5128,7 +5128,7 @@ interface HTMLIFrameElement extends HTMLElement, GetSVGDocument { /** * Raised when the object has been completely received from the server. */ - onload: (this: HTMLIFrameElement, ev: Event) => any; + onload(this: HTMLIFrameElement, ev: Event): any; readonly sandbox: DOMSettableTokenList; /** * Sets or retrieves whether the frame can be scrolled. From dd466ae599587d863c678debf44129d618467738 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 19 Sep 2017 15:29:36 -0700 Subject: [PATCH 09/56] Update tsconfig baselines --- .../tsConfig/Default initialized TSConfig/tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../Initialized TSConfig with files options/tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + 8 files changed, 8 insertions(+) diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index 0f5b2378468..bae0e1c0eb0 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -22,6 +22,7 @@ "strict": true /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index a545124a723..4d0db39092f 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -22,6 +22,7 @@ "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index b53ac2d8552..010a1186211 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -22,6 +22,7 @@ "strict": true /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index 4e06e06d159..b78057d0708 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -22,6 +22,7 @@ "strict": true /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index 94808d89ed0..be4fc190f9b 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -22,6 +22,7 @@ "strict": true /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index 0f5b2378468..bae0e1c0eb0 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -22,6 +22,7 @@ "strict": true /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index d165b0f2775..d7496d18dbb 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -22,6 +22,7 @@ "strict": true /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index 2a169b3aaaf..6d6964b8294 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -22,6 +22,7 @@ "strict": true, /* Enable all strict type-checking options. */ // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ From 54070786e4387b439dfc828f20d31d22e0df4a26 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 20 Sep 2017 10:52:56 -0700 Subject: [PATCH 10/56] Report external files in initial case --- src/server/project.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/project.ts b/src/server/project.ts index d588bae88ae..e6b611e0950 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -772,9 +772,10 @@ namespace ts.server { // unknown version - return everything const projectFileNames = this.getFileNames(); const externalFiles = this.getExternalFiles().map(f => toNormalizedPath(f)); - this.lastReportedFileNames = arrayToSet(projectFileNames.concat(externalFiles)); + const allFiles = projectFileNames.concat(externalFiles); + this.lastReportedFileNames = arrayToSet(allFiles); this.lastReportedVersion = this.projectStructureVersion; - return { info, files: projectFileNames, projectErrors: this.getGlobalProjectErrors() }; + return { info, files: allFiles, projectErrors: this.getGlobalProjectErrors() }; } } From 24698dd353e9303ed311705630bfcd8bf75ad88a Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 20 Sep 2017 13:49:57 -0700 Subject: [PATCH 11/56] Revert dom.generated.d.ts and fix duplicate declarations --- src/lib/dom.generated.d.ts | 212 +++++++++++++++++-------------------- 1 file changed, 96 insertions(+), 116 deletions(-) diff --git a/src/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index 93bfb982955..420be2f0f5d 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -4230,25 +4230,20 @@ interface HTMLBodyElement extends HTMLElement { bgProperties: string; link: any; noWrap: boolean; - onafterprint(this: HTMLBodyElement, ev: Event): any; - onbeforeprint(this: HTMLBodyElement, ev: Event): any; - onbeforeunload(this: HTMLBodyElement, ev: BeforeUnloadEvent): any; - onblur(this: HTMLBodyElement, ev: FocusEvent): any; - onerror(this: HTMLBodyElement, ev: ErrorEvent): any; - onfocus(this: HTMLBodyElement, ev: FocusEvent): any; - onhashchange(this: HTMLBodyElement, ev: HashChangeEvent): any; - onload(this: HTMLBodyElement, ev: Event): any; - onmessage(this: HTMLBodyElement, ev: MessageEvent): any; - onoffline(this: HTMLBodyElement, ev: Event): any; - ononline(this: HTMLBodyElement, ev: Event): any; - onorientationchange(this: HTMLBodyElement, ev: Event): any; - onpagehide(this: HTMLBodyElement, ev: PageTransitionEvent): any; - onpageshow(this: HTMLBodyElement, ev: PageTransitionEvent): any; - onpopstate(this: HTMLBodyElement, ev: PopStateEvent): any; - onresize(this: HTMLBodyElement, ev: UIEvent): any; - onscroll(this: HTMLBodyElement, ev: UIEvent): any; - onstorage(this: HTMLBodyElement, ev: StorageEvent): any; - onunload(this: HTMLBodyElement, ev: Event): any; + onafterprint: (this: HTMLBodyElement, ev: Event) => any; + onbeforeprint: (this: HTMLBodyElement, ev: Event) => any; + onbeforeunload: (this: HTMLBodyElement, ev: BeforeUnloadEvent) => any; + onhashchange: (this: HTMLBodyElement, ev: HashChangeEvent) => any; + onmessage: (this: HTMLBodyElement, ev: MessageEvent) => any; + onoffline: (this: HTMLBodyElement, ev: Event) => any; + ononline: (this: HTMLBodyElement, ev: Event) => any; + onorientationchange: (this: HTMLBodyElement, ev: Event) => any; + onpagehide: (this: HTMLBodyElement, ev: PageTransitionEvent) => any; + onpageshow: (this: HTMLBodyElement, ev: PageTransitionEvent) => any; + onpopstate: (this: HTMLBodyElement, ev: PopStateEvent) => any; + onresize: (this: HTMLBodyElement, ev: UIEvent) => any; + onstorage: (this: HTMLBodyElement, ev: StorageEvent) => any; + onunload: (this: HTMLBodyElement, ev: Event) => any; text: any; vLink: any; addEventListener(type: K, listener: (this: HTMLBodyElement, ev: HTMLBodyElementEventMap[K]) => any, useCapture?: boolean): void; @@ -4565,73 +4560,73 @@ interface HTMLElement extends Element { readonly offsetParent: Element; readonly offsetTop: number; readonly offsetWidth: number; - onabort(this: HTMLElement, ev: UIEvent): any; - onactivate(this: HTMLElement, ev: UIEvent): any; - onbeforeactivate(this: HTMLElement, ev: UIEvent): any; - onbeforecopy(this: HTMLElement, ev: ClipboardEvent): any; - onbeforecut(this: HTMLElement, ev: ClipboardEvent): any; - onbeforedeactivate(this: HTMLElement, ev: UIEvent): any; - onbeforepaste(this: HTMLElement, ev: ClipboardEvent): any; - onblur(this: HTMLElement, ev: FocusEvent): any; - oncanplay(this: HTMLElement, ev: Event): any; - oncanplaythrough(this: HTMLElement, ev: Event): any; - onchange(this: HTMLElement, ev: Event): any; - onclick(this: HTMLElement, ev: MouseEvent): any; - oncontextmenu(this: HTMLElement, ev: PointerEvent): any; - oncopy(this: HTMLElement, ev: ClipboardEvent): any; - oncuechange(this: HTMLElement, ev: Event): any; - oncut(this: HTMLElement, ev: ClipboardEvent): any; - ondblclick(this: HTMLElement, ev: MouseEvent): any; - ondeactivate(this: HTMLElement, ev: UIEvent): any; - ondrag(this: HTMLElement, ev: DragEvent): any; - ondragend(this: HTMLElement, ev: DragEvent): any; - ondragenter(this: HTMLElement, ev: DragEvent): any; - ondragleave(this: HTMLElement, ev: DragEvent): any; - ondragover(this: HTMLElement, ev: DragEvent): any; - ondragstart(this: HTMLElement, ev: DragEvent): any; - ondrop(this: HTMLElement, ev: DragEvent): any; - ondurationchange(this: HTMLElement, ev: Event): any; - onemptied(this: HTMLElement, ev: Event): any; - onended(this: HTMLElement, ev: MediaStreamErrorEvent): any; - onerror(this: HTMLElement, ev: ErrorEvent): any; - onfocus(this: HTMLElement, ev: FocusEvent): any; - oninput(this: HTMLElement, ev: Event): any; - oninvalid(this: HTMLElement, ev: Event): any; - onkeydown(this: HTMLElement, ev: KeyboardEvent): any; - onkeypress(this: HTMLElement, ev: KeyboardEvent): any; - onkeyup(this: HTMLElement, ev: KeyboardEvent): any; - onload(this: HTMLElement, ev: Event): any; - onloadeddata(this: HTMLElement, ev: Event): any; - onloadedmetadata(this: HTMLElement, ev: Event): any; - onloadstart(this: HTMLElement, ev: Event): any; - onmousedown(this: HTMLElement, ev: MouseEvent): any; - onmouseenter(this: HTMLElement, ev: MouseEvent): any; - onmouseleave(this: HTMLElement, ev: MouseEvent): any; - onmousemove(this: HTMLElement, ev: MouseEvent): any; - onmouseout(this: HTMLElement, ev: MouseEvent): any; - onmouseover(this: HTMLElement, ev: MouseEvent): any; - onmouseup(this: HTMLElement, ev: MouseEvent): any; - onmousewheel(this: HTMLElement, ev: WheelEvent): any; - onmscontentzoom(this: HTMLElement, ev: UIEvent): any; - onmsmanipulationstatechanged(this: HTMLElement, ev: MSManipulationEvent): any; - onpaste(this: HTMLElement, ev: ClipboardEvent): any; - onpause(this: HTMLElement, ev: Event): any; - onplay(this: HTMLElement, ev: Event): any; - onplaying(this: HTMLElement, ev: Event): any; - onprogress(this: HTMLElement, ev: ProgressEvent): any; - onratechange(this: HTMLElement, ev: Event): any; - onreset(this: HTMLElement, ev: Event): any; - onscroll(this: HTMLElement, ev: UIEvent): any; - onseeked(this: HTMLElement, ev: Event): any; - onseeking(this: HTMLElement, ev: Event): any; - onselect(this: HTMLElement, ev: UIEvent): any; - onselectstart(this: HTMLElement, ev: Event): any; - onstalled(this: HTMLElement, ev: Event): any; - onsubmit(this: HTMLElement, ev: Event): any; - onsuspend(this: HTMLElement, ev: Event): any; - ontimeupdate(this: HTMLElement, ev: Event): any; - onvolumechange(this: HTMLElement, ev: Event): any; - onwaiting(this: HTMLElement, ev: Event): any; + onabort: (this: HTMLElement, ev: UIEvent) => any; + onactivate: (this: HTMLElement, ev: UIEvent) => any; + onbeforeactivate: (this: HTMLElement, ev: UIEvent) => any; + onbeforecopy: (this: HTMLElement, ev: ClipboardEvent) => any; + onbeforecut: (this: HTMLElement, ev: ClipboardEvent) => any; + onbeforedeactivate: (this: HTMLElement, ev: UIEvent) => any; + onbeforepaste: (this: HTMLElement, ev: ClipboardEvent) => any; + onblur: (this: HTMLElement, ev: FocusEvent) => any; + oncanplay: (this: HTMLElement, ev: Event) => any; + oncanplaythrough: (this: HTMLElement, ev: Event) => any; + onchange: (this: HTMLElement, ev: Event) => any; + onclick: (this: HTMLElement, ev: MouseEvent) => any; + oncontextmenu: (this: HTMLElement, ev: PointerEvent) => any; + oncopy: (this: HTMLElement, ev: ClipboardEvent) => any; + oncuechange: (this: HTMLElement, ev: Event) => any; + oncut: (this: HTMLElement, ev: ClipboardEvent) => any; + ondblclick: (this: HTMLElement, ev: MouseEvent) => any; + ondeactivate: (this: HTMLElement, ev: UIEvent) => any; + ondrag: (this: HTMLElement, ev: DragEvent) => any; + ondragend: (this: HTMLElement, ev: DragEvent) => any; + ondragenter: (this: HTMLElement, ev: DragEvent) => any; + ondragleave: (this: HTMLElement, ev: DragEvent) => any; + ondragover: (this: HTMLElement, ev: DragEvent) => any; + ondragstart: (this: HTMLElement, ev: DragEvent) => any; + ondrop: (this: HTMLElement, ev: DragEvent) => any; + ondurationchange: (this: HTMLElement, ev: Event) => any; + onemptied: (this: HTMLElement, ev: Event) => any; + onended: (this: HTMLElement, ev: MediaStreamErrorEvent) => any; + onerror: (this: HTMLElement, ev: ErrorEvent) => any; + onfocus: (this: HTMLElement, ev: FocusEvent) => any; + oninput: (this: HTMLElement, ev: Event) => any; + oninvalid: (this: HTMLElement, ev: Event) => any; + onkeydown: (this: HTMLElement, ev: KeyboardEvent) => any; + onkeypress: (this: HTMLElement, ev: KeyboardEvent) => any; + onkeyup: (this: HTMLElement, ev: KeyboardEvent) => any; + onload: (this: HTMLElement, ev: Event) => any; + onloadeddata: (this: HTMLElement, ev: Event) => any; + onloadedmetadata: (this: HTMLElement, ev: Event) => any; + onloadstart: (this: HTMLElement, ev: Event) => any; + onmousedown: (this: HTMLElement, ev: MouseEvent) => any; + onmouseenter: (this: HTMLElement, ev: MouseEvent) => any; + onmouseleave: (this: HTMLElement, ev: MouseEvent) => any; + onmousemove: (this: HTMLElement, ev: MouseEvent) => any; + onmouseout: (this: HTMLElement, ev: MouseEvent) => any; + onmouseover: (this: HTMLElement, ev: MouseEvent) => any; + onmouseup: (this: HTMLElement, ev: MouseEvent) => any; + onmousewheel: (this: HTMLElement, ev: WheelEvent) => any; + onmscontentzoom: (this: HTMLElement, ev: UIEvent) => any; + onmsmanipulationstatechanged: (this: HTMLElement, ev: MSManipulationEvent) => any; + onpaste: (this: HTMLElement, ev: ClipboardEvent) => any; + onpause: (this: HTMLElement, ev: Event) => any; + onplay: (this: HTMLElement, ev: Event) => any; + onplaying: (this: HTMLElement, ev: Event) => any; + onprogress: (this: HTMLElement, ev: ProgressEvent) => any; + onratechange: (this: HTMLElement, ev: Event) => any; + onreset: (this: HTMLElement, ev: Event) => any; + onscroll: (this: HTMLElement, ev: UIEvent) => any; + onseeked: (this: HTMLElement, ev: Event) => any; + onseeking: (this: HTMLElement, ev: Event) => any; + onselect: (this: HTMLElement, ev: UIEvent) => any; + onselectstart: (this: HTMLElement, ev: Event) => any; + onstalled: (this: HTMLElement, ev: Event) => any; + onsubmit: (this: HTMLElement, ev: Event) => any; + onsuspend: (this: HTMLElement, ev: Event) => any; + ontimeupdate: (this: HTMLElement, ev: Event) => any; + onvolumechange: (this: HTMLElement, ev: Event) => any; + onwaiting: (this: HTMLElement, ev: Event) => any; outerText: string; spellcheck: boolean; readonly style: CSSStyleDeclaration; @@ -4901,10 +4896,6 @@ interface HTMLFrameElement extends HTMLElement, GetSVGDocument { * Sets or retrieves whether the user can resize the frame. */ noResize: boolean; - /** - * Raised when the object has been completely received from the server. - */ - onload(this: HTMLFrameElement, ev: Event): any; /** * Sets or retrieves whether the frame can be scrolled. */ @@ -4967,31 +4958,23 @@ interface HTMLFrameSetElement extends HTMLElement { */ frameSpacing: any; name: string; - onafterprint(this: HTMLFrameSetElement, ev: Event): any; - onbeforeprint(this: HTMLFrameSetElement, ev: Event): any; - onbeforeunload(this: HTMLFrameSetElement, ev: BeforeUnloadEvent): any; - /** - * Fires when the object loses the input focus. - */ - onblur(this: HTMLFrameSetElement, ev: FocusEvent): any; - onerror(this: HTMLFrameSetElement, ev: ErrorEvent): any; + onafterprint: (this: HTMLFrameSetElement, ev: Event) => any; + onbeforeprint: (this: HTMLFrameSetElement, ev: Event) => any; + onbeforeunload: (this: HTMLFrameSetElement, ev: BeforeUnloadEvent) => any; /** * Fires when the object receives focus. */ - onfocus(this: HTMLFrameSetElement, ev: FocusEvent): any; - onhashchange(this: HTMLFrameSetElement, ev: HashChangeEvent): any; - onload(this: HTMLFrameSetElement, ev: Event): any; - onmessage(this: HTMLFrameSetElement, ev: MessageEvent): any; - onoffline(this: HTMLFrameSetElement, ev: Event): any; - ononline(this: HTMLFrameSetElement, ev: Event): any; - onorientationchange(this: HTMLFrameSetElement, ev: Event): any; - onpagehide(this: HTMLFrameSetElement, ev: PageTransitionEvent): any; - onpageshow(this: HTMLFrameSetElement, ev: PageTransitionEvent): any; - onpopstate(this: HTMLFrameSetElement, ev: PopStateEvent): any; - onresize(this: HTMLFrameSetElement, ev: UIEvent): any; - onscroll(this: HTMLFrameSetElement, ev: UIEvent): any; - onstorage(this: HTMLFrameSetElement, ev: StorageEvent): any; - onunload(this: HTMLFrameSetElement, ev: Event): any; + onhashchange: (this: HTMLFrameSetElement, ev: HashChangeEvent) => any; + onmessage: (this: HTMLFrameSetElement, ev: MessageEvent) => any; + onoffline: (this: HTMLFrameSetElement, ev: Event) => any; + ononline: (this: HTMLFrameSetElement, ev: Event) => any; + onorientationchange: (this: HTMLFrameSetElement, ev: Event) => any; + onpagehide: (this: HTMLFrameSetElement, ev: PageTransitionEvent) => any; + onpageshow: (this: HTMLFrameSetElement, ev: PageTransitionEvent) => any; + onpopstate: (this: HTMLFrameSetElement, ev: PopStateEvent) => any; + onresize: (this: HTMLFrameSetElement, ev: UIEvent) => any; + onstorage: (this: HTMLFrameSetElement, ev: StorageEvent) => any; + onunload: (this: HTMLFrameSetElement, ev: Event) => any; /** * Sets or retrieves the frame heights of the object. */ @@ -5125,10 +5108,7 @@ interface HTMLIFrameElement extends HTMLElement, GetSVGDocument { * Sets or retrieves whether the user can resize the frame. */ noResize: boolean; - /** - * Raised when the object has been completely received from the server. - */ - onload(this: HTMLIFrameElement, ev: Event): any; + readonly sandbox: DOMSettableTokenList; /** * Sets or retrieves whether the frame can be scrolled. From f8e2cc13918c08c9697474c3cdc516fcee9596b0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Sep 2017 07:10:11 -0700 Subject: [PATCH 12/56] Properly flag and structurally compare marker type references --- src/compiler/checker.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4bacb88ef4a..fce58ad866f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8948,7 +8948,7 @@ namespace ts { if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; - if (source.flags & TypeFlags.MarkerType && target.flags & TypeFlags.MarkerType) { + if (source.flags & TypeFlags.MarkerType && target.flags & TypeFlags.MarkerType && !(source.flags & TypeFlags.Object || target.flags & TypeFlags.Object)) { return source === markerSubType && target === markerSuperType ? Ternary.True : Ternary.False; } @@ -9414,9 +9414,11 @@ namespace ts { } } else { - if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target) { - // We have type references to the same generic type. Obtain the variance information for the - // type parameters and relate the type arguments accordingly. + if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target && + !(source.flags & TypeFlags.MarkerType || target.flags & TypeFlags.MarkerType)) { + // We have type references to the same generic type, and the type references are not marker + // type references (which we always compare structurally). Obtain the variance information + // for the type parameters and relate the type arguments accordingly. const variances = getVariances((source).target); if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { return result; @@ -9841,8 +9843,12 @@ namespace ts { } } - function getVarianceType(type: GenericType, source: TypeParameter, target: Type) { - return createTypeReference(type, map(type.typeParameters, t => t === source ? target : t)); + // Return a type reference where the source type parameter is replaced with the target marker + // type, and flag the result as a marker type reference. + function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) { + const result = createTypeReference(type, map(type.typeParameters, t => t === source ? target : t)); + result.flags |= TypeFlags.MarkerType; + return result; } // Return an array containing the variance of each type parameter. The variance is effectively @@ -9867,15 +9873,15 @@ namespace ts { // We first compare instantiations where the type parameter is replaced with // marker types that have a known subtype relationship. From this we can infer // invariance, covariance, contravariance or bivariance. - const typeWithSuper = getVarianceType(type, tp, markerSuperType); - const typeWithSub = getVarianceType(type, tp, markerSubType); + const typeWithSuper = getMarkerTypeReference(type, tp, markerSuperType); + const typeWithSub = getMarkerTypeReference(type, tp, markerSubType); let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : 0) | (isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : 0); // If the instantiations appear to be related bivariantly, it may be because the // type parameter is omnivariant (i.e. it isn't witnessed anywhere in the generic // type). To determine this we compare instantiations where the type parameter is // replaced with marker types that are known to be unrelated. - if (variance === Variance.Bivariant && isTypeAssignableTo(getVarianceType(type, tp, markerOtherType), typeWithSuper)) { + if (variance === Variance.Bivariant && isTypeAssignableTo(getMarkerTypeReference(type, tp, markerOtherType), typeWithSuper)) { variance = Variance.Omnivariant; } variances.push(variance); From 589e1f440c7b76d359d8e9f9fd8722d199da3c59 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Sep 2017 08:52:22 -0700 Subject: [PATCH 13/56] Update comment --- 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 fce58ad866f..574ce5c6d80 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9417,8 +9417,8 @@ namespace ts { if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (source).target === (target).target && !(source.flags & TypeFlags.MarkerType || target.flags & TypeFlags.MarkerType)) { // We have type references to the same generic type, and the type references are not marker - // type references (which we always compare structurally). Obtain the variance information - // for the type parameters and relate the type arguments accordingly. + // type references (which are intended by be compared structurally). Obtain the variance + // information for the type parameters and relate the type arguments accordingly. const variances = getVariances((source).target); if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { return result; From afc8a261ccc6b19873032f9f5adb47b2ce874eef Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Sep 2017 21:31:11 -0700 Subject: [PATCH 14/56] Always perform structural comparison when variance check fails --- src/compiler/checker.ts | 117 ++++++++++++---------------------------- src/compiler/types.ts | 4 +- 2 files changed, 35 insertions(+), 86 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 574ce5c6d80..25dc3030556 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -282,9 +282,9 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const markerSuperType = createType(TypeFlags.MarkerType); - const markerSubType = createType(TypeFlags.MarkerType); - const markerOtherType = createType(TypeFlags.MarkerType); + const markerSuperType = createType(TypeFlags.TypeParameter); + const markerSubType = createType(TypeFlags.TypeParameter); + markerSubType.constraint = markerSuperType; const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); @@ -8948,10 +8948,6 @@ namespace ts { if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True; - if (source.flags & TypeFlags.MarkerType && target.flags & TypeFlags.MarkerType && !(source.flags & TypeFlags.Object || target.flags & TypeFlags.Object)) { - return source === markerSubType && target === markerSuperType ? Ternary.True : Ternary.False; - } - if (getObjectFlags(source) & ObjectFlags.ObjectLiteral && source.flags & TypeFlags.FreshLiteral) { if (hasExcessProperties(source, target, reportErrors)) { if (reportErrors) { @@ -9219,41 +9215,15 @@ namespace ts { // in the process of computing variance information for recursive types and when // comparing 'this' type arguments. const variance = i < variances.length ? variances[i] : Variance.Covariant; - // We simply ignore omnivariant type arguments (because they're never witnessed). - if (variance !== Variance.Omnivariant) { - const s = sources[i]; - const t = targets[i]; - let related = Ternary.True; - if (variance === Variance.Covariant) { - related = isRelatedTo(s, t, reportErrors); - } - else if (variance === Variance.Contravariant) { - related = isRelatedTo(t, s, reportErrors); - } - else if (variance === Variance.Bivariant) { - // In the bivariant case we first compare contravariantly without reporting - // errors. Then, if that doesn't succeed, we compare covariantly with error - // reporting. Thus, error elaboration will be based on the the covariant check, - // which is generally easier to reason about. - related = isRelatedTo(t, s, /*reportErrors*/ false); - if (!related) { - related = isRelatedTo(s, t, reportErrors); - } - } - else { - // In the invariant case we first compare covariantly, and only when that - // succeeds do we proceed to compare contravariantly. Thus, error elaboration - // will typically be based on the covariant check. - related = isRelatedTo(s, t, reportErrors); - if (related) { - related &= isRelatedTo(t, s, reportErrors); - } - } - if (!related) { - return Ternary.False; - } - result &= related; + const s = sources[i]; + const t = targets[i]; + const related = variance === Variance.Covariant ? isRelatedTo(s, t, reportErrors) : + variance === Variance.Contravariant ? isRelatedTo(t, s, reportErrors) : + Ternary.False; + if (!related) { + return Ternary.False; } + result &= related; } return result; } @@ -9418,21 +9388,14 @@ namespace ts { !(source.flags & TypeFlags.MarkerType || target.flags & TypeFlags.MarkerType)) { // We have type references to the same generic type, and the type references are not marker // type references (which are intended by be compared structurally). Obtain the variance - // information for the type parameters and relate the type arguments accordingly. + // information for the type parameters and relate the type arguments accordingly. If we do + // not succeed, fall through and do a structural comparison instead (there are instances + // where the variance information isn't accurate, e.g. when type parameters are used only + // in bivariant positions or when a type argument is 'any' or 'void'.) const variances = getVariances((source).target); if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { return result; } - // The type arguments did not relate appropriately, but it may be because getVariances was - // invoked recursively and returned emptyArray (in which case typeArgumentsRelatedTo defaulted - // to covariance for all type arguments). It might also be the case that the target type has a - // 'void' type argument for a covariant type parameter that is only used in return positions - // within the generic type (in which case any type argument is permitted on the source side). - // In those cases we proceed with a structural comparison. Otherwise, we know for certain the - // instantiations aren't related and we can return here. - if (variances !== emptyArray && !hasCovariantVoidArgument(target, variances)) { - return Ternary.False; - } } // Even if relationship doesn't hold for unions, intersections, or generic type references, // it may hold in a structural comparison. @@ -9851,13 +9814,18 @@ namespace ts { return result; } - // Return an array containing the variance of each type parameter. The variance is effectively - // a digest of the type comparisons that occur for each type argument when instantiations of the - // generic type are structurally compared. We infer the variance information by comparing - // instantiations of the generic type for type arguments with known relations. Note that the - // function returns the emptyArray singleton to signal that it has been invoked recursively for - // the given generic type. + // Return an array containing the variance of each type parameter. The variance information is + // computed by comparing instantiations of the generic type for type arguments with known relations. + // A type parameter is marked as covariant if a covariant comparison succeeds; otherwise, it is + // marked contravariant if a contravarint comparison succeeds; otherwise, it is marked invariant. + // One form of variance doesn't exclude another, so this information simply serves to indicate + // a "primary" relationship that can be checked as an optimization ahead of a full structural + // comparison. The function returns the emptyArray singleton if we're not in strictFunctionTypes + // mode or if the function has been invoked recursively for the given generic type. function getVariances(type: GenericType): Variance[] { + if (!strictFunctionTypes) { + return emptyArray; + } const typeParameters = type.typeParameters || emptyArray; let variances = type.variances; if (!variances) { @@ -9870,20 +9838,14 @@ namespace ts { type.variances = emptyArray; variances = []; for (const tp of typeParameters) { - // We first compare instantiations where the type parameter is replaced with - // marker types that have a known subtype relationship. From this we can infer - // invariance, covariance, contravariance or bivariance. + // We compare instantiations where the type parameter is replaced with marker types + // that have a known subtype relationship. From this we infer covariance, contravariance + // or invariance. const typeWithSuper = getMarkerTypeReference(type, tp, markerSuperType); const typeWithSub = getMarkerTypeReference(type, tp, markerSubType); - let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : 0) | - (isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : 0); - // If the instantiations appear to be related bivariantly, it may be because the - // type parameter is omnivariant (i.e. it isn't witnessed anywhere in the generic - // type). To determine this we compare instantiations where the type parameter is - // replaced with marker types that are known to be unrelated. - if (variance === Variance.Bivariant && isTypeAssignableTo(getMarkerTypeReference(type, tp, markerOtherType), typeWithSuper)) { - variance = Variance.Omnivariant; - } + const variance = isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : + isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : + Variance.Invariant; variances.push(variance); } } @@ -9892,17 +9854,6 @@ namespace ts { return variances; } - // Return true if the given type reference has a 'void' type argument for a covariant type parameter. - // See comment at call in recursiveTypeRelatedTo for when this case matters. - function hasCovariantVoidArgument(type: TypeReference, variances: Variance[]): boolean { - for (let i = 0; i < variances.length; i++) { - if (variances[i] === Variance.Covariant && type.typeArguments[i].flags & TypeFlags.Void) { - return true; - } - } - return false; - } - function isUnconstrainedTypeParameter(type: Type) { return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(type); } @@ -10709,9 +10660,9 @@ namespace ts { const sourceTypes = (source).typeArguments || emptyArray; const targetTypes = (target).typeArguments || emptyArray; const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length; - const variances = strictFunctionTypes ? getVariances((source).target) : undefined; + const variances = getVariances((source).target); for (let i = 0; i < count; i++) { - if (variances && i < variances.length && variances[i] === Variance.Contravariant) { + if (i < variances.length && variances[i] === Variance.Contravariant) { inferFromContravariantTypes(sourceTypes[i], targetTypes[i]); } else { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 3f07716fd1b..8ab999032bc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3345,11 +3345,9 @@ namespace ts { } export const enum Variance { - Invariant = 0, // Both covariant and contravariant + Invariant = 0, // Neither covariant nor contravariant Covariant = 1, // Covariant Contravariant = 2, // Contravariant - Bivariant = 3, // Either covariant or contravariant - Omnivariant = 4 // Unwitnessed type parameter } // Generic class and interface types From 70e8f7364e813c5bff4ada3ecb581d31eeb38122 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 21 Sep 2017 21:40:31 -0700 Subject: [PATCH 15/56] Add tests --- .../reference/strictFunctionTypes1.js | 25 ++ .../reference/strictFunctionTypes1.symbols | 96 +++++++ .../reference/strictFunctionTypes1.types | 102 +++++++ .../strictFunctionTypesErrors.errors.txt | 271 ++++++++++++++++++ .../reference/strictFunctionTypesErrors.js | 170 +++++++++++ tests/cases/compiler/strictFunctionTypes1.ts | 18 ++ .../compiler/strictFunctionTypesErrors.ts | 113 ++++++++ 7 files changed, 795 insertions(+) create mode 100644 tests/baselines/reference/strictFunctionTypes1.js create mode 100644 tests/baselines/reference/strictFunctionTypes1.symbols create mode 100644 tests/baselines/reference/strictFunctionTypes1.types create mode 100644 tests/baselines/reference/strictFunctionTypesErrors.errors.txt create mode 100644 tests/baselines/reference/strictFunctionTypesErrors.js create mode 100644 tests/cases/compiler/strictFunctionTypes1.ts create mode 100644 tests/cases/compiler/strictFunctionTypesErrors.ts diff --git a/tests/baselines/reference/strictFunctionTypes1.js b/tests/baselines/reference/strictFunctionTypes1.js new file mode 100644 index 00000000000..28a7a7f61d6 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypes1.js @@ -0,0 +1,25 @@ +//// [strictFunctionTypes1.ts] +declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; +declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; +declare function f3(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T; + +interface Func { (x: T): void } + +declare function f4(f1: Func, f2: Func): Func; + +declare function fo(x: Object): void; +declare function fs(x: string): void; +declare function fx(f: (x: "def") => void): void; + +const x1 = f1(fo, fs); // (x: string) => void +const x2 = f2("abc", fo, fs); // "abc" +const x3 = f3("abc", fo, fx); // "abc" | "def" +const x4 = f4(fo, fs); // Func + + +//// [strictFunctionTypes1.js] +"use strict"; +var x1 = f1(fo, fs); // (x: string) => void +var x2 = f2("abc", fo, fs); // "abc" +var x3 = f3("abc", fo, fx); // "abc" | "def" +var x4 = f4(fo, fs); // Func diff --git a/tests/baselines/reference/strictFunctionTypes1.symbols b/tests/baselines/reference/strictFunctionTypes1.symbols new file mode 100644 index 00000000000..70253411201 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypes1.symbols @@ -0,0 +1,96 @@ +=== tests/cases/compiler/strictFunctionTypes1.ts === +declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; +>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 0, 0)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20)) +>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 0, 23)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 0, 28)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20)) +>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 0, 42)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 0, 48)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 0, 65)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 0, 20)) + +declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; +>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 0, 79)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20)) +>obj : Symbol(obj, Decl(strictFunctionTypes1.ts, 1, 23)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20)) +>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 1, 30)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 1, 36)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20)) +>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 1, 50)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 1, 56)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 1, 20)) + +declare function f3(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T; +>f3 : Symbol(f3, Decl(strictFunctionTypes1.ts, 1, 74)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20)) +>obj : Symbol(obj, Decl(strictFunctionTypes1.ts, 2, 23)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20)) +>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 2, 30)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 2, 36)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20)) +>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 2, 50)) +>f : Symbol(f, Decl(strictFunctionTypes1.ts, 2, 56)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 2, 60)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 2, 20)) + +interface Func { (x: T): void } +>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 4, 15)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 4, 21)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 4, 15)) + +declare function f4(f1: Func, f2: Func): Func; +>f4 : Symbol(f4, Decl(strictFunctionTypes1.ts, 4, 34)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20)) +>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 6, 23)) +>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20)) +>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 6, 35)) +>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20)) +>Func : Symbol(Func, Decl(strictFunctionTypes1.ts, 2, 87)) +>T : Symbol(T, Decl(strictFunctionTypes1.ts, 6, 20)) + +declare function fo(x: Object): void; +>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 8, 20)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare function fs(x: string): void; +>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 9, 20)) + +declare function fx(f: (x: "def") => void): void; +>fx : Symbol(fx, Decl(strictFunctionTypes1.ts, 9, 37)) +>f : Symbol(f, Decl(strictFunctionTypes1.ts, 10, 20)) +>x : Symbol(x, Decl(strictFunctionTypes1.ts, 10, 24)) + +const x1 = f1(fo, fs); // (x: string) => void +>x1 : Symbol(x1, Decl(strictFunctionTypes1.ts, 12, 5)) +>f1 : Symbol(f1, Decl(strictFunctionTypes1.ts, 0, 0)) +>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58)) +>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37)) + +const x2 = f2("abc", fo, fs); // "abc" +>x2 : Symbol(x2, Decl(strictFunctionTypes1.ts, 13, 5)) +>f2 : Symbol(f2, Decl(strictFunctionTypes1.ts, 0, 79)) +>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58)) +>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37)) + +const x3 = f3("abc", fo, fx); // "abc" | "def" +>x3 : Symbol(x3, Decl(strictFunctionTypes1.ts, 14, 5)) +>f3 : Symbol(f3, Decl(strictFunctionTypes1.ts, 1, 74)) +>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58)) +>fx : Symbol(fx, Decl(strictFunctionTypes1.ts, 9, 37)) + +const x4 = f4(fo, fs); // Func +>x4 : Symbol(x4, Decl(strictFunctionTypes1.ts, 15, 5)) +>f4 : Symbol(f4, Decl(strictFunctionTypes1.ts, 4, 34)) +>fo : Symbol(fo, Decl(strictFunctionTypes1.ts, 6, 58)) +>fs : Symbol(fs, Decl(strictFunctionTypes1.ts, 8, 37)) + diff --git a/tests/baselines/reference/strictFunctionTypes1.types b/tests/baselines/reference/strictFunctionTypes1.types new file mode 100644 index 00000000000..9701d78cff0 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypes1.types @@ -0,0 +1,102 @@ +=== tests/cases/compiler/strictFunctionTypes1.ts === +declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; +>f1 : (f1: (x: T) => void, f2: (x: T) => void) => (x: T) => void +>T : T +>f1 : (x: T) => void +>x : T +>T : T +>f2 : (x: T) => void +>x : T +>T : T +>x : T +>T : T + +declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; +>f2 : (obj: T, f1: (x: T) => void, f2: (x: T) => void) => T +>T : T +>obj : T +>T : T +>f1 : (x: T) => void +>x : T +>T : T +>f2 : (x: T) => void +>x : T +>T : T +>T : T + +declare function f3(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T; +>f3 : (obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T +>T : T +>obj : T +>T : T +>f1 : (x: T) => void +>x : T +>T : T +>f2 : (f: (x: T) => void) => void +>f : (x: T) => void +>x : T +>T : T +>T : T + +interface Func { (x: T): void } +>Func : Func +>T : T +>x : T +>T : T + +declare function f4(f1: Func, f2: Func): Func; +>f4 : (f1: Func, f2: Func) => Func +>T : T +>f1 : Func +>Func : Func +>T : T +>f2 : Func +>Func : Func +>T : T +>Func : Func +>T : T + +declare function fo(x: Object): void; +>fo : (x: Object) => void +>x : Object +>Object : Object + +declare function fs(x: string): void; +>fs : (x: string) => void +>x : string + +declare function fx(f: (x: "def") => void): void; +>fx : (f: (x: "def") => void) => void +>f : (x: "def") => void +>x : "def" + +const x1 = f1(fo, fs); // (x: string) => void +>x1 : (x: string) => void +>f1(fo, fs) : (x: string) => void +>f1 : (f1: (x: T) => void, f2: (x: T) => void) => (x: T) => void +>fo : (x: Object) => void +>fs : (x: string) => void + +const x2 = f2("abc", fo, fs); // "abc" +>x2 : "abc" +>f2("abc", fo, fs) : "abc" +>f2 : (obj: T, f1: (x: T) => void, f2: (x: T) => void) => T +>"abc" : "abc" +>fo : (x: Object) => void +>fs : (x: string) => void + +const x3 = f3("abc", fo, fx); // "abc" | "def" +>x3 : "def" | "abc" +>f3("abc", fo, fx) : "def" | "abc" +>f3 : (obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void) => T +>"abc" : "abc" +>fo : (x: Object) => void +>fx : (f: (x: "def") => void) => void + +const x4 = f4(fo, fs); // Func +>x4 : Func +>f4(fo, fs) : Func +>f4 : (f1: Func, f2: Func) => Func +>fo : (x: Object) => void +>fs : (x: string) => void + diff --git a/tests/baselines/reference/strictFunctionTypesErrors.errors.txt b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt new file mode 100644 index 00000000000..3ff71ea26e7 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt @@ -0,0 +1,271 @@ +tests/cases/compiler/strictFunctionTypesErrors.ts(10,1): error TS2322: Type '(x: string) => Object' is not assignable to type '(x: Object) => Object'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(11,1): error TS2322: Type '(x: string) => string' is not assignable to type '(x: Object) => Object'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(13,1): error TS2322: Type '(x: Object) => Object' is not assignable to type '(x: Object) => string'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(14,1): error TS2322: Type '(x: string) => Object' is not assignable to type '(x: Object) => string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(15,1): error TS2322: Type '(x: string) => string' is not assignable to type '(x: Object) => string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(21,1): error TS2322: Type '(x: Object) => Object' is not assignable to type '(x: string) => string'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(23,1): error TS2322: Type '(x: string) => Object' is not assignable to type '(x: string) => string'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(33,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(34,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(36,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(37,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(38,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(44,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(46,1): error TS2322: Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(57,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(58,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(61,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, Object>'. + Type 'Func' is not assignable to type 'Func'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(62,1): error TS2322: Type 'Func, string>' is not assignable to type 'Func, Object>'. + Type 'Func' is not assignable to type 'Func'. +tests/cases/compiler/strictFunctionTypesErrors.ts(65,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. + Type 'Func' is not assignable to type 'Func'. +tests/cases/compiler/strictFunctionTypesErrors.ts(66,1): error TS2322: Type 'Func, string>' is not assignable to type 'Func, string>'. + Type 'Func' is not assignable to type 'Func'. +tests/cases/compiler/strictFunctionTypesErrors.ts(67,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(74,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Func' is not assignable to type 'Func'. +tests/cases/compiler/strictFunctionTypesErrors.ts(75,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(76,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(79,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(80,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Object' is not assignable to type 'string'. +tests/cases/compiler/strictFunctionTypesErrors.ts(83,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Func' is not assignable to type 'Func'. +tests/cases/compiler/strictFunctionTypesErrors.ts(84,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. + Type 'Func' is not assignable to type 'Func'. +tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Comparer2' is not assignable to type 'Comparer2'. + Type 'Animal' is not assignable to type 'Dog'. + + +==== tests/cases/compiler/strictFunctionTypesErrors.ts (29 errors) ==== + export {} + + + declare let f1: (x: Object) => Object; + declare let f2: (x: Object) => string; + declare let f3: (x: string) => Object; + declare let f4: (x: string) => string; + + f1 = f2; // Ok + f1 = f3; // Error + ~~ +!!! error TS2322: Type '(x: string) => Object' is not assignable to type '(x: Object) => Object'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + f1 = f4; // Error + ~~ +!!! error TS2322: Type '(x: string) => string' is not assignable to type '(x: Object) => Object'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + f2 = f1; // Error + ~~ +!!! error TS2322: Type '(x: Object) => Object' is not assignable to type '(x: Object) => string'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + f2 = f3; // Error + ~~ +!!! error TS2322: Type '(x: string) => Object' is not assignable to type '(x: Object) => string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + f2 = f4; // Error + ~~ +!!! error TS2322: Type '(x: string) => string' is not assignable to type '(x: Object) => string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + f3 = f1; // Ok + f3 = f2; // Ok + f3 = f4; // Ok + + f4 = f1; // Error + ~~ +!!! error TS2322: Type '(x: Object) => Object' is not assignable to type '(x: string) => string'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + f4 = f2; // Ok + f4 = f3; // Error + ~~ +!!! error TS2322: Type '(x: string) => Object' is not assignable to type '(x: string) => string'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + interface Func { (x: T): U } + + declare let g1: Func; + declare let g2: Func; + declare let g3: Func; + declare let g4: Func; + + g1 = g2; // Ok + g1 = g3; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + g1 = g4; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + g2 = g1; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + g2 = g3; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + g2 = g4; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + g3 = g1; // Ok + g3 = g2; // Ok + g3 = g4; // Ok + + g4 = g1; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + g4 = g2; // Ok + g4 = g3; // Error + ~~ +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + declare let h1: Func, Object>; + declare let h2: Func, string>; + declare let h3: Func, Object>; + declare let h4: Func, string>; + + h1 = h2; // Ok + h1 = h3; // Ok + h1 = h4; // Ok + + h2 = h1; // Error + ~~ +!!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + h2 = h3; // Error + ~~ +!!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + h2 = h4; // Ok + + h3 = h1; // Error + ~~ +!!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, Object>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + h3 = h2; // Error + ~~ +!!! error TS2322: Type 'Func, string>' is not assignable to type 'Func, Object>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. + h3 = h4; // Ok + + h4 = h1; // Error + ~~ +!!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. + h4 = h2; // Error + ~~ +!!! error TS2322: Type 'Func, string>' is not assignable to type 'Func, string>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. + h4 = h3; // Error + ~~ +!!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + declare let i1: Func>; + declare let i2: Func>; + declare let i3: Func>; + declare let i4: Func>; + + i1 = i2; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. + i1 = i3; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + i1 = i4; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + i2 = i1; // Ok + i2 = i3; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + i2 = i4; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. + + i3 = i1; // Ok + i3 = i2; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. + i3 = i4; // Error + ~~ +!!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. +!!! error TS2322: Type 'Func' is not assignable to type 'Func'. + + i4 = i1; // Ok + i4 = i2; // Ok + i4 = i3; // Ok + + interface Animal { animal: void } + interface Dog extends Animal { dog: void } + interface Cat extends Animal { cat: void } + + interface Comparer1 { + compare(a: T, b: T): number; + } + + declare let animalComparer1: Comparer1; + declare let dogComparer1: Comparer1; + + animalComparer1 = dogComparer1; // Ok + dogComparer1 = animalComparer1; // Ok + + interface Comparer2 { + compare: (a: T, b: T) => number; + } + + declare let animalComparer2: Comparer2; + declare let dogComparer2: Comparer2; + + animalComparer2 = dogComparer2; // Error + ~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'Comparer2' is not assignable to type 'Comparer2'. +!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'. + dogComparer2 = animalComparer2; // Ok + \ No newline at end of file diff --git a/tests/baselines/reference/strictFunctionTypesErrors.js b/tests/baselines/reference/strictFunctionTypesErrors.js new file mode 100644 index 00000000000..af41c9395f9 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.js @@ -0,0 +1,170 @@ +//// [strictFunctionTypesErrors.ts] +export {} + + +declare let f1: (x: Object) => Object; +declare let f2: (x: Object) => string; +declare let f3: (x: string) => Object; +declare let f4: (x: string) => string; + +f1 = f2; // Ok +f1 = f3; // Error +f1 = f4; // Error + +f2 = f1; // Error +f2 = f3; // Error +f2 = f4; // Error + +f3 = f1; // Ok +f3 = f2; // Ok +f3 = f4; // Ok + +f4 = f1; // Error +f4 = f2; // Ok +f4 = f3; // Error + +interface Func { (x: T): U } + +declare let g1: Func; +declare let g2: Func; +declare let g3: Func; +declare let g4: Func; + +g1 = g2; // Ok +g1 = g3; // Error +g1 = g4; // Error + +g2 = g1; // Error +g2 = g3; // Error +g2 = g4; // Error + +g3 = g1; // Ok +g3 = g2; // Ok +g3 = g4; // Ok + +g4 = g1; // Error +g4 = g2; // Ok +g4 = g3; // Error + +declare let h1: Func, Object>; +declare let h2: Func, string>; +declare let h3: Func, Object>; +declare let h4: Func, string>; + +h1 = h2; // Ok +h1 = h3; // Ok +h1 = h4; // Ok + +h2 = h1; // Error +h2 = h3; // Error +h2 = h4; // Ok + +h3 = h1; // Error +h3 = h2; // Error +h3 = h4; // Ok + +h4 = h1; // Error +h4 = h2; // Error +h4 = h3; // Error + +declare let i1: Func>; +declare let i2: Func>; +declare let i3: Func>; +declare let i4: Func>; + +i1 = i2; // Error +i1 = i3; // Error +i1 = i4; // Error + +i2 = i1; // Ok +i2 = i3; // Error +i2 = i4; // Error + +i3 = i1; // Ok +i3 = i2; // Error +i3 = i4; // Error + +i4 = i1; // Ok +i4 = i2; // Ok +i4 = i3; // Ok + +interface Animal { animal: void } +interface Dog extends Animal { dog: void } +interface Cat extends Animal { cat: void } + +interface Comparer1 { + compare(a: T, b: T): number; +} + +declare let animalComparer1: Comparer1; +declare let dogComparer1: Comparer1; + +animalComparer1 = dogComparer1; // Ok +dogComparer1 = animalComparer1; // Ok + +interface Comparer2 { + compare: (a: T, b: T) => number; +} + +declare let animalComparer2: Comparer2; +declare let dogComparer2: Comparer2; + +animalComparer2 = dogComparer2; // Error +dogComparer2 = animalComparer2; // Ok + + +//// [strictFunctionTypesErrors.js] +"use strict"; +exports.__esModule = true; +f1 = f2; // Ok +f1 = f3; // Error +f1 = f4; // Error +f2 = f1; // Error +f2 = f3; // Error +f2 = f4; // Error +f3 = f1; // Ok +f3 = f2; // Ok +f3 = f4; // Ok +f4 = f1; // Error +f4 = f2; // Ok +f4 = f3; // Error +g1 = g2; // Ok +g1 = g3; // Error +g1 = g4; // Error +g2 = g1; // Error +g2 = g3; // Error +g2 = g4; // Error +g3 = g1; // Ok +g3 = g2; // Ok +g3 = g4; // Ok +g4 = g1; // Error +g4 = g2; // Ok +g4 = g3; // Error +h1 = h2; // Ok +h1 = h3; // Ok +h1 = h4; // Ok +h2 = h1; // Error +h2 = h3; // Error +h2 = h4; // Ok +h3 = h1; // Error +h3 = h2; // Error +h3 = h4; // Ok +h4 = h1; // Error +h4 = h2; // Error +h4 = h3; // Error +i1 = i2; // Error +i1 = i3; // Error +i1 = i4; // Error +i2 = i1; // Ok +i2 = i3; // Error +i2 = i4; // Error +i3 = i1; // Ok +i3 = i2; // Error +i3 = i4; // Error +i4 = i1; // Ok +i4 = i2; // Ok +i4 = i3; // Ok +animalComparer1 = dogComparer1; // Ok +dogComparer1 = animalComparer1; // Ok +animalComparer2 = dogComparer2; // Error +dogComparer2 = animalComparer2; // Ok diff --git a/tests/cases/compiler/strictFunctionTypes1.ts b/tests/cases/compiler/strictFunctionTypes1.ts new file mode 100644 index 00000000000..7d8ffc689ff --- /dev/null +++ b/tests/cases/compiler/strictFunctionTypes1.ts @@ -0,0 +1,18 @@ +// @strict: true + +declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; +declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; +declare function f3(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T; + +interface Func { (x: T): void } + +declare function f4(f1: Func, f2: Func): Func; + +declare function fo(x: Object): void; +declare function fs(x: string): void; +declare function fx(f: (x: "def") => void): void; + +const x1 = f1(fo, fs); // (x: string) => void +const x2 = f2("abc", fo, fs); // "abc" +const x3 = f3("abc", fo, fx); // "abc" | "def" +const x4 = f4(fo, fs); // Func diff --git a/tests/cases/compiler/strictFunctionTypesErrors.ts b/tests/cases/compiler/strictFunctionTypesErrors.ts new file mode 100644 index 00000000000..5c6909e6806 --- /dev/null +++ b/tests/cases/compiler/strictFunctionTypesErrors.ts @@ -0,0 +1,113 @@ +export {} + +// @strict: true + +declare let f1: (x: Object) => Object; +declare let f2: (x: Object) => string; +declare let f3: (x: string) => Object; +declare let f4: (x: string) => string; + +f1 = f2; // Ok +f1 = f3; // Error +f1 = f4; // Error + +f2 = f1; // Error +f2 = f3; // Error +f2 = f4; // Error + +f3 = f1; // Ok +f3 = f2; // Ok +f3 = f4; // Ok + +f4 = f1; // Error +f4 = f2; // Ok +f4 = f3; // Error + +interface Func { (x: T): U } + +declare let g1: Func; +declare let g2: Func; +declare let g3: Func; +declare let g4: Func; + +g1 = g2; // Ok +g1 = g3; // Error +g1 = g4; // Error + +g2 = g1; // Error +g2 = g3; // Error +g2 = g4; // Error + +g3 = g1; // Ok +g3 = g2; // Ok +g3 = g4; // Ok + +g4 = g1; // Error +g4 = g2; // Ok +g4 = g3; // Error + +declare let h1: Func, Object>; +declare let h2: Func, string>; +declare let h3: Func, Object>; +declare let h4: Func, string>; + +h1 = h2; // Ok +h1 = h3; // Ok +h1 = h4; // Ok + +h2 = h1; // Error +h2 = h3; // Error +h2 = h4; // Ok + +h3 = h1; // Error +h3 = h2; // Error +h3 = h4; // Ok + +h4 = h1; // Error +h4 = h2; // Error +h4 = h3; // Error + +declare let i1: Func>; +declare let i2: Func>; +declare let i3: Func>; +declare let i4: Func>; + +i1 = i2; // Error +i1 = i3; // Error +i1 = i4; // Error + +i2 = i1; // Ok +i2 = i3; // Error +i2 = i4; // Error + +i3 = i1; // Ok +i3 = i2; // Error +i3 = i4; // Error + +i4 = i1; // Ok +i4 = i2; // Ok +i4 = i3; // Ok + +interface Animal { animal: void } +interface Dog extends Animal { dog: void } +interface Cat extends Animal { cat: void } + +interface Comparer1 { + compare(a: T, b: T): number; +} + +declare let animalComparer1: Comparer1; +declare let dogComparer1: Comparer1; + +animalComparer1 = dogComparer1; // Ok +dogComparer1 = animalComparer1; // Ok + +interface Comparer2 { + compare: (a: T, b: T) => number; +} + +declare let animalComparer2: Comparer2; +declare let dogComparer2: Comparer2; + +animalComparer2 = dogComparer2; // Error +dogComparer2 = animalComparer2; // Ok From 91691f6079edaf77dce77df5e0c0d1176bc0a7b8 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 25 Sep 2017 16:59:18 -0700 Subject: [PATCH 16/56] Strict function type checking only for certain function types --- src/compiler/checker.ts | 120 +++++++++++++++++++++++++++++----------- src/compiler/types.ts | 2 + 2 files changed, 91 insertions(+), 31 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 25dc3030556..5d3957652d8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -285,6 +285,7 @@ namespace ts { const markerSuperType = createType(TypeFlags.TypeParameter); const markerSubType = createType(TypeFlags.TypeParameter); markerSubType.constraint = markerSuperType; + const markerOtherType = createType(TypeFlags.TypeParameter); const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); @@ -3548,7 +3549,7 @@ namespace ts { return; } - if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { + if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length && isStrictSignature(resolved.callSignatures[0])) { const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags); if (parenthesizeSignature) { writePunctuation(writer, SyntaxKind.OpenParenToken); @@ -3559,7 +3560,7 @@ namespace ts { } return; } - if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { + if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length && isStrictSignature(resolved.constructSignatures[0])) { if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.OpenParenToken); } @@ -8521,6 +8522,17 @@ namespace ts { /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False; } + // A signature is considered strict if it is declared in a function type literal, a constructor type + // literal, a function expression, an arrow function, or a function declaration with no overloads. A + // strict signature is subject to strict checking in strictFunctionTypes mode. + function isStrictSignature(signature: Signature) { + const declaration = signature.declaration; + const kind = declaration ? declaration.kind : SyntaxKind.Unknown; + return kind === SyntaxKind.FunctionType || kind === SyntaxKind.ConstructorType || + kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction || + (kind === SyntaxKind.FunctionDeclaration && getSingleCallSignature(getTypeOfSymbol(getSymbolOfNode(declaration)))); + } + type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void; /** @@ -8546,9 +8558,7 @@ namespace ts { source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } - const targetKind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; - const strictVariance = strictFunctionTypes && targetKind !== SyntaxKind.MethodDeclaration && targetKind !== SyntaxKind.MethodSignature; - + const strictVariance = strictFunctionTypes && isStrictSignature(target); let result = Ternary.True; const sourceThisType = getThisTypeOfSignature(source); @@ -9215,15 +9225,41 @@ namespace ts { // in the process of computing variance information for recursive types and when // comparing 'this' type arguments. const variance = i < variances.length ? variances[i] : Variance.Covariant; - const s = sources[i]; - const t = targets[i]; - const related = variance === Variance.Covariant ? isRelatedTo(s, t, reportErrors) : - variance === Variance.Contravariant ? isRelatedTo(t, s, reportErrors) : - Ternary.False; - if (!related) { - return Ternary.False; + // We ignore arguments for independent type parameters (because they're never witnessed). + if (variance !== Variance.Independent) { + const s = sources[i]; + const t = targets[i]; + let related = Ternary.True; + if (variance === Variance.Covariant) { + related = isRelatedTo(s, t, reportErrors); + } + else if (variance === Variance.Contravariant) { + related = isRelatedTo(t, s, reportErrors); + } + else if (variance === Variance.Bivariant) { + // In the bivariant case we first compare contravariantly without reporting + // errors. Then, if that doesn't succeed, we compare covariantly with error + // reporting. Thus, error elaboration will be based on the the covariant check, + // which is generally easier to reason about. + related = isRelatedTo(t, s, /*reportErrors*/ false); + if (!related) { + related = isRelatedTo(s, t, reportErrors); + } + } + else { + // In the invariant case we first compare covariantly, and only when that + // succeeds do we proceed to compare contravariantly. Thus, error elaboration + // will typically be based on the covariant check. + related = isRelatedTo(s, t, reportErrors); + if (related) { + related &= isRelatedTo(t, s, reportErrors); + } + } + if (!related) { + return Ternary.False; + } + result &= related; } - result &= related; } return result; } @@ -9388,14 +9424,21 @@ namespace ts { !(source.flags & TypeFlags.MarkerType || target.flags & TypeFlags.MarkerType)) { // We have type references to the same generic type, and the type references are not marker // type references (which are intended by be compared structurally). Obtain the variance - // information for the type parameters and relate the type arguments accordingly. If we do - // not succeed, fall through and do a structural comparison instead (there are instances - // where the variance information isn't accurate, e.g. when type parameters are used only - // in bivariant positions or when a type argument is 'any' or 'void'.) + // information for the type parameters and relate the type arguments accordingly. const variances = getVariances((source).target); if (result = typeArgumentsRelatedTo(source, target, variances, reportErrors)) { return result; } + // The type arguments did not relate appropriately, but it may be because we have no variance + // information (in which case typeArgumentsRelatedTo defaulted to covariance for all type + // arguments). It might also be the case that the target type has a 'void' type argument for + // a covariant type parameter that is only used in return positions within the generic type + // (in which case any type argument is permitted on the source side). In those cases we proceed + // with a structural comparison. Otherwise, we know for certain the instantiations aren't + // related and we can return here. + if (variances !== emptyArray && !hasCovariantVoidArgument(target, variances)) { + return Ternary.False; + } } // Even if relationship doesn't hold for unions, intersections, or generic type references, // it may hold in a structural comparison. @@ -9814,14 +9857,12 @@ namespace ts { return result; } - // Return an array containing the variance of each type parameter. The variance information is - // computed by comparing instantiations of the generic type for type arguments with known relations. - // A type parameter is marked as covariant if a covariant comparison succeeds; otherwise, it is - // marked contravariant if a contravarint comparison succeeds; otherwise, it is marked invariant. - // One form of variance doesn't exclude another, so this information simply serves to indicate - // a "primary" relationship that can be checked as an optimization ahead of a full structural - // comparison. The function returns the emptyArray singleton if we're not in strictFunctionTypes - // mode or if the function has been invoked recursively for the given generic type. + // Return an array containing the variance of each type parameter. The variance is effectively + // a digest of the type comparisons that occur for each type argument when instantiations of the + // generic type are structurally compared. We infer the variance information by comparing + // instantiations of the generic type for type arguments with known relations. The function + // returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function + // has been invoked recursively for the given generic type. function getVariances(type: GenericType): Variance[] { if (!strictFunctionTypes) { return emptyArray; @@ -9838,14 +9879,20 @@ namespace ts { type.variances = emptyArray; variances = []; for (const tp of typeParameters) { - // We compare instantiations where the type parameter is replaced with marker types - // that have a known subtype relationship. From this we infer covariance, contravariance - // or invariance. + // We first compare instantiations where the type parameter is replaced with + // marker types that have a known subtype relationship. From this we can infer + // invariance, covariance, contravariance or bivariance. const typeWithSuper = getMarkerTypeReference(type, tp, markerSuperType); const typeWithSub = getMarkerTypeReference(type, tp, markerSubType); - const variance = isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : - isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : - Variance.Invariant; + let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : 0) | + (isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : 0); + // If the instantiations appear to be related bivariantly it may be because the + // type parameter is independent (i.e. it isn't witnessed anywhere in the generic + // type). To determine this we compare instantiations where the type parameter is + // replaced with marker types that are known to be unrelated. + if (variance === Variance.Bivariant && isTypeAssignableTo(getMarkerTypeReference(type, tp, markerOtherType), typeWithSuper)) { + variance = Variance.Independent; + } variances.push(variance); } } @@ -9854,6 +9901,17 @@ namespace ts { return variances; } + // Return true if the given type reference has a 'void' type argument for a covariant type parameter. + // See comment at call in recursiveTypeRelatedTo for when this case matters. + function hasCovariantVoidArgument(type: TypeReference, variances: Variance[]): boolean { + for (let i = 0; i < variances.length; i++) { + if (variances[i] === Variance.Covariant && type.typeArguments[i].flags & TypeFlags.Void) { + return true; + } + } + return false; + } + function isUnconstrainedTypeParameter(type: Type) { return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(type); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8ab999032bc..a867d9ebe2a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3348,6 +3348,8 @@ namespace ts { Invariant = 0, // Neither covariant nor contravariant Covariant = 1, // Covariant Contravariant = 2, // Contravariant + Bivariant = 3, // Both covariant and contravariant + Independent = 4, // Unwitnessed type parameter } // Generic class and interface types From 6a481e8ddc1431924b67c7d17216f3d96d0b7db7 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 25 Sep 2017 16:59:39 -0700 Subject: [PATCH 17/56] Update tests --- .../strictFunctionTypesErrors.errors.txt | 90 +++++++++++++------ .../reference/strictFunctionTypesErrors.js | 2 +- .../compiler/strictFunctionTypesErrors.ts | 2 +- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/tests/baselines/reference/strictFunctionTypesErrors.errors.txt b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt index 3ff71ea26e7..eb0c2d00233 100644 --- a/tests/baselines/reference/strictFunctionTypesErrors.errors.txt +++ b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt @@ -17,15 +17,19 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(21,1): error TS2322: Type '(x: tests/cases/compiler/strictFunctionTypesErrors.ts(23,1): error TS2322: Type '(x: string) => Object' is not assignable to type '(x: string) => string'. Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(33,1): error TS2322: Type 'Func' is not assignable to type 'Func'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(34,1): error TS2322: Type 'Func' is not assignable to type 'Func'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(36,1): error TS2322: Type 'Func' is not assignable to type 'Func'. Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(37,1): error TS2322: Type 'Func' is not assignable to type 'Func'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(38,1): error TS2322: Type 'Func' is not assignable to type 'Func'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(44,1): error TS2322: Type 'Func' is not assignable to type 'Func'. Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(46,1): error TS2322: Type 'Func' is not assignable to type 'Func'. @@ -35,32 +39,46 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(57,1): error TS2322: Type 'Fun tests/cases/compiler/strictFunctionTypesErrors.ts(58,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(61,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, Object>'. - Type 'Func' is not assignable to type 'Func'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(62,1): error TS2322: Type 'Func, string>' is not assignable to type 'Func, Object>'. - Type 'Func' is not assignable to type 'Func'. + Types of parameters 'x' and 'x' are incompatible. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(65,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. - Type 'Func' is not assignable to type 'Func'. + Types of parameters 'x' and 'x' are incompatible. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(66,1): error TS2322: Type 'Func, string>' is not assignable to type 'Func, string>'. - Type 'Func' is not assignable to type 'Func'. + Types of parameters 'x' and 'x' are incompatible. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(67,1): error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(74,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. Type 'Func' is not assignable to type 'Func'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(75,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(76,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(79,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(80,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. - Type 'Object' is not assignable to type 'string'. + Types of parameters 'x' and 'x' are incompatible. + Type 'Object' is not assignable to type 'string'. tests/cases/compiler/strictFunctionTypesErrors.ts(83,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. Type 'Func' is not assignable to type 'Func'. tests/cases/compiler/strictFunctionTypesErrors.ts(84,1): error TS2322: Type 'Func>' is not assignable to type 'Func>'. Type 'Func' is not assignable to type 'Func'. tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Comparer2' is not assignable to type 'Comparer2'. Type 'Animal' is not assignable to type 'Dog'. + Property 'dog' is missing in type 'Animal'. ==== tests/cases/compiler/strictFunctionTypesErrors.ts (29 errors) ==== @@ -113,7 +131,7 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co !!! error TS2322: Type '(x: string) => Object' is not assignable to type '(x: string) => string'. !!! error TS2322: Type 'Object' is not assignable to type 'string'. - interface Func { (x: T): U } + type Func = (x: T) => U; declare let g1: Func; declare let g2: Func; @@ -124,11 +142,13 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co g1 = g3; // Error ~~ !!! error TS2322: Type 'Func' is not assignable to type 'Func'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. g1 = g4; // Error ~~ !!! error TS2322: Type 'Func' is not assignable to type 'Func'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. g2 = g1; // Error ~~ @@ -137,11 +157,13 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co g2 = g3; // Error ~~ !!! error TS2322: Type 'Func' is not assignable to type 'Func'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. g2 = g4; // Error ~~ !!! error TS2322: Type 'Func' is not assignable to type 'Func'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. g3 = g1; // Ok g3 = g2; // Ok @@ -179,22 +201,29 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co h3 = h1; // Error ~~ !!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, Object>'. -!!! error TS2322: Type 'Func' is not assignable to type 'Func'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. h3 = h2; // Error ~~ !!! error TS2322: Type 'Func, string>' is not assignable to type 'Func, Object>'. -!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. h3 = h4; // Ok h4 = h1; // Error ~~ !!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. -!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. h4 = h2; // Error ~~ !!! error TS2322: Type 'Func, string>' is not assignable to type 'Func, string>'. -!!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. h4 = h3; // Error ~~ !!! error TS2322: Type 'Func, Object>' is not assignable to type 'Func, string>'. @@ -209,24 +238,30 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co ~~ !!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. !!! error TS2322: Type 'Func' is not assignable to type 'Func'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. i1 = i3; // Error ~~ !!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. i1 = i4; // Error ~~ !!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. i2 = i1; // Ok i2 = i3; // Error ~~ !!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. i2 = i4; // Error ~~ !!! error TS2322: Type 'Func>' is not assignable to type 'Func>'. -!!! error TS2322: Type 'Object' is not assignable to type 'string'. +!!! error TS2322: Types of parameters 'x' and 'x' are incompatible. +!!! error TS2322: Type 'Object' is not assignable to type 'string'. i3 = i1; // Ok i3 = i2; // Error @@ -267,5 +302,6 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co ~~~~~~~~~~~~~~~ !!! error TS2322: Type 'Comparer2' is not assignable to type 'Comparer2'. !!! error TS2322: Type 'Animal' is not assignable to type 'Dog'. +!!! error TS2322: Property 'dog' is missing in type 'Animal'. dogComparer2 = animalComparer2; // Ok \ No newline at end of file diff --git a/tests/baselines/reference/strictFunctionTypesErrors.js b/tests/baselines/reference/strictFunctionTypesErrors.js index af41c9395f9..0049fe970ce 100644 --- a/tests/baselines/reference/strictFunctionTypesErrors.js +++ b/tests/baselines/reference/strictFunctionTypesErrors.js @@ -23,7 +23,7 @@ f4 = f1; // Error f4 = f2; // Ok f4 = f3; // Error -interface Func { (x: T): U } +type Func = (x: T) => U; declare let g1: Func; declare let g2: Func; diff --git a/tests/cases/compiler/strictFunctionTypesErrors.ts b/tests/cases/compiler/strictFunctionTypesErrors.ts index 5c6909e6806..32029364d76 100644 --- a/tests/cases/compiler/strictFunctionTypesErrors.ts +++ b/tests/cases/compiler/strictFunctionTypesErrors.ts @@ -23,7 +23,7 @@ f4 = f1; // Error f4 = f2; // Ok f4 = f3; // Error -interface Func { (x: T): U } +type Func = (x: T) => U; declare let g1: Func; declare let g2: Func; From 1795614c415264834b1b8552d1fac5607ed7dda0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 25 Sep 2017 17:06:09 -0700 Subject: [PATCH 18/56] Accept new baselines --- .../reference/commentsClassMembers.js | 8 +++-- .../baselines/reference/commentsInterface.js | 8 +++-- tests/baselines/reference/jsDocTypeTag2.js | 30 +++++++++++++++---- .../typeGuardFunctionOfFormThisErrors.js | 4 ++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/tests/baselines/reference/commentsClassMembers.js b/tests/baselines/reference/commentsClassMembers.js index f53c64060a3..d5cee0e1950 100644 --- a/tests/baselines/reference/commentsClassMembers.js +++ b/tests/baselines/reference/commentsClassMembers.js @@ -548,11 +548,15 @@ declare class c1 { } declare var i1: c1; declare var i1_p: number; -declare var i1_f: (b: number) => number; +declare var i1_f: { + (b: number): number; +}; declare var i1_r: number; declare var i1_prop: number; declare var i1_nc_p: number; -declare var i1_ncf: (b: number) => number; +declare var i1_ncf: { + (b: number): number; +}; declare var i1_ncr: number; declare var i1_ncprop: number; declare var i1_s_p: number; diff --git a/tests/baselines/reference/commentsInterface.js b/tests/baselines/reference/commentsInterface.js index 3deac9871f1..b7ceffa58af 100644 --- a/tests/baselines/reference/commentsInterface.js +++ b/tests/baselines/reference/commentsInterface.js @@ -142,9 +142,13 @@ declare var i2_i_nc_x: number; declare var i2_i_nc_foo: (b: number) => string; declare var i2_i_nc_foo_r: string; declare var i2_i_r: number; -declare var i2_i_fnfoo: (b: number) => string; +declare var i2_i_fnfoo: { + (b: number): string; +}; declare var i2_i_fnfoo_r: string; -declare var i2_i_nc_fnfoo: (b: number) => string; +declare var i2_i_nc_fnfoo: { + (b: number): string; +}; declare var i2_i_nc_fnfoo_r: string; interface i3 { /** Comment i3 x*/ diff --git a/tests/baselines/reference/jsDocTypeTag2.js b/tests/baselines/reference/jsDocTypeTag2.js index d54b4557f06..bde3f3f9c1e 100644 --- a/tests/baselines/reference/jsDocTypeTag2.js +++ b/tests/baselines/reference/jsDocTypeTag2.js @@ -472,6 +472,18 @@ "text": " ", "kind": "space" }, + { + "text": "{", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": " ", + "kind": "space" + }, { "text": "(", "kind": "punctuation" @@ -497,11 +509,7 @@ "kind": "punctuation" }, { - "text": " ", - "kind": "space" - }, - { - "text": "=>", + "text": ":", "kind": "punctuation" }, { @@ -511,6 +519,18 @@ { "text": "number", "kind": "keyword" + }, + { + "text": ";", + "kind": "punctuation" + }, + { + "text": "\n", + "kind": "lineBreak" + }, + { + "text": "}", + "kind": "punctuation" } ], "documentation": [], diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js index 84db5d4a4a9..6ba90de54f1 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js @@ -154,4 +154,6 @@ declare let c: number | number[]; declare let holder: { invalidGuard: (c: any) => this is number; }; -declare let detached: () => this is FollowerGuard; +declare let detached: { + (): this is FollowerGuard; +}; From 3eea1a9e9a7f575c4469f57e6fb72877e4f13e60 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 14 Sep 2017 16:04:44 -0700 Subject: [PATCH 19/56] Generalize extract method to handle constants as well Major changes: 1) Instead of skipping undesirable scopes, include them and mark them with errors. Constants can be extracted into more scopes. 2) Update the tests to call through the "public" API. This caused some baseline changes. 3) Rename refactoring to "Extract Symbol" for generality. 4) Return a second ApplicableRefactorInfo for constants. Distinguish the two by splitting the action name. --- src/compiler/diagnosticMessages.json | 12 +- src/harness/unittests/extractMethods.ts | 25 +- src/services/refactors/extractMethod.ts | 429 +++++++++++++----- .../reference/extractMethod/extractMethod1.ts | 8 +- .../extractMethod/extractMethod10.ts | 10 +- .../extractMethod/extractMethod11.ts | 10 +- .../extractMethod/extractMethod12.ts | 6 +- .../extractMethod/extractMethod13.ts | 6 +- .../extractMethod/extractMethod14.ts | 14 +- .../extractMethod/extractMethod15.ts | 14 +- .../extractMethod/extractMethod16.ts | 4 +- .../extractMethod/extractMethod17.ts | 8 +- .../extractMethod/extractMethod18.ts | 8 +- .../extractMethod/extractMethod19.ts | 4 +- .../reference/extractMethod/extractMethod2.ts | 8 +- .../extractMethod/extractMethod20.ts | 8 +- .../extractMethod/extractMethod21.ts | 4 +- .../extractMethod/extractMethod22.ts | 4 +- .../extractMethod/extractMethod23.ts | 6 +- .../extractMethod/extractMethod24.ts | 6 +- .../extractMethod/extractMethod25.ts | 4 +- .../extractMethod/extractMethod26.ts | 8 +- .../extractMethod/extractMethod27.ts | 8 +- .../extractMethod/extractMethod28.ts | 8 +- .../extractMethod/extractMethod29.ts | 4 +- .../reference/extractMethod/extractMethod3.ts | 8 +- .../extractMethod/extractMethod30.ts | 4 +- .../extractMethod/extractMethod31.ts | 4 +- .../extractMethod/extractMethod32.ts | 4 +- .../extractMethod/extractMethod33.ts | 4 +- .../reference/extractMethod/extractMethod4.ts | 8 +- .../reference/extractMethod/extractMethod5.ts | 8 +- .../reference/extractMethod/extractMethod6.ts | 8 +- .../reference/extractMethod/extractMethod7.ts | 8 +- .../reference/extractMethod/extractMethod8.ts | 8 +- .../reference/extractMethod/extractMethod9.ts | 8 +- .../extract-method-empty-namespace.ts | 4 +- .../fourslash/extract-method-formatting.ts | 4 +- .../fourslash/extract-method-not-for-empty.ts | 2 +- .../extract-method-not-for-import.ts | 2 +- .../fourslash/extract-method-uniqueName.ts | 4 +- tests/cases/fourslash/extract-method1.ts | 8 +- tests/cases/fourslash/extract-method10.ts | 4 +- tests/cases/fourslash/extract-method11.ts | 4 +- tests/cases/fourslash/extract-method13.ts | 24 +- tests/cases/fourslash/extract-method14.ts | 4 +- tests/cases/fourslash/extract-method15.ts | 4 +- tests/cases/fourslash/extract-method17.ts | 4 +- tests/cases/fourslash/extract-method18.ts | 4 +- tests/cases/fourslash/extract-method19.ts | 4 +- tests/cases/fourslash/extract-method2.ts | 4 +- tests/cases/fourslash/extract-method20.ts | 4 +- tests/cases/fourslash/extract-method21.ts | 10 +- tests/cases/fourslash/extract-method22.ts | 2 +- tests/cases/fourslash/extract-method23.ts | 2 +- tests/cases/fourslash/extract-method24.ts | 4 +- tests/cases/fourslash/extract-method25.ts | 4 +- tests/cases/fourslash/extract-method26.ts | 8 +- tests/cases/fourslash/extract-method3.ts | 6 +- tests/cases/fourslash/extract-method4.ts | 2 +- tests/cases/fourslash/extract-method5.ts | 4 +- tests/cases/fourslash/extract-method6.ts | 4 +- tests/cases/fourslash/extract-method7.ts | 6 +- tests/cases/fourslash/extract-method8.ts | 6 +- tests/cases/fourslash/extract-method9.ts | 4 +- 65 files changed, 539 insertions(+), 305 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index bf8fcdc4f84..573993f1b5e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3703,7 +3703,7 @@ "code": 95002 }, - "Extract function": { + "Extract symbol": { "category": "Message", "code": 95003 }, @@ -3711,5 +3711,15 @@ "Extract to {0}": { "category": "Message", "code": 95004 + }, + + "Extract function": { + "category": "Message", + "code": 95005 + }, + + "Extract constant": { + "category": "Message", + "code": 95006 } } diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts index 190cd1d5be0..3a161baa8e7 100644 --- a/src/harness/unittests/extractMethods.ts +++ b/src/harness/unittests/extractMethods.ts @@ -105,7 +105,7 @@ namespace ts { if (!selectionRange) { throw new Error(`Test ${s} does not specify selection range`); } - const result = refactor.extractMethod.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + const result = refactor.extractSymbol.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); assert(result.targetRange === undefined, "failure expected"); const sortedErrors = result.errors.map(e => e.messageText).sort(); assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors"); @@ -119,7 +119,7 @@ namespace ts { if (!selectionRange) { throw new Error(`Test ${s} does not specify selection range`); } - const result = refactor.extractMethod.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + const result = refactor.extractSymbol.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); const expectedRange = t.ranges.get("extracted"); if (expectedRange) { let start: number, end: number; @@ -407,7 +407,7 @@ function test(x: number) { testExtractRangeFailed("extractRangeFailed9", `var x = ([#||]1 + 2);`, [ - "Statement or expression expected." + "Cannot extract empty range." ]); testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single identifier."]); @@ -604,7 +604,7 @@ function test(x: number) { // doesn't handle type parameter shadowing. testExtractMethod("extractMethod14", `function F(t1: T) { - function F(t2: T) { + function G(t2: T) { [#|t1.toString(); t2.toString();|] } @@ -612,7 +612,7 @@ function test(x: number) { // Confirm that the constraint is preserved. testExtractMethod("extractMethod15", `function F(t1: T) { - function F(t2: U) { + function G(t2: U) { [#|t2.toString();|] } }`); @@ -799,19 +799,20 @@ function parsePrimaryExpression(): any { newLineCharacter, program, file: sourceFile, - startPosition: -1, + startPosition: selectionRange.start, + endPosition: selectionRange.end, rulesProvider: getRuleProvider() }; - const result = refactor.extractMethod.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert.equal(result.errors, undefined, "expect no errors"); - const results = refactor.extractMethod.getPossibleExtractions(result.targetRange, context); + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.equal(rangeToExtract.errors, undefined, "expect no errors"); + const actions = refactor.extractSymbol.getAvailableActions(context)[0].actions; // TODO (acasey): smarter index const data: string[] = []; data.push(`// ==ORIGINAL==`); data.push(sourceFile.text); - for (const r of results) { - const { renameLocation, edits } = refactor.extractMethod.getExtractionAtIndex(result.targetRange, context, results.indexOf(r)); + for (const action of actions) { + const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name); assert.lengthOf(edits, 1); - data.push(`// ==SCOPE::${r.scopeDescription}==`); + data.push(`// ==SCOPE::${action.description}==`); const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); data.push(newTextWithRename); diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractMethod.ts index 481834c5a09..a1212e7260c 100644 --- a/src/services/refactors/extractMethod.ts +++ b/src/services/refactors/extractMethod.ts @@ -2,18 +2,21 @@ /// /* @internal */ -namespace ts.refactor.extractMethod { - const extractMethod: Refactor = { - name: "Extract Method", - description: Diagnostics.Extract_function.message, +namespace ts.refactor.extractSymbol { + const extractSymbol: Refactor = { + name: "Extract Symbol", + description: Diagnostics.Extract_symbol.message, getAvailableActions, getEditsForAction, }; - registerRefactor(extractMethod); + registerRefactor(extractSymbol); - /** Compute the associated code actions */ - function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { + /** + * Compute the associated code actions + * Exported for tests. + */ + export function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { const rangeToExtract = getRangeToExtract(context.file, { start: context.startPosition, length: getRefactorContextLength(context) }); const targetRange: TargetRange = rangeToExtract.targetRange; @@ -27,54 +30,90 @@ namespace ts.refactor.extractMethod { return undefined; } - const actions: RefactorActionInfo[] = []; - const usedNames: Map = createMap(); + const functionActions: RefactorActionInfo[] = []; + const usedFunctionNames: Map = createMap(); + + const constantActions: RefactorActionInfo[] = []; + const usedConstantNames: Map = createMap(); let i = 0; - for (const { scopeDescription, errors } of extractions) { + for (const extraction of extractions) { // Skip these since we don't have a way to report errors yet - if (errors.length) { - continue; + if (extraction.functionErrors.length === 0) { + // Don't issue refactorings with duplicated names. + // Scopes come back in "innermost first" order, so extractions will + // preferentially go into nearer scopes + const description = formatStringFromArgs(Diagnostics.Extract_to_0.message, [extraction.functionDescription]); + if (!usedFunctionNames.has(description)) { + usedFunctionNames.set(description, true); + functionActions.push({ + description, + name: `function_scope_${i}` + }); + } } - // Don't issue refactorings with duplicated names. - // Scopes come back in "innermost first" order, so extractions will - // preferentially go into nearer scopes - const description = formatStringFromArgs(Diagnostics.Extract_to_0.message, [scopeDescription]); - if (!usedNames.has(description)) { - usedNames.set(description, true); - actions.push({ - description, - name: `scope_${i}` - }); + // Skip these since we don't have a way to report errors yet + if (extraction.constantErrors.length === 0) { + // Don't issue refactorings with duplicated names. + // Scopes come back in "innermost first" order, so extractions will + // preferentially go into nearer scopes + const description = formatStringFromArgs(Diagnostics.Extract_to_0.message, [extraction.constantDescription]); + if (!usedConstantNames.has(description)) { + usedConstantNames.set(description, true); + constantActions.push({ + description, + name: `constant_scope_${i}` + }); + } } + // *do* increment i anyway because we'll look for the i-th scope // later when actually doing the refactoring if the user requests it i++; } - if (actions.length === 0) { - return undefined; + const infos: ApplicableRefactorInfo[] = []; + + if (functionActions.length) { + infos.push({ + name: extractSymbol.name, + description: Diagnostics.Extract_function.message, + actions: functionActions + }); } - return [{ - name: extractMethod.name, - description: extractMethod.description, - inlineable: true, - actions - }]; + if (constantActions.length) { + infos.push({ + name: extractSymbol.name, + description: Diagnostics.Extract_constant.message, + actions: constantActions + }); + } + + return infos.length ? infos : undefined; } - function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { + /* Exported for tests */ + export function getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined { const rangeToExtract = getRangeToExtract(context.file, { start: context.startPosition, length: getRefactorContextLength(context) }); const targetRange: TargetRange = rangeToExtract.targetRange; - const parsedIndexMatch = /^scope_(\d+)$/.exec(actionName); - Debug.assert(!!parsedIndexMatch, "Scope name should have matched the regexp"); - const index = +parsedIndexMatch[1]; - Debug.assert(isFinite(index), "Expected to parse a finite number from the scope index"); + const parsedFunctionIndexMatch = /^function_scope_(\d+)$/.exec(actionName); + if (parsedFunctionIndexMatch) { + const index = +parsedFunctionIndexMatch[1]; + Debug.assert(isFinite(index), "Expected to parse a finite number from the function scope index"); + return getFunctionExtractionAtIndex(targetRange, context, index); + } - return getExtractionAtIndex(targetRange, context, index); + const parsedConstantIndexMatch = /^constant_scope_(\d+)$/.exec(actionName); + if (parsedConstantIndexMatch) { + const index = +parsedConstantIndexMatch[1]; + Debug.assert(isFinite(index), "Expected to parse a finite number from the constant scope index"); + return getConstantExtractionAtIndex(targetRange, context, index); + } + + Debug.fail("Unrecognized action name"); } // Move these into diagnostic messages if they become user-facing @@ -83,7 +122,11 @@ namespace ts.refactor.extractMethod { return { message, code: 0, category: DiagnosticCategory.Message, key: message }; } - export const CannotExtractFunction: DiagnosticMessage = createMessage("Cannot extract function."); + export const CannotExtractRange: DiagnosticMessage = createMessage("Cannot extract range."); + export const CannotExtractImport: DiagnosticMessage = createMessage("Cannot extract import statement."); + export const CannotExtractSuper: DiagnosticMessage = createMessage("Cannot extract super call."); + export const CannotExtractEmpty: DiagnosticMessage = createMessage("Cannot extract empty range."); + export const ExpressionExpected: DiagnosticMessage = createMessage("expression expected."); export const StatementOrExpressionExpected: DiagnosticMessage = createMessage("Statement or expression expected."); export const CannotExtractRangeContainingConditionalBreakOrContinueStatements: DiagnosticMessage = createMessage("Cannot extract range containing conditional break or continue statements."); export const CannotExtractRangeContainingConditionalReturnStatement: DiagnosticMessage = createMessage("Cannot extract range containing conditional return statement."); @@ -91,11 +134,13 @@ namespace ts.refactor.extractMethod { export const CannotExtractRangeThatContainsWritesToReferencesLocatedOutsideOfTheTargetRangeInGenerators: DiagnosticMessage = createMessage("Cannot extract range containing writes to references located outside of the target range in generators."); export const TypeWillNotBeVisibleInTheNewScope = createMessage("Type will not visible in the new scope."); export const FunctionWillNotBeVisibleInTheNewScope = createMessage("Function will not visible in the new scope."); - export const InsufficientSelection = createMessage("Select more than a single identifier."); + export const CannotExtractIdentifier = createMessage("Select more than a single identifier."); export const CannotExtractExportedEntity = createMessage("Cannot extract exported declaration"); export const CannotCombineWritesAndReturns = createMessage("Cannot combine writes and returns"); export const CannotExtractReadonlyPropertyInitializerOutsideConstructor = createMessage("Cannot move initialization of read-only class property outside of the constructor"); export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts"); + export const CannotAccessVariablesFromNestedScopes = createMessage("Cannot access variables from nested scopes"); + export const CannotExtractToOtherFunctionLike = createMessage("Cannot extract method to a function-like scope that is not a function"); } enum RangeFacts { @@ -150,7 +195,7 @@ namespace ts.refactor.extractMethod { const { length } = span; if (length === 0) { - return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.StatementOrExpressionExpected)] }; + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractEmpty)] }; } // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. @@ -167,7 +212,7 @@ namespace ts.refactor.extractMethod { if (!start || !end) { // cannot find either start or end node - return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractFunction)] }; + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; } if (start.parent !== end.parent) { @@ -193,13 +238,13 @@ namespace ts.refactor.extractMethod { } else { // start and end nodes belong to different subtrees - return createErrorResult(sourceFile, span.start, length, Messages.CannotExtractFunction); + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; } } if (start !== end) { // start and end should be statements and parent should be either block or a source file if (!isBlockLike(start.parent)) { - return createErrorResult(sourceFile, span.start, length, Messages.CannotExtractFunction); + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; } const statements: Statement[] = []; for (const statement of (start.parent).statements) { @@ -216,22 +261,21 @@ namespace ts.refactor.extractMethod { } return { targetRange: { range: statements, facts: rangeFacts, declarations } }; } - else { - // We have a single node (start) - const errors = checkRootNode(start) || checkNode(start); - if (errors) { - return { errors }; - } - return { targetRange: { range: getStatementOrExpressionRange(start), facts: rangeFacts, declarations } }; + + if (isExpressionStatement(start)) { + start = start.expression; } - function createErrorResult(sourceFile: SourceFile, start: number, length: number, message: DiagnosticMessage): RangeToExtract { - return { errors: [createFileDiagnostic(sourceFile, start, length, message)] }; + // We have a single node (start) + const errors = checkRootNode(start) || checkNode(start); + if (errors) { + return { errors }; } + return { targetRange: { range: getStatementOrExpressionRange(start), facts: rangeFacts, declarations } }; function checkRootNode(node: Node): Diagnostic[] | undefined { - if (isIdentifier(isExpressionStatement(node) ? node.expression : node)) { - return [createDiagnosticForNode(node, Messages.InsufficientSelection)]; + if (isIdentifier(node)) { + return [createDiagnosticForNode(node, Messages.CannotExtractIdentifier)]; } return undefined; } @@ -309,7 +353,7 @@ namespace ts.refactor.extractMethod { // Some things can't be extracted in certain situations switch (node.kind) { case SyntaxKind.ImportDeclaration: - (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractFunction)); + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractImport)); return true; case SyntaxKind.SuperKeyword: // For a super *constructor call*, we have to be extracting the entire class, @@ -318,7 +362,7 @@ namespace ts.refactor.extractMethod { // Super constructor call const containingClass = getContainingClass(node); if (containingClass.pos < span.start || containingClass.end >= (span.start + span.length)) { - (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractFunction)); + (errors || (errors = [])).push(createDiagnosticForNode(node, Messages.CannotExtractSuper)); return true; } } @@ -328,7 +372,7 @@ namespace ts.refactor.extractMethod { break; } - if (!node || isFunctionLike(node) || isClassLike(node)) { + if (!node || isFunctionLikeDeclaration(node) || isClassLike(node)) { switch (node.kind) { case SyntaxKind.FunctionDeclaration: case SyntaxKind.ClassDeclaration: @@ -439,9 +483,8 @@ namespace ts.refactor.extractMethod { return undefined; } - function isValidExtractionTarget(node: Node): node is Scope { - // Note that we don't use isFunctionLike because we don't want to put the extracted closure *inside* a method - return (node.kind === SyntaxKind.FunctionDeclaration) || isSourceFile(node) || isModuleBlock(node) || isClassLike(node); + function isScope(node: Node): node is Scope { + return isFunctionLikeDeclaration(node) || isSourceFile(node) || isModuleBlock(node) || isClassLike(node); } /** @@ -468,14 +511,14 @@ namespace ts.refactor.extractMethod { // * Function declaration // * Class declaration or expression // * Module/namespace or source file - if (current !== start && isValidExtractionTarget(current)) { + if (current !== start && isScope(current)) { (scopes = scopes || []).push(current); } // A function parameter's initializer is actually in the outer scope, not the function declaration if (current && current.parent && current.parent.kind === SyntaxKind.Parameter) { // Skip all the way to the outer scope of the function that declared this parameter - current = findAncestor(current, parent => isFunctionLike(parent)).parent; + current = findAncestor(current, parent => isFunctionLikeDeclaration(parent)).parent; } else { current = current.parent; @@ -485,29 +528,42 @@ namespace ts.refactor.extractMethod { return scopes; } - // exported only for tests - export function getExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo { - const { scopes, readsAndWrites: { target, usagesPerScope, errorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); - Debug.assert(!errorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?"); + function getFunctionExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo { + const { scopes, readsAndWrites: { target, usagesPerScope, functionErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); + Debug.assert(!functionErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?"); context.cancellationToken.throwIfCancellationRequested(); return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context); } + function getConstantExtractionAtIndex(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number): RefactorEditInfo { + const { scopes, readsAndWrites: { target, usagesPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); + Debug.assert(!constantErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?"); + context.cancellationToken.throwIfCancellationRequested(); + Debug.assert(target === targetRange.range); + return extractConstantInScope(target as Expression, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange.facts, context); + } + interface PossibleExtraction { - readonly scopeDescription: string; - readonly errors: ReadonlyArray; + readonly functionDescription: string; + readonly functionErrors: ReadonlyArray; + readonly constantDescription: string; + readonly constantErrors: ReadonlyArray; } /** * Given a piece of text to extract ('targetRange'), computes a list of possible extractions. * Each returned ExtractResultForScope corresponds to a possible target scope and is either a set of changes * or an error explaining why we can't extract into that scope. */ - // exported only for tests - export function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray | undefined { - const { scopes, readsAndWrites: { errorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); + function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray | undefined { + const { scopes, readsAndWrites: { functionErrorsPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); // Need the inner type annotation to avoid https://github.com/Microsoft/TypeScript/issues/7547 - return scopes.map((scope, i): PossibleExtraction => - ({ scopeDescription: getDescriptionForScope(scope), errors: errorsPerScope[i] })); + const extractions = scopes.map((scope, i): PossibleExtraction => ({ + functionDescription: getDescriptionForFunctionInScope(scope), + functionErrors: functionErrorsPerScope[i], + constantDescription: getDescriptionForConstantInScope(scope), + constantErrors: constantErrorsPerScope[i], + })); + return extractions; } function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[], readonly readsAndWrites: ReadsAndWrites } { @@ -533,13 +589,20 @@ namespace ts.refactor.extractMethod { return { scopes, readsAndWrites }; } - function getDescriptionForScope(scope: Scope): string { + function getDescriptionForFunctionInScope(scope: Scope): string { return isFunctionLikeDeclaration(scope) ? `inner function in ${getDescriptionForFunctionLikeDeclaration(scope)}` : isClassLike(scope) ? `method in ${getDescriptionForClassLikeDeclaration(scope)}` : `function in ${getDescriptionForModuleLikeDeclaration(scope)}`; } + function getDescriptionForConstantInScope(scope: Scope): string { + return isFunctionLikeDeclaration(scope) + ? `constant in ${getDescriptionForFunctionLikeDeclaration(scope)}` + : isClassLike(scope) + ? `readonly field in ${getDescriptionForClassLikeDeclaration(scope)}` + : `constant in ${getDescriptionForModuleLikeDeclaration(scope)}`; + } function getDescriptionForFunctionLikeDeclaration(scope: FunctionLikeDeclaration): string { switch (scope.kind) { case SyntaxKind.Constructor: @@ -573,12 +636,12 @@ namespace ts.refactor.extractMethod { : scope.externalModuleIndicator ? "module scope" : "global scope"; } - function getUniqueName(fileText: string): string { - let functionNameText = "newFunction"; - for (let i = 1; fileText.indexOf(functionNameText) !== -1; i++) { - functionNameText = `newFunction_${i}`; + function getUniqueName(baseName: string, fileText: string): string { + let nameText = baseName; + for (let i = 1; fileText.indexOf(nameText) !== -1; i++) { + nameText = `${baseName}_${i}`; } - return functionNameText; + return nameText; } /** @@ -596,7 +659,7 @@ namespace ts.refactor.extractMethod { // Make a unique name for the extracted function const file = scope.getSourceFile(); - const functionNameText = getUniqueName(file.text); + const functionNameText = getUniqueName(isClassLike(scope) ? "newMethod" : "newFunction", file.text); const isJS = isInJavaScriptFile(scope); const functionName = createIdentifier(functionNameText); @@ -688,7 +751,7 @@ namespace ts.refactor.extractMethod { const changeTracker = textChanges.ChangeTracker.fromContext(context); const minInsertionPos = (isReadonlyArray(range.range) ? lastOrUndefined(range.range) : range.range).end; - const nodeToInsertBefore = getNodeToInsertBefore(minInsertionPos, scope); + const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, { suffix: context.newLineCharacter + context.newLineCharacter }); } @@ -774,27 +837,130 @@ namespace ts.refactor.extractMethod { const renameRange = isReadonlyArray(range.range) ? range.range[0] : range.range; const renameFilename = renameRange.getSourceFile().fileName; - const renameLocation = getRenameLocation(edits, renameFilename, functionNameText); + const renameLocation = getRenameLocation(edits, renameFilename, functionNameText, /*isDeclaredBeforeUse*/ false); return { renameFilename, renameLocation, edits }; } - function getRenameLocation(edits: ReadonlyArray, renameFilename: string, functionNameText: string): number { + /** + * Result of 'extractRange' operation for a specific scope. + * Stores either a list of changes that should be applied to extract a range or a list of errors + */ + function extractConstantInScope( + node: Expression, + scope: Scope, + { substitutions }: ScopeUsages, + rangeFacts: RangeFacts, + context: RefactorContext): RefactorEditInfo { + + const checker = context.program.getTypeChecker(); + + // Make a unique name for the extracted variable + const file = scope.getSourceFile(); + const localNameText = getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file.text); + const isJS = isInJavaScriptFile(scope); + + const variableType = isJS + ? undefined + : checker.typeToTypeNode(checker.getContextualType(node)); + + const initializer = transformConstantInitializer(node, substitutions); + + const changeTracker = textChanges.ChangeTracker.fromContext(context); + + if (isClassLike(scope)) { + // always create private method in TypeScript files + const modifiers: Modifier[] = []; + if (!isJS) { + modifiers.push(createToken(SyntaxKind.PrivateKeyword)); + } + if (rangeFacts & RangeFacts.InStaticRegion) { + modifiers.push(createToken(SyntaxKind.StaticKeyword)); + } + if (!isJS) { + modifiers.push(createToken(SyntaxKind.ReadonlyKeyword)); + } + + const newVariable = createProperty( + /*decorators*/ undefined, + modifiers.length ? modifiers : undefined, + localNameText, + /*questionToken*/ undefined, + variableType, + initializer); + + const localReference = createPropertyAccess( + rangeFacts & RangeFacts.InStaticRegion + ? createIdentifier(scope.name.getText()) + : createThis(), + createIdentifier(localNameText)); + + // Declare + const minInsertionPos = node.end; + const nodeToInsertBefore = getNodeToInsertConstantBefore(minInsertionPos, scope); + changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter }); + + // Consume + changeTracker.replaceNodeWithNodes(context.file, node, [localReference], { nodeSeparator: context.newLineCharacter }); + } + else { + const newVariable = createVariableStatement( + /*modifiers*/ undefined, + createVariableDeclarationList( + [createVariableDeclaration(localNameText, variableType, initializer)], + NodeFlags.Const)); + + // If the parent is an expression statement, replace the statement with the declaration + if (node.parent.kind === SyntaxKind.ExpressionStatement) { + changeTracker.replaceNodeWithNodes(context.file, node.parent, [newVariable], { nodeSeparator: context.newLineCharacter }); + } + else { + // Declare + const minInsertionPos = node.end; + const nodeToInsertBefore = getNodeToInsertConstantBefore(minInsertionPos, scope); + changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newVariable, { suffix: context.newLineCharacter + context.newLineCharacter }); + + // Consume + const localReference = createIdentifier(localNameText); + changeTracker.replaceNodeWithNodes(context.file, node, [localReference], { nodeSeparator: context.newLineCharacter }); + } + } + + const edits = changeTracker.getChanges(); + + const renameFilename = node.getSourceFile().fileName; + const renameLocation = getRenameLocation(edits, renameFilename, localNameText, /*isDeclaredBeforeUse*/ true); + return { renameFilename, renameLocation, edits }; + } + + /** + * @return The index of the (only) reference to the extracted symbol. We want the cursor + * to be on the reference, rather than the declaration, because it's closer to where the + * user was before extracting it. + */ + function getRenameLocation(edits: ReadonlyArray, renameFilename: string, functionNameText: string, isDeclaredBeforeUse: boolean): number { let delta = 0; + let lastPos = -1; for (const { fileName, textChanges } of edits) { Debug.assert(fileName === renameFilename); for (const change of textChanges) { const { span, newText } = change; - // TODO(acasey): We are assuming that the call expression comes before the function declaration, - // because we want the new cursor to be on the call expression, - // which is closer to where the user was before extracting the function. const index = newText.indexOf(functionNameText); if (index !== -1) { - return span.start + delta + index; + lastPos = span.start + delta + index; + + // If the reference comes first, return immediately. + if (!isDeclaredBeforeUse) { + return lastPos; + } } delta += newText.length - span.length; } } - throw new Error(); // Didn't find the text we inserted? + + // If the declaration comes first, return the position of the last occurrence. + Debug.assert(isDeclaredBeforeUse); + Debug.assert(lastPos >= 0); + return lastPos; } function getFirstDeclaration(type: Type): Declaration | undefined { @@ -899,7 +1065,7 @@ namespace ts.refactor.extractMethod { } else { const oldIgnoreReturns = ignoreReturns; - ignoreReturns = ignoreReturns || isFunctionLike(node) || isClassLike(node); + ignoreReturns = ignoreReturns || isFunctionLikeDeclaration(node) || isClassLike(node); const substitution = substitutions.get(getNodeId(node).toString()); const result = substitution || visitEachChild(node, visitor, nullTransformationContext); ignoreReturns = oldIgnoreReturns; @@ -908,8 +1074,19 @@ namespace ts.refactor.extractMethod { } } + function transformConstantInitializer(initializer: Expression, substitutions: ReadonlyMap): Expression { + return substitutions.size + ? visitor(initializer) as Expression + : initializer; + + function visitor(node: Node): VisitResult { + const substitution = substitutions.get(getNodeId(node).toString()); + return substitution || visitEachChild(node, visitor, nullTransformationContext); + } + } + function getStatementsOrClassElements(scope: Scope): ReadonlyArray | ReadonlyArray { - if (isFunctionLike(scope)) { + if (isFunctionLikeDeclaration(scope)) { const body = scope.body; if (isBlock(body)) { return body.statements; @@ -932,9 +1109,23 @@ namespace ts.refactor.extractMethod { * If `scope` contains a function after `minPos`, then return the first such function. * Otherwise, return `undefined`. */ - function getNodeToInsertBefore(minPos: number, scope: Scope): Node | undefined { + function getNodeToInsertFunctionBefore(minPos: number, scope: Scope): Node | undefined { return find(getStatementsOrClassElements(scope), child => - child.pos >= minPos && isFunctionLike(child) && !isConstructorDeclaration(child)); + child.pos >= minPos && isFunctionLikeDeclaration(child) && !isConstructorDeclaration(child)); + } + + function getNodeToInsertConstantBefore(minPos: number, scope: Scope): Node { + const isClassLikeScope = isClassLike(scope); + const children = getStatementsOrClassElements(scope); + let prevChild: Statement | ClassElement | undefined = undefined; + for (const child of children) { + if (child.pos >= minPos || (isClassLikeScope && !isPropertyDeclaration(child))) { + break; + } + prevChild = child; + } + + return prevChild || children[0]; // There must be one - minPos is in one. } function getPropertyAssignmentsForWrites(writes: ReadonlyArray): ShorthandPropertyAssignment[] { @@ -982,7 +1173,8 @@ namespace ts.refactor.extractMethod { interface ReadsAndWrites { readonly target: Expression | Block; readonly usagesPerScope: ReadonlyArray; - readonly errorsPerScope: ReadonlyArray>; + readonly functionErrorsPerScope: ReadonlyArray>; + readonly constantErrorsPerScope: ReadonlyArray>; } function collectReadsAndWrites( targetRange: TargetRange, @@ -995,14 +1187,24 @@ namespace ts.refactor.extractMethod { const allTypeParameterUsages = createMap(); // Key is type ID const usagesPerScope: ScopeUsages[] = []; const substitutionsPerScope: Map[] = []; - const errorsPerScope: Diagnostic[][] = []; + const functionErrorsPerScope: Diagnostic[][] = []; + const constantErrorsPerScope: Diagnostic[][] = []; const visibleDeclarationsInExtractedRange: Symbol[] = []; + const expressionDiagnostics = + isReadonlyArray(targetRange.range) + ? ((start, end) => [createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected)])(firstOrUndefined(targetRange.range).getStart(), lastOrUndefined(targetRange.range).end) + : []; + // initialize results - for (const _ of scopes) { + for (const scope of scopes) { usagesPerScope.push({ usages: createMap(), typeParameterUsages: createMap(), substitutions: createMap() }); substitutionsPerScope.push(createMap()); - errorsPerScope.push([]); + functionErrorsPerScope.push( + isFunctionLikeDeclaration(scope) && scope.kind !== SyntaxKind.FunctionDeclaration + ? [createDiagnosticForNode(scope, Messages.CannotExtractToOtherFunctionLike)] + : []); + constantErrorsPerScope.push(expressionDiagnostics); } const seenUsages = createMap(); @@ -1054,6 +1256,13 @@ namespace ts.refactor.extractMethod { } for (let i = 0; i < scopes.length; i++) { + if (!isReadonlyArray(targetRange.range)) { + const scopeUsages = usagesPerScope[i]; + if (scopeUsages.usages.size > 0 || scopeUsages.typeParameterUsages.size > 0) { + constantErrorsPerScope[i].push(createDiagnosticForNode(targetRange.range, Messages.CannotAccessVariablesFromNestedScopes)); + } + } + let hasWrite = false; let readonlyClassPropertyWrite: Declaration | undefined = undefined; usagesPerScope[i].usages.forEach(value => { @@ -1068,10 +1277,14 @@ namespace ts.refactor.extractMethod { }); if (hasWrite && !isReadonlyArray(targetRange.range) && isExpression(targetRange.range)) { - errorsPerScope[i].push(createDiagnosticForNode(targetRange.range, Messages.CannotCombineWritesAndReturns)); + const diag = createDiagnosticForNode(targetRange.range, Messages.CannotCombineWritesAndReturns); + functionErrorsPerScope[i].push(diag); + constantErrorsPerScope[i].push(diag); } else if (readonlyClassPropertyWrite && i > 0) { - errorsPerScope[i].push(createDiagnosticForNode(readonlyClassPropertyWrite, Messages.CannotExtractReadonlyPropertyInitializerOutsideConstructor)); + const diag = createDiagnosticForNode(readonlyClassPropertyWrite, Messages.CannotExtractReadonlyPropertyInitializerOutsideConstructor); + functionErrorsPerScope[i].push(diag); + constantErrorsPerScope[i].push(diag); } } @@ -1081,7 +1294,7 @@ namespace ts.refactor.extractMethod { forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations); } - return { target, usagesPerScope, errorsPerScope }; + return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope }; function hasTypeParameters(node: Node) { return isDeclarationWithTypeParameters(node) && @@ -1211,8 +1424,12 @@ namespace ts.refactor.extractMethod { if (targetRange.facts & RangeFacts.IsGenerator && usage === Usage.Write) { // this is write to a reference located outside of the target scope and range is extracted into generator // currently this is unsupported scenario - for (const errors of errorsPerScope) { - errors.push(createDiagnosticForNode(identifier, Messages.CannotExtractRangeThatContainsWritesToReferencesLocatedOutsideOfTheTargetRangeInGenerators)); + const diag = createDiagnosticForNode(identifier, Messages.CannotExtractRangeThatContainsWritesToReferencesLocatedOutsideOfTheTargetRangeInGenerators); + for (const errors of functionErrorsPerScope) { + errors.push(diag); + } + for (const errors of constantErrorsPerScope) { + errors.push(diag); } } for (let i = 0; i < scopes.length; i++) { @@ -1230,7 +1447,9 @@ namespace ts.refactor.extractMethod { // If the symbol is a type parameter that won't be in scope, we'll pass it as a type argument // so there's no problem. if (!(symbol.flags & SymbolFlags.TypeParameter)) { - errorsPerScope[i].push(createDiagnosticForNode(identifier, Messages.TypeWillNotBeVisibleInTheNewScope)); + const diag = createDiagnosticForNode(identifier, Messages.TypeWillNotBeVisibleInTheNewScope); + functionErrorsPerScope[i].push(diag); + constantErrorsPerScope[i].push(diag); } } else { @@ -1250,8 +1469,12 @@ namespace ts.refactor.extractMethod { // Otherwise check and recurse. const sym = checker.getSymbolAtLocation(node); if (sym && visibleDeclarationsInExtractedRange.some(d => d === sym)) { - for (const scope of errorsPerScope) { - scope.push(createDiagnosticForNode(node, Messages.CannotExtractExportedEntity)); + const diag = createDiagnosticForNode(node, Messages.CannotExtractExportedEntity); + for (const errors of functionErrorsPerScope) { + errors.push(diag); + } + for (const errors of constantErrorsPerScope) { + errors.push(diag); } return true; } diff --git a/tests/baselines/reference/extractMethod/extractMethod1.ts b/tests/baselines/reference/extractMethod/extractMethod1.ts index 86c28b5f4d2..74303bbf3e1 100644 --- a/tests/baselines/reference/extractMethod/extractMethod1.ts +++ b/tests/baselines/reference/extractMethod/extractMethod1.ts @@ -14,7 +14,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { let x = 1; function foo() { @@ -34,7 +34,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { let x = 1; function foo() { @@ -55,7 +55,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let x = 1; function foo() { @@ -76,7 +76,7 @@ namespace A { return a; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let x = 1; function foo() { diff --git a/tests/baselines/reference/extractMethod/extractMethod10.ts b/tests/baselines/reference/extractMethod/extractMethod10.ts index e3eb73b661e..65fbc9fb4c9 100644 --- a/tests/baselines/reference/extractMethod/extractMethod10.ts +++ b/tests/baselines/reference/extractMethod/extractMethod10.ts @@ -9,22 +9,22 @@ namespace A { } } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== namespace A { export interface I { x: number }; class C { a() { let z = 1; - return this./*RENAME*/newFunction(); + return this./*RENAME*/newMethod(); } - private newFunction() { + private newMethod() { let a1: I = { x: 1 }; return a1.x + 10; } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { export interface I { x: number }; class C { @@ -39,7 +39,7 @@ namespace A { return a1.x + 10; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { export interface I { x: number }; class C { diff --git a/tests/baselines/reference/extractMethod/extractMethod11.ts b/tests/baselines/reference/extractMethod/extractMethod11.ts index 43fdd75b76f..55472063055 100644 --- a/tests/baselines/reference/extractMethod/extractMethod11.ts +++ b/tests/baselines/reference/extractMethod/extractMethod11.ts @@ -11,18 +11,18 @@ namespace A { } } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== namespace A { let y = 1; class C { a() { let z = 1; var __return: any; - ({ __return, z } = this./*RENAME*/newFunction(z)); + ({ __return, z } = this./*RENAME*/newMethod(z)); return __return; } - private newFunction(z: number) { + private newMethod(z: number) { let a1 = { x: 1 }; y = 10; z = 42; @@ -30,7 +30,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let y = 1; class C { @@ -49,7 +49,7 @@ namespace A { return { __return: a1.x + 10, z }; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let y = 1; class C { diff --git a/tests/baselines/reference/extractMethod/extractMethod12.ts b/tests/baselines/reference/extractMethod/extractMethod12.ts index 2f3082cf280..c8f066657a2 100644 --- a/tests/baselines/reference/extractMethod/extractMethod12.ts +++ b/tests/baselines/reference/extractMethod/extractMethod12.ts @@ -13,7 +13,7 @@ namespace A { } } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== namespace A { let y = 1; class C { @@ -21,11 +21,11 @@ namespace A { a() { let z = 1; var __return: any; - ({ __return, z } = this./*RENAME*/newFunction(z)); + ({ __return, z } = this./*RENAME*/newMethod(z)); return __return; } - private newFunction(z: number) { + private newMethod(z: number) { let a1 = { x: 1 }; y = 10; z = 42; diff --git a/tests/baselines/reference/extractMethod/extractMethod13.ts b/tests/baselines/reference/extractMethod/extractMethod13.ts index 121d7eeecfa..c40f29711f4 100644 --- a/tests/baselines/reference/extractMethod/extractMethod13.ts +++ b/tests/baselines/reference/extractMethod/extractMethod13.ts @@ -14,7 +14,7 @@ } } } -// ==SCOPE::inner function in function 'F2'== +// ==SCOPE::Extract to inner function in function 'F2'== (u1a: U1a, u1b: U1b) => { function F1(t1a: T1a, t1b: T1b) { (u2a: U2a, u2b: U2b) => { @@ -34,7 +34,7 @@ } } } -// ==SCOPE::inner function in function 'F1'== +// ==SCOPE::Extract to inner function in function 'F1'== (u1a: U1a, u1b: U1b) => { function F1(t1a: T1a, t1b: T1b) { (u2a: U2a, u2b: U2b) => { @@ -54,7 +54,7 @@ } } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== (u1a: U1a, u1b: U1b) => { function F1(t1a: T1a, t1b: T1b) { (u2a: U2a, u2b: U2b) => { diff --git a/tests/baselines/reference/extractMethod/extractMethod14.ts b/tests/baselines/reference/extractMethod/extractMethod14.ts index d3dcded2c42..943c8250edd 100644 --- a/tests/baselines/reference/extractMethod/extractMethod14.ts +++ b/tests/baselines/reference/extractMethod/extractMethod14.ts @@ -1,13 +1,13 @@ // ==ORIGINAL== function F(t1: T) { - function F(t2: T) { + function G(t2: T) { t1.toString(); t2.toString(); } } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'G'== function F(t1: T) { - function F(t2: T) { + function G(t2: T) { /*RENAME*/newFunction(); function newFunction() { @@ -16,9 +16,9 @@ function F(t1: T) { } } } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'F'== function F(t1: T) { - function F(t2: T) { + function G(t2: T) { /*RENAME*/newFunction(t2); } @@ -27,9 +27,9 @@ function F(t1: T) { t2.toString(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function F(t1: T) { - function F(t2: T) { + function G(t2: T) { /*RENAME*/newFunction(t1, t2); } } diff --git a/tests/baselines/reference/extractMethod/extractMethod15.ts b/tests/baselines/reference/extractMethod/extractMethod15.ts index 50516445c87..b6e3e0f04b6 100644 --- a/tests/baselines/reference/extractMethod/extractMethod15.ts +++ b/tests/baselines/reference/extractMethod/extractMethod15.ts @@ -1,12 +1,12 @@ // ==ORIGINAL== function F(t1: T) { - function F(t2: U) { + function G(t2: U) { t2.toString(); } } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'G'== function F(t1: T) { - function F(t2: U) { + function G(t2: U) { /*RENAME*/newFunction(); function newFunction() { @@ -14,9 +14,9 @@ function F(t1: T) { } } } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'F'== function F(t1: T) { - function F(t2: U) { + function G(t2: U) { /*RENAME*/newFunction(t2); } @@ -24,9 +24,9 @@ function F(t1: T) { t2.toString(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function F(t1: T) { - function F(t2: U) { + function G(t2: U) { /*RENAME*/newFunction(t2); } } diff --git a/tests/baselines/reference/extractMethod/extractMethod16.ts b/tests/baselines/reference/extractMethod/extractMethod16.ts index e58bbf576c6..223307ff349 100644 --- a/tests/baselines/reference/extractMethod/extractMethod16.ts +++ b/tests/baselines/reference/extractMethod/extractMethod16.ts @@ -2,7 +2,7 @@ function F() { const array: T[] = []; } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'F'== function F() { const array: T[] = /*RENAME*/newFunction(); @@ -10,7 +10,7 @@ function F() { return []; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function F() { const array: T[] = /*RENAME*/newFunction(); } diff --git a/tests/baselines/reference/extractMethod/extractMethod17.ts b/tests/baselines/reference/extractMethod/extractMethod17.ts index f79abcca792..e1f20af6867 100644 --- a/tests/baselines/reference/extractMethod/extractMethod17.ts +++ b/tests/baselines/reference/extractMethod/extractMethod17.ts @@ -4,17 +4,17 @@ class C { t1.toString(); } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== class C { M(t1: T1, t2: T2) { - this./*RENAME*/newFunction(t1); + this./*RENAME*/newMethod(t1); } - private newFunction(t1: T1) { + private newMethod(t1: T1) { t1.toString(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== class C { M(t1: T1, t2: T2) { /*RENAME*/newFunction(t1); diff --git a/tests/baselines/reference/extractMethod/extractMethod18.ts b/tests/baselines/reference/extractMethod/extractMethod18.ts index 122eced75d5..775a1f3ed15 100644 --- a/tests/baselines/reference/extractMethod/extractMethod18.ts +++ b/tests/baselines/reference/extractMethod/extractMethod18.ts @@ -4,17 +4,17 @@ class C { t1.toString(); } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== class C { M(t1: T1, t2: T2) { - this./*RENAME*/newFunction(t1); + this./*RENAME*/newMethod(t1); } - private newFunction(t1: T1) { + private newMethod(t1: T1) { t1.toString(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== class C { M(t1: T1, t2: T2) { /*RENAME*/newFunction(t1); diff --git a/tests/baselines/reference/extractMethod/extractMethod19.ts b/tests/baselines/reference/extractMethod/extractMethod19.ts index 61d35f97db5..e9107198b18 100644 --- a/tests/baselines/reference/extractMethod/extractMethod19.ts +++ b/tests/baselines/reference/extractMethod/extractMethod19.ts @@ -2,7 +2,7 @@ function F(v: V) { v.toString(); } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'F'== function F(v: V) { /*RENAME*/newFunction(); @@ -10,7 +10,7 @@ function F(v: V) { v.toString(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function F(v: V) { /*RENAME*/newFunction(v); } diff --git a/tests/baselines/reference/extractMethod/extractMethod2.ts b/tests/baselines/reference/extractMethod/extractMethod2.ts index b83cc6f32c6..201dd173adc 100644 --- a/tests/baselines/reference/extractMethod/extractMethod2.ts +++ b/tests/baselines/reference/extractMethod/extractMethod2.ts @@ -12,7 +12,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { let x = 1; function foo() { @@ -30,7 +30,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { let x = 1; function foo() { @@ -48,7 +48,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let x = 1; function foo() { @@ -66,7 +66,7 @@ namespace A { return foo(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let x = 1; function foo() { diff --git a/tests/baselines/reference/extractMethod/extractMethod20.ts b/tests/baselines/reference/extractMethod/extractMethod20.ts index 6e0148e0991..723d8a99ca6 100644 --- a/tests/baselines/reference/extractMethod/extractMethod20.ts +++ b/tests/baselines/reference/extractMethod/extractMethod20.ts @@ -5,18 +5,18 @@ const _ = class { return a1.x + 10; } } -// ==SCOPE::method in anonymous class expression== +// ==SCOPE::Extract to method in anonymous class expression== const _ = class { a() { - return this./*RENAME*/newFunction(); + return this./*RENAME*/newMethod(); } - private newFunction() { + private newMethod() { let a1 = { x: 1 }; return a1.x + 10; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== const _ = class { a() { return /*RENAME*/newFunction(); diff --git a/tests/baselines/reference/extractMethod/extractMethod21.ts b/tests/baselines/reference/extractMethod/extractMethod21.ts index 2c4ffd1bdb8..b0469d81384 100644 --- a/tests/baselines/reference/extractMethod/extractMethod21.ts +++ b/tests/baselines/reference/extractMethod/extractMethod21.ts @@ -4,7 +4,7 @@ function foo() { x++; return; } -// ==SCOPE::inner function in function 'foo'== +// ==SCOPE::Extract to inner function in function 'foo'== function foo() { let x = 10; return /*RENAME*/newFunction(); @@ -14,7 +14,7 @@ function foo() { return; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function foo() { let x = 10; x = /*RENAME*/newFunction(x); diff --git a/tests/baselines/reference/extractMethod/extractMethod22.ts b/tests/baselines/reference/extractMethod/extractMethod22.ts index 990bfdf0575..09fbc7a5b82 100644 --- a/tests/baselines/reference/extractMethod/extractMethod22.ts +++ b/tests/baselines/reference/extractMethod/extractMethod22.ts @@ -6,7 +6,7 @@ function test() { return 1; } } -// ==SCOPE::inner function in function 'test'== +// ==SCOPE::Extract to inner function in function 'test'== function test() { try { } @@ -18,7 +18,7 @@ function test() { return 1; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function test() { try { } diff --git a/tests/baselines/reference/extractMethod/extractMethod23.ts b/tests/baselines/reference/extractMethod/extractMethod23.ts index b9bc4264ea6..1a0eaf098fd 100644 --- a/tests/baselines/reference/extractMethod/extractMethod23.ts +++ b/tests/baselines/reference/extractMethod/extractMethod23.ts @@ -6,7 +6,7 @@ namespace NS { } function M3() { } } -// ==SCOPE::inner function in function 'M2'== +// ==SCOPE::Extract to inner function in function 'M2'== namespace NS { function M1() { } function M2() { @@ -18,7 +18,7 @@ namespace NS { } function M3() { } } -// ==SCOPE::function in namespace 'NS'== +// ==SCOPE::Extract to function in namespace 'NS'== namespace NS { function M1() { } function M2() { @@ -30,7 +30,7 @@ namespace NS { function M3() { } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace NS { function M1() { } function M2() { diff --git a/tests/baselines/reference/extractMethod/extractMethod24.ts b/tests/baselines/reference/extractMethod/extractMethod24.ts index a9dc25d32ea..7b80180c5d3 100644 --- a/tests/baselines/reference/extractMethod/extractMethod24.ts +++ b/tests/baselines/reference/extractMethod/extractMethod24.ts @@ -6,7 +6,7 @@ function Outer() { } function M3() { } } -// ==SCOPE::inner function in function 'M2'== +// ==SCOPE::Extract to inner function in function 'M2'== function Outer() { function M1() { } function M2() { @@ -18,7 +18,7 @@ function Outer() { } function M3() { } } -// ==SCOPE::inner function in function 'Outer'== +// ==SCOPE::Extract to inner function in function 'Outer'== function Outer() { function M1() { } function M2() { @@ -30,7 +30,7 @@ function Outer() { function M3() { } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function Outer() { function M1() { } function M2() { diff --git a/tests/baselines/reference/extractMethod/extractMethod25.ts b/tests/baselines/reference/extractMethod/extractMethod25.ts index dc376781346..3233c404307 100644 --- a/tests/baselines/reference/extractMethod/extractMethod25.ts +++ b/tests/baselines/reference/extractMethod/extractMethod25.ts @@ -4,7 +4,7 @@ function M2() { return 1; } function M3() { } -// ==SCOPE::inner function in function 'M2'== +// ==SCOPE::Extract to inner function in function 'M2'== function M1() { } function M2() { return /*RENAME*/newFunction(); @@ -14,7 +14,7 @@ function M2() { } } function M3() { } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function M1() { } function M2() { return /*RENAME*/newFunction(); diff --git a/tests/baselines/reference/extractMethod/extractMethod26.ts b/tests/baselines/reference/extractMethod/extractMethod26.ts index b49e8ab9508..6f4d85c7801 100644 --- a/tests/baselines/reference/extractMethod/extractMethod26.ts +++ b/tests/baselines/reference/extractMethod/extractMethod26.ts @@ -6,19 +6,19 @@ class C { } M3() { } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== class C { M1() { } M2() { - return this./*RENAME*/newFunction(); + return this./*RENAME*/newMethod(); } - private newFunction() { + private newMethod() { return 1; } M3() { } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== class C { M1() { } M2() { diff --git a/tests/baselines/reference/extractMethod/extractMethod27.ts b/tests/baselines/reference/extractMethod/extractMethod27.ts index 0ec214bb5ed..3426c4ec5c3 100644 --- a/tests/baselines/reference/extractMethod/extractMethod27.ts +++ b/tests/baselines/reference/extractMethod/extractMethod27.ts @@ -7,20 +7,20 @@ class C { constructor() { } M3() { } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== class C { M1() { } M2() { - return this./*RENAME*/newFunction(); + return this./*RENAME*/newMethod(); } constructor() { } - private newFunction() { + private newMethod() { return 1; } M3() { } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== class C { M1() { } M2() { diff --git a/tests/baselines/reference/extractMethod/extractMethod28.ts b/tests/baselines/reference/extractMethod/extractMethod28.ts index ab6ce220bd7..b6e94cb47c5 100644 --- a/tests/baselines/reference/extractMethod/extractMethod28.ts +++ b/tests/baselines/reference/extractMethod/extractMethod28.ts @@ -7,20 +7,20 @@ class C { M3() { } constructor() { } } -// ==SCOPE::method in class 'C'== +// ==SCOPE::Extract to method in class 'C'== class C { M1() { } M2() { - return this./*RENAME*/newFunction(); + return this./*RENAME*/newMethod(); } - private newFunction() { + private newMethod() { return 1; } M3() { } constructor() { } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== class C { M1() { } M2() { diff --git a/tests/baselines/reference/extractMethod/extractMethod29.ts b/tests/baselines/reference/extractMethod/extractMethod29.ts index aa7004d254e..9baea5870f5 100644 --- a/tests/baselines/reference/extractMethod/extractMethod29.ts +++ b/tests/baselines/reference/extractMethod/extractMethod29.ts @@ -16,7 +16,7 @@ function parseUnaryExpression(operator: string): UnaryExpression { function parsePrimaryExpression(): any { throw "Not implemented"; } -// ==SCOPE::inner function in function 'parseUnaryExpression'== +// ==SCOPE::Extract to inner function in function 'parseUnaryExpression'== interface UnaryExpression { kind: "Unary"; operator: string; @@ -38,7 +38,7 @@ function parseUnaryExpression(operator: string): UnaryExpression { function parsePrimaryExpression(): any { throw "Not implemented"; } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== interface UnaryExpression { kind: "Unary"; operator: string; diff --git a/tests/baselines/reference/extractMethod/extractMethod3.ts b/tests/baselines/reference/extractMethod/extractMethod3.ts index 0af79791fe7..b5ae7592157 100644 --- a/tests/baselines/reference/extractMethod/extractMethod3.ts +++ b/tests/baselines/reference/extractMethod/extractMethod3.ts @@ -11,7 +11,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { function foo() { } @@ -28,7 +28,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { function foo() { } @@ -45,7 +45,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { function foo() { } @@ -62,7 +62,7 @@ namespace A { return foo(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { function foo() { } diff --git a/tests/baselines/reference/extractMethod/extractMethod30.ts b/tests/baselines/reference/extractMethod/extractMethod30.ts index 67dc1208abc..35b86b668ff 100644 --- a/tests/baselines/reference/extractMethod/extractMethod30.ts +++ b/tests/baselines/reference/extractMethod/extractMethod30.ts @@ -2,7 +2,7 @@ function F() { let t: T; } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'F'== function F() { /*RENAME*/newFunction(); @@ -10,7 +10,7 @@ function F() { let t: T; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function F() { /*RENAME*/newFunction(); } diff --git a/tests/baselines/reference/extractMethod/extractMethod31.ts b/tests/baselines/reference/extractMethod/extractMethod31.ts index 754814dcc26..77120091828 100644 --- a/tests/baselines/reference/extractMethod/extractMethod31.ts +++ b/tests/baselines/reference/extractMethod/extractMethod31.ts @@ -10,7 +10,7 @@ namespace N { } } } -// ==SCOPE::function in namespace 'N'== +// ==SCOPE::Extract to function in namespace 'N'== namespace N { export const value = 1; @@ -27,7 +27,7 @@ namespace N { return f; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace N { export const value = 1; diff --git a/tests/baselines/reference/extractMethod/extractMethod32.ts b/tests/baselines/reference/extractMethod/extractMethod32.ts index b9b870a08fa..529b81e8f9b 100644 --- a/tests/baselines/reference/extractMethod/extractMethod32.ts +++ b/tests/baselines/reference/extractMethod/extractMethod32.ts @@ -11,7 +11,7 @@ namespace N { } } } -// ==SCOPE::function in namespace 'N'== +// ==SCOPE::Extract to function in namespace 'N'== namespace N { export const value = 1; @@ -28,7 +28,7 @@ namespace N { }; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace N { export const value = 1; diff --git a/tests/baselines/reference/extractMethod/extractMethod33.ts b/tests/baselines/reference/extractMethod/extractMethod33.ts index 79a6626a535..fd716646e66 100644 --- a/tests/baselines/reference/extractMethod/extractMethod33.ts +++ b/tests/baselines/reference/extractMethod/extractMethod33.ts @@ -2,7 +2,7 @@ function F() { function G() { } } -// ==SCOPE::inner function in function 'F'== +// ==SCOPE::Extract to inner function in function 'F'== function F() { /*RENAME*/newFunction(); @@ -10,7 +10,7 @@ function F() { function G() { } } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== function F() { /*RENAME*/newFunction(); } diff --git a/tests/baselines/reference/extractMethod/extractMethod4.ts b/tests/baselines/reference/extractMethod/extractMethod4.ts index e0a636135d4..6ef3e843ee1 100644 --- a/tests/baselines/reference/extractMethod/extractMethod4.ts +++ b/tests/baselines/reference/extractMethod/extractMethod4.ts @@ -13,7 +13,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { function foo() { } @@ -32,7 +32,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { function foo() { } @@ -51,7 +51,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { function foo() { } @@ -70,7 +70,7 @@ namespace A { return foo(); } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { function foo() { } diff --git a/tests/baselines/reference/extractMethod/extractMethod5.ts b/tests/baselines/reference/extractMethod/extractMethod5.ts index fc15f6762e5..e2f72721b95 100644 --- a/tests/baselines/reference/extractMethod/extractMethod5.ts +++ b/tests/baselines/reference/extractMethod/extractMethod5.ts @@ -14,7 +14,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { let x = 1; export function foo() { @@ -34,7 +34,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { let x = 1; export function foo() { @@ -55,7 +55,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let x = 1; export function foo() { @@ -76,7 +76,7 @@ namespace A { return a; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let x = 1; export function foo() { diff --git a/tests/baselines/reference/extractMethod/extractMethod6.ts b/tests/baselines/reference/extractMethod/extractMethod6.ts index 37112b80e9e..6b4852dc722 100644 --- a/tests/baselines/reference/extractMethod/extractMethod6.ts +++ b/tests/baselines/reference/extractMethod/extractMethod6.ts @@ -14,7 +14,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { let x = 1; export function foo() { @@ -34,7 +34,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { let x = 1; export function foo() { @@ -56,7 +56,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let x = 1; export function foo() { @@ -78,7 +78,7 @@ namespace A { return { __return: foo(), a }; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let x = 1; export function foo() { diff --git a/tests/baselines/reference/extractMethod/extractMethod7.ts b/tests/baselines/reference/extractMethod/extractMethod7.ts index 8859b7b4fdd..9712cedda8d 100644 --- a/tests/baselines/reference/extractMethod/extractMethod7.ts +++ b/tests/baselines/reference/extractMethod/extractMethod7.ts @@ -16,7 +16,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { let x = 1; export namespace C { @@ -38,7 +38,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { let x = 1; export namespace C { @@ -62,7 +62,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let x = 1; export namespace C { @@ -86,7 +86,7 @@ namespace A { return { __return: C.foo(), a }; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let x = 1; export namespace C { diff --git a/tests/baselines/reference/extractMethod/extractMethod8.ts b/tests/baselines/reference/extractMethod/extractMethod8.ts index cb06470d385..adb8adbe56a 100644 --- a/tests/baselines/reference/extractMethod/extractMethod8.ts +++ b/tests/baselines/reference/extractMethod/extractMethod8.ts @@ -8,7 +8,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { let x = 1; namespace B { @@ -22,7 +22,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { let x = 1; namespace B { @@ -36,7 +36,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { let x = 1; namespace B { @@ -50,7 +50,7 @@ namespace A { return 1 + a1 + x; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { let x = 1; namespace B { diff --git a/tests/baselines/reference/extractMethod/extractMethod9.ts b/tests/baselines/reference/extractMethod/extractMethod9.ts index 022dab82363..9c1f81edf45 100644 --- a/tests/baselines/reference/extractMethod/extractMethod9.ts +++ b/tests/baselines/reference/extractMethod/extractMethod9.ts @@ -8,7 +8,7 @@ namespace A { } } } -// ==SCOPE::inner function in function 'a'== +// ==SCOPE::Extract to inner function in function 'a'== namespace A { export interface I { x: number }; namespace B { @@ -22,7 +22,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'B'== +// ==SCOPE::Extract to function in namespace 'B'== namespace A { export interface I { x: number }; namespace B { @@ -36,7 +36,7 @@ namespace A { } } } -// ==SCOPE::function in namespace 'A'== +// ==SCOPE::Extract to function in namespace 'A'== namespace A { export interface I { x: number }; namespace B { @@ -50,7 +50,7 @@ namespace A { return a1.x + 10; } } -// ==SCOPE::function in global scope== +// ==SCOPE::Extract to function in global scope== namespace A { export interface I { x: number }; namespace B { diff --git a/tests/cases/fourslash/extract-method-empty-namespace.ts b/tests/cases/fourslash/extract-method-empty-namespace.ts index 3a29992350d..bef4cdd12fe 100644 --- a/tests/cases/fourslash/extract-method-empty-namespace.ts +++ b/tests/cases/fourslash/extract-method-empty-namespace.ts @@ -6,8 +6,8 @@ goTo.select('start', 'end') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_1", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to function in global scope", newContent: `function f() { /*RENAME*/newFunction(); diff --git a/tests/cases/fourslash/extract-method-formatting.ts b/tests/cases/fourslash/extract-method-formatting.ts index e4193fd8db3..d4c2836e815 100644 --- a/tests/cases/fourslash/extract-method-formatting.ts +++ b/tests/cases/fourslash/extract-method-formatting.ts @@ -7,8 +7,8 @@ goTo.select('start', 'end') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_1", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to function in global scope", newContent: `function f(x: number): number { return /*RENAME*/newFunction(x); diff --git a/tests/cases/fourslash/extract-method-not-for-empty.ts b/tests/cases/fourslash/extract-method-not-for-empty.ts index 756716441cd..253614d3160 100644 --- a/tests/cases/fourslash/extract-method-not-for-empty.ts +++ b/tests/cases/fourslash/extract-method-not-for-empty.ts @@ -3,4 +3,4 @@ ////"/**/foo"; goTo.marker(""); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method-not-for-import.ts b/tests/cases/fourslash/extract-method-not-for-import.ts index a72d793611d..2852d856264 100644 --- a/tests/cases/fourslash/extract-method-not-for-import.ts +++ b/tests/cases/fourslash/extract-method-not-for-import.ts @@ -7,4 +7,4 @@ ////export default function f() {} goTo.marker(""); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method-uniqueName.ts b/tests/cases/fourslash/extract-method-uniqueName.ts index 8f024ad6a47..4271d9e84d2 100644 --- a/tests/cases/fourslash/extract-method-uniqueName.ts +++ b/tests/cases/fourslash/extract-method-uniqueName.ts @@ -7,8 +7,8 @@ // it's omitted right now. goTo.select('start', 'end') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to function in global scope", newContent: `/*RENAME*/newFunction_1(); diff --git a/tests/cases/fourslash/extract-method1.ts b/tests/cases/fourslash/extract-method1.ts index a8d421923b1..f4693877ed8 100644 --- a/tests/cases/fourslash/extract-method1.ts +++ b/tests/cases/fourslash/extract-method1.ts @@ -14,18 +14,18 @@ goTo.select('start', 'end') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to method in class 'Foo'", newContent: `class Foo { someMethod(m: number) { - this./*RENAME*/newFunction(m); + this./*RENAME*/newMethod(m); var q = 10; return q; } - private newFunction(m: number) { + private newMethod(m: number) { var x = m; x = x * 3; var y = 30; diff --git a/tests/cases/fourslash/extract-method10.ts b/tests/cases/fourslash/extract-method10.ts index 215196d6c51..ed92e6b06ac 100644 --- a/tests/cases/fourslash/extract-method10.ts +++ b/tests/cases/fourslash/extract-method10.ts @@ -5,8 +5,8 @@ goTo.select('1', '2'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: 'scope_0', + refactorName: "Extract Symbol", + actionName: 'function_scope_0', actionDescription: "Extract to function in module scope", newContent: `export {}; // Make this a module diff --git a/tests/cases/fourslash/extract-method11.ts b/tests/cases/fourslash/extract-method11.ts index 705f2373104..a7d90f04f42 100644 --- a/tests/cases/fourslash/extract-method11.ts +++ b/tests/cases/fourslash/extract-method11.ts @@ -20,9 +20,9 @@ for (const m of ['1', '2', '3', '4', '5']) { goTo.select(m + 'a', m + 'b'); - verify.not.refactorAvailable('Extract Method'); + verify.not.refactorAvailable('Extract Symbol'); } // Verify we can still extract the entire class goTo.select('oka', 'okb'); -verify.refactorAvailable('Extract Method'); +verify.refactorAvailable('Extract Symbol', 'function_scope_0'); diff --git a/tests/cases/fourslash/extract-method13.ts b/tests/cases/fourslash/extract-method13.ts index 409b5f890ad..fc0b22f3d48 100644 --- a/tests/cases/fourslash/extract-method13.ts +++ b/tests/cases/fourslash/extract-method13.ts @@ -11,16 +11,16 @@ goTo.select('a', 'b'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to method in class 'C'", newContent: `class C { static j = 1 + 1; - constructor(q: string = C./*RENAME*/newFunction()) { + constructor(q: string = C./*RENAME*/newMethod()) { } - private static newFunction(): string { + private static newMethod(): string { return "hello"; } }` @@ -28,30 +28,30 @@ edit.applyRefactor({ verify.currentFileContentIs(`class C { static j = 1 + 1; - constructor(q: string = C.newFunction()) { + constructor(q: string = C.newMethod()) { } - private static newFunction(): string { + private static newMethod(): string { return "hello"; } }`); goTo.select('c', 'd'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to method in class 'C'", newContent: `class C { - static j = C./*RENAME*/newFunction_1(); - constructor(q: string = C.newFunction()) { + static j = C./*RENAME*/newMethod_1(); + constructor(q: string = C.newMethod()) { } - private static newFunction_1() { + private static newMethod_1() { return 1 + 1; } - private static newFunction(): string { + private static newMethod(): string { return "hello"; } }` diff --git a/tests/cases/fourslash/extract-method14.ts b/tests/cases/fourslash/extract-method14.ts index e2b58a36450..ea051aabbe7 100644 --- a/tests/cases/fourslash/extract-method14.ts +++ b/tests/cases/fourslash/extract-method14.ts @@ -12,8 +12,8 @@ goTo.select('a', 'b'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_1", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to function in global scope", newContent: `function foo() { diff --git a/tests/cases/fourslash/extract-method15.ts b/tests/cases/fourslash/extract-method15.ts index c3db3186cdf..e46ff39ad6a 100644 --- a/tests/cases/fourslash/extract-method15.ts +++ b/tests/cases/fourslash/extract-method15.ts @@ -10,8 +10,8 @@ goTo.select('a', 'b'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_1", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to function in global scope", newContent: `function foo() { diff --git a/tests/cases/fourslash/extract-method17.ts b/tests/cases/fourslash/extract-method17.ts index ce54896604d..467ff91ebb2 100644 --- a/tests/cases/fourslash/extract-method17.ts +++ b/tests/cases/fourslash/extract-method17.ts @@ -6,5 +6,5 @@ //// } goTo.select('start', 'end') -verify.refactorAvailable('Extract Method', 'scope_0'); -verify.not.refactorAvailable('Extract Method', 'scope_1'); +verify.refactorAvailable('Extract Symbol', 'function_scope_0'); +verify.not.refactorAvailable('Extract Symbol', 'function_scope_1'); diff --git a/tests/cases/fourslash/extract-method18.ts b/tests/cases/fourslash/extract-method18.ts index 8ff1cc3028d..d53e79f4930 100644 --- a/tests/cases/fourslash/extract-method18.ts +++ b/tests/cases/fourslash/extract-method18.ts @@ -10,8 +10,8 @@ goTo.select('a', 'b') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_1", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to function in global scope", newContent: `function fn() { diff --git a/tests/cases/fourslash/extract-method19.ts b/tests/cases/fourslash/extract-method19.ts index 56d6b02560f..87534a17d4a 100644 --- a/tests/cases/fourslash/extract-method19.ts +++ b/tests/cases/fourslash/extract-method19.ts @@ -10,8 +10,8 @@ goTo.select('a', 'b') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to inner function in function 'fn'", newContent: `function fn() { diff --git a/tests/cases/fourslash/extract-method2.ts b/tests/cases/fourslash/extract-method2.ts index 6fbe7394c2f..7f197d4775b 100644 --- a/tests/cases/fourslash/extract-method2.ts +++ b/tests/cases/fourslash/extract-method2.ts @@ -11,8 +11,8 @@ //// } goTo.select('start', 'end') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_2", + refactorName: "Extract Symbol", + actionName: "function_scope_3", actionDescription: "Extract to function in global scope", newContent: `namespace NS { diff --git a/tests/cases/fourslash/extract-method20.ts b/tests/cases/fourslash/extract-method20.ts index 04990001b5f..75927f0fd5c 100644 --- a/tests/cases/fourslash/extract-method20.ts +++ b/tests/cases/fourslash/extract-method20.ts @@ -10,5 +10,5 @@ //// } goTo.select('a', 'b') -verify.refactorAvailable('Extract Method', 'scope_0'); -verify.not.refactorAvailable('Extract Method', 'scope_1'); +verify.refactorAvailable('Extract Symbol', 'function_scope_0'); +verify.not.refactorAvailable('Extract Symbol', 'function_scope_1'); diff --git a/tests/cases/fourslash/extract-method21.ts b/tests/cases/fourslash/extract-method21.ts index 8e3baf61949..cbb1f55e071 100644 --- a/tests/cases/fourslash/extract-method21.ts +++ b/tests/cases/fourslash/extract-method21.ts @@ -10,19 +10,19 @@ goTo.select('start', 'end') -verify.refactorAvailable('Extract Method'); +verify.refactorAvailable('Extract Symbol', 'function_scope_1'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to method in class 'Foo'", newContent: `class Foo { static method() { - return Foo./*RENAME*/newFunction(); + return Foo./*RENAME*/newMethod(); } - private static newFunction() { + private static newMethod() { return 1; } }` diff --git a/tests/cases/fourslash/extract-method22.ts b/tests/cases/fourslash/extract-method22.ts index 7486520e700..85f88f3952c 100644 --- a/tests/cases/fourslash/extract-method22.ts +++ b/tests/cases/fourslash/extract-method22.ts @@ -7,4 +7,4 @@ //// } goTo.select('start', 'end') -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method23.ts b/tests/cases/fourslash/extract-method23.ts index 7da8f175f13..ad5248a37e2 100644 --- a/tests/cases/fourslash/extract-method23.ts +++ b/tests/cases/fourslash/extract-method23.ts @@ -5,4 +5,4 @@ //// } goTo.select('start', 'end') -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method24.ts b/tests/cases/fourslash/extract-method24.ts index 5706c5c7a54..8c750edd9f9 100644 --- a/tests/cases/fourslash/extract-method24.ts +++ b/tests/cases/fourslash/extract-method24.ts @@ -8,8 +8,8 @@ goTo.select('a', 'b') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_1", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to function in global scope", newContent: `function M() { diff --git a/tests/cases/fourslash/extract-method25.ts b/tests/cases/fourslash/extract-method25.ts index 4fb2193adf3..a06262ca475 100644 --- a/tests/cases/fourslash/extract-method25.ts +++ b/tests/cases/fourslash/extract-method25.ts @@ -9,8 +9,8 @@ goTo.select('a', 'b') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to inner function in function 'fn'", newContent: `function fn() { diff --git a/tests/cases/fourslash/extract-method26.ts b/tests/cases/fourslash/extract-method26.ts index 68982eda86c..2915118fd8e 100644 --- a/tests/cases/fourslash/extract-method26.ts +++ b/tests/cases/fourslash/extract-method26.ts @@ -13,17 +13,17 @@ goTo.select('a', 'b') edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_1", actionDescription: "Extract to method in class 'C'", newContent: `class C { M() { - const q = this./*RENAME*/newFunction(); + const q = this./*RENAME*/newMethod(); q.toString(); } - newFunction() { + newMethod() { return 1 + 2; } }` diff --git a/tests/cases/fourslash/extract-method3.ts b/tests/cases/fourslash/extract-method3.ts index 27a520d0555..43402a7ac20 100644 --- a/tests/cases/fourslash/extract-method3.ts +++ b/tests/cases/fourslash/extract-method3.ts @@ -10,9 +10,9 @@ //// } //// } -// Don't offer to 'extract method' a single identifier +// Don't offer to 'extract symbol' a single identifier goTo.marker('a'); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); goTo.select('a', 'b'); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method4.ts b/tests/cases/fourslash/extract-method4.ts index ec8f39f3541..93eabbc0ae5 100644 --- a/tests/cases/fourslash/extract-method4.ts +++ b/tests/cases/fourslash/extract-method4.ts @@ -11,4 +11,4 @@ // Should rewrite to a = newFunc(); function() { return b = c = d; } goTo.select('1', '2'); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method5.ts b/tests/cases/fourslash/extract-method5.ts index b27d9a8209b..ce164d43c6e 100644 --- a/tests/cases/fourslash/extract-method5.ts +++ b/tests/cases/fourslash/extract-method5.ts @@ -10,8 +10,8 @@ goTo.select('start', 'end'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to inner function in function 'f'", newContent: `function f() { diff --git a/tests/cases/fourslash/extract-method6.ts b/tests/cases/fourslash/extract-method6.ts index f188ab319e9..be21505ac13 100644 --- a/tests/cases/fourslash/extract-method6.ts +++ b/tests/cases/fourslash/extract-method6.ts @@ -10,7 +10,7 @@ //// }/*f1b*/ goTo.select('f1a', 'f1b'); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); goTo.select('g1a', 'g1b'); -verify.not.refactorAvailable('Extract Method'); +verify.not.refactorAvailable('Extract Symbol'); diff --git a/tests/cases/fourslash/extract-method7.ts b/tests/cases/fourslash/extract-method7.ts index 0ce39c5a309..2998b6bbee3 100644 --- a/tests/cases/fourslash/extract-method7.ts +++ b/tests/cases/fourslash/extract-method7.ts @@ -1,15 +1,15 @@ /// // You cannot extract a function initializer into the function's body. -// The innermost scope (scope_0) is the sibling of the function, not the function itself. +// The innermost scope (function_scope_0) is the sibling of the function, not the function itself. //// function fn(x = /*a*/3/*b*/) { //// } goTo.select('a', 'b'); edit.applyRefactor({ - refactorName: "Extract Method", - actionName: "scope_0", + refactorName: "Extract Symbol", + actionName: "function_scope_0", actionDescription: "Extract to function in global scope", newContent: `function fn(x = /*RENAME*/newFunction()) { diff --git a/tests/cases/fourslash/extract-method8.ts b/tests/cases/fourslash/extract-method8.ts index 28068dd7c78..12d1dab2828 100644 --- a/tests/cases/fourslash/extract-method8.ts +++ b/tests/cases/fourslash/extract-method8.ts @@ -4,14 +4,14 @@ //// namespace ns { //// /*a*/export function fn() { -//// +//// //// } //// fn(); //// /*b*/ //// } goTo.select('a', 'b'); -verify.not.refactorAvailable("Extract Method"); +verify.not.refactorAvailable("Extract Symbol"); edit.deleteAtCaret('export'.length); goTo.select('a', 'b'); -verify.refactorAvailable("Extract Method"); +verify.refactorAvailable("Extract Symbol", 'function_scope_0'); diff --git a/tests/cases/fourslash/extract-method9.ts b/tests/cases/fourslash/extract-method9.ts index f70ef20a87b..41af6cbf5f4 100644 --- a/tests/cases/fourslash/extract-method9.ts +++ b/tests/cases/fourslash/extract-method9.ts @@ -1,11 +1,11 @@ /// //// function f() { -//// /*a*/function q() { } +//// /*a*/function q() { } //// q();/*b*/ //// q(); //// } goTo.select('a', 'b'); -verify.not.refactorAvailable("Extract Method"); +verify.not.refactorAvailable("Extract Symbol"); From eb1fb5c1643a830418805f5ba812ec7faa445e99 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Mon, 25 Sep 2017 18:42:09 -0700 Subject: [PATCH 20/56] Rename extractMethod.ts to extractSymbol.ts --- src/services/refactors/{extractMethod.ts => extractSymbol.ts} | 0 src/services/refactors/refactors.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/services/refactors/{extractMethod.ts => extractSymbol.ts} (100%) diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractSymbol.ts similarity index 100% rename from src/services/refactors/extractMethod.ts rename to src/services/refactors/extractSymbol.ts diff --git a/src/services/refactors/refactors.ts b/src/services/refactors/refactors.ts index 3a33ccc83c2..680b7f8b02f 100644 --- a/src/services/refactors/refactors.ts +++ b/src/services/refactors/refactors.ts @@ -1,2 +1,2 @@ /// -/// +/// From 2601bbcea721a0d759ecfd929cf13872c9fb2058 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 26 Sep 2017 10:37:25 -0700 Subject: [PATCH 21/56] Add simple tests for Extract Constant --- Jakefile.js | 1 + src/harness/tsconfig.json | 1 + src/harness/unittests/extractConstants.ts | 250 ++++++++++++++++++ src/harness/unittests/extractMethods.ts | 3 +- src/services/refactors/extractSymbol.ts | 28 +- ...ractConstant_BlockScopes_NoDependencies.ts | 14 + .../extractConstant/extractConstant_Class.ts | 16 ++ .../extractConstant_ClassInsertionPosition.ts | 46 ++++ .../extractConstant_ExpressionStatement.ts | 4 + ...tConstant_ExpressionStatementExpression.ts | 4 + .../extractConstant_Function.ts | 16 ++ .../extractConstant/extractConstant_Method.ts | 30 +++ .../extractConstant_Namespace.ts | 16 ++ .../extractConstant_TopLevel.ts | 6 + 14 files changed, 422 insertions(+), 13 deletions(-) create mode 100644 src/harness/unittests/extractConstants.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Class.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Function.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Method.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Namespace.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_TopLevel.ts diff --git a/Jakefile.js b/Jakefile.js index 7b4991b74b6..b1823f4ea0e 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -138,6 +138,7 @@ var harnessSources = harnessCoreSources.concat([ "projectErrors.ts", "matchFiles.ts", "initializeTSConfig.ts", + "extractConstants.ts", "extractMethods.ts", "printer.ts", "textChanges.ts", diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index c6e78138638..53654f7365f 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -128,6 +128,7 @@ "./unittests/printer.ts", "./unittests/transform.ts", "./unittests/customTransforms.ts", + "./unittests/extractConstants.ts", "./unittests/extractMethods.ts", "./unittests/textChanges.ts", "./unittests/telemetry.ts", diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts new file mode 100644 index 00000000000..f5018811b1a --- /dev/null +++ b/src/harness/unittests/extractConstants.ts @@ -0,0 +1,250 @@ +/// +/// + +namespace ts { + interface Range { + start: number; + end: number; + name: string; + } + + interface Test { + source: string; + ranges: Map; + } + + // TODO (acasey): share + function extractTest(source: string): Test { + const activeRanges: Range[] = []; + let text = ""; + let lastPos = 0; + let pos = 0; + const ranges = createMap(); + + while (pos < source.length) { + if (source.charCodeAt(pos) === CharacterCodes.openBracket && + (source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) { + const saved = pos; + pos += 2; + const s = pos; + consumeIdentifier(); + const e = pos; + if (source.charCodeAt(pos) === CharacterCodes.bar) { + pos++; + text += source.substring(lastPos, saved); + const name = s === e + ? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted" + : source.substring(s, e); + activeRanges.push({ name, start: text.length, end: undefined }); + lastPos = pos; + continue; + } + else { + pos = saved; + } + } + else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) { + text += source.substring(lastPos, pos); + activeRanges[activeRanges.length - 1].end = text.length; + const range = activeRanges.pop(); + if (range.name in ranges) { + throw new Error(`Duplicate name of range ${range.name}`); + } + ranges.set(range.name, range); + pos += 2; + lastPos = pos; + continue; + } + pos++; + } + text += source.substring(lastPos, pos); + + function consumeIdentifier() { + while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) { + pos++; + } + } + return { source: text, ranges }; + } + + // TODO (acasey): share + const newLineCharacter = "\n"; + function getRuleProvider(action?: (opts: FormatCodeSettings) => void) { + const options = { + indentSize: 4, + tabSize: 4, + newLineCharacter, + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Smart, + insertSpaceAfterConstructor: false, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, + insertSpaceBeforeFunctionParenthesis: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, + }; + if (action) { + action(options); + } + const rulesProvider = new formatting.RulesProvider(); + rulesProvider.ensureUpToDate(options); + return rulesProvider; + } + + describe("extractConstants", () => { + testExtractConstant("extractConstant_TopLevel", + `let x = [#|1|];`); + + testExtractConstant("extractConstant_Namespace", + `namespace N { + let x = [#|1|]; +}`); + + testExtractConstant("extractConstant_Class", + `class C { + x = [#|1|]; +}`); + + testExtractConstant("extractConstant_Method", + `class C { + M() { + let x = [#|1|]; + } +}`); + + testExtractConstant("extractConstant_Function", + `function F() { + let x = [#|1|]; +}`); + + testExtractConstant("extractConstant_ExpressionStatement", + `[#|"hello";|]`); + + testExtractConstant("extractConstant_ExpressionStatementExpression", + `[#|"hello"|];`); + + testExtractConstant("extractConstant_BlockScopes_NoDependencies", + `for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = [#|1|]; + } +}`); + + testExtractConstant("extractConstant_ClassInsertionPosition", + `class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = [#|1|]; + } +}`); + + testExtractConstantFailed("extractConstant_Parameters", + `function F() { + let w = 1; + let x = [#|w + 1|]; +}`); + + testExtractConstantFailed("extractConstant_TypeParameters", + `function F(t: T) { + let x = [#|t + 1|]; +}`); + + testExtractConstantFailed("extractConstant_BlockScopes_Dependencies", + `for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = [#|i + 1|]; + } +}`); + }); + + // TODO (acasey): share? + function testExtractConstant(caption: string, text: string) { + it(caption, () => { + Harness.Baseline.runBaseline(`extractConstant/${caption}.ts`, () => { + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + const f = { + path: "/a.ts", + content: t.source + }; + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: selectionRange.start, + endPosition: selectionRange.end, + rulesProvider: getRuleProvider() + }; + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); + const infos = refactor.extractSymbol.getAvailableActions(context); + const actions = find(infos, info => info.description === Diagnostics.Extract_constant.message).actions; + const data: string[] = []; + data.push(`// ==ORIGINAL==`); + data.push(sourceFile.text); + for (const action of actions) { + const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name); + assert.lengthOf(edits, 1); + data.push(`// ==SCOPE::${action.description}==`); + const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); + const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); + data.push(newTextWithRename); + } + return data.join(newLineCharacter); + }); + }); + } + + // TODO (acasey): share? + function testExtractConstantFailed(caption: string, text: string) { + it(caption, () => { + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + const f = { + path: "/a.ts", + content: t.source + }; + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: selectionRange.start, + endPosition: selectionRange.end, + rulesProvider: getRuleProvider() + }; + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); + const infos = refactor.extractSymbol.getAvailableActions(context); + assert.isUndefined(find(infos, info => info.description === Diagnostics.Extract_constant.message)); + }); + } +} diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts index 3a161baa8e7..be5d21a80ae 100644 --- a/src/harness/unittests/extractMethods.ts +++ b/src/harness/unittests/extractMethods.ts @@ -805,7 +805,8 @@ function parsePrimaryExpression(): any { }; const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); assert.equal(rangeToExtract.errors, undefined, "expect no errors"); - const actions = refactor.extractSymbol.getAvailableActions(context)[0].actions; // TODO (acasey): smarter index + const infos = refactor.extractSymbol.getAvailableActions(context); + const actions = find(infos, info => info.description === Diagnostics.Extract_function.message).actions; const data: string[] = []; data.push(`// ==ORIGINAL==`); data.push(sourceFile.text); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index a1212e7260c..0c3b3726312 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -262,10 +262,6 @@ namespace ts.refactor.extractSymbol { return { targetRange: { range: statements, facts: rangeFacts, declarations } }; } - if (isExpressionStatement(start)) { - start = start.expression; - } - // We have a single node (start) const errors = checkRootNode(start) || checkNode(start); if (errors) { @@ -274,7 +270,7 @@ namespace ts.refactor.extractSymbol { return { targetRange: { range: getStatementOrExpressionRange(start), facts: rangeFacts, declarations } }; function checkRootNode(node: Node): Diagnostic[] | undefined { - if (isIdentifier(node)) { + if (isIdentifier(isExpressionStatement(node) ? node.expression : node)) { return [createDiagnosticForNode(node, Messages.CannotExtractIdentifier)]; } return undefined; @@ -539,8 +535,10 @@ namespace ts.refactor.extractSymbol { const { scopes, readsAndWrites: { target, usagesPerScope, constantErrorsPerScope } } = getPossibleExtractionsWorker(targetRange, context); Debug.assert(!constantErrorsPerScope[requestedChangesIndex].length, "The extraction went missing? How?"); context.cancellationToken.throwIfCancellationRequested(); - Debug.assert(target === targetRange.range); - return extractConstantInScope(target as Expression, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange.facts, context); + const expression = isExpression(target) + ? target + : (target.statements[0] as ExpressionStatement).expression; + return extractConstantInScope(expression, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange.facts, context); } interface PossibleExtraction { @@ -1114,18 +1112,24 @@ namespace ts.refactor.extractSymbol { child.pos >= minPos && isFunctionLikeDeclaration(child) && !isConstructorDeclaration(child)); } - function getNodeToInsertConstantBefore(minPos: number, scope: Scope): Node { - const isClassLikeScope = isClassLike(scope); + // TODO (acasey): need to dig into nested statements + function getNodeToInsertConstantBefore(maxPos: number, scope: Scope): Node { const children = getStatementsOrClassElements(scope); + Debug.assert(children.length > 0); // There must be at least one child, since we extracted from one. + + const isClassLikeScope = isClassLike(scope); let prevChild: Statement | ClassElement | undefined = undefined; for (const child of children) { - if (child.pos >= minPos || (isClassLikeScope && !isPropertyDeclaration(child))) { + if (child.pos >= maxPos) { break; } prevChild = child; + if (isClassLikeScope && !isPropertyDeclaration(child)) { + break; + } } - return prevChild || children[0]; // There must be one - minPos is in one. + return prevChild; } function getPropertyAssignmentsForWrites(writes: ReadonlyArray): ShorthandPropertyAssignment[] { @@ -1192,7 +1196,7 @@ namespace ts.refactor.extractSymbol { const visibleDeclarationsInExtractedRange: Symbol[] = []; const expressionDiagnostics = - isReadonlyArray(targetRange.range) + isReadonlyArray(targetRange.range) && !(targetRange.range.length === 1 && isExpressionStatement(targetRange.range[0])) ? ((start, end) => [createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected)])(firstOrUndefined(targetRange.range).getStart(), lastOrUndefined(targetRange.range).end) : []; diff --git a/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.ts b/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.ts new file mode 100644 index 00000000000..25609a3a801 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.ts @@ -0,0 +1,14 @@ +// ==ORIGINAL== +for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = 1; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Class.ts b/tests/baselines/reference/extractConstant/extractConstant_Class.ts new file mode 100644 index 00000000000..eb06cf6c0cf --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Class.ts @@ -0,0 +1,16 @@ +// ==ORIGINAL== +class C { + x = 1; +} +// ==SCOPE::Extract to readonly field in class 'C'== +class C { + private readonly newProperty = 1; + + x = this./*RENAME*/newProperty; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.ts b/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.ts new file mode 100644 index 00000000000..e024fda34bc --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.ts @@ -0,0 +1,46 @@ +// ==ORIGINAL== +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = 1; + } +} +// ==SCOPE::Extract to constant in method 'M3== +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; + } +} +// ==SCOPE::Extract to readonly field in class 'C'== +class C { + a = 1; + b = 2; + private readonly newProperty = 1; + + M1() { } + M2() { } + M3() { + let x = this./*RENAME*/newProperty; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.ts b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.ts new file mode 100644 index 00000000000..6bf35cd17e1 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.ts @@ -0,0 +1,4 @@ +// ==ORIGINAL== +"hello"; +// ==SCOPE::Extract to constant in global scope== +const /*RENAME*/newLocal = "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.ts b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.ts new file mode 100644 index 00000000000..6bf35cd17e1 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.ts @@ -0,0 +1,4 @@ +// ==ORIGINAL== +"hello"; +// ==SCOPE::Extract to constant in global scope== +const /*RENAME*/newLocal = "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Function.ts b/tests/baselines/reference/extractConstant/extractConstant_Function.ts new file mode 100644 index 00000000000..67c8255c4b4 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Function.ts @@ -0,0 +1,16 @@ +// ==ORIGINAL== +function F() { + let x = 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +function F() { + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Method.ts b/tests/baselines/reference/extractConstant/extractConstant_Method.ts new file mode 100644 index 00000000000..1ae4c8b1cb5 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Method.ts @@ -0,0 +1,30 @@ +// ==ORIGINAL== +class C { + M() { + let x = 1; + } +} +// ==SCOPE::Extract to constant in method 'M== +class C { + M() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; + } +} +// ==SCOPE::Extract to readonly field in class 'C'== +class C { + private readonly newProperty = 1; + + M() { + let x = this./*RENAME*/newProperty; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + M() { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Namespace.ts b/tests/baselines/reference/extractConstant/extractConstant_Namespace.ts new file mode 100644 index 00000000000..8f25847165f --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Namespace.ts @@ -0,0 +1,16 @@ +// ==ORIGINAL== +namespace N { + let x = 1; +} +// ==SCOPE::Extract to constant in namespace 'N'== +namespace N { + const newLocal = 1; + + let x = /*RENAME*/newLocal; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +namespace N { + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_TopLevel.ts b/tests/baselines/reference/extractConstant/extractConstant_TopLevel.ts new file mode 100644 index 00000000000..fb0447583ff --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_TopLevel.ts @@ -0,0 +1,6 @@ +// ==ORIGINAL== +let x = 1; +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +let x = /*RENAME*/newLocal; \ No newline at end of file From 52ab05e99da4f4f3d4f481f12f7e069ae099790b Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 26 Sep 2017 16:26:12 -0700 Subject: [PATCH 22/56] Rename extractMethods.ts to extractFunctions.ts for consistency --- Jakefile.js | 2 +- src/harness/tsconfig.json | 2 +- .../unittests/{extractMethods.ts => extractFunctions.ts} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/harness/unittests/{extractMethods.ts => extractFunctions.ts} (100%) diff --git a/Jakefile.js b/Jakefile.js index b1823f4ea0e..359549d8248 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -139,7 +139,7 @@ var harnessSources = harnessCoreSources.concat([ "matchFiles.ts", "initializeTSConfig.ts", "extractConstants.ts", - "extractMethods.ts", + "extractFunctions.ts", "printer.ts", "textChanges.ts", "telemetry.ts", diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 53654f7365f..cb2cb00b861 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -129,7 +129,7 @@ "./unittests/transform.ts", "./unittests/customTransforms.ts", "./unittests/extractConstants.ts", - "./unittests/extractMethods.ts", + "./unittests/extractFunctions.ts", "./unittests/textChanges.ts", "./unittests/telemetry.ts", "./unittests/languageService.ts", diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractFunctions.ts similarity index 100% rename from src/harness/unittests/extractMethods.ts rename to src/harness/unittests/extractFunctions.ts From 697bce74b86021ccd28445a3af700d230279e16d Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 26 Sep 2017 16:46:32 -0700 Subject: [PATCH 23/56] Split range tests and helpers out of extractFunctions.ts --- Jakefile.js | 2 + src/harness/tsconfig.json | 2 + src/harness/unittests/extractConstants.ts | 176 +------- src/harness/unittests/extractFunctions.ts | 457 +------------------- src/harness/unittests/extractRanges.ts | 319 ++++++++++++++ src/harness/unittests/extractTestHelpers.ts | 177 ++++++++ 6 files changed, 505 insertions(+), 628 deletions(-) create mode 100644 src/harness/unittests/extractRanges.ts create mode 100644 src/harness/unittests/extractTestHelpers.ts diff --git a/Jakefile.js b/Jakefile.js index 359549d8248..8a4c67ac84b 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -140,6 +140,8 @@ var harnessSources = harnessCoreSources.concat([ "initializeTSConfig.ts", "extractConstants.ts", "extractFunctions.ts", + "extractRanges.ts", + "extractTestHelpers.ts", "printer.ts", "textChanges.ts", "telemetry.ts", diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index cb2cb00b861..88999b2d979 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -130,6 +130,8 @@ "./unittests/customTransforms.ts", "./unittests/extractConstants.ts", "./unittests/extractFunctions.ts", + "./unittests/extractRanges.ts", + "./unittests/extractTestHelpers.ts", "./unittests/textChanges.ts", "./unittests/telemetry.ts", "./unittests/languageService.ts", diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index f5018811b1a..fb776eb6af3 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -1,104 +1,6 @@ -/// -/// +/// namespace ts { - interface Range { - start: number; - end: number; - name: string; - } - - interface Test { - source: string; - ranges: Map; - } - - // TODO (acasey): share - function extractTest(source: string): Test { - const activeRanges: Range[] = []; - let text = ""; - let lastPos = 0; - let pos = 0; - const ranges = createMap(); - - while (pos < source.length) { - if (source.charCodeAt(pos) === CharacterCodes.openBracket && - (source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) { - const saved = pos; - pos += 2; - const s = pos; - consumeIdentifier(); - const e = pos; - if (source.charCodeAt(pos) === CharacterCodes.bar) { - pos++; - text += source.substring(lastPos, saved); - const name = s === e - ? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted" - : source.substring(s, e); - activeRanges.push({ name, start: text.length, end: undefined }); - lastPos = pos; - continue; - } - else { - pos = saved; - } - } - else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) { - text += source.substring(lastPos, pos); - activeRanges[activeRanges.length - 1].end = text.length; - const range = activeRanges.pop(); - if (range.name in ranges) { - throw new Error(`Duplicate name of range ${range.name}`); - } - ranges.set(range.name, range); - pos += 2; - lastPos = pos; - continue; - } - pos++; - } - text += source.substring(lastPos, pos); - - function consumeIdentifier() { - while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) { - pos++; - } - } - return { source: text, ranges }; - } - - // TODO (acasey): share - const newLineCharacter = "\n"; - function getRuleProvider(action?: (opts: FormatCodeSettings) => void) { - const options = { - indentSize: 4, - tabSize: 4, - newLineCharacter, - convertTabsToSpaces: true, - indentStyle: ts.IndentStyle.Smart, - insertSpaceAfterConstructor: false, - insertSpaceAfterCommaDelimiter: true, - insertSpaceAfterSemicolonInForStatements: true, - insertSpaceBeforeAndAfterBinaryOperators: true, - insertSpaceAfterKeywordsInControlFlowStatements: true, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, - insertSpaceBeforeFunctionParenthesis: false, - placeOpenBraceOnNewLineForFunctions: false, - placeOpenBraceOnNewLineForControlBlocks: false, - }; - if (action) { - action(options); - } - const rulesProvider = new formatting.RulesProvider(); - rulesProvider.ensureUpToDate(options); - return rulesProvider; - } - describe("extractConstants", () => { testExtractConstant("extractConstant_TopLevel", `let x = [#|1|];`); @@ -168,83 +70,11 @@ namespace ts { }`); }); - // TODO (acasey): share? function testExtractConstant(caption: string, text: string) { - it(caption, () => { - Harness.Baseline.runBaseline(`extractConstant/${caption}.ts`, () => { - const t = extractTest(text); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${caption} does not specify selection range`); - } - const f = { - path: "/a.ts", - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); - const sourceFile = program.getSourceFile(f.path); - const context: RefactorContext = { - cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, - newLineCharacter, - program, - file: sourceFile, - startPosition: selectionRange.start, - endPosition: selectionRange.end, - rulesProvider: getRuleProvider() - }; - const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); - const infos = refactor.extractSymbol.getAvailableActions(context); - const actions = find(infos, info => info.description === Diagnostics.Extract_constant.message).actions; - const data: string[] = []; - data.push(`// ==ORIGINAL==`); - data.push(sourceFile.text); - for (const action of actions) { - const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name); - assert.lengthOf(edits, 1); - data.push(`// ==SCOPE::${action.description}==`); - const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); - const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); - data.push(newTextWithRename); - } - return data.join(newLineCharacter); - }); - }); + testExtractSymbol(caption, text, "extractConstant", Diagnostics.Extract_constant); } - // TODO (acasey): share? function testExtractConstantFailed(caption: string, text: string) { - it(caption, () => { - const t = extractTest(text); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${caption} does not specify selection range`); - } - const f = { - path: "/a.ts", - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); - const sourceFile = program.getSourceFile(f.path); - const context: RefactorContext = { - cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, - newLineCharacter, - program, - file: sourceFile, - startPosition: selectionRange.start, - endPosition: selectionRange.end, - rulesProvider: getRuleProvider() - }; - const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); - const infos = refactor.extractSymbol.getAvailableActions(context); - assert.isUndefined(find(infos, info => info.description === Diagnostics.Extract_constant.message)); - }); + testExtractSymbolFailed(caption, text, Diagnostics.Extract_constant); } } diff --git a/src/harness/unittests/extractFunctions.ts b/src/harness/unittests/extractFunctions.ts index be5d21a80ae..2497b2eec51 100644 --- a/src/harness/unittests/extractFunctions.ts +++ b/src/harness/unittests/extractFunctions.ts @@ -1,417 +1,7 @@ -/// -/// +/// namespace ts { - interface Range { - start: number; - end: number; - name: string; - } - - interface Test { - source: string; - ranges: Map; - } - - function extractTest(source: string): Test { - const activeRanges: Range[] = []; - let text = ""; - let lastPos = 0; - let pos = 0; - const ranges = createMap(); - - while (pos < source.length) { - if (source.charCodeAt(pos) === CharacterCodes.openBracket && - (source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) { - const saved = pos; - pos += 2; - const s = pos; - consumeIdentifier(); - const e = pos; - if (source.charCodeAt(pos) === CharacterCodes.bar) { - pos++; - text += source.substring(lastPos, saved); - const name = s === e - ? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted" - : source.substring(s, e); - activeRanges.push({ name, start: text.length, end: undefined }); - lastPos = pos; - continue; - } - else { - pos = saved; - } - } - else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) { - text += source.substring(lastPos, pos); - activeRanges[activeRanges.length - 1].end = text.length; - const range = activeRanges.pop(); - if (range.name in ranges) { - throw new Error(`Duplicate name of range ${range.name}`); - } - ranges.set(range.name, range); - pos += 2; - lastPos = pos; - continue; - } - pos++; - } - text += source.substring(lastPos, pos); - - function consumeIdentifier() { - while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) { - pos++; - } - } - return { source: text, ranges }; - } - - const newLineCharacter = "\n"; - function getRuleProvider(action?: (opts: FormatCodeSettings) => void) { - const options = { - indentSize: 4, - tabSize: 4, - newLineCharacter, - convertTabsToSpaces: true, - indentStyle: ts.IndentStyle.Smart, - insertSpaceAfterConstructor: false, - insertSpaceAfterCommaDelimiter: true, - insertSpaceAfterSemicolonInForStatements: true, - insertSpaceBeforeAndAfterBinaryOperators: true, - insertSpaceAfterKeywordsInControlFlowStatements: true, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, - insertSpaceBeforeFunctionParenthesis: false, - placeOpenBraceOnNewLineForFunctions: false, - placeOpenBraceOnNewLineForControlBlocks: false, - }; - if (action) { - action(options); - } - const rulesProvider = new formatting.RulesProvider(); - rulesProvider.ensureUpToDate(options); - return rulesProvider; - } - - function testExtractRangeFailed(caption: string, s: string, expectedErrors: string[]) { - return it(caption, () => { - const t = extractTest(s); - const file = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${s} does not specify selection range`); - } - const result = refactor.extractSymbol.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert(result.targetRange === undefined, "failure expected"); - const sortedErrors = result.errors.map(e => e.messageText).sort(); - assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors"); - }); - } - - function testExtractRange(s: string): void { - const t = extractTest(s); - const f = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${s} does not specify selection range`); - } - const result = refactor.extractSymbol.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - const expectedRange = t.ranges.get("extracted"); - if (expectedRange) { - let start: number, end: number; - if (ts.isArray(result.targetRange.range)) { - start = result.targetRange.range[0].getStart(f); - end = ts.lastOrUndefined(result.targetRange.range).getEnd(); - } - else { - start = result.targetRange.range.getStart(f); - end = result.targetRange.range.getEnd(); - } - assert.equal(start, expectedRange.start, "incorrect start of range"); - assert.equal(end, expectedRange.end, "incorrect end of range"); - } - else { - assert.isTrue(!result.targetRange, `expected range to extract to be undefined`); - } - } - describe("extractMethods", () => { - it("get extract range from selection", () => { - testExtractRange(` - [#| - [$|var x = 1; - var y = 2;|]|] - `); - testExtractRange(` - [#| - var x = 1; - var y = 2|]; - `); - testExtractRange(` - [#|var x = 1|]; - var y = 2; - `); - testExtractRange(` - if ([#|[#extracted|a && b && c && d|]|]) { - } - `); - testExtractRange(` - if [#|(a && b && c && d|]) { - } - `); - testExtractRange(` - if (a && b && c && d) { - [#| [$|var x = 1; - console.log(x);|] |] - } - `); - testExtractRange(` - [#| - if (a) { - return 100; - } |] - `); - testExtractRange(` - function foo() { - [#| [$|if (a) { - } - return 100|] |] - } - `); - testExtractRange(` - [#| - [$|l1: - if (x) { - break l1; - }|]|] - `); - testExtractRange(` - [#| - [$|l2: - { - if (x) { - } - break l2; - }|]|] - `); - testExtractRange(` - while (true) { - [#| if(x) { - } - break; |] - } - `); - testExtractRange(` - while (true) { - [#| if(x) { - } - continue; |] - } - `); - testExtractRange(` - l3: - { - [#| - if (x) { - } - break l3; |] - } - `); - testExtractRange(` - function f() { - while (true) { - [#| - if (x) { - return; - } |] - } - } - `); - testExtractRange(` - function f() { - while (true) { - [#| - [$|if (x) { - } - return;|] - |] - } - } - `); - testExtractRange(` - function f() { - return [#| [$|1 + 2|] |]+ 3; - } - } - `); - testExtractRange(` - function f() { - return [$|1 + [#|2 + 3|]|]; - } - } - `); - testExtractRange(` - function f() { - return [$|1 + 2 + [#|3 + 4|]|]; - } - } - `); - }); - - testExtractRangeFailed("extractRangeFailed1", - ` -namespace A { - function f() { - [#| - let x = 1 - if (x) { - return 10; - } - |] - } -} - `, - [ - "Cannot extract range containing conditional return statement." - ]); - - testExtractRangeFailed("extractRangeFailed2", - ` -namespace A { - function f() { - while (true) { - [#| - let x = 1 - if (x) { - break; - } - |] - } - } -} - `, - [ - "Cannot extract range containing conditional break or continue statements." - ]); - - testExtractRangeFailed("extractRangeFailed3", - ` -namespace A { - function f() { - while (true) { - [#| - let x = 1 - if (x) { - continue; - } - |] - } - } -} - `, - [ - "Cannot extract range containing conditional break or continue statements." - ]); - - testExtractRangeFailed("extractRangeFailed4", - ` -namespace A { - function f() { - l1: { - [#| - let x = 1 - if (x) { - break l1; - } - |] - } - } -} - `, - [ - "Cannot extract range containing labeled break or continue with target outside of the range." - ]); - - testExtractRangeFailed("extractRangeFailed5", - ` -namespace A { - function f() { - [#| - try { - f2() - return 10; - } - catch (e) { - } - |] - } - function f2() { - } -} - `, - [ - "Cannot extract range containing conditional return statement." - ]); - - testExtractRangeFailed("extractRangeFailed6", - ` -namespace A { - function f() { - [#| - try { - f2() - } - catch (e) { - return 10; - } - |] - } - function f2() { - } -} - `, - [ - "Cannot extract range containing conditional return statement." - ]); - - testExtractRangeFailed("extractRangeFailed7", - ` -function test(x: number) { - while (x) { - x--; - [#|break;|] - } -} - `, - [ - "Cannot extract range containing conditional break or continue statements." - ]); - - testExtractRangeFailed("extractRangeFailed8", - ` -function test(x: number) { - switch (x) { - case 1: - [#|break;|] - } -} - `, - [ - "Cannot extract range containing conditional break or continue statements." - ]); - - testExtractRangeFailed("extractRangeFailed9", - `var x = ([#||]1 + 2);`, - [ - "Cannot extract empty range." - ]); - - testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single identifier."]); - testExtractMethod("extractMethod1", `namespace A { let x = 1; @@ -776,50 +366,7 @@ function parsePrimaryExpression(): any { }`); }); - function testExtractMethod(caption: string, text: string) { - it(caption, () => { - Harness.Baseline.runBaseline(`extractMethod/${caption}.ts`, () => { - const t = extractTest(text); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${caption} does not specify selection range`); - } - const f = { - path: "/a.ts", - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); - const sourceFile = program.getSourceFile(f.path); - const context: RefactorContext = { - cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, - newLineCharacter, - program, - file: sourceFile, - startPosition: selectionRange.start, - endPosition: selectionRange.end, - rulesProvider: getRuleProvider() - }; - const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert.equal(rangeToExtract.errors, undefined, "expect no errors"); - const infos = refactor.extractSymbol.getAvailableActions(context); - const actions = find(infos, info => info.description === Diagnostics.Extract_function.message).actions; - const data: string[] = []; - data.push(`// ==ORIGINAL==`); - data.push(sourceFile.text); - for (const action of actions) { - const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name); - assert.lengthOf(edits, 1); - data.push(`// ==SCOPE::${action.description}==`); - const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); - const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); - data.push(newTextWithRename); - } - return data.join(newLineCharacter); - }); - }); + testExtractSymbol(caption, text, "extractMethod", Diagnostics.Extract_function); } } diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts new file mode 100644 index 00000000000..55535a6dc44 --- /dev/null +++ b/src/harness/unittests/extractRanges.ts @@ -0,0 +1,319 @@ +/// + +namespace ts { + function testExtractRangeFailed(caption: string, s: string, expectedErrors: string[]) { + return it(caption, () => { + const t = extractTest(s); + const file = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${s} does not specify selection range`); + } + const result = refactor.extractSymbol.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert(result.targetRange === undefined, "failure expected"); + const sortedErrors = result.errors.map(e => e.messageText).sort(); + assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors"); + }); + } + + function testExtractRange(s: string): void { + const t = extractTest(s); + const f = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${s} does not specify selection range`); + } + const result = refactor.extractSymbol.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + const expectedRange = t.ranges.get("extracted"); + if (expectedRange) { + let start: number, end: number; + if (ts.isArray(result.targetRange.range)) { + start = result.targetRange.range[0].getStart(f); + end = ts.lastOrUndefined(result.targetRange.range).getEnd(); + } + else { + start = result.targetRange.range.getStart(f); + end = result.targetRange.range.getEnd(); + } + assert.equal(start, expectedRange.start, "incorrect start of range"); + assert.equal(end, expectedRange.end, "incorrect end of range"); + } + else { + assert.isTrue(!result.targetRange, `expected range to extract to be undefined`); + } + } + + describe("extractRanges", () => { + it("get extract range from selection", () => { + testExtractRange(` + [#| + [$|var x = 1; + var y = 2;|]|] + `); + testExtractRange(` + [#| + var x = 1; + var y = 2|]; + `); + testExtractRange(` + [#|var x = 1|]; + var y = 2; + `); + testExtractRange(` + if ([#|[#extracted|a && b && c && d|]|]) { + } + `); + testExtractRange(` + if [#|(a && b && c && d|]) { + } + `); + testExtractRange(` + if (a && b && c && d) { + [#| [$|var x = 1; + console.log(x);|] |] + } + `); + testExtractRange(` + [#| + if (a) { + return 100; + } |] + `); + testExtractRange(` + function foo() { + [#| [$|if (a) { + } + return 100|] |] + } + `); + testExtractRange(` + [#| + [$|l1: + if (x) { + break l1; + }|]|] + `); + testExtractRange(` + [#| + [$|l2: + { + if (x) { + } + break l2; + }|]|] + `); + testExtractRange(` + while (true) { + [#| if(x) { + } + break; |] + } + `); + testExtractRange(` + while (true) { + [#| if(x) { + } + continue; |] + } + `); + testExtractRange(` + l3: + { + [#| + if (x) { + } + break l3; |] + } + `); + testExtractRange(` + function f() { + while (true) { + [#| + if (x) { + return; + } |] + } + } + `); + testExtractRange(` + function f() { + while (true) { + [#| + [$|if (x) { + } + return;|] + |] + } + } + `); + testExtractRange(` + function f() { + return [#| [$|1 + 2|] |]+ 3; + } + } + `); + testExtractRange(` + function f() { + return [$|1 + [#|2 + 3|]|]; + } + } + `); + testExtractRange(` + function f() { + return [$|1 + 2 + [#|3 + 4|]|]; + } + } + `); + }); + + testExtractRangeFailed("extractRangeFailed1", + ` +namespace A { +function f() { + [#| + let x = 1 + if (x) { + return 10; + } + |] +} +} + `, + [ + "Cannot extract range containing conditional return statement." + ]); + + testExtractRangeFailed("extractRangeFailed2", + ` +namespace A { +function f() { + while (true) { + [#| + let x = 1 + if (x) { + break; + } + |] + } +} +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + + testExtractRangeFailed("extractRangeFailed3", + ` +namespace A { +function f() { + while (true) { + [#| + let x = 1 + if (x) { + continue; + } + |] + } +} +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + + testExtractRangeFailed("extractRangeFailed4", + ` +namespace A { +function f() { + l1: { + [#| + let x = 1 + if (x) { + break l1; + } + |] + } +} +} + `, + [ + "Cannot extract range containing labeled break or continue with target outside of the range." + ]); + + testExtractRangeFailed("extractRangeFailed5", + ` +namespace A { +function f() { + [#| + try { + f2() + return 10; + } + catch (e) { + } + |] +} +function f2() { +} +} + `, + [ + "Cannot extract range containing conditional return statement." + ]); + + testExtractRangeFailed("extractRangeFailed6", + ` +namespace A { +function f() { + [#| + try { + f2() + } + catch (e) { + return 10; + } + |] +} +function f2() { +} +} + `, + [ + "Cannot extract range containing conditional return statement." + ]); + + testExtractRangeFailed("extractRangeFailed7", + ` +function test(x: number) { +while (x) { + x--; + [#|break;|] +} +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + + testExtractRangeFailed("extractRangeFailed8", + ` +function test(x: number) { +switch (x) { + case 1: + [#|break;|] +} +} + `, + [ + "Cannot extract range containing conditional break or continue statements." + ]); + + testExtractRangeFailed("extractRangeFailed9", + `var x = ([#||]1 + 2);`, + [ + "Cannot extract empty range." + ]); + + testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single identifier."]); + }); +} \ No newline at end of file diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts new file mode 100644 index 00000000000..e619f9343d8 --- /dev/null +++ b/src/harness/unittests/extractTestHelpers.ts @@ -0,0 +1,177 @@ +/// +/// + +namespace ts { + export interface Range { + start: number; + end: number; + name: string; + } + + export interface Test { + source: string; + ranges: Map; + } + + export function extractTest(source: string): Test { + const activeRanges: Range[] = []; + let text = ""; + let lastPos = 0; + let pos = 0; + const ranges = createMap(); + + while (pos < source.length) { + if (source.charCodeAt(pos) === CharacterCodes.openBracket && + (source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) { + const saved = pos; + pos += 2; + const s = pos; + consumeIdentifier(); + const e = pos; + if (source.charCodeAt(pos) === CharacterCodes.bar) { + pos++; + text += source.substring(lastPos, saved); + const name = s === e + ? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted" + : source.substring(s, e); + activeRanges.push({ name, start: text.length, end: undefined }); + lastPos = pos; + continue; + } + else { + pos = saved; + } + } + else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) { + text += source.substring(lastPos, pos); + activeRanges[activeRanges.length - 1].end = text.length; + const range = activeRanges.pop(); + if (range.name in ranges) { + throw new Error(`Duplicate name of range ${range.name}`); + } + ranges.set(range.name, range); + pos += 2; + lastPos = pos; + continue; + } + pos++; + } + text += source.substring(lastPos, pos); + + function consumeIdentifier() { + while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) { + pos++; + } + } + return { source: text, ranges }; + } + + export const newLineCharacter = "\n"; + export function getRuleProvider(action?: (opts: FormatCodeSettings) => void) { + const options = { + indentSize: 4, + tabSize: 4, + newLineCharacter, + convertTabsToSpaces: true, + indentStyle: ts.IndentStyle.Smart, + insertSpaceAfterConstructor: false, + insertSpaceAfterCommaDelimiter: true, + insertSpaceAfterSemicolonInForStatements: true, + insertSpaceBeforeAndAfterBinaryOperators: true, + insertSpaceAfterKeywordsInControlFlowStatements: true, + insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, + insertSpaceBeforeFunctionParenthesis: false, + placeOpenBraceOnNewLineForFunctions: false, + placeOpenBraceOnNewLineForControlBlocks: false, + }; + if (action) { + action(options); + } + const rulesProvider = new formatting.RulesProvider(); + rulesProvider.ensureUpToDate(options); + return rulesProvider; + } + + export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage) { + it(caption, () => { + Harness.Baseline.runBaseline(`${baselineFolder}/${caption}.ts`, () => { + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + const f = { + path: "/a.ts", + content: t.source + }; + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: selectionRange.start, + endPosition: selectionRange.end, + rulesProvider: getRuleProvider() + }; + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); + const infos = refactor.extractSymbol.getAvailableActions(context); + const actions = find(infos, info => info.description === description.message).actions; + const data: string[] = []; + data.push(`// ==ORIGINAL==`); + data.push(sourceFile.text); + for (const action of actions) { + const { renameLocation, edits } = refactor.extractSymbol.getEditsForAction(context, action.name); + assert.lengthOf(edits, 1); + data.push(`// ==SCOPE::${action.description}==`); + const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); + const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); + data.push(newTextWithRename); + } + return data.join(newLineCharacter); + }); + }); + } + + export function testExtractSymbolFailed(caption: string, text: string, description: DiagnosticMessage) { + it(caption, () => { + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + const f = { + path: "/a.ts", + content: t.source + }; + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: selectionRange.start, + endPosition: selectionRange.end, + rulesProvider: getRuleProvider() + }; + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.isUndefined(rangeToExtract.errors, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); + const infos = refactor.extractSymbol.getAvailableActions(context); + assert.isUndefined(find(infos, info => info.description === description.message)); + }); + } +} \ No newline at end of file From cb6037b563e2908fd212ac5b7e82241c63762a48 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 27 Sep 2017 10:24:52 -0700 Subject: [PATCH 24/56] Forbid extraction of constants to class scopes in JS --- src/harness/unittests/extractConstants.ts | 4 +-- src/services/refactors/extractSymbol.ts | 30 +++++++++++-------- .../extractConstant_Parameters.ts | 12 ++++++++ .../extractConstant_TypeParameters.ts | 10 +++++++ 4 files changed, 42 insertions(+), 14 deletions(-) create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Parameters.ts create mode 100644 tests/baselines/reference/extractConstant/extractConstant_TypeParameters.ts diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index fb776eb6af3..e4ec0932a73 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -51,13 +51,13 @@ namespace ts { } }`); - testExtractConstantFailed("extractConstant_Parameters", + testExtractConstant("extractConstant_Parameters", `function F() { let w = 1; let x = [#|w + 1|]; }`); - testExtractConstantFailed("extractConstant_TypeParameters", + testExtractConstant("extractConstant_TypeParameters", `function F(t: T) { let x = [#|t + 1|]; }`); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 0c3b3726312..2b7f9e0dcb8 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -141,6 +141,7 @@ namespace ts.refactor.extractSymbol { export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts"); export const CannotAccessVariablesFromNestedScopes = createMessage("Cannot access variables from nested scopes"); export const CannotExtractToOtherFunctionLike = createMessage("Cannot extract method to a function-like scope that is not a function"); + export const CannotExtractToJSClass = createMessage("Cannot extract constant to a class scope in JS"); } enum RangeFacts { @@ -866,21 +867,17 @@ namespace ts.refactor.extractSymbol { const changeTracker = textChanges.ChangeTracker.fromContext(context); if (isClassLike(scope)) { - // always create private method in TypeScript files + Debug.assert(!isJS); // See CannotExtractToJSClass const modifiers: Modifier[] = []; - if (!isJS) { - modifiers.push(createToken(SyntaxKind.PrivateKeyword)); - } + modifiers.push(createToken(SyntaxKind.PrivateKeyword)); if (rangeFacts & RangeFacts.InStaticRegion) { modifiers.push(createToken(SyntaxKind.StaticKeyword)); } - if (!isJS) { - modifiers.push(createToken(SyntaxKind.ReadonlyKeyword)); - } + modifiers.push(createToken(SyntaxKind.ReadonlyKeyword)); const newVariable = createProperty( /*decorators*/ undefined, - modifiers.length ? modifiers : undefined, + modifiers, localNameText, /*questionToken*/ undefined, variableType, @@ -1195,20 +1192,29 @@ namespace ts.refactor.extractSymbol { const constantErrorsPerScope: Diagnostic[][] = []; const visibleDeclarationsInExtractedRange: Symbol[] = []; - const expressionDiagnostics = + const expressionDiagnostic = isReadonlyArray(targetRange.range) && !(targetRange.range.length === 1 && isExpressionStatement(targetRange.range[0])) - ? ((start, end) => [createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected)])(firstOrUndefined(targetRange.range).getStart(), lastOrUndefined(targetRange.range).end) - : []; + ? ((start, end) => createFileDiagnostic(sourceFile, start, end - start, Messages.ExpressionExpected))(firstOrUndefined(targetRange.range).getStart(), lastOrUndefined(targetRange.range).end) + : undefined; // initialize results for (const scope of scopes) { usagesPerScope.push({ usages: createMap(), typeParameterUsages: createMap(), substitutions: createMap() }); substitutionsPerScope.push(createMap()); + functionErrorsPerScope.push( isFunctionLikeDeclaration(scope) && scope.kind !== SyntaxKind.FunctionDeclaration ? [createDiagnosticForNode(scope, Messages.CannotExtractToOtherFunctionLike)] : []); - constantErrorsPerScope.push(expressionDiagnostics); + + const constantErrors = []; + if (expressionDiagnostic) { + constantErrors.push(expressionDiagnostic); + } + if (isClassLike(scope) && isInJavaScriptFile(scope)) { + constantErrors.push(createDiagnosticForNode(scope, Messages.CannotExtractToJSClass)); + } + constantErrorsPerScope.push(constantErrors); } const seenUsages = createMap(); diff --git a/tests/baselines/reference/extractConstant/extractConstant_Parameters.ts b/tests/baselines/reference/extractConstant/extractConstant_Parameters.ts new file mode 100644 index 00000000000..e6c9c474fbc --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Parameters.ts @@ -0,0 +1,12 @@ +// ==ORIGINAL== +function F() { + let w = 1; + let x = w + 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F() { + let w = 1; + const newLocal = w + 1; + + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_TypeParameters.ts b/tests/baselines/reference/extractConstant/extractConstant_TypeParameters.ts new file mode 100644 index 00000000000..a323e988176 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_TypeParameters.ts @@ -0,0 +1,10 @@ +// ==ORIGINAL== +function F(t: T) { + let x = t + 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F(t: T) { + const newLocal = t + 1; + + let x = /*RENAME*/newLocal; +} \ No newline at end of file From 13e60bc497b494f7505d498f5c45129a5424a3b2 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 27 Sep 2017 10:35:13 -0700 Subject: [PATCH 25/56] Use resources, rather than string literals, in test baselines --- src/harness/unittests/extractRanges.ts | 18 +++++++++--------- src/services/refactors/extractSymbol.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index 55535a6dc44..fcffffeba9d 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -180,7 +180,7 @@ function f() { } `, [ - "Cannot extract range containing conditional return statement." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message ]); testExtractRangeFailed("extractRangeFailed2", @@ -199,7 +199,7 @@ function f() { } `, [ - "Cannot extract range containing conditional break or continue statements." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message ]); testExtractRangeFailed("extractRangeFailed3", @@ -218,7 +218,7 @@ function f() { } `, [ - "Cannot extract range containing conditional break or continue statements." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message ]); testExtractRangeFailed("extractRangeFailed4", @@ -237,7 +237,7 @@ function f() { } `, [ - "Cannot extract range containing labeled break or continue with target outside of the range." + refactor.extractSymbol.Messages.CannotExtractRangeContainingLabeledBreakOrContinueStatementWithTargetOutsideOfTheRange.message ]); testExtractRangeFailed("extractRangeFailed5", @@ -258,7 +258,7 @@ function f2() { } `, [ - "Cannot extract range containing conditional return statement." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message ]); testExtractRangeFailed("extractRangeFailed6", @@ -279,7 +279,7 @@ function f2() { } `, [ - "Cannot extract range containing conditional return statement." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message ]); testExtractRangeFailed("extractRangeFailed7", @@ -292,7 +292,7 @@ while (x) { } `, [ - "Cannot extract range containing conditional break or continue statements." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message ]); testExtractRangeFailed("extractRangeFailed8", @@ -305,7 +305,7 @@ switch (x) { } `, [ - "Cannot extract range containing conditional break or continue statements." + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message ]); testExtractRangeFailed("extractRangeFailed9", @@ -314,6 +314,6 @@ switch (x) { "Cannot extract empty range." ]); - testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single identifier."]); + testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]); }); } \ No newline at end of file diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 2b7f9e0dcb8..517fd558569 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -117,7 +117,7 @@ namespace ts.refactor.extractSymbol { } // Move these into diagnostic messages if they become user-facing - namespace Messages { + export namespace Messages { function createMessage(message: string): DiagnosticMessage { return { message, code: 0, category: DiagnosticCategory.Message, key: message }; } From e6bfce193c7f5e80421835003a40748df5a53634 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 27 Sep 2017 10:40:12 -0700 Subject: [PATCH 26/56] Add additional TODO about insertion positions --- src/services/refactors/extractSymbol.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 517fd558569..75b21e809ed 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1110,6 +1110,7 @@ namespace ts.refactor.extractSymbol { } // TODO (acasey): need to dig into nested statements + // TODO (acasey): don't insert before pinned comments, directives, or triple-slash references function getNodeToInsertConstantBefore(maxPos: number, scope: Scope): Node { const children = getStatementsOrClassElements(scope); Debug.assert(children.length > 0); // There must be at least one child, since we extracted from one. @@ -1126,6 +1127,7 @@ namespace ts.refactor.extractSymbol { } } + Debug.assert(prevChild !== undefined); return prevChild; } From 8683ac92c8b010c43cf43ff6217d6574c9a70d42 Mon Sep 17 00:00:00 2001 From: Armando Aguirre Date: Wed, 27 Sep 2017 15:18:25 -0700 Subject: [PATCH 27/56] Fixed formatting on script blocks, added regrestion tests, fixed minor bugs --- src/harness/harness.ts | 2 +- src/services/formatting/formatting.ts | 4 +-- .../fourslash/formatSimulatingScriptBlocks.ts | 31 +++++++++++++++++++ .../formattingAfterMultiLineIfCondition.ts | 4 +-- 4 files changed, 36 insertions(+), 5 deletions(-) create mode 100644 tests/cases/fourslash/formatSimulatingScriptBlocks.ts diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 7c4ccc7c960..429362ab6cb 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -2066,8 +2066,8 @@ namespace Harness { export function runMultifileBaseline(relativeFileBase: string, extension: string, generateContent: () => IterableIterator<[string, string, number]> | IterableIterator<[string, string]>, opts?: BaselineOptions, referencedExtensions?: string[]): void { const gen = generateContent(); const writtenFiles = ts.createMap(); - /* tslint:disable-next-line:no-null-keyword */ const errors: Error[] = []; + // tslint:disable-next-line:no-null-keyword if (gen !== null) { for (let {done, value} = gen.next(); !done; { done, value } = gen.next()) { const [name, content, count] = value as [string, string, number | undefined]; diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 5f26990d3a4..1d1deb57fc8 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -663,9 +663,9 @@ namespace ts.formatting { undecoratedChildStartLine = sourceFile.getLineAndCharacterOfPosition(getNonDecoratorTokenPosOfNode(child, sourceFile)).line; } - // if child is a list item - try to get its indentation + // if child is a list item - try to get its indentation, only if parent is within the original range. let childIndentationAmount = Constants.Unknown; - if (isListItem) { + if (isListItem && parent.pos >= originalRange.pos && parent.end <= originalRange.end) { childIndentationAmount = tryComputeIndentationForListItem(childStartPos, child.end, parentStartLine, originalRange, inheritedIndentation); if (childIndentationAmount !== Constants.Unknown) { inheritedIndentation = childIndentationAmount; diff --git a/tests/cases/fourslash/formatSimulatingScriptBlocks.ts b/tests/cases/fourslash/formatSimulatingScriptBlocks.ts new file mode 100644 index 00000000000..dd64f587a98 --- /dev/null +++ b/tests/cases/fourslash/formatSimulatingScriptBlocks.ts @@ -0,0 +1,31 @@ +/// + +/////* BEGIN EXTERNAL SOURCE */ +/////*begin5*/ +//// var a = 1; +//// alert("/*end5*//********//*begin4*/"); +//// /*end4*/ +/////* END EXTERNAL SOURCE */ +//// +/////* BEGIN EXTERNAL SOURCE */ +/////*begin3*/ +//// var b = 1; +//// +//// var c = "/*end3*//********//*begin2*/"; +//// var d = 1; +//// +//// var e = "/*end2*//********//*begin1*/"; +//// var f = 1; +//// /*end1*/ +/////* END EXTERNAL SOURCE */ + +format.setOption("BaseIndentSize", 12); +format.selection("begin1", "end1"); +format.selection("begin2", "end2"); +format.selection("begin3", "end3"); + +format.setOption("BaseIndentSize", 24); +format.selection("begin4", "end4"); +format.selection("begin5", "end5"); + +verify.currentFileContentIs("/* BEGIN EXTERNAL SOURCE */\n\n var a = 1;\n alert(\"/********/\");\n\n/* END EXTERNAL SOURCE */\n\n/* BEGIN EXTERNAL SOURCE */\n\n var b = 1;\n\n var c = \"/********/\";\n var d = 1;\n\n var e = \"/********/\";\n var f = 1;\n\n/* END EXTERNAL SOURCE */"); diff --git a/tests/cases/fourslash/formattingAfterMultiLineIfCondition.ts b/tests/cases/fourslash/formattingAfterMultiLineIfCondition.ts index 21812b0b2b3..c19c3e76fea 100644 --- a/tests/cases/fourslash/formattingAfterMultiLineIfCondition.ts +++ b/tests/cases/fourslash/formattingAfterMultiLineIfCondition.ts @@ -3,7 +3,7 @@ //// var foo; //// if (foo && //// foo) { -/////*comment*/ // This is a comment +/////*comment*/ // This is a comment //// foo.toString(); //// /**/ @@ -11,4 +11,4 @@ goTo.marker(); edit.insert('}'); goTo.marker('comment'); // Comment below multi-line 'if' condition formatting -verify.currentLineContentIs(' // This is a comment'); \ No newline at end of file +verify.currentLineContentIs(' // This is a comment'); \ No newline at end of file From 386e76543aca28eddd98d514c408f4853e441ba2 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 27 Sep 2017 18:08:35 -0700 Subject: [PATCH 28/56] TODOs for repeated substitution --- src/harness/unittests/extractConstants.ts | 7 +++++++ src/harness/unittests/extractFunctions.ts | 7 +++++++ src/services/refactors/extractSymbol.ts | 6 +++--- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index e4ec0932a73..e2c64afb526 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -62,6 +62,13 @@ namespace ts { let x = [#|t + 1|]; }`); +// TODO (acasey): handle repeated substitution +// testExtractConstant("extractConstant_RepeatedSubstitution", +// `namespace X { +// export const j = 10; +// export const y = [#|j * j|]; +// }`); + testExtractConstantFailed("extractConstant_BlockScopes_Dependencies", `for (let i = 0; i < 10; i++) { for (let j = 0; j < 10; j++) { diff --git a/src/harness/unittests/extractFunctions.ts b/src/harness/unittests/extractFunctions.ts index 2497b2eec51..4e7cba9606c 100644 --- a/src/harness/unittests/extractFunctions.ts +++ b/src/harness/unittests/extractFunctions.ts @@ -364,6 +364,13 @@ function parsePrimaryExpression(): any { `function F() { [#|function G() { }|] }`); + +// TODO (acasey): handle repeated substitution +// testExtractMethod("extractMethod_RepeatedSubstitution", +// `namespace X { +// export const j = 10; +// export const y = [#|j * j|]; +// }`); }); function testExtractMethod(caption: string, text: string) { diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 75b21e809ed..375acb7e585 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1382,9 +1382,9 @@ namespace ts.refactor.extractSymbol { if (symbolId) { for (let i = 0; i < scopes.length; i++) { // push substitution from map to map to simplify rewriting - const substitition = substitutionsPerScope[i].get(symbolId); - if (substitition) { - usagesPerScope[i].substitutions.set(getNodeId(n).toString(), substitition); + const substitution = substitutionsPerScope[i].get(symbolId); + if (substitution) { + usagesPerScope[i].substitutions.set(getNodeId(n).toString(), substitution); } } } From b6629f4fac42f27d478d54596dd68ed81e1cb174 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 27 Sep 2017 20:39:14 -0700 Subject: [PATCH 29/56] Remove unused arguments. --- 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 d89247b70f9..5c1c57289d1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14573,7 +14573,7 @@ namespace ts { if (node.expression) { const type = checkExpression(node.expression, checkMode); if (node.dotDotDotToken && type !== anyType && !isArrayType(type)) { - error(node, Diagnostics.JSX_spread_child_must_be_an_array_type, node.toString(), typeToString(type)); + error(node, Diagnostics.JSX_spread_child_must_be_an_array_type); } return type; } From 76d92a5dd6d433d1b62958f7c8b153ed3b8e5d65 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 27 Sep 2017 21:25:55 -0700 Subject: [PATCH 30/56] Remove unused arguments in program.ts. --- src/compiler/program.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 src/compiler/program.ts diff --git a/src/compiler/program.ts b/src/compiler/program.ts old mode 100644 new mode 100755 index c8ff2496725..1875213fe50 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1582,7 +1582,7 @@ namespace ts { fail(Diagnostics.File_0_not_found, fileName); } else if (refFile && host.getCanonicalFileName(fileName) === host.getCanonicalFileName(refFile.fileName)) { - fail(Diagnostics.A_file_cannot_have_a_reference_to_itself, fileName); + fail(Diagnostics.A_file_cannot_have_a_reference_to_itself); } } return sourceFile; From a92d315eb6ec7ed123321bb1ad2dd9579369f245 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 28 Sep 2017 09:57:32 -0700 Subject: [PATCH 31/56] Remove unnecessary cast (#18822) --- src/compiler/utilities.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index cc7002ca059..a33482985c4 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1401,11 +1401,10 @@ namespace ts { /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder - export function getSpecialPropertyAssignmentKind(expression: ts.BinaryExpression): SpecialPropertyAssignmentKind { - if (!isInJavaScriptFile(expression)) { + export function getSpecialPropertyAssignmentKind(expr: ts.BinaryExpression): SpecialPropertyAssignmentKind { + if (!isInJavaScriptFile(expr)) { return SpecialPropertyAssignmentKind.None; } - const expr = expression; if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || expr.left.kind !== SyntaxKind.PropertyAccessExpression) { return SpecialPropertyAssignmentKind.None; } From 5225b40aabb442b38a2465ca4853aa098e23f26e Mon Sep 17 00:00:00 2001 From: Armando Aguirre Date: Thu, 28 Sep 2017 10:23:30 -0700 Subject: [PATCH 32/56] Addedn rangeContainsRange helper function --- src/services/formatting/formatting.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 1d1deb57fc8..3808cb78940 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -665,7 +665,8 @@ namespace ts.formatting { // if child is a list item - try to get its indentation, only if parent is within the original range. let childIndentationAmount = Constants.Unknown; - if (isListItem && parent.pos >= originalRange.pos && parent.end <= originalRange.end) { + + if (isListItem && rangeContainsRange(originalRange, parent)) { childIndentationAmount = tryComputeIndentationForListItem(childStartPos, child.end, parentStartLine, originalRange, inheritedIndentation); if (childIndentationAmount !== Constants.Unknown) { inheritedIndentation = childIndentationAmount; From 49d24fd89e5a7f40f7c27d9ffb8e421198295c1a Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 28 Sep 2017 10:36:16 -0700 Subject: [PATCH 33/56] Rename extractMethod tests to extractFunction for consistency --- src/harness/unittests/extractFunctions.ts | 76 +++++++++---------- .../extractFunction1.ts} | 0 .../extractFunction10.ts} | 0 .../extractFunction11.ts} | 0 .../extractFunction12.ts} | 0 .../extractFunction13.ts} | 0 .../extractFunction14.ts} | 0 .../extractFunction15.ts} | 0 .../extractFunction16.ts} | 0 .../extractFunction17.ts} | 0 .../extractFunction18.ts} | 0 .../extractFunction19.ts} | 0 .../extractFunction2.ts} | 0 .../extractFunction20.ts} | 0 .../extractFunction21.ts} | 0 .../extractFunction22.ts} | 0 .../extractFunction23.ts} | 0 .../extractFunction24.ts} | 0 .../extractFunction25.ts} | 0 .../extractFunction26.ts} | 0 .../extractFunction27.ts} | 0 .../extractFunction28.ts} | 0 .../extractFunction29.ts} | 0 .../extractFunction3.ts} | 0 .../extractFunction30.ts} | 0 .../extractFunction31.ts} | 0 .../extractFunction32.ts} | 0 .../extractFunction33.ts} | 0 .../extractFunction4.ts} | 0 .../extractFunction5.ts} | 0 .../extractFunction6.ts} | 0 .../extractFunction7.ts} | 0 .../extractFunction8.ts} | 0 .../extractFunction9.ts} | 0 34 files changed, 38 insertions(+), 38 deletions(-) rename tests/baselines/reference/{extractMethod/extractMethod1.ts => extractFunction/extractFunction1.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod10.ts => extractFunction/extractFunction10.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod11.ts => extractFunction/extractFunction11.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod12.ts => extractFunction/extractFunction12.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod13.ts => extractFunction/extractFunction13.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod14.ts => extractFunction/extractFunction14.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod15.ts => extractFunction/extractFunction15.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod16.ts => extractFunction/extractFunction16.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod17.ts => extractFunction/extractFunction17.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod18.ts => extractFunction/extractFunction18.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod19.ts => extractFunction/extractFunction19.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod2.ts => extractFunction/extractFunction2.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod20.ts => extractFunction/extractFunction20.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod21.ts => extractFunction/extractFunction21.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod22.ts => extractFunction/extractFunction22.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod23.ts => extractFunction/extractFunction23.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod24.ts => extractFunction/extractFunction24.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod25.ts => extractFunction/extractFunction25.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod26.ts => extractFunction/extractFunction26.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod27.ts => extractFunction/extractFunction27.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod28.ts => extractFunction/extractFunction28.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod29.ts => extractFunction/extractFunction29.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod3.ts => extractFunction/extractFunction3.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod30.ts => extractFunction/extractFunction30.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod31.ts => extractFunction/extractFunction31.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod32.ts => extractFunction/extractFunction32.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod33.ts => extractFunction/extractFunction33.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod4.ts => extractFunction/extractFunction4.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod5.ts => extractFunction/extractFunction5.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod6.ts => extractFunction/extractFunction6.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod7.ts => extractFunction/extractFunction7.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod8.ts => extractFunction/extractFunction8.ts} (100%) rename tests/baselines/reference/{extractMethod/extractMethod9.ts => extractFunction/extractFunction9.ts} (100%) diff --git a/src/harness/unittests/extractFunctions.ts b/src/harness/unittests/extractFunctions.ts index 4e7cba9606c..522ea7b1293 100644 --- a/src/harness/unittests/extractFunctions.ts +++ b/src/harness/unittests/extractFunctions.ts @@ -1,8 +1,8 @@ /// namespace ts { - describe("extractMethods", () => { - testExtractMethod("extractMethod1", + describe("extractFunctions", () => { + testExtractFunction("extractFunction1", `namespace A { let x = 1; function foo() { @@ -18,7 +18,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod2", + testExtractFunction("extractFunction2", `namespace A { let x = 1; function foo() { @@ -32,7 +32,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod3", + testExtractFunction("extractFunction3", `namespace A { function foo() { } @@ -45,7 +45,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod4", + testExtractFunction("extractFunction4", `namespace A { function foo() { } @@ -60,7 +60,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod5", + testExtractFunction("extractFunction5", `namespace A { let x = 1; export function foo() { @@ -76,7 +76,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod6", + testExtractFunction("extractFunction6", `namespace A { let x = 1; export function foo() { @@ -92,7 +92,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod7", + testExtractFunction("extractFunction7", `namespace A { let x = 1; export namespace C { @@ -110,7 +110,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod8", + testExtractFunction("extractFunction8", `namespace A { let x = 1; namespace B { @@ -120,7 +120,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod9", + testExtractFunction("extractFunction9", `namespace A { export interface I { x: number }; namespace B { @@ -130,7 +130,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod10", + testExtractFunction("extractFunction10", `namespace A { export interface I { x: number }; class C { @@ -141,7 +141,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod11", + testExtractFunction("extractFunction11", `namespace A { let y = 1; class C { @@ -154,7 +154,7 @@ namespace ts { } } }`); - testExtractMethod("extractMethod12", + testExtractFunction("extractFunction12", `namespace A { let y = 1; class C { @@ -174,7 +174,7 @@ namespace ts { // In all cases, we could use type inference, rather than passing explicit type arguments. // Note the inclusion of arrow functions to ensure that some type parameters are not from // targetable scopes. - testExtractMethod("extractMethod13", + testExtractFunction("extractFunction13", `(u1a: U1a, u1b: U1b) => { function F1(t1a: T1a, t1b: T1b) { (u2a: U2a, u2b: U2b) => { @@ -192,7 +192,7 @@ namespace ts { }`); // This test is descriptive, rather than normative. The current implementation // doesn't handle type parameter shadowing. - testExtractMethod("extractMethod14", + testExtractFunction("extractFunction14", `function F(t1: T) { function G(t2: T) { [#|t1.toString(); @@ -200,38 +200,38 @@ namespace ts { } }`); // Confirm that the constraint is preserved. - testExtractMethod("extractMethod15", + testExtractFunction("extractFunction15", `function F(t1: T) { function G(t2: U) { [#|t2.toString();|] } }`); // Confirm that the contextual type of an extracted expression counts as a use. - testExtractMethod("extractMethod16", + testExtractFunction("extractFunction16", `function F() { const array: T[] = [#|[]|]; }`); // Class type parameter - testExtractMethod("extractMethod17", + testExtractFunction("extractFunction17", `class C { M(t1: T1, t2: T2) { [#|t1.toString()|]; } }`); - // Method type parameter - testExtractMethod("extractMethod18", + // Function type parameter + testExtractFunction("extractFunction18", `class C { M(t1: T1, t2: T2) { [#|t1.toString()|]; } }`); // Coupled constraints - testExtractMethod("extractMethod19", + testExtractFunction("extractFunction19", `function F(v: V) { [#|v.toString()|]; }`); - testExtractMethod("extractMethod20", + testExtractFunction("extractFunction20", `const _ = class { a() { [#|let a1 = { x: 1 }; @@ -239,14 +239,14 @@ namespace ts { } }`); // Write + void return - testExtractMethod("extractMethod21", + testExtractFunction("extractFunction21", `function foo() { let x = 10; [#|x++; return;|] }`); // Return in finally block - testExtractMethod("extractMethod22", + testExtractFunction("extractFunction22", `function test() { try { } @@ -255,7 +255,7 @@ namespace ts { } }`); // Extraction position - namespace - testExtractMethod("extractMethod23", + testExtractFunction("extractFunction23", `namespace NS { function M1() { } function M2() { @@ -264,7 +264,7 @@ namespace ts { function M3() { } }`); // Extraction position - function - testExtractMethod("extractMethod24", + testExtractFunction("extractFunction24", `function Outer() { function M1() { } function M2() { @@ -273,14 +273,14 @@ namespace ts { function M3() { } }`); // Extraction position - file - testExtractMethod("extractMethod25", + testExtractFunction("extractFunction25", `function M1() { } function M2() { [#|return 1;|] } function M3() { }`); // Extraction position - class without ctor - testExtractMethod("extractMethod26", + testExtractFunction("extractFunction26", `class C { M1() { } M2() { @@ -289,7 +289,7 @@ function M3() { }`); M3() { } }`); // Extraction position - class with ctor in middle - testExtractMethod("extractMethod27", + testExtractFunction("extractFunction27", `class C { M1() { } M2() { @@ -299,7 +299,7 @@ function M3() { }`); M3() { } }`); // Extraction position - class with ctor at end - testExtractMethod("extractMethod28", + testExtractFunction("extractFunction28", `class C { M1() { } M2() { @@ -309,7 +309,7 @@ function M3() { }`); constructor() { } }`); // Shorthand property names - testExtractMethod("extractMethod29", + testExtractFunction("extractFunction29", `interface UnaryExpression { kind: "Unary"; operator: string; @@ -328,12 +328,12 @@ function parsePrimaryExpression(): any { throw "Not implemented"; }`); // Type parameter as declared type - testExtractMethod("extractMethod30", + testExtractFunction("extractFunction30", `function F() { [#|let t: T;|] }`); // Return in nested function - testExtractMethod("extractMethod31", + testExtractFunction("extractFunction31", `namespace N { export const value = 1; @@ -346,7 +346,7 @@ function parsePrimaryExpression(): any { } }`); // Return in nested class - testExtractMethod("extractMethod32", + testExtractFunction("extractFunction32", `namespace N { export const value = 1; @@ -360,20 +360,20 @@ function parsePrimaryExpression(): any { } }`); // Selection excludes leading trivia of declaration - testExtractMethod("extractMethod33", + testExtractFunction("extractFunction33", `function F() { [#|function G() { }|] }`); // TODO (acasey): handle repeated substitution -// testExtractMethod("extractMethod_RepeatedSubstitution", +// testExtractFunction("extractFunction_RepeatedSubstitution", // `namespace X { // export const j = 10; // export const y = [#|j * j|]; // }`); }); - function testExtractMethod(caption: string, text: string) { - testExtractSymbol(caption, text, "extractMethod", Diagnostics.Extract_function); + function testExtractFunction(caption: string, text: string) { + testExtractSymbol(caption, text, "extractFunction", Diagnostics.Extract_function); } } diff --git a/tests/baselines/reference/extractMethod/extractMethod1.ts b/tests/baselines/reference/extractFunction/extractFunction1.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod1.ts rename to tests/baselines/reference/extractFunction/extractFunction1.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod10.ts b/tests/baselines/reference/extractFunction/extractFunction10.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod10.ts rename to tests/baselines/reference/extractFunction/extractFunction10.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod11.ts b/tests/baselines/reference/extractFunction/extractFunction11.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod11.ts rename to tests/baselines/reference/extractFunction/extractFunction11.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod12.ts b/tests/baselines/reference/extractFunction/extractFunction12.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod12.ts rename to tests/baselines/reference/extractFunction/extractFunction12.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod13.ts b/tests/baselines/reference/extractFunction/extractFunction13.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod13.ts rename to tests/baselines/reference/extractFunction/extractFunction13.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod14.ts b/tests/baselines/reference/extractFunction/extractFunction14.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod14.ts rename to tests/baselines/reference/extractFunction/extractFunction14.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod15.ts b/tests/baselines/reference/extractFunction/extractFunction15.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod15.ts rename to tests/baselines/reference/extractFunction/extractFunction15.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod16.ts b/tests/baselines/reference/extractFunction/extractFunction16.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod16.ts rename to tests/baselines/reference/extractFunction/extractFunction16.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod17.ts b/tests/baselines/reference/extractFunction/extractFunction17.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod17.ts rename to tests/baselines/reference/extractFunction/extractFunction17.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod18.ts b/tests/baselines/reference/extractFunction/extractFunction18.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod18.ts rename to tests/baselines/reference/extractFunction/extractFunction18.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod19.ts b/tests/baselines/reference/extractFunction/extractFunction19.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod19.ts rename to tests/baselines/reference/extractFunction/extractFunction19.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod2.ts b/tests/baselines/reference/extractFunction/extractFunction2.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod2.ts rename to tests/baselines/reference/extractFunction/extractFunction2.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod20.ts b/tests/baselines/reference/extractFunction/extractFunction20.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod20.ts rename to tests/baselines/reference/extractFunction/extractFunction20.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod21.ts b/tests/baselines/reference/extractFunction/extractFunction21.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod21.ts rename to tests/baselines/reference/extractFunction/extractFunction21.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod22.ts b/tests/baselines/reference/extractFunction/extractFunction22.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod22.ts rename to tests/baselines/reference/extractFunction/extractFunction22.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod23.ts b/tests/baselines/reference/extractFunction/extractFunction23.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod23.ts rename to tests/baselines/reference/extractFunction/extractFunction23.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod24.ts b/tests/baselines/reference/extractFunction/extractFunction24.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod24.ts rename to tests/baselines/reference/extractFunction/extractFunction24.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod25.ts b/tests/baselines/reference/extractFunction/extractFunction25.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod25.ts rename to tests/baselines/reference/extractFunction/extractFunction25.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod26.ts b/tests/baselines/reference/extractFunction/extractFunction26.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod26.ts rename to tests/baselines/reference/extractFunction/extractFunction26.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod27.ts b/tests/baselines/reference/extractFunction/extractFunction27.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod27.ts rename to tests/baselines/reference/extractFunction/extractFunction27.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod28.ts b/tests/baselines/reference/extractFunction/extractFunction28.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod28.ts rename to tests/baselines/reference/extractFunction/extractFunction28.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod29.ts b/tests/baselines/reference/extractFunction/extractFunction29.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod29.ts rename to tests/baselines/reference/extractFunction/extractFunction29.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod3.ts b/tests/baselines/reference/extractFunction/extractFunction3.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod3.ts rename to tests/baselines/reference/extractFunction/extractFunction3.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod30.ts b/tests/baselines/reference/extractFunction/extractFunction30.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod30.ts rename to tests/baselines/reference/extractFunction/extractFunction30.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod31.ts b/tests/baselines/reference/extractFunction/extractFunction31.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod31.ts rename to tests/baselines/reference/extractFunction/extractFunction31.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod32.ts b/tests/baselines/reference/extractFunction/extractFunction32.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod32.ts rename to tests/baselines/reference/extractFunction/extractFunction32.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod33.ts b/tests/baselines/reference/extractFunction/extractFunction33.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod33.ts rename to tests/baselines/reference/extractFunction/extractFunction33.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod4.ts b/tests/baselines/reference/extractFunction/extractFunction4.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod4.ts rename to tests/baselines/reference/extractFunction/extractFunction4.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod5.ts b/tests/baselines/reference/extractFunction/extractFunction5.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod5.ts rename to tests/baselines/reference/extractFunction/extractFunction5.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod6.ts b/tests/baselines/reference/extractFunction/extractFunction6.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod6.ts rename to tests/baselines/reference/extractFunction/extractFunction6.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod7.ts b/tests/baselines/reference/extractFunction/extractFunction7.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod7.ts rename to tests/baselines/reference/extractFunction/extractFunction7.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod8.ts b/tests/baselines/reference/extractFunction/extractFunction8.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod8.ts rename to tests/baselines/reference/extractFunction/extractFunction8.ts diff --git a/tests/baselines/reference/extractMethod/extractMethod9.ts b/tests/baselines/reference/extractFunction/extractFunction9.ts similarity index 100% rename from tests/baselines/reference/extractMethod/extractMethod9.ts rename to tests/baselines/reference/extractFunction/extractFunction9.ts From 1a2de721b597f087f0fcf91937fa972b010dc647 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 28 Sep 2017 12:34:54 -0700 Subject: [PATCH 34/56] Fixes to @augments handling (#18775) * Fixes to @augments handling * Renames and diagnostic changes * Add test for < > characters * Use more specific return type --- src/compiler/checker.ts | 56 +++++++++++++++---- src/compiler/diagnosticMessages.json | 8 +++ src/compiler/parser.ts | 40 ++++++++++--- src/compiler/scanner.ts | 6 ++ src/compiler/types.ts | 2 +- src/compiler/utilities.ts | 8 ++- src/harness/unittests/jsDocParsing.ts | 5 ++ src/services/completions.ts | 3 +- ...omments.parsesCorrectly.<> characters.json | 35 ++++++++++++ .../jsdocAugmentsMissingType.errors.txt | 14 +++-- .../jsdocAugmentsMissingType.symbols | 2 - .../reference/jsdocAugmentsMissingType.types | 6 +- .../jsdocAugments_nameMismatch.errors.txt | 12 ++++ .../jsdocAugments_nameMismatch.symbols | 12 ++++ .../jsdocAugments_nameMismatch.types | 12 ++++ .../reference/jsdocAugments_noExtends.symbols | 21 +++++++ .../reference/jsdocAugments_noExtends.types | 23 ++++++++ .../jsdocAugments_notAClass.errors.txt | 10 ++++ .../reference/jsdocAugments_notAClass.symbols | 8 +++ .../reference/jsdocAugments_notAClass.types | 8 +++ .../jsdocAugments_withTypeParameter.symbols | 23 ++++++++ .../jsdocAugments_withTypeParameter.types | 23 ++++++++ .../compiler/jsdocAugments_nameMismatch.ts | 10 ++++ .../cases/compiler/jsdocAugments_noExtends.ts | 13 +++++ .../cases/compiler/jsdocAugments_notAClass.ts | 8 +++ .../jsdocAugments_withTypeParameter.ts | 14 +++++ tests/cases/fourslash/jsDocAugments.ts | 2 +- 27 files changed, 350 insertions(+), 34 deletions(-) create mode 100644 tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json create mode 100644 tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt create mode 100644 tests/baselines/reference/jsdocAugments_nameMismatch.symbols create mode 100644 tests/baselines/reference/jsdocAugments_nameMismatch.types create mode 100644 tests/baselines/reference/jsdocAugments_noExtends.symbols create mode 100644 tests/baselines/reference/jsdocAugments_noExtends.types create mode 100644 tests/baselines/reference/jsdocAugments_notAClass.errors.txt create mode 100644 tests/baselines/reference/jsdocAugments_notAClass.symbols create mode 100644 tests/baselines/reference/jsdocAugments_notAClass.types create mode 100644 tests/baselines/reference/jsdocAugments_withTypeParameter.symbols create mode 100644 tests/baselines/reference/jsdocAugments_withTypeParameter.types create mode 100644 tests/cases/compiler/jsdocAugments_nameMismatch.ts create mode 100644 tests/cases/compiler/jsdocAugments_noExtends.ts create mode 100644 tests/cases/compiler/jsdocAugments_notAClass.ts create mode 100644 tests/cases/compiler/jsdocAugments_withTypeParameter.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d89247b70f9..4337d1fb79b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4882,7 +4882,16 @@ namespace ts { } function getBaseTypeNodeOfClass(type: InterfaceType): ExpressionWithTypeArguments { - return getClassExtendsHeritageClauseElement(type.symbol.valueDeclaration); + const decl = type.symbol.valueDeclaration; + if (isInJavaScriptFile(decl)) { + // Prefer an @augments tag because it may have type parameters. + const tag = getJSDocAugmentsTag(decl); + if (tag) { + return tag.class; + } + } + + return getClassExtendsHeritageClauseElement(decl); } function getConstructorsForTypeArguments(type: Type, typeArgumentNodes: ReadonlyArray, location: Node): Signature[] { @@ -4986,15 +4995,6 @@ namespace ts { baseType = getReturnTypeOfSignature(constructors[0]); } - // In a JS file, you can use the @augments jsdoc tag to specify a base type with type parameters - const valueDecl = type.symbol.valueDeclaration; - if (valueDecl && isInJavaScriptFile(valueDecl)) { - const augTag = getJSDocAugmentsTag(type.symbol.valueDeclaration); - if (augTag && augTag.typeExpression && augTag.typeExpression.type) { - baseType = getTypeFromTypeNode(augTag.typeExpression.type); - } - } - if (baseType === unknownType) { return; } @@ -5003,7 +5003,7 @@ namespace ts { return; } if (type === baseType || hasBaseType(baseType, type)) { - error(valueDecl, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, + error(type.symbol.valueDeclaration, Diagnostics.Type_0_recursively_references_itself_as_a_base_type, typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType)); return; } @@ -19789,6 +19789,38 @@ namespace ts { } } + function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { + const cls = getJSDocHost(node); + if (!isClassDeclaration(cls) && !isClassExpression(cls)) { + error(cls, Diagnostics.JSDoc_augments_is_not_attached_to_a_class_declaration); + return; + } + + const name = getIdentifierFromEntityNameExpression(node.class.expression); + const extend = getClassExtendsHeritageClauseElement(cls); + if (extend) { + const className = getIdentifierFromEntityNameExpression(extend.expression); + if (className && name.escapedText !== className.escapedText) { + error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, + unescapeLeadingUnderscores(name.escapedText), + unescapeLeadingUnderscores(className.escapedText)); + } + } + } + + function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier; + function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined; + function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined { + switch (node.kind) { + case SyntaxKind.Identifier: + return node as Identifier; + case SyntaxKind.PropertyAccessExpression: + return (node as PropertyAccessExpression).name; + default: + return undefined; + } + } + function checkFunctionOrMethodDeclaration(node: FunctionDeclaration | MethodDeclaration): void { checkDecorators(node); checkSignatureDeclaration(node); @@ -22483,6 +22515,8 @@ namespace ts { case SyntaxKind.ParenthesizedType: case SyntaxKind.TypeOperator: return checkSourceElement((node).type); + case SyntaxKind.JSDocAugmentsTag: + return checkJSDocAugmentsTag(node as JSDocAugmentsTag); case SyntaxKind.JSDocTypedefTag: return checkJSDocTypedefTag(node as JSDocTypedefTag); case SyntaxKind.JSDocParameterTag: diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 573993f1b5e..ab17ff1d3a3 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3511,6 +3511,14 @@ "category": "Error", "code": 8021 }, + "JSDoc '@augments' is not attached to a class declaration.": { + "category": "Error", + "code": 8022 + }, + "JSDoc '@augments {0}' does not match the 'extends {1}' clause.": { + "category": "Error", + "code": 8023 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e2eb3efc012..baa89c61335 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -424,7 +424,7 @@ namespace ts { case SyntaxKind.JSDocTypeTag: return visitNode(cbNode, (node).typeExpression); case SyntaxKind.JSDocAugmentsTag: - return visitNode(cbNode, (node).typeExpression); + return visitNode(cbNode, (node).class); case SyntaxKind.JSDocTemplateTag: return visitNodes(cbNode, cbNodes, (node).typeParameters); case SyntaxKind.JSDocTypedefTag: @@ -5624,13 +5624,16 @@ namespace ts { function parseExpressionWithTypeArguments(): ExpressionWithTypeArguments { const node = createNode(SyntaxKind.ExpressionWithTypeArguments); node.expression = parseLeftHandSideExpressionOrHigher(); - if (token() === SyntaxKind.LessThanToken) { - node.typeArguments = parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken); - } - + node.typeArguments = tryParseTypeArguments(); return finishNode(node); } + function tryParseTypeArguments(): NodeArray | undefined { + return token() === SyntaxKind.LessThanToken + ? parseBracketedList(ParsingContext.TypeArguments, parseType, SyntaxKind.LessThanToken, SyntaxKind.GreaterThanToken) + : undefined; + } + function isHeritageClause(): boolean { return token() === SyntaxKind.ExtendsKeyword || token() === SyntaxKind.ImplementsKeyword; } @@ -6604,15 +6607,36 @@ namespace ts { } function parseAugmentsTag(atToken: AtToken, tagName: Identifier): JSDocAugmentsTag { - const typeExpression = parseJSDocTypeExpression(/*requireBraces*/ true); - const result = createNode(SyntaxKind.JSDocAugmentsTag, atToken.pos); result.atToken = atToken; result.tagName = tagName; - result.typeExpression = typeExpression; + result.class = parseExpressionWithTypeArgumentsForAugments(); return finishNode(result); } + function parseExpressionWithTypeArgumentsForAugments(): ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression } { + const usedBrace = parseOptional(SyntaxKind.OpenBraceToken); + const node = createNode(SyntaxKind.ExpressionWithTypeArguments) as ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; + node.expression = parsePropertyAccessEntityNameExpression(); + node.typeArguments = tryParseTypeArguments(); + const res = finishNode(node); + if (usedBrace) { + parseExpected(SyntaxKind.CloseBraceToken); + } + return res; + } + + function parsePropertyAccessEntityNameExpression() { + let node: Identifier | PropertyAccessEntityNameExpression = parseJSDocIdentifierName(/*createIfMissing*/ true); + while (token() === SyntaxKind.DotToken) { + const prop: PropertyAccessEntityNameExpression = createNode(SyntaxKind.PropertyAccessExpression, node.pos) as PropertyAccessEntityNameExpression; + prop.expression = node; + prop.name = parseJSDocIdentifierName(); + node = finishNode(prop); + } + return node; + } + function parseClassTag(atToken: AtToken, tagName: Identifier): JSDocClassTag { const tag = createNode(SyntaxKind.JSDocClassTag, atToken.pos); tag.atToken = atToken; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index c9c14198279..b19a1466328 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -1856,6 +1856,12 @@ namespace ts { case CharacterCodes.closeBracket: pos++; return token = SyntaxKind.CloseBracketToken; + case CharacterCodes.lessThan: + pos++; + return token = SyntaxKind.LessThanToken; + case CharacterCodes.greaterThan: + pos++; + return token = SyntaxKind.GreaterThanToken; case CharacterCodes.equals: pos++; return token = SyntaxKind.EqualsToken; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 7a120064a64..1f27edcaf89 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2161,7 +2161,7 @@ namespace ts { export interface JSDocAugmentsTag extends JSDocTag { kind: SyntaxKind.JSDocAugmentsTag; - typeExpression: JSDocTypeExpression; + class: ExpressionWithTypeArguments & { expression: Identifier | PropertyAccessEntityNameExpression }; } export interface JSDocClassTag extends JSDocTag { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a33482985c4..e7d973865fb 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1580,8 +1580,7 @@ namespace ts { return undefined; } const name = node.name.escapedText; - Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment); - const func = node.parent!.parent!; + const func = getJSDocHost(node); if (!isFunctionLike(func)) { return undefined; } @@ -1590,6 +1589,11 @@ namespace ts { return parameter && parameter.symbol; } + export function getJSDocHost(node: JSDocTag): HasJSDoc { + Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment); + return node.parent!.parent!; + } + export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined { const name = node.name.escapedText; const { typeParameters } = (node.parent.parent.parent as ts.SignatureDeclaration | ts.InterfaceDeclaration | ts.ClassDeclaration); diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 2116d87dc6f..5873a594077 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -300,6 +300,11 @@ namespace ts { * @property {number} age * @property {string} name */`); + parsesCorrectly("<> characters", +`/** + * @param x hi +< > still part of the previous comment + */`); }); }); describe("getFirstToken", () => { diff --git a/src/services/completions.ts b/src/services/completions.ts index e271ef12104..44ad611de79 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -581,11 +581,10 @@ namespace ts.Completions { return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot: (isRightOfDot || isRightOfOpenTag), request, keywordFilters }; - type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; + type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression { switch (tag.kind) { - case SyntaxKind.JSDocAugmentsTag: case SyntaxKind.JSDocParameterTag: case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocReturnTag: diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json new file mode 100644 index 00000000000..93d7cf14737 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json @@ -0,0 +1,35 @@ +{ + "kind": "JSDocComment", + "pos": 0, + "end": 61, + "tags": { + "0": { + "kind": "JSDocParameterTag", + "pos": 7, + "end": 16, + "atToken": { + "kind": "AtToken", + "pos": 7, + "end": 8 + }, + "tagName": { + "kind": "Identifier", + "pos": 8, + "end": 13, + "escapedText": "param" + }, + "name": { + "kind": "Identifier", + "pos": 14, + "end": 15, + "escapedText": "x" + }, + "isNameFirst": true, + "isBracketed": false, + "comment": "hi\n< > still part of the previous comment" + }, + "length": 1, + "pos": 7, + "end": 16 + } +} \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt b/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt index b5ac97db31c..4c234c3e025 100644 --- a/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt +++ b/tests/baselines/reference/jsdocAugmentsMissingType.errors.txt @@ -1,14 +1,20 @@ -/a.js(2,14): error TS1005: '{' expected. +/a.js(2,14): error TS1003: Identifier expected. +/a.js(2,14): error TS8023: JSDoc '@augments ' does not match the 'extends A' clause. +/a.js(5,14): error TS2339: Property 'x' does not exist on type 'B'. -==== /a.js (1 errors) ==== +==== /a.js (3 errors) ==== class A { constructor() { this.x = 0; } } /** @augments */ - ~ -!!! error TS1005: '{' expected. + +!!! error TS1003: Identifier expected. + +!!! error TS8023: JSDoc '@augments ' does not match the 'extends A' clause. class B extends A { m() { this.x + ~ +!!! error TS2339: Property 'x' does not exist on type 'B'. } } \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugmentsMissingType.symbols b/tests/baselines/reference/jsdocAugmentsMissingType.symbols index 0ef9debf119..e7952c6f97d 100644 --- a/tests/baselines/reference/jsdocAugmentsMissingType.symbols +++ b/tests/baselines/reference/jsdocAugmentsMissingType.symbols @@ -14,9 +14,7 @@ class B extends A { >m : Symbol(B.m, Decl(a.js, 2, 19)) this.x ->this.x : Symbol(A.x, Decl(a.js, 0, 25)) >this : Symbol(B, Decl(a.js, 0, 41)) ->x : Symbol(A.x, Decl(a.js, 0, 25)) } } diff --git a/tests/baselines/reference/jsdocAugmentsMissingType.types b/tests/baselines/reference/jsdocAugmentsMissingType.types index 6d687d81d16..de0ba42cdd2 100644 --- a/tests/baselines/reference/jsdocAugmentsMissingType.types +++ b/tests/baselines/reference/jsdocAugmentsMissingType.types @@ -10,15 +10,15 @@ class A { constructor() { this.x = 0; } } /** @augments */ class B extends A { >B : B ->A : A +>A : typeof A m() { >m : () => void this.x ->this.x : number +>this.x : any >this : this ->x : number +>x : any } } diff --git a/tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt b/tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt new file mode 100644 index 00000000000..f6d9d882101 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_nameMismatch.errors.txt @@ -0,0 +1,12 @@ +/b.js(4,15): error TS8023: JSDoc '@augments A' does not match the 'extends B' clause. + + +==== /b.js (1 errors) ==== + class A {} + class B {} + + /** @augments A */ + ~ +!!! error TS8023: JSDoc '@augments A' does not match the 'extends B' clause. + class C extends B {} + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugments_nameMismatch.symbols b/tests/baselines/reference/jsdocAugments_nameMismatch.symbols new file mode 100644 index 00000000000..14accc021d1 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_nameMismatch.symbols @@ -0,0 +1,12 @@ +=== /b.js === +class A {} +>A : Symbol(A, Decl(b.js, 0, 0)) + +class B {} +>B : Symbol(B, Decl(b.js, 0, 10)) + +/** @augments A */ +class C extends B {} +>C : Symbol(C, Decl(b.js, 1, 10)) +>B : Symbol(B, Decl(b.js, 0, 10)) + diff --git a/tests/baselines/reference/jsdocAugments_nameMismatch.types b/tests/baselines/reference/jsdocAugments_nameMismatch.types new file mode 100644 index 00000000000..9522124750b --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_nameMismatch.types @@ -0,0 +1,12 @@ +=== /b.js === +class A {} +>A : A + +class B {} +>B : B + +/** @augments A */ +class C extends B {} +>C : C +>B : A + diff --git a/tests/baselines/reference/jsdocAugments_noExtends.symbols b/tests/baselines/reference/jsdocAugments_noExtends.symbols new file mode 100644 index 00000000000..cd3d8575f26 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_noExtends.symbols @@ -0,0 +1,21 @@ +=== /b.js === +class A { constructor() { this.x = 0; } } +>A : Symbol(A, Decl(b.js, 0, 0)) +>this.x : Symbol(A.x, Decl(b.js, 0, 25)) +>this : Symbol(A, Decl(b.js, 0, 0)) +>x : Symbol(A.x, Decl(b.js, 0, 25)) + +/** @augments A */ +class B { +>B : Symbol(B, Decl(b.js, 0, 41)) + + m() { +>m : Symbol(B.m, Decl(b.js, 3, 9)) + + return this.x; +>this.x : Symbol(A.x, Decl(b.js, 0, 25)) +>this : Symbol(B, Decl(b.js, 0, 41)) +>x : Symbol(A.x, Decl(b.js, 0, 25)) + } +} + diff --git a/tests/baselines/reference/jsdocAugments_noExtends.types b/tests/baselines/reference/jsdocAugments_noExtends.types new file mode 100644 index 00000000000..5a2c5632ab7 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_noExtends.types @@ -0,0 +1,23 @@ +=== /b.js === +class A { constructor() { this.x = 0; } } +>A : A +>this.x = 0 : 0 +>this.x : number +>this : this +>x : number +>0 : 0 + +/** @augments A */ +class B { +>B : B + + m() { +>m : () => number + + return this.x; +>this.x : number +>this : this +>x : number + } +} + diff --git a/tests/baselines/reference/jsdocAugments_notAClass.errors.txt b/tests/baselines/reference/jsdocAugments_notAClass.errors.txt new file mode 100644 index 00000000000..9f8528f0cd8 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_notAClass.errors.txt @@ -0,0 +1,10 @@ +/b.js(3,10): error TS8022: JSDoc '@augments' is not attached to a class declaration. + + +==== /b.js (1 errors) ==== + class A {} + /** @augments A */ + function b() {} + ~ +!!! error TS8022: JSDoc '@augments' is not attached to a class declaration. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugments_notAClass.symbols b/tests/baselines/reference/jsdocAugments_notAClass.symbols new file mode 100644 index 00000000000..59f8333d712 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_notAClass.symbols @@ -0,0 +1,8 @@ +=== /b.js === +class A {} +>A : Symbol(A, Decl(b.js, 0, 0)) + +/** @augments A */ +function b() {} +>b : Symbol(b, Decl(b.js, 0, 10)) + diff --git a/tests/baselines/reference/jsdocAugments_notAClass.types b/tests/baselines/reference/jsdocAugments_notAClass.types new file mode 100644 index 00000000000..e9272530298 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_notAClass.types @@ -0,0 +1,8 @@ +=== /b.js === +class A {} +>A : A + +/** @augments A */ +function b() {} +>b : () => void + diff --git a/tests/baselines/reference/jsdocAugments_withTypeParameter.symbols b/tests/baselines/reference/jsdocAugments_withTypeParameter.symbols new file mode 100644 index 00000000000..59c07273319 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_withTypeParameter.symbols @@ -0,0 +1,23 @@ +=== /a.d.ts === +declare class A { x: T } +>A : Symbol(A, Decl(a.d.ts, 0, 0)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) + +=== /b.js === +/** @augments A */ +class B extends A { +>B : Symbol(B, Decl(b.js, 0, 0)) +>A : Symbol(A, Decl(a.d.ts, 0, 0)) + + m() { +>m : Symbol(B.m, Decl(b.js, 1, 19)) + + return this.x; +>this.x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>this : Symbol(B, Decl(b.js, 0, 0)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) + } +} + diff --git a/tests/baselines/reference/jsdocAugments_withTypeParameter.types b/tests/baselines/reference/jsdocAugments_withTypeParameter.types new file mode 100644 index 00000000000..fb922808b7f --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_withTypeParameter.types @@ -0,0 +1,23 @@ +=== /a.d.ts === +declare class A { x: T } +>A : A +>T : T +>x : T +>T : T + +=== /b.js === +/** @augments A */ +class B extends A { +>B : B +>A : A + + m() { +>m : () => number + + return this.x; +>this.x : number +>this : this +>x : number + } +} + diff --git a/tests/cases/compiler/jsdocAugments_nameMismatch.ts b/tests/cases/compiler/jsdocAugments_nameMismatch.ts new file mode 100644 index 00000000000..8112c57ae83 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_nameMismatch.ts @@ -0,0 +1,10 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /b.js +class A {} +class B {} + +/** @augments A */ +class C extends B {} diff --git a/tests/cases/compiler/jsdocAugments_noExtends.ts b/tests/cases/compiler/jsdocAugments_noExtends.ts new file mode 100644 index 00000000000..87719522b9f --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_noExtends.ts @@ -0,0 +1,13 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /b.js +class A { constructor() { this.x = 0; } } + +/** @augments A */ +class B { + m() { + return this.x; + } +} diff --git a/tests/cases/compiler/jsdocAugments_notAClass.ts b/tests/cases/compiler/jsdocAugments_notAClass.ts new file mode 100644 index 00000000000..cd25e91ad3d --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_notAClass.ts @@ -0,0 +1,8 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /b.js +class A {} +/** @augments A */ +function b() {} diff --git a/tests/cases/compiler/jsdocAugments_withTypeParameter.ts b/tests/cases/compiler/jsdocAugments_withTypeParameter.ts new file mode 100644 index 00000000000..e94df03fbc0 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_withTypeParameter.ts @@ -0,0 +1,14 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.d.ts +declare class A { x: T } + +// @Filename: /b.js +/** @augments A */ +class B extends A { + m() { + return this.x; + } +} diff --git a/tests/cases/fourslash/jsDocAugments.ts b/tests/cases/fourslash/jsDocAugments.ts index 24458c529fb..1938cd0e2aa 100644 --- a/tests/cases/fourslash/jsDocAugments.ts +++ b/tests/cases/fourslash/jsDocAugments.ts @@ -15,7 +15,7 @@ // @Filename: declarations.d.ts //// declare class Thing { -//// mine: T; +//// mine: T; //// } goTo.marker(); From 0b7dd5a4a56bfdb0d5e43d7dda6844e85652f7f7 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 28 Sep 2017 13:32:38 -0700 Subject: [PATCH 35/56] Rename test baseline to be compatibile with windows (#18827) --- src/harness/unittests/jsDocParsing.ts | 2 +- ....parsesCorrectly.less-than and greater-than characters.json} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/baselines/reference/JSDocParsing/{DocComments.parsesCorrectly.<> characters.json => DocComments.parsesCorrectly.less-than and greater-than characters.json} (100%) diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 5873a594077..b7215f5ea35 100644 --- a/src/harness/unittests/jsDocParsing.ts +++ b/src/harness/unittests/jsDocParsing.ts @@ -300,7 +300,7 @@ namespace ts { * @property {number} age * @property {string} name */`); - parsesCorrectly("<> characters", + parsesCorrectly("less-than and greater-than characters", `/** * @param x hi < > still part of the previous comment diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json similarity index 100% rename from tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.<> characters.json rename to tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json From 4bba6ee02eff31997e72438762c42adbc7f4d59d Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 28 Sep 2017 13:43:39 -0700 Subject: [PATCH 36/56] Support accessing enum types from JSDoc (#18703) --- src/compiler/checker.ts | 57 ++++++++++--------- .../reference/jsdocAccessEnumType.symbols | 16 ++++++ .../reference/jsdocAccessEnumType.types | 16 ++++++ tests/cases/compiler/jsdocAccessEnumType.ts | 11 ++++ 4 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 tests/baselines/reference/jsdocAccessEnumType.symbols create mode 100644 tests/baselines/reference/jsdocAccessEnumType.types create mode 100644 tests/cases/compiler/jsdocAccessEnumType.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fdcac968859..541820a4110 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5252,6 +5252,10 @@ namespace ts { } function getDeclaredTypeOfSymbol(symbol: Symbol): Type { + return tryGetDeclaredTypeOfSymbol(symbol) || unknownType; + } + + function tryGetDeclaredTypeOfSymbol(symbol: Symbol): Type | undefined { if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { return getDeclaredTypeOfClassOrInterface(symbol); } @@ -5270,7 +5274,7 @@ namespace ts { if (symbol.flags & SymbolFlags.Alias) { return getDeclaredTypeOfAlias(symbol); } - return unknownType; + return undefined; } // A type reference is considered independent if each type argument is considered independent. @@ -6872,17 +6876,6 @@ namespace ts { return type; } - /** - * Get type from reference to named type that cannot be generic (enum or type parameter) - */ - function getTypeFromNonGenericTypeReference(node: TypeReferenceType, symbol: Symbol): Type { - if (node.typeArguments) { - error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol)); - return unknownType; - } - return getDeclaredTypeOfSymbol(symbol); - } - function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.TypeReference: @@ -6919,24 +6912,34 @@ namespace ts { return type; } - if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) { - // A jsdoc TypeReference may have resolved to a value (as opposed to a type). If - // the symbol is a constructor function, return the inferred class type; otherwise, - // the type of this reference is just the type of the value we resolved to. - const valueType = getTypeOfSymbol(symbol); - if (valueType.symbol && !isInferredClassType(valueType)) { - const referenceType = getTypeReferenceTypeWorker(node, valueType.symbol, typeArguments); - if (referenceType) { - return referenceType; - } + // Get type from reference to named type that cannot be generic (enum or type parameter) + const res = tryGetDeclaredTypeOfSymbol(symbol); + if (res !== undefined) { + if (typeArguments) { + error(node, Diagnostics.Type_0_is_not_generic, symbolToString(symbol)); + return unknownType; } - - // Resolve the type reference as a Type for the purpose of reporting errors. - resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type); - return valueType; + return res; } - return getTypeFromNonGenericTypeReference(node, symbol); + if (!(symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node))) { + return unknownType; + } + + // A jsdoc TypeReference may have resolved to a value (as opposed to a type). If + // the symbol is a constructor function, return the inferred class type; otherwise, + // the type of this reference is just the type of the value we resolved to. + const valueType = getTypeOfSymbol(symbol); + if (valueType.symbol && !isInferredClassType(valueType)) { + const referenceType = getTypeReferenceTypeWorker(node, valueType.symbol, typeArguments); + if (referenceType) { + return referenceType; + } + } + + // Resolve the type reference as a Type for the purpose of reporting errors. + resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type); + return valueType; } function getTypeReferenceTypeWorker(node: TypeReferenceType, symbol: Symbol, typeArguments: Type[]): Type | undefined { diff --git a/tests/baselines/reference/jsdocAccessEnumType.symbols b/tests/baselines/reference/jsdocAccessEnumType.symbols new file mode 100644 index 00000000000..348a3cb17d4 --- /dev/null +++ b/tests/baselines/reference/jsdocAccessEnumType.symbols @@ -0,0 +1,16 @@ +=== /a.ts === +export enum E { A } +>E : Symbol(E, Decl(a.ts, 0, 0)) +>A : Symbol(E.A, Decl(a.ts, 0, 15)) + +=== /b.js === +import { E } from "./a"; +>E : Symbol(E, Decl(b.js, 0, 8)) + +/** @type {E} */ +const e = E.A; +>e : Symbol(e, Decl(b.js, 2, 5)) +>E.A : Symbol(E.A, Decl(a.ts, 0, 15)) +>E : Symbol(E, Decl(b.js, 0, 8)) +>A : Symbol(E.A, Decl(a.ts, 0, 15)) + diff --git a/tests/baselines/reference/jsdocAccessEnumType.types b/tests/baselines/reference/jsdocAccessEnumType.types new file mode 100644 index 00000000000..a08c2b4e179 --- /dev/null +++ b/tests/baselines/reference/jsdocAccessEnumType.types @@ -0,0 +1,16 @@ +=== /a.ts === +export enum E { A } +>E : E +>A : E + +=== /b.js === +import { E } from "./a"; +>E : typeof E + +/** @type {E} */ +const e = E.A; +>e : E +>E.A : E +>E : typeof E +>A : E + diff --git a/tests/cases/compiler/jsdocAccessEnumType.ts b/tests/cases/compiler/jsdocAccessEnumType.ts new file mode 100644 index 00000000000..2676d86cc31 --- /dev/null +++ b/tests/cases/compiler/jsdocAccessEnumType.ts @@ -0,0 +1,11 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.ts +export enum E { A } + +// @Filename: /b.js +import { E } from "./a"; +/** @type {E} */ +const e = E.A; From 7959bd0a3d6775fd083ffe551c89c648ec9b6a94 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 28 Sep 2017 13:44:38 -0700 Subject: [PATCH 37/56] Check JSDoc `@param` tag names (#18777) --- src/compiler/checker.ts | 11 ++- src/compiler/diagnosticMessages.json | 4 + .../reference/jsdocParamTagInvalid.errors.txt | 9 +++ .../reference/jsdocParamTagInvalid.symbols | 6 ++ .../reference/jsdocParamTagInvalid.types | 6 ++ .../jsdocParamTagTypeLiteral.errors.txt | 78 +++++++++++++++++++ tests/cases/compiler/jsdocParamTagInvalid.ts | 7 ++ 7 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/jsdocParamTagInvalid.errors.txt create mode 100644 tests/baselines/reference/jsdocParamTagInvalid.symbols create mode 100644 tests/baselines/reference/jsdocParamTagInvalid.types create mode 100644 tests/baselines/reference/jsdocParamTagTypeLiteral.errors.txt create mode 100644 tests/cases/compiler/jsdocParamTagInvalid.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 541820a4110..ff105ce9d1c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19792,6 +19792,15 @@ namespace ts { } } + function checkJSDocParameterTag(node: JSDocParameterTag) { + checkSourceElement(node.typeExpression); + if (!getParameterSymbolFromJSDoc(node)) { + error(node.name, + Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, + unescapeLeadingUnderscores((node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name).escapedText)); + } + } + function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void { const cls = getJSDocHost(node); if (!isClassDeclaration(cls) && !isClassExpression(cls)) { @@ -22523,7 +22532,7 @@ namespace ts { case SyntaxKind.JSDocTypedefTag: return checkJSDocTypedefTag(node as JSDocTypedefTag); case SyntaxKind.JSDocParameterTag: - return checkSourceElement((node as JSDocParameterTag).typeExpression); + return checkJSDocParameterTag(node as JSDocParameterTag); case SyntaxKind.JSDocFunctionType: checkSignatureDeclaration(node as JSDocFunctionType); // falls through diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ab17ff1d3a3..4465976d170 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3519,6 +3519,10 @@ "category": "Error", "code": 8023 }, + "JSDoc '@param' tag has name '{0}', but there is no parameter with that name.": { + "category": "Error", + "code": 8024 + }, "Only identifiers/qualified-names with optional type arguments are currently supported in a class 'extends' clause.": { "category": "Error", "code": 9002 diff --git a/tests/baselines/reference/jsdocParamTagInvalid.errors.txt b/tests/baselines/reference/jsdocParamTagInvalid.errors.txt new file mode 100644 index 00000000000..08111e0dffe --- /dev/null +++ b/tests/baselines/reference/jsdocParamTagInvalid.errors.txt @@ -0,0 +1,9 @@ +/a.js(1,21): error TS8024: JSDoc '@param' tag has name 'colour', but there is no parameter with that name. + + +==== /a.js (1 errors) ==== + /** @param {string} colour */ + ~~~~~~ +!!! error TS8024: JSDoc '@param' tag has name 'colour', but there is no parameter with that name. + function f(color) {} + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocParamTagInvalid.symbols b/tests/baselines/reference/jsdocParamTagInvalid.symbols new file mode 100644 index 00000000000..130e83590a3 --- /dev/null +++ b/tests/baselines/reference/jsdocParamTagInvalid.symbols @@ -0,0 +1,6 @@ +=== /a.js === +/** @param {string} colour */ +function f(color) {} +>f : Symbol(f, Decl(a.js, 0, 0)) +>color : Symbol(color, Decl(a.js, 1, 11)) + diff --git a/tests/baselines/reference/jsdocParamTagInvalid.types b/tests/baselines/reference/jsdocParamTagInvalid.types new file mode 100644 index 00000000000..9cdf8f0c970 --- /dev/null +++ b/tests/baselines/reference/jsdocParamTagInvalid.types @@ -0,0 +1,6 @@ +=== /a.js === +/** @param {string} colour */ +function f(color) {} +>f : (color: any) => void +>color : any + diff --git a/tests/baselines/reference/jsdocParamTagTypeLiteral.errors.txt b/tests/baselines/reference/jsdocParamTagTypeLiteral.errors.txt new file mode 100644 index 00000000000..854f6dd2eaa --- /dev/null +++ b/tests/baselines/reference/jsdocParamTagTypeLiteral.errors.txt @@ -0,0 +1,78 @@ +tests/cases/conformance/jsdoc/0.js(3,20): error TS8024: JSDoc '@param' tag has name 'unrelated', but there is no parameter with that name. + + +==== tests/cases/conformance/jsdoc/0.js (1 errors) ==== + /** + * @param {Object} notSpecial + * @param {string} unrelated - not actually related because it's not notSpecial.unrelated + ~~~~~~~~~ +!!! error TS8024: JSDoc '@param' tag has name 'unrelated', but there is no parameter with that name. + */ + function normal(notSpecial) { + notSpecial; // should just be 'any' + } + normal(12); + + /** + * @param {Object} opts1 doc1 + * @param {string} opts1.x doc2 + * @param {string=} opts1.y doc3 + * @param {string} [opts1.z] doc4 + * @param {string} [opts1.w="hi"] doc5 + */ + function foo1(opts1) { + opts1.x; + } + + foo1({x: 'abc'}); + + /** + * @param {Object[]} opts2 + * @param {string} opts2[].anotherX + * @param {string=} opts2[].anotherY + */ + function foo2(/** @param opts2 bad idea theatre! */opts2) { + opts2[0].anotherX; + } + + foo2([{anotherX: "world"}]); + + /** + * @param {object} opts3 + * @param {string} opts3.x + */ + function foo3(opts3) { + opts3.x; + } + foo3({x: 'abc'}); + + /** + * @param {object[]} opts4 + * @param {string} opts4[].x + * @param {string=} opts4[].y + * @param {string} [opts4[].z] + * @param {string} [opts4[].w="hi"] + */ + function foo4(opts4) { + opts4[0].x; + } + + foo4([{ x: 'hi' }]); + + /** + * @param {object[]} opts5 - Let's test out some multiple nesting levels + * @param {string} opts5[].help - (This one is just normal) + * @param {object} opts5[].what - Look at us go! Here's the first nest! + * @param {string} opts5[].what.a - (Another normal one) + * @param {Object[]} opts5[].what.bad - Now we're nesting inside a nested type + * @param {string} opts5[].what.bad[].idea - I don't think you can get back out of this level... + * @param {boolean} opts5[].what.bad[].oh - Oh ... that's how you do it. + * @param {number} opts5[].unnest - Here we are almost all the way back at the beginning. + */ + function foo5(opts5) { + opts5[0].what.bad[0].idea; + opts5[0].unnest; + } + + foo5([{ help: "help", what: { a: 'a', bad: [{ idea: 'idea', oh: false }] }, unnest: 1 }]); + \ No newline at end of file diff --git a/tests/cases/compiler/jsdocParamTagInvalid.ts b/tests/cases/compiler/jsdocParamTagInvalid.ts new file mode 100644 index 00000000000..79e17bf8ff1 --- /dev/null +++ b/tests/cases/compiler/jsdocParamTagInvalid.ts @@ -0,0 +1,7 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.js +/** @param {string} colour */ +function f(color) {} From 0d5d5cdf28f6a5216759bcf85433d0779176eb10 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 28 Sep 2017 14:13:48 -0700 Subject: [PATCH 38/56] For any Extract* baseline that is valid JS, produce a JS baseline --- src/harness/unittests/extractTestHelpers.ts | 73 +++++++++++-------- ...ractConstant_BlockScopes_NoDependencies.js | 14 ++++ .../extractConstant/extractConstant_Class.js | 10 +++ .../extractConstant_ClassInsertionPosition.js | 34 +++++++++ .../extractConstant_ExpressionStatement.js | 4 + ...tConstant_ExpressionStatementExpression.js | 4 + .../extractConstant_Function.js | 16 ++++ .../extractConstant/extractConstant_Method.js | 22 ++++++ .../extractConstant_Parameters.js | 12 +++ .../extractConstant_TopLevel.js | 6 ++ .../extractFunction/extractFunction20.js | 28 +++++++ .../extractFunction/extractFunction21.js | 26 +++++++ .../extractFunction/extractFunction22.js | 31 ++++++++ .../extractFunction/extractFunction24.js | 43 +++++++++++ .../extractFunction/extractFunction25.js | 26 +++++++ .../extractFunction/extractFunction26.js | 31 ++++++++ .../extractFunction/extractFunction27.js | 34 +++++++++ .../extractFunction/extractFunction28.js | 34 +++++++++ .../extractFunction/extractFunction33.js | 19 +++++ 19 files changed, 437 insertions(+), 30 deletions(-) create mode 100644 tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Class.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Function.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Method.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_Parameters.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_TopLevel.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction20.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction21.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction22.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction24.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction25.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction26.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction27.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction28.js create mode 100644 tests/baselines/reference/extractFunction/extractFunction33.js diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index e619f9343d8..d42b0839580 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -98,35 +98,48 @@ namespace ts { } export function testExtractSymbol(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage) { - it(caption, () => { - Harness.Baseline.runBaseline(`${baselineFolder}/${caption}.ts`, () => { - const t = extractTest(text); - const selectionRange = t.ranges.get("selection"); - if (!selectionRange) { - throw new Error(`Test ${caption} does not specify selection range`); - } - const f = { - path: "/a.ts", - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); - const sourceFile = program.getSourceFile(f.path); - const context: RefactorContext = { - cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, - newLineCharacter, - program, - file: sourceFile, - startPosition: selectionRange.start, - endPosition: selectionRange.end, - rulesProvider: getRuleProvider() - }; - const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); - assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); - const infos = refactor.extractSymbol.getAvailableActions(context); - const actions = find(infos, info => info.description === description.message).actions; + const t = extractTest(text); + const selectionRange = t.ranges.get("selection"); + if (!selectionRange) { + throw new Error(`Test ${caption} does not specify selection range`); + } + + [Extension.Ts, Extension.Js].forEach(extension => + it(`${caption} [${extension}]`, () => runBaseline(extension))); + + function runBaseline(extension: Extension) { + const f = { + path: "/a" + extension, + content: t.source + }; + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + + // Don't bother generating JS baselines for inputs that aren't valid JS. + const diags = program.getSyntacticDiagnostics(); + if (diags && diags.length) { + assert.equal(Extension.Js, extension); + return; + } + + const sourceFile = program.getSourceFile(f.path); + const context: RefactorContext = { + cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, + newLineCharacter, + program, + file: sourceFile, + startPosition: selectionRange.start, + endPosition: selectionRange.end, + rulesProvider: getRuleProvider() + }; + const rangeToExtract = refactor.extractSymbol.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end)); + assert.equal(rangeToExtract.errors, undefined, rangeToExtract.errors && "Range error: " + rangeToExtract.errors[0].messageText); + const infos = refactor.extractSymbol.getAvailableActions(context); + const actions = find(infos, info => info.description === description.message).actions; + + Harness.Baseline.runBaseline(`${baselineFolder}/${caption}${extension}`, () => { const data: string[] = []; data.push(`// ==ORIGINAL==`); data.push(sourceFile.text); @@ -140,7 +153,7 @@ namespace ts { } return data.join(newLineCharacter); }); - }); + } } export function testExtractSymbolFailed(caption: string, text: string, description: DiagnosticMessage) { diff --git a/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js b/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js new file mode 100644 index 00000000000..25609a3a801 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_BlockScopes_NoDependencies.js @@ -0,0 +1,14 @@ +// ==ORIGINAL== +for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = 1; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +for (let i = 0; i < 10; i++) { + for (let j = 0; j < 10; j++) { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Class.js b/tests/baselines/reference/extractConstant/extractConstant_Class.js new file mode 100644 index 00000000000..6b581141cd6 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Class.js @@ -0,0 +1,10 @@ +// ==ORIGINAL== +class C { + x = 1; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js b/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js new file mode 100644 index 00000000000..4e7bcc0521a --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ClassInsertionPosition.js @@ -0,0 +1,34 @@ +// ==ORIGINAL== +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = 1; + } +} +// ==SCOPE::Extract to constant in method 'M3== +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + a = 1; + b = 2; + M1() { } + M2() { } + M3() { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js new file mode 100644 index 00000000000..6bf35cd17e1 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatement.js @@ -0,0 +1,4 @@ +// ==ORIGINAL== +"hello"; +// ==SCOPE::Extract to constant in global scope== +const /*RENAME*/newLocal = "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js new file mode 100644 index 00000000000..6bf35cd17e1 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_ExpressionStatementExpression.js @@ -0,0 +1,4 @@ +// ==ORIGINAL== +"hello"; +// ==SCOPE::Extract to constant in global scope== +const /*RENAME*/newLocal = "hello"; \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Function.js b/tests/baselines/reference/extractConstant/extractConstant_Function.js new file mode 100644 index 00000000000..67c8255c4b4 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Function.js @@ -0,0 +1,16 @@ +// ==ORIGINAL== +function F() { + let x = 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +function F() { + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Method.js b/tests/baselines/reference/extractConstant/extractConstant_Method.js new file mode 100644 index 00000000000..bd7c7f86359 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Method.js @@ -0,0 +1,22 @@ +// ==ORIGINAL== +class C { + M() { + let x = 1; + } +} +// ==SCOPE::Extract to constant in method 'M== +class C { + M() { + const newLocal = 1; + + let x = /*RENAME*/newLocal; + } +} +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +class C { + M() { + let x = /*RENAME*/newLocal; + } +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_Parameters.js b/tests/baselines/reference/extractConstant/extractConstant_Parameters.js new file mode 100644 index 00000000000..e6c9c474fbc --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_Parameters.js @@ -0,0 +1,12 @@ +// ==ORIGINAL== +function F() { + let w = 1; + let x = w + 1; +} +// ==SCOPE::Extract to constant in function 'F'== +function F() { + let w = 1; + const newLocal = w + 1; + + let x = /*RENAME*/newLocal; +} \ No newline at end of file diff --git a/tests/baselines/reference/extractConstant/extractConstant_TopLevel.js b/tests/baselines/reference/extractConstant/extractConstant_TopLevel.js new file mode 100644 index 00000000000..fb0447583ff --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_TopLevel.js @@ -0,0 +1,6 @@ +// ==ORIGINAL== +let x = 1; +// ==SCOPE::Extract to constant in global scope== +const newLocal = 1; + +let x = /*RENAME*/newLocal; \ No newline at end of file diff --git a/tests/baselines/reference/extractFunction/extractFunction20.js b/tests/baselines/reference/extractFunction/extractFunction20.js new file mode 100644 index 00000000000..455fee113fb --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction20.js @@ -0,0 +1,28 @@ +// ==ORIGINAL== +const _ = class { + a() { + let a1 = { x: 1 }; + return a1.x + 10; + } +} +// ==SCOPE::Extract to method in anonymous class expression== +const _ = class { + a() { + return this./*RENAME*/newMethod(); + } + + newMethod() { + let a1 = { x: 1 }; + return a1.x + 10; + } +} +// ==SCOPE::Extract to function in global scope== +const _ = class { + a() { + return /*RENAME*/newFunction(); + } +} +function newFunction() { + let a1 = { x: 1 }; + return a1.x + 10; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction21.js b/tests/baselines/reference/extractFunction/extractFunction21.js new file mode 100644 index 00000000000..af7928a039f --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction21.js @@ -0,0 +1,26 @@ +// ==ORIGINAL== +function foo() { + let x = 10; + x++; + return; +} +// ==SCOPE::Extract to inner function in function 'foo'== +function foo() { + let x = 10; + return /*RENAME*/newFunction(); + + function newFunction() { + x++; + return; + } +} +// ==SCOPE::Extract to function in global scope== +function foo() { + let x = 10; + x = /*RENAME*/newFunction(x); + return; +} +function newFunction(x) { + x++; + return x; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction22.js b/tests/baselines/reference/extractFunction/extractFunction22.js new file mode 100644 index 00000000000..09fbc7a5b82 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction22.js @@ -0,0 +1,31 @@ +// ==ORIGINAL== +function test() { + try { + } + finally { + return 1; + } +} +// ==SCOPE::Extract to inner function in function 'test'== +function test() { + try { + } + finally { + return /*RENAME*/newFunction(); + } + + function newFunction() { + return 1; + } +} +// ==SCOPE::Extract to function in global scope== +function test() { + try { + } + finally { + return /*RENAME*/newFunction(); + } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction24.js b/tests/baselines/reference/extractFunction/extractFunction24.js new file mode 100644 index 00000000000..7b80180c5d3 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction24.js @@ -0,0 +1,43 @@ +// ==ORIGINAL== +function Outer() { + function M1() { } + function M2() { + return 1; + } + function M3() { } +} +// ==SCOPE::Extract to inner function in function 'M2'== +function Outer() { + function M1() { } + function M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } + } + function M3() { } +} +// ==SCOPE::Extract to inner function in function 'Outer'== +function Outer() { + function M1() { } + function M2() { + return /*RENAME*/newFunction(); + } + function newFunction() { + return 1; + } + + function M3() { } +} +// ==SCOPE::Extract to function in global scope== +function Outer() { + function M1() { } + function M2() { + return /*RENAME*/newFunction(); + } + function M3() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction25.js b/tests/baselines/reference/extractFunction/extractFunction25.js new file mode 100644 index 00000000000..3233c404307 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction25.js @@ -0,0 +1,26 @@ +// ==ORIGINAL== +function M1() { } +function M2() { + return 1; +} +function M3() { } +// ==SCOPE::Extract to inner function in function 'M2'== +function M1() { } +function M2() { + return /*RENAME*/newFunction(); + + function newFunction() { + return 1; + } +} +function M3() { } +// ==SCOPE::Extract to function in global scope== +function M1() { } +function M2() { + return /*RENAME*/newFunction(); +} +function newFunction() { + return 1; +} + +function M3() { } \ No newline at end of file diff --git a/tests/baselines/reference/extractFunction/extractFunction26.js b/tests/baselines/reference/extractFunction/extractFunction26.js new file mode 100644 index 00000000000..ccc4bacb477 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction26.js @@ -0,0 +1,31 @@ +// ==ORIGINAL== +class C { + M1() { } + M2() { + return 1; + } + M3() { } +} +// ==SCOPE::Extract to method in class 'C'== +class C { + M1() { } + M2() { + return this./*RENAME*/newMethod(); + } + newMethod() { + return 1; + } + + M3() { } +} +// ==SCOPE::Extract to function in global scope== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + } + M3() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction27.js b/tests/baselines/reference/extractFunction/extractFunction27.js new file mode 100644 index 00000000000..1f894d17bad --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction27.js @@ -0,0 +1,34 @@ +// ==ORIGINAL== +class C { + M1() { } + M2() { + return 1; + } + constructor() { } + M3() { } +} +// ==SCOPE::Extract to method in class 'C'== +class C { + M1() { } + M2() { + return this./*RENAME*/newMethod(); + } + constructor() { } + newMethod() { + return 1; + } + + M3() { } +} +// ==SCOPE::Extract to function in global scope== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + } + constructor() { } + M3() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction28.js b/tests/baselines/reference/extractFunction/extractFunction28.js new file mode 100644 index 00000000000..d205edbd4bf --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction28.js @@ -0,0 +1,34 @@ +// ==ORIGINAL== +class C { + M1() { } + M2() { + return 1; + } + M3() { } + constructor() { } +} +// ==SCOPE::Extract to method in class 'C'== +class C { + M1() { } + M2() { + return this./*RENAME*/newMethod(); + } + newMethod() { + return 1; + } + + M3() { } + constructor() { } +} +// ==SCOPE::Extract to function in global scope== +class C { + M1() { } + M2() { + return /*RENAME*/newFunction(); + } + M3() { } + constructor() { } +} +function newFunction() { + return 1; +} diff --git a/tests/baselines/reference/extractFunction/extractFunction33.js b/tests/baselines/reference/extractFunction/extractFunction33.js new file mode 100644 index 00000000000..fd716646e66 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction33.js @@ -0,0 +1,19 @@ +// ==ORIGINAL== +function F() { + function G() { } +} +// ==SCOPE::Extract to inner function in function 'F'== +function F() { + /*RENAME*/newFunction(); + + function newFunction() { + function G() { } + } +} +// ==SCOPE::Extract to function in global scope== +function F() { + /*RENAME*/newFunction(); +} +function newFunction() { + function G() { } +} From a73a553f589d46a39259ca64c991f847a6981d70 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 28 Sep 2017 14:31:35 -0700 Subject: [PATCH 39/56] Assert that Extract* baselines are syntactically valid --- src/harness/unittests/extractTestHelpers.ts | 33 +++++++++++++-------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index d42b0839580..ea1cccd32ea 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -108,23 +108,16 @@ namespace ts { it(`${caption} [${extension}]`, () => runBaseline(extension))); function runBaseline(extension: Extension) { - const f = { - path: "/a" + extension, - content: t.source - }; - const host = projectSystem.createServerHost([f, projectSystem.libFile]); - const projectService = projectSystem.createProjectService(host); - projectService.openClientFile(f.path); - const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + const path = "/a" + extension; + const program = makeProgram({ path, content: t.source }); - // Don't bother generating JS baselines for inputs that aren't valid JS. - const diags = program.getSyntacticDiagnostics(); - if (diags && diags.length) { + if (hasSyntacticDiagnostics(program)) { + // Don't bother generating JS baselines for inputs that aren't valid JS. assert.equal(Extension.Js, extension); return; } - const sourceFile = program.getSourceFile(f.path); + const sourceFile = program.getSourceFile(path); const context: RefactorContext = { cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } }, newLineCharacter, @@ -150,10 +143,26 @@ namespace ts { const newText = textChanges.applyChanges(sourceFile.text, edits[0].textChanges); const newTextWithRename = newText.slice(0, renameLocation) + "/*RENAME*/" + newText.slice(renameLocation); data.push(newTextWithRename); + + const diagProgram = makeProgram({ path, content: newText }); + assert.isFalse(hasSyntacticDiagnostics(diagProgram)); } return data.join(newLineCharacter); }); } + + function makeProgram(f: {path: string, content: string }) { + const host = projectSystem.createServerHost([f, projectSystem.libFile]); + const projectService = projectSystem.createProjectService(host); + projectService.openClientFile(f.path); + const program = projectService.inferredProjects[0].getLanguageService().getProgram(); + return program; + } + + function hasSyntacticDiagnostics(program: Program) { + const diags = program.getSyntacticDiagnostics(); + return length(diags) > 0; + } } export function testExtractSymbolFailed(caption: string, text: string, description: DiagnosticMessage) { From 5613be4907aab27f2669658570f17a1e64ecccce Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 28 Sep 2017 14:33:35 -0700 Subject: [PATCH 40/56] Only methods and constructors are bivariant in --strictFunctionTypes mode --- src/compiler/checker.ts | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5d3957652d8..ceb9d70847f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3549,7 +3549,7 @@ namespace ts { return; } - if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length && isStrictSignature(resolved.callSignatures[0])) { + if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) { const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags); if (parenthesizeSignature) { writePunctuation(writer, SyntaxKind.OpenParenToken); @@ -3560,7 +3560,7 @@ namespace ts { } return; } - if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length && isStrictSignature(resolved.constructSignatures[0])) { + if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) { if (flags & TypeFormatFlags.InElementType) { writePunctuation(writer, SyntaxKind.OpenParenToken); } @@ -8522,17 +8522,6 @@ namespace ts { /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False; } - // A signature is considered strict if it is declared in a function type literal, a constructor type - // literal, a function expression, an arrow function, or a function declaration with no overloads. A - // strict signature is subject to strict checking in strictFunctionTypes mode. - function isStrictSignature(signature: Signature) { - const declaration = signature.declaration; - const kind = declaration ? declaration.kind : SyntaxKind.Unknown; - return kind === SyntaxKind.FunctionType || kind === SyntaxKind.ConstructorType || - kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction || - (kind === SyntaxKind.FunctionDeclaration && getSingleCallSignature(getTypeOfSymbol(getSymbolOfNode(declaration)))); - } - type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void; /** @@ -8558,7 +8547,9 @@ namespace ts { source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } - const strictVariance = strictFunctionTypes && isStrictSignature(target); + const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown; + const strictVariance = strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration && + kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor; let result = Ternary.True; const sourceThisType = getThisTypeOfSignature(source); From 1609196b22a4590039da899b57cabdc5ef7869ce Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 28 Sep 2017 14:34:03 -0700 Subject: [PATCH 41/56] Accept new baselines --- .../reference/commentsClassMembers.js | 8 ++--- .../baselines/reference/commentsInterface.js | 8 ++--- tests/baselines/reference/jsDocTypeTag2.js | 30 ++++--------------- .../typeGuardFunctionOfFormThisErrors.js | 4 +-- 4 files changed, 10 insertions(+), 40 deletions(-) diff --git a/tests/baselines/reference/commentsClassMembers.js b/tests/baselines/reference/commentsClassMembers.js index d5cee0e1950..f53c64060a3 100644 --- a/tests/baselines/reference/commentsClassMembers.js +++ b/tests/baselines/reference/commentsClassMembers.js @@ -548,15 +548,11 @@ declare class c1 { } declare var i1: c1; declare var i1_p: number; -declare var i1_f: { - (b: number): number; -}; +declare var i1_f: (b: number) => number; declare var i1_r: number; declare var i1_prop: number; declare var i1_nc_p: number; -declare var i1_ncf: { - (b: number): number; -}; +declare var i1_ncf: (b: number) => number; declare var i1_ncr: number; declare var i1_ncprop: number; declare var i1_s_p: number; diff --git a/tests/baselines/reference/commentsInterface.js b/tests/baselines/reference/commentsInterface.js index b7ceffa58af..3deac9871f1 100644 --- a/tests/baselines/reference/commentsInterface.js +++ b/tests/baselines/reference/commentsInterface.js @@ -142,13 +142,9 @@ declare var i2_i_nc_x: number; declare var i2_i_nc_foo: (b: number) => string; declare var i2_i_nc_foo_r: string; declare var i2_i_r: number; -declare var i2_i_fnfoo: { - (b: number): string; -}; +declare var i2_i_fnfoo: (b: number) => string; declare var i2_i_fnfoo_r: string; -declare var i2_i_nc_fnfoo: { - (b: number): string; -}; +declare var i2_i_nc_fnfoo: (b: number) => string; declare var i2_i_nc_fnfoo_r: string; interface i3 { /** Comment i3 x*/ diff --git a/tests/baselines/reference/jsDocTypeTag2.js b/tests/baselines/reference/jsDocTypeTag2.js index bde3f3f9c1e..d54b4557f06 100644 --- a/tests/baselines/reference/jsDocTypeTag2.js +++ b/tests/baselines/reference/jsDocTypeTag2.js @@ -472,18 +472,6 @@ "text": " ", "kind": "space" }, - { - "text": "{", - "kind": "punctuation" - }, - { - "text": "\n", - "kind": "lineBreak" - }, - { - "text": " ", - "kind": "space" - }, { "text": "(", "kind": "punctuation" @@ -509,7 +497,11 @@ "kind": "punctuation" }, { - "text": ":", + "text": " ", + "kind": "space" + }, + { + "text": "=>", "kind": "punctuation" }, { @@ -519,18 +511,6 @@ { "text": "number", "kind": "keyword" - }, - { - "text": ";", - "kind": "punctuation" - }, - { - "text": "\n", - "kind": "lineBreak" - }, - { - "text": "}", - "kind": "punctuation" } ], "documentation": [], diff --git a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js index 6ba90de54f1..84db5d4a4a9 100644 --- a/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js +++ b/tests/baselines/reference/typeGuardFunctionOfFormThisErrors.js @@ -154,6 +154,4 @@ declare let c: number | number[]; declare let holder: { invalidGuard: (c: any) => this is number; }; -declare let detached: { - (): this is FollowerGuard; -}; +declare let detached: () => this is FollowerGuard; From 41676248e545428d2ef4125781616a281a102302 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 28 Sep 2017 14:54:52 -0700 Subject: [PATCH 42/56] Eliminate special case for extracting from a binary operator chain 1) It assumed left-associativity and was, therefore, wrong for (e.g.) exponentiation. 2) Arguably, if a user selects `a + |b + c|`, they want to extract `b + c`, not `a + b + c`. Not being able to do so is surprising (and we may eventually want to allow it), but so is having the rest of the least-common subtree extracted. Fixes #18268 --- src/harness/unittests/extractFunctions.ts | 10 --- src/harness/unittests/extractRanges.ts | 25 ++++--- src/services/refactors/extractSymbol.ts | 31 ++------- .../extractFunction/extractFunction8.ts | 65 ------------------- 4 files changed, 17 insertions(+), 114 deletions(-) delete mode 100644 tests/baselines/reference/extractFunction/extractFunction8.ts diff --git a/src/harness/unittests/extractFunctions.ts b/src/harness/unittests/extractFunctions.ts index 522ea7b1293..27c21ce3fb6 100644 --- a/src/harness/unittests/extractFunctions.ts +++ b/src/harness/unittests/extractFunctions.ts @@ -109,16 +109,6 @@ namespace ts { return C.foo();|] } } -}`); - testExtractFunction("extractFunction8", - `namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return 1 + [#|a1 + x|] + 100; - } - } }`); testExtractFunction("extractFunction9", `namespace A { diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index fcffffeba9d..2ddcac482a9 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -152,18 +152,6 @@ namespace ts { } } `); - testExtractRange(` - function f() { - return [$|1 + [#|2 + 3|]|]; - } - } - `); - testExtractRange(` - function f() { - return [$|1 + 2 + [#|3 + 4|]|]; - } - } - `); }); testExtractRangeFailed("extractRangeFailed1", @@ -311,7 +299,18 @@ switch (x) { testExtractRangeFailed("extractRangeFailed9", `var x = ([#||]1 + 2);`, [ - "Cannot extract empty range." + refactor.extractSymbol.Messages.CannotExtractEmpty.message + ]); + + testExtractRangeFailed("extractRangeFailed10", + ` + function f() { + return 1 + [#|2 + 3|]; + } + } + `, + [ + refactor.extractSymbol.Messages.CannotExtractRange.message ]); testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.CannotExtractIdentifier.message]); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 375acb7e585..b88a013f52b 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -201,9 +201,9 @@ namespace ts.refactor.extractSymbol { // Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span. // This may fail (e.g. you select two statements in the root of a source file) - let start = getParentNodeInSpan(getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false), sourceFile, span); + const start = getParentNodeInSpan(getTokenAtPosition(sourceFile, span.start, /*includeJsDocComment*/ false), sourceFile, span); // Do the same for the ending position - let end = getParentNodeInSpan(findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span)), sourceFile, span); + const end = getParentNodeInSpan(findTokenOnLeftOfPosition(sourceFile, textSpanEnd(span)), sourceFile, span); const declarations: Symbol[] = []; @@ -217,31 +217,10 @@ namespace ts.refactor.extractSymbol { } if (start.parent !== end.parent) { - // handle cases like 1 + [2 + 3] + 4 - // user selection is marked with []. - // in this case 2 + 3 does not belong to the same tree node - // instead the shape of the tree looks like this: - // + - // / \ - // + 4 - // / \ - // + 3 - // / \ - // 1 2 - // in this case there is no such one node that covers ends of selection and is located inside the selection - // to handle this we check if both start and end of the selection belong to some binary operation - // and start node is parented by the parent of the end node - // if this is the case - expand the selection to the entire parent of end node (in this case it will be [1 + 2 + 3] + 4) - const startParent = skipParentheses(start.parent); - const endParent = skipParentheses(end.parent); - if (isBinaryExpression(startParent) && isBinaryExpression(endParent) && isNodeDescendantOf(startParent, endParent)) { - start = end = endParent; - } - else { - // start and end nodes belong to different subtrees - return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; - } + // start and end nodes belong to different subtrees + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.CannotExtractRange)] }; } + if (start !== end) { // start and end should be statements and parent should be either block or a source file if (!isBlockLike(start.parent)) { diff --git a/tests/baselines/reference/extractFunction/extractFunction8.ts b/tests/baselines/reference/extractFunction/extractFunction8.ts deleted file mode 100644 index adb8adbe56a..00000000000 --- a/tests/baselines/reference/extractFunction/extractFunction8.ts +++ /dev/null @@ -1,65 +0,0 @@ -// ==ORIGINAL== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return 1 + a1 + x + 100; - } - } -} -// ==SCOPE::Extract to inner function in function 'a'== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction() + 100; - - function newFunction() { - return 1 + a1 + x; - } - } - } -} -// ==SCOPE::Extract to function in namespace 'B'== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction(a1) + 100; - } - - function newFunction(a1: number) { - return 1 + a1 + x; - } - } -} -// ==SCOPE::Extract to function in namespace 'A'== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction(a1) + 100; - } - } - - function newFunction(a1: number) { - return 1 + a1 + x; - } -} -// ==SCOPE::Extract to function in global scope== -namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return /*RENAME*/newFunction(a1, x) + 100; - } - } -} -function newFunction(a1: number, x: number) { - return 1 + a1 + x; -} From c626d9d47cc8e070a7d92ece1e39aacdaf80f425 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 28 Sep 2017 15:44:17 -0700 Subject: [PATCH 43/56] Accept new baselines --- .../strictFunctionTypesErrors.symbols | 366 +++++++++++++++ .../reference/strictFunctionTypesErrors.types | 418 ++++++++++++++++++ 2 files changed, 784 insertions(+) create mode 100644 tests/baselines/reference/strictFunctionTypesErrors.symbols create mode 100644 tests/baselines/reference/strictFunctionTypesErrors.types diff --git a/tests/baselines/reference/strictFunctionTypesErrors.symbols b/tests/baselines/reference/strictFunctionTypesErrors.symbols new file mode 100644 index 00000000000..ce3a81f524f --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.symbols @@ -0,0 +1,366 @@ +=== tests/cases/compiler/strictFunctionTypesErrors.ts === +export {} + + +declare let f1: (x: Object) => Object; +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) +>x : Symbol(x, Decl(strictFunctionTypesErrors.ts, 3, 17)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let f2: (x: Object) => string; +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) +>x : Symbol(x, Decl(strictFunctionTypesErrors.ts, 4, 17)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let f3: (x: string) => Object; +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) +>x : Symbol(x, Decl(strictFunctionTypesErrors.ts, 5, 17)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let f4: (x: string) => string; +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) +>x : Symbol(x, Decl(strictFunctionTypesErrors.ts, 6, 17)) + +f1 = f2; // Ok +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) + +f1 = f3; // Error +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) + +f1 = f4; // Error +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) + +f2 = f1; // Error +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) + +f2 = f3; // Error +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) + +f2 = f4; // Error +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) + +f3 = f1; // Ok +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) + +f3 = f2; // Ok +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) + +f3 = f4; // Ok +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) + +f4 = f1; // Error +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) +>f1 : Symbol(f1, Decl(strictFunctionTypesErrors.ts, 3, 11)) + +f4 = f2; // Ok +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) +>f2 : Symbol(f2, Decl(strictFunctionTypesErrors.ts, 4, 11)) + +f4 = f3; // Error +>f4 : Symbol(f4, Decl(strictFunctionTypesErrors.ts, 6, 11)) +>f3 : Symbol(f3, Decl(strictFunctionTypesErrors.ts, 5, 11)) + +type Func = (x: T) => U; +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 24, 10)) +>U : Symbol(U, Decl(strictFunctionTypesErrors.ts, 24, 12)) +>x : Symbol(x, Decl(strictFunctionTypesErrors.ts, 24, 19)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 24, 10)) +>U : Symbol(U, Decl(strictFunctionTypesErrors.ts, 24, 12)) + +declare let g1: Func; +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let g2: Func; +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let g3: Func; +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let g4: Func; +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) + +g1 = g2; // Ok +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) + +g1 = g3; // Error +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) + +g1 = g4; // Error +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) + +g2 = g1; // Error +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) + +g2 = g3; // Error +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) + +g2 = g4; // Error +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) + +g3 = g1; // Ok +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) + +g3 = g2; // Ok +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) + +g3 = g4; // Ok +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) + +g4 = g1; // Error +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) +>g1 : Symbol(g1, Decl(strictFunctionTypesErrors.ts, 26, 11)) + +g4 = g2; // Ok +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) +>g2 : Symbol(g2, Decl(strictFunctionTypesErrors.ts, 27, 11)) + +g4 = g3; // Error +>g4 : Symbol(g4, Decl(strictFunctionTypesErrors.ts, 29, 11)) +>g3 : Symbol(g3, Decl(strictFunctionTypesErrors.ts, 28, 11)) + +declare let h1: Func, Object>; +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let h2: Func, string>; +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let h3: Func, Object>; +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let h4: Func, string>; +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) + +h1 = h2; // Ok +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) + +h1 = h3; // Ok +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) + +h1 = h4; // Ok +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) + +h2 = h1; // Error +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) + +h2 = h3; // Error +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) + +h2 = h4; // Ok +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) + +h3 = h1; // Error +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) + +h3 = h2; // Error +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) + +h3 = h4; // Ok +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) + +h4 = h1; // Error +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) +>h1 : Symbol(h1, Decl(strictFunctionTypesErrors.ts, 47, 11)) + +h4 = h2; // Error +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) +>h2 : Symbol(h2, Decl(strictFunctionTypesErrors.ts, 48, 11)) + +h4 = h3; // Error +>h4 : Symbol(h4, Decl(strictFunctionTypesErrors.ts, 50, 11)) +>h3 : Symbol(h3, Decl(strictFunctionTypesErrors.ts, 49, 11)) + +declare let i1: Func>; +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let i2: Func>; +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) + +declare let i3: Func>; +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + +declare let i4: Func>; +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) +>Func : Symbol(Func, Decl(strictFunctionTypesErrors.ts, 22, 8)) + +i1 = i2; // Error +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) + +i1 = i3; // Error +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) + +i1 = i4; // Error +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) + +i2 = i1; // Ok +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) + +i2 = i3; // Error +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) + +i2 = i4; // Error +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) + +i3 = i1; // Ok +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) + +i3 = i2; // Error +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) + +i3 = i4; // Error +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) + +i4 = i1; // Ok +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) +>i1 : Symbol(i1, Decl(strictFunctionTypesErrors.ts, 68, 11)) + +i4 = i2; // Ok +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) +>i2 : Symbol(i2, Decl(strictFunctionTypesErrors.ts, 69, 11)) + +i4 = i3; // Ok +>i4 : Symbol(i4, Decl(strictFunctionTypesErrors.ts, 71, 11)) +>i3 : Symbol(i3, Decl(strictFunctionTypesErrors.ts, 70, 11)) + +interface Animal { animal: void } +>Animal : Symbol(Animal, Decl(strictFunctionTypesErrors.ts, 87, 8)) +>animal : Symbol(Animal.animal, Decl(strictFunctionTypesErrors.ts, 89, 18)) + +interface Dog extends Animal { dog: void } +>Dog : Symbol(Dog, Decl(strictFunctionTypesErrors.ts, 89, 33)) +>Animal : Symbol(Animal, Decl(strictFunctionTypesErrors.ts, 87, 8)) +>dog : Symbol(Dog.dog, Decl(strictFunctionTypesErrors.ts, 90, 30)) + +interface Cat extends Animal { cat: void } +>Cat : Symbol(Cat, Decl(strictFunctionTypesErrors.ts, 90, 42)) +>Animal : Symbol(Animal, Decl(strictFunctionTypesErrors.ts, 87, 8)) +>cat : Symbol(Cat.cat, Decl(strictFunctionTypesErrors.ts, 91, 30)) + +interface Comparer1 { +>Comparer1 : Symbol(Comparer1, Decl(strictFunctionTypesErrors.ts, 91, 42)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 93, 20)) + + compare(a: T, b: T): number; +>compare : Symbol(Comparer1.compare, Decl(strictFunctionTypesErrors.ts, 93, 24)) +>a : Symbol(a, Decl(strictFunctionTypesErrors.ts, 94, 12)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 93, 20)) +>b : Symbol(b, Decl(strictFunctionTypesErrors.ts, 94, 17)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 93, 20)) +} + +declare let animalComparer1: Comparer1; +>animalComparer1 : Symbol(animalComparer1, Decl(strictFunctionTypesErrors.ts, 97, 11)) +>Comparer1 : Symbol(Comparer1, Decl(strictFunctionTypesErrors.ts, 91, 42)) +>Animal : Symbol(Animal, Decl(strictFunctionTypesErrors.ts, 87, 8)) + +declare let dogComparer1: Comparer1; +>dogComparer1 : Symbol(dogComparer1, Decl(strictFunctionTypesErrors.ts, 98, 11)) +>Comparer1 : Symbol(Comparer1, Decl(strictFunctionTypesErrors.ts, 91, 42)) +>Dog : Symbol(Dog, Decl(strictFunctionTypesErrors.ts, 89, 33)) + +animalComparer1 = dogComparer1; // Ok +>animalComparer1 : Symbol(animalComparer1, Decl(strictFunctionTypesErrors.ts, 97, 11)) +>dogComparer1 : Symbol(dogComparer1, Decl(strictFunctionTypesErrors.ts, 98, 11)) + +dogComparer1 = animalComparer1; // Ok +>dogComparer1 : Symbol(dogComparer1, Decl(strictFunctionTypesErrors.ts, 98, 11)) +>animalComparer1 : Symbol(animalComparer1, Decl(strictFunctionTypesErrors.ts, 97, 11)) + +interface Comparer2 { +>Comparer2 : Symbol(Comparer2, Decl(strictFunctionTypesErrors.ts, 101, 31)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 103, 20)) + + compare: (a: T, b: T) => number; +>compare : Symbol(Comparer2.compare, Decl(strictFunctionTypesErrors.ts, 103, 24)) +>a : Symbol(a, Decl(strictFunctionTypesErrors.ts, 104, 14)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 103, 20)) +>b : Symbol(b, Decl(strictFunctionTypesErrors.ts, 104, 19)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 103, 20)) +} + +declare let animalComparer2: Comparer2; +>animalComparer2 : Symbol(animalComparer2, Decl(strictFunctionTypesErrors.ts, 107, 11)) +>Comparer2 : Symbol(Comparer2, Decl(strictFunctionTypesErrors.ts, 101, 31)) +>Animal : Symbol(Animal, Decl(strictFunctionTypesErrors.ts, 87, 8)) + +declare let dogComparer2: Comparer2; +>dogComparer2 : Symbol(dogComparer2, Decl(strictFunctionTypesErrors.ts, 108, 11)) +>Comparer2 : Symbol(Comparer2, Decl(strictFunctionTypesErrors.ts, 101, 31)) +>Dog : Symbol(Dog, Decl(strictFunctionTypesErrors.ts, 89, 33)) + +animalComparer2 = dogComparer2; // Error +>animalComparer2 : Symbol(animalComparer2, Decl(strictFunctionTypesErrors.ts, 107, 11)) +>dogComparer2 : Symbol(dogComparer2, Decl(strictFunctionTypesErrors.ts, 108, 11)) + +dogComparer2 = animalComparer2; // Ok +>dogComparer2 : Symbol(dogComparer2, Decl(strictFunctionTypesErrors.ts, 108, 11)) +>animalComparer2 : Symbol(animalComparer2, Decl(strictFunctionTypesErrors.ts, 107, 11)) + diff --git a/tests/baselines/reference/strictFunctionTypesErrors.types b/tests/baselines/reference/strictFunctionTypesErrors.types new file mode 100644 index 00000000000..f2d3b52a5b9 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.types @@ -0,0 +1,418 @@ +=== tests/cases/compiler/strictFunctionTypesErrors.ts === +export {} + + +declare let f1: (x: Object) => Object; +>f1 : (x: Object) => Object +>x : Object +>Object : Object +>Object : Object + +declare let f2: (x: Object) => string; +>f2 : (x: Object) => string +>x : Object +>Object : Object + +declare let f3: (x: string) => Object; +>f3 : (x: string) => Object +>x : string +>Object : Object + +declare let f4: (x: string) => string; +>f4 : (x: string) => string +>x : string + +f1 = f2; // Ok +>f1 = f2 : (x: Object) => string +>f1 : (x: Object) => Object +>f2 : (x: Object) => string + +f1 = f3; // Error +>f1 = f3 : (x: string) => Object +>f1 : (x: Object) => Object +>f3 : (x: string) => Object + +f1 = f4; // Error +>f1 = f4 : (x: string) => string +>f1 : (x: Object) => Object +>f4 : (x: string) => string + +f2 = f1; // Error +>f2 = f1 : (x: Object) => Object +>f2 : (x: Object) => string +>f1 : (x: Object) => Object + +f2 = f3; // Error +>f2 = f3 : (x: string) => Object +>f2 : (x: Object) => string +>f3 : (x: string) => Object + +f2 = f4; // Error +>f2 = f4 : (x: string) => string +>f2 : (x: Object) => string +>f4 : (x: string) => string + +f3 = f1; // Ok +>f3 = f1 : (x: Object) => Object +>f3 : (x: string) => Object +>f1 : (x: Object) => Object + +f3 = f2; // Ok +>f3 = f2 : (x: Object) => string +>f3 : (x: string) => Object +>f2 : (x: Object) => string + +f3 = f4; // Ok +>f3 = f4 : (x: string) => string +>f3 : (x: string) => Object +>f4 : (x: string) => string + +f4 = f1; // Error +>f4 = f1 : (x: Object) => Object +>f4 : (x: string) => string +>f1 : (x: Object) => Object + +f4 = f2; // Ok +>f4 = f2 : (x: Object) => string +>f4 : (x: string) => string +>f2 : (x: Object) => string + +f4 = f3; // Error +>f4 = f3 : (x: string) => Object +>f4 : (x: string) => string +>f3 : (x: string) => Object + +type Func = (x: T) => U; +>Func : (x: T) => U +>T : T +>U : U +>x : T +>T : T +>U : U + +declare let g1: Func; +>g1 : (x: Object) => Object +>Func : (x: T) => U +>Object : Object +>Object : Object + +declare let g2: Func; +>g2 : (x: Object) => string +>Func : (x: T) => U +>Object : Object + +declare let g3: Func; +>g3 : (x: string) => Object +>Func : (x: T) => U +>Object : Object + +declare let g4: Func; +>g4 : (x: string) => string +>Func : (x: T) => U + +g1 = g2; // Ok +>g1 = g2 : (x: Object) => string +>g1 : (x: Object) => Object +>g2 : (x: Object) => string + +g1 = g3; // Error +>g1 = g3 : (x: string) => Object +>g1 : (x: Object) => Object +>g3 : (x: string) => Object + +g1 = g4; // Error +>g1 = g4 : (x: string) => string +>g1 : (x: Object) => Object +>g4 : (x: string) => string + +g2 = g1; // Error +>g2 = g1 : (x: Object) => Object +>g2 : (x: Object) => string +>g1 : (x: Object) => Object + +g2 = g3; // Error +>g2 = g3 : (x: string) => Object +>g2 : (x: Object) => string +>g3 : (x: string) => Object + +g2 = g4; // Error +>g2 = g4 : (x: string) => string +>g2 : (x: Object) => string +>g4 : (x: string) => string + +g3 = g1; // Ok +>g3 = g1 : (x: Object) => Object +>g3 : (x: string) => Object +>g1 : (x: Object) => Object + +g3 = g2; // Ok +>g3 = g2 : (x: Object) => string +>g3 : (x: string) => Object +>g2 : (x: Object) => string + +g3 = g4; // Ok +>g3 = g4 : (x: string) => string +>g3 : (x: string) => Object +>g4 : (x: string) => string + +g4 = g1; // Error +>g4 = g1 : (x: Object) => Object +>g4 : (x: string) => string +>g1 : (x: Object) => Object + +g4 = g2; // Ok +>g4 = g2 : (x: Object) => string +>g4 : (x: string) => string +>g2 : (x: Object) => string + +g4 = g3; // Error +>g4 = g3 : (x: string) => Object +>g4 : (x: string) => string +>g3 : (x: string) => Object + +declare let h1: Func, Object>; +>h1 : (x: Func) => Object +>Func : (x: T) => U +>Func : (x: T) => U +>Object : Object +>Object : Object + +declare let h2: Func, string>; +>h2 : (x: Func) => string +>Func : (x: T) => U +>Func : (x: T) => U +>Object : Object + +declare let h3: Func, Object>; +>h3 : (x: Func) => Object +>Func : (x: T) => U +>Func : (x: T) => U +>Object : Object + +declare let h4: Func, string>; +>h4 : (x: Func) => string +>Func : (x: T) => U +>Func : (x: T) => U + +h1 = h2; // Ok +>h1 = h2 : (x: Func) => string +>h1 : (x: Func) => Object +>h2 : (x: Func) => string + +h1 = h3; // Ok +>h1 = h3 : (x: Func) => Object +>h1 : (x: Func) => Object +>h3 : (x: Func) => Object + +h1 = h4; // Ok +>h1 = h4 : (x: Func) => string +>h1 : (x: Func) => Object +>h4 : (x: Func) => string + +h2 = h1; // Error +>h2 = h1 : (x: Func) => Object +>h2 : (x: Func) => string +>h1 : (x: Func) => Object + +h2 = h3; // Error +>h2 = h3 : (x: Func) => Object +>h2 : (x: Func) => string +>h3 : (x: Func) => Object + +h2 = h4; // Ok +>h2 = h4 : (x: Func) => string +>h2 : (x: Func) => string +>h4 : (x: Func) => string + +h3 = h1; // Error +>h3 = h1 : (x: Func) => Object +>h3 : (x: Func) => Object +>h1 : (x: Func) => Object + +h3 = h2; // Error +>h3 = h2 : (x: Func) => string +>h3 : (x: Func) => Object +>h2 : (x: Func) => string + +h3 = h4; // Ok +>h3 = h4 : (x: Func) => string +>h3 : (x: Func) => Object +>h4 : (x: Func) => string + +h4 = h1; // Error +>h4 = h1 : (x: Func) => Object +>h4 : (x: Func) => string +>h1 : (x: Func) => Object + +h4 = h2; // Error +>h4 = h2 : (x: Func) => string +>h4 : (x: Func) => string +>h2 : (x: Func) => string + +h4 = h3; // Error +>h4 = h3 : (x: Func) => Object +>h4 : (x: Func) => string +>h3 : (x: Func) => Object + +declare let i1: Func>; +>i1 : (x: Object) => Func +>Func : (x: T) => U +>Object : Object +>Func : (x: T) => U +>Object : Object + +declare let i2: Func>; +>i2 : (x: Object) => Func +>Func : (x: T) => U +>Object : Object +>Func : (x: T) => U + +declare let i3: Func>; +>i3 : (x: string) => Func +>Func : (x: T) => U +>Func : (x: T) => U +>Object : Object + +declare let i4: Func>; +>i4 : (x: string) => Func +>Func : (x: T) => U +>Func : (x: T) => U + +i1 = i2; // Error +>i1 = i2 : (x: Object) => Func +>i1 : (x: Object) => Func +>i2 : (x: Object) => Func + +i1 = i3; // Error +>i1 = i3 : (x: string) => Func +>i1 : (x: Object) => Func +>i3 : (x: string) => Func + +i1 = i4; // Error +>i1 = i4 : (x: string) => Func +>i1 : (x: Object) => Func +>i4 : (x: string) => Func + +i2 = i1; // Ok +>i2 = i1 : (x: Object) => Func +>i2 : (x: Object) => Func +>i1 : (x: Object) => Func + +i2 = i3; // Error +>i2 = i3 : (x: string) => Func +>i2 : (x: Object) => Func +>i3 : (x: string) => Func + +i2 = i4; // Error +>i2 = i4 : (x: string) => Func +>i2 : (x: Object) => Func +>i4 : (x: string) => Func + +i3 = i1; // Ok +>i3 = i1 : (x: Object) => Func +>i3 : (x: string) => Func +>i1 : (x: Object) => Func + +i3 = i2; // Error +>i3 = i2 : (x: Object) => Func +>i3 : (x: string) => Func +>i2 : (x: Object) => Func + +i3 = i4; // Error +>i3 = i4 : (x: string) => Func +>i3 : (x: string) => Func +>i4 : (x: string) => Func + +i4 = i1; // Ok +>i4 = i1 : (x: Object) => Func +>i4 : (x: string) => Func +>i1 : (x: Object) => Func + +i4 = i2; // Ok +>i4 = i2 : (x: Object) => Func +>i4 : (x: string) => Func +>i2 : (x: Object) => Func + +i4 = i3; // Ok +>i4 = i3 : (x: string) => Func +>i4 : (x: string) => Func +>i3 : (x: string) => Func + +interface Animal { animal: void } +>Animal : Animal +>animal : void + +interface Dog extends Animal { dog: void } +>Dog : Dog +>Animal : Animal +>dog : void + +interface Cat extends Animal { cat: void } +>Cat : Cat +>Animal : Animal +>cat : void + +interface Comparer1 { +>Comparer1 : Comparer1 +>T : T + + compare(a: T, b: T): number; +>compare : (a: T, b: T) => number +>a : T +>T : T +>b : T +>T : T +} + +declare let animalComparer1: Comparer1; +>animalComparer1 : Comparer1 +>Comparer1 : Comparer1 +>Animal : Animal + +declare let dogComparer1: Comparer1; +>dogComparer1 : Comparer1 +>Comparer1 : Comparer1 +>Dog : Dog + +animalComparer1 = dogComparer1; // Ok +>animalComparer1 = dogComparer1 : Comparer1 +>animalComparer1 : Comparer1 +>dogComparer1 : Comparer1 + +dogComparer1 = animalComparer1; // Ok +>dogComparer1 = animalComparer1 : Comparer1 +>dogComparer1 : Comparer1 +>animalComparer1 : Comparer1 + +interface Comparer2 { +>Comparer2 : Comparer2 +>T : T + + compare: (a: T, b: T) => number; +>compare : (a: T, b: T) => number +>a : T +>T : T +>b : T +>T : T +} + +declare let animalComparer2: Comparer2; +>animalComparer2 : Comparer2 +>Comparer2 : Comparer2 +>Animal : Animal + +declare let dogComparer2: Comparer2; +>dogComparer2 : Comparer2 +>Comparer2 : Comparer2 +>Dog : Dog + +animalComparer2 = dogComparer2; // Error +>animalComparer2 = dogComparer2 : Comparer2 +>animalComparer2 : Comparer2 +>dogComparer2 : Comparer2 + +dogComparer2 = animalComparer2; // Ok +>dogComparer2 = animalComparer2 : Comparer2 +>dogComparer2 : Comparer2 +>animalComparer2 : Comparer2 + From 683d6c7ddd6fff9478bc99b81c0e6882cfb2290b Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 29 Sep 2017 09:57:31 -0700 Subject: [PATCH 44/56] Add helper functions for using `unescapeLeadingUnderscores` (#18793) * Add helper functions for using `unescapeLeadingUnderscores` * More cleanup --- src/compiler/binder.ts | 6 +- src/compiler/checker.ts | 103 ++++++++++----------- src/compiler/emitter.ts | 4 +- src/compiler/factory.ts | 6 +- src/compiler/transformers/destructuring.ts | 2 +- src/compiler/transformers/es2015.ts | 12 +-- src/compiler/transformers/es2017.ts | 2 +- src/compiler/transformers/es5.ts | 2 +- src/compiler/transformers/esnext.ts | 2 +- src/compiler/transformers/generators.ts | 16 ++-- src/compiler/transformers/jsx.ts | 7 +- src/compiler/transformers/module/module.ts | 2 +- src/compiler/transformers/module/system.ts | 10 +- src/compiler/transformers/ts.ts | 4 +- src/compiler/transformers/utilities.ts | 19 ++-- src/compiler/utilities.ts | 14 ++- src/services/services.ts | 4 +- 17 files changed, 109 insertions(+), 106 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 70141c853ca..796c9603423 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -244,7 +244,7 @@ namespace ts { } Debug.assert(isWellKnownSymbolSyntactically(nameExpression)); - return getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores((nameExpression).name.escapedText)); + return getPropertyNameForKnownSymbolName(idText((nameExpression).name)); } return getEscapedTextOfIdentifierOrLiteral(name); } @@ -1777,7 +1777,7 @@ namespace ts { // otherwise report generic error message. const span = getErrorSpanForNode(file, name); file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, - getStrictModeEvalOrArgumentsMessage(contextNode), unescapeLeadingUnderscores(identifier.escapedText))); + getStrictModeEvalOrArgumentsMessage(contextNode), idText(identifier))); } } } @@ -2431,7 +2431,7 @@ namespace ts { if (node.name) { node.name.parent = node; } - file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, unescapeLeadingUnderscores(prototypeSymbol.escapedName))); + file.bindDiagnostics.push(createDiagnosticForNode(symbolExport.declarations[0], Diagnostics.Duplicate_identifier_0, symbolName(prototypeSymbol))); } symbol.exports.set(prototypeSymbol.escapedName, prototypeSymbol); prototypeSymbol.parent = symbol; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff105ce9d1c..f6aeec2f704 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -227,8 +227,8 @@ namespace ts { getApparentType, isArrayLikeType, getAllPossiblePropertiesOfTypes, - getSuggestionForNonexistentProperty: (node, type) => unescapeLeadingUnderscores(getSuggestionForNonexistentProperty(node, type)), - getSuggestionForNonexistentSymbol: (location, name, meaning) => unescapeLeadingUnderscores(getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning)), + getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type), + getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), getBaseConstraintOfType, resolveName(name, location, meaning) { return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false); @@ -1144,11 +1144,11 @@ namespace ts { !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) { - let suggestion: __String | undefined; + let suggestion: string | undefined; if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) { suggestion = getSuggestionForNonexistentSymbol(originalLocation, name, meaning); if (suggestion) { - error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg), unescapeLeadingUnderscores(suggestion)); + error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg), suggestion); } } if (!suggestion) { @@ -2904,7 +2904,7 @@ namespace ts { parameterDeclaration.name.kind === SyntaxKind.Identifier ? setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) : cloneBindingName(parameterDeclaration.name) : - unescapeLeadingUnderscores(parameterSymbol.escapedName); + symbolName(parameterSymbol); const questionToken = isOptionalParameter(parameterDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined; let parameterType = getTypeOfSymbol(parameterSymbol); @@ -3118,7 +3118,7 @@ namespace ts { return `"${escapeString(stringValue, CharacterCodes.doubleQuote)}"`; } } - return unescapeLeadingUnderscores(symbol.escapedName); + return symbolName(symbol); } function getSymbolDisplayBuilder(): SymbolDisplayBuilder { @@ -3577,7 +3577,7 @@ namespace ts { continue; } if (getDeclarationModifierFlagsFromSymbol(p) & (ModifierFlags.Private | ModifierFlags.Protected)) { - writer.reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(p.escapedName)); + writer.reportPrivateInBaseOfClassExpression(symbolName(p)); } } const t = getTypeOfSymbol(p); @@ -7063,11 +7063,11 @@ namespace ts { } const type = getDeclaredTypeOfSymbol(symbol); if (!(type.flags & TypeFlags.Object)) { - error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, unescapeLeadingUnderscores(symbol.escapedName)); + error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_be_a_class_or_interface_type, symbolName(symbol)); return arity ? emptyGenericType : emptyObjectType; } if (length((type).typeParameters) !== arity) { - error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, unescapeLeadingUnderscores(symbol.escapedName), arity); + error(getTypeDeclaration(symbol), Diagnostics.Global_type_0_must_have_1_type_parameter_s, symbolName(symbol), arity); return arity ? emptyGenericType : emptyObjectType; } return type; @@ -7580,7 +7580,7 @@ namespace ts { function getLiteralTypeFromPropertyName(prop: Symbol) { return getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier || startsWith(prop.escapedName as string, "__@") ? neverType : - getLiteralType(unescapeLeadingUnderscores(prop.escapedName)); + getLiteralType(symbolName(prop)); } function getLiteralTypeFromPropertyNames(type: Type) { @@ -7619,7 +7619,7 @@ namespace ts { const propName = indexType.flags & TypeFlags.StringOrNumberLiteral ? escapeLeadingUnderscores("" + (indexType).value) : accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? - getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores(((accessExpression.argumentExpression).name).escapedText)) : + getPropertyNameForKnownSymbolName(idText(((accessExpression.argumentExpression).name))) : undefined; if (propName !== undefined) { const prop = getPropertyOfType(objectType, propName); @@ -8573,8 +8573,8 @@ namespace ts { if (!related) { if (reportErrors) { errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible, - unescapeLeadingUnderscores(sourceParams[i < sourceMax ? i : sourceMax].escapedName), - unescapeLeadingUnderscores(targetParams[i < targetMax ? i : targetMax].escapedName)); + symbolName(sourceParams[i < sourceMax ? i : sourceMax]), + symbolName(targetParams[i < targetMax ? i : targetMax])); } return Ternary.False; } @@ -8728,7 +8728,7 @@ namespace ts { const targetProperty = getPropertyOfType(targetEnumType, property.escapedName); if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) { if (errorReporter) { - errorReporter(Diagnostics.Property_0_is_missing_in_type_1, unescapeLeadingUnderscores(property.escapedName), + errorReporter(Diagnostics.Property_0_is_missing_in_type_1, symbolName(property), typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType)); } enumRelation.set(id, false); @@ -9077,7 +9077,7 @@ namespace ts { if (suggestion !== undefined) { reportError(Diagnostics.Object_literal_may_only_specify_known_properties_but_0_does_not_exist_in_type_1_Did_you_mean_to_write_2, - symbolToString(prop), typeToString(target), unescapeLeadingUnderscores(suggestion)); + symbolToString(prop), typeToString(target), suggestion); } else { reportError(Diagnostics.Object_literal_may_only_specify_known_properties_and_0_does_not_exist_in_type_1, @@ -10288,7 +10288,7 @@ namespace ts { const t = getTypeOfSymbol(p); if (t.flags & TypeFlags.ContainsWideningType) { if (!reportWideningErrorsInType(t)) { - error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, unescapeLeadingUnderscores(p.escapedName), typeToString(getWidenedType(t))); + error(p.valueDeclaration, Diagnostics.Object_literal_s_property_0_implicitly_has_an_1_type, symbolName(p), typeToString(getWidenedType(t))); } errorReported = true; } @@ -10892,7 +10892,7 @@ namespace ts { } if (node.kind === SyntaxKind.PropertyAccessExpression) { const key = getFlowCacheKey((node).expression); - return key && key + "." + unescapeLeadingUnderscores((node).name.escapedText); + return key && key + "." + idText((node).name); } if (node.kind === SyntaxKind.BindingElement) { const container = (node as BindingElement).parent.parent; @@ -10909,7 +10909,7 @@ namespace ts { const name = element.propertyName || element.name; switch (name.kind) { case SyntaxKind.Identifier: - return unescapeLeadingUnderscores(name.escapedText); + return idText(name); case SyntaxKind.ComputedPropertyName: return isStringOrNumericLiteral(name.expression) ? name.expression.text : undefined; case SyntaxKind.StringLiteral: @@ -14005,7 +14005,7 @@ namespace ts { } // Wasn't found - error(node, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(node.tagName.escapedText), "JSX." + JsxNames.IntrinsicElements); + error(node, Diagnostics.Property_0_does_not_exist_on_type_1, idText(node.tagName), "JSX." + JsxNames.IntrinsicElements); return links.resolvedSymbol = unknownSymbol; } else { @@ -14260,8 +14260,8 @@ namespace ts { // Hello World const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements); if (intrinsicElementsType !== unknownType) { - const stringLiteralTypeName = escapeLeadingUnderscores((elementType).value); - const intrinsicProp = getPropertyOfType(intrinsicElementsType, stringLiteralTypeName); + const stringLiteralTypeName = (elementType).value; + const intrinsicProp = getPropertyOfType(intrinsicElementsType, escapeLeadingUnderscores(stringLiteralTypeName)); if (intrinsicProp) { return getTypeOfSymbol(intrinsicProp); } @@ -14269,7 +14269,7 @@ namespace ts { if (indexSignatureType) { return indexSignatureType; } - error(openingLikeElement, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(stringLiteralTypeName), "JSX." + JsxNames.IntrinsicElements); + error(openingLikeElement, Diagnostics.Property_0_does_not_exist_on_type_1, stringLiteralTypeName, "JSX." + JsxNames.IntrinsicElements); } // If we need to report an error, we already done so here. So just return any to prevent any more error downstream return anyType; @@ -14563,7 +14563,7 @@ namespace ts { if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) { for (const attribute of openingLikeElement.attributes.properties) { if (isJsxAttribute(attribute) && !isKnownProperty(targetAttributesType, attribute.name.escapedText, /*isComparingJsxAttributes*/ true)) { - error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, unescapeLeadingUnderscores(attribute.name.escapedText), typeToString(targetAttributesType)); + error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, idText(attribute.name), typeToString(targetAttributesType)); // We break here so that errors won't be cascading break; } @@ -14762,7 +14762,7 @@ namespace ts { if (assignmentKind) { if (isReferenceToReadonlyEntity(node, prop) || isReferenceThroughNamespaceImport(node)) { - error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, unescapeLeadingUnderscores(right.escapedText)); + error(right, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property, idText(right)); return unknownType; } } @@ -14788,13 +14788,13 @@ namespace ts { if (isInPropertyInitializer(node) && !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) && !isPropertyDeclaredInAncestorClass(prop)) { - error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText)); + error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, idText(right)); } else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration && node.parent.kind !== SyntaxKind.TypeReference && !isInAmbientContext(valueDeclaration) && !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)) { - error(right, Diagnostics.Class_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText)); + error(right, Diagnostics.Class_0_used_before_its_declaration, idText(right)); } } @@ -14851,7 +14851,7 @@ namespace ts { } const suggestion = getSuggestionForNonexistentProperty(propNode, containingType); if (suggestion !== undefined) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), unescapeLeadingUnderscores(suggestion)); + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion); } else { errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); @@ -14859,25 +14859,20 @@ namespace ts { diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo)); } - function getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): __String | undefined { - const suggestion = getSpellingSuggestionForName(unescapeLeadingUnderscores(node.escapedText), getPropertiesOfType(containingType), SymbolFlags.Value); - return suggestion && suggestion.escapedName; + function getSuggestionForNonexistentProperty(node: Identifier, containingType: Type): string | undefined { + const suggestion = getSpellingSuggestionForName(idText(node), getPropertiesOfType(containingType), SymbolFlags.Value); + return suggestion && symbolName(suggestion); } - function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): __String { + function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): string { const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ false, (symbols, name, meaning) => { const symbol = getSymbol(symbols, name, meaning); - if (symbol) { - // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function - // So the table *contains* `x` but `x` isn't actually in scope. - // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. - return symbol; - } - return getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning); + // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function + // So the table *contains* `x` but `x` isn't actually in scope. + // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. + return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning); }); - if (result) { - return result.escapedName; - } + return result && symbolName(result); } /** @@ -14907,7 +14902,7 @@ namespace ts { } name = name.toLowerCase(); for (const candidate of symbols) { - let candidateName = unescapeLeadingUnderscores(candidate.escapedName); + let candidateName = symbolName(candidate); if (candidate.flags & meaning && candidateName && Math.abs(candidateName.length - name.length) < maximumLengthDifference) { @@ -15715,7 +15710,7 @@ namespace ts { const element = node; switch (element.name.kind) { case SyntaxKind.Identifier: - return getLiteralType(unescapeLeadingUnderscores(element.name.escapedText)); + return getLiteralType(idText(element.name)); case SyntaxKind.NumericLiteral: case SyntaxKind.StringLiteral: return getLiteralType(element.name.text); @@ -18593,7 +18588,7 @@ namespace ts { memberName = member.name.text; break; case SyntaxKind.Identifier: - memberName = unescapeLeadingUnderscores(member.name.escapedText); + memberName = idText(member.name); break; default: continue; @@ -19572,7 +19567,7 @@ namespace ts { const collidingSymbol = getSymbol(node.locals, rootName.escapedText, SymbolFlags.Value); if (collidingSymbol) { error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, - unescapeLeadingUnderscores(rootName.escapedText), + idText(rootName), entityNameToString(promiseConstructorName)); return unknownType; } @@ -19971,11 +19966,11 @@ namespace ts { !isParameterPropertyDeclaration(parameter) && !parameterIsThisKeyword(parameter) && !parameterNameStartsWithUnderscore(name)) { - error(name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(local.escapedName)); + error(name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(local)); } } else if (compilerOptions.noUnusedLocals) { - forEach(local.declarations, d => errorUnusedLocal(d, unescapeLeadingUnderscores(local.escapedName))); + forEach(local.declarations, d => errorUnusedLocal(d, symbolName(local))); } } }); @@ -20010,7 +20005,7 @@ namespace ts { } function isIdentifierThatStartsWithUnderScore(node: Node) { - return node.kind === SyntaxKind.Identifier && unescapeLeadingUnderscores((node).escapedText).charCodeAt(0) === CharacterCodes._; + return node.kind === SyntaxKind.Identifier && idText(node).charCodeAt(0) === CharacterCodes._; } function checkUnusedClassMembers(node: ClassDeclaration | ClassExpression): void { @@ -20019,13 +20014,13 @@ namespace ts { for (const member of node.members) { if (member.kind === SyntaxKind.MethodDeclaration || member.kind === SyntaxKind.PropertyDeclaration) { if (!member.symbol.isReferenced && hasModifier(member, ModifierFlags.Private)) { - error(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(member.symbol.escapedName)); + error(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(member.symbol)); } } else if (member.kind === SyntaxKind.Constructor) { for (const parameter of (member).parameters) { if (!parameter.symbol.isReferenced && hasModifier(parameter, ModifierFlags.Private)) { - error(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(parameter.symbol.escapedName)); + error(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, symbolName(parameter.symbol)); } } } @@ -20046,7 +20041,7 @@ namespace ts { } for (const typeParameter of node.typeParameters) { if (!getMergedSymbol(typeParameter.symbol).isReferenced && !isIdentifierThatStartsWithUnderScore(typeParameter.name)) { - error(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(typeParameter.symbol.escapedName)); + error(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, symbolName(typeParameter.symbol)); } } } @@ -20059,7 +20054,7 @@ namespace ts { if (!local.isReferenced && !local.exportSymbol) { for (const declaration of local.declarations) { if (!isAmbientModule(declaration)) { - errorUnusedLocal(declaration, unescapeLeadingUnderscores(local.escapedName)); + errorUnusedLocal(declaration, symbolName(local)); } } } @@ -22354,7 +22349,7 @@ namespace ts { const symbol = resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true); if (symbol && (symbol === undefinedSymbol || isGlobalSourceFile(getDeclarationContainer(symbol.declarations[0])))) { - error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, unescapeLeadingUnderscores(exportedName.escapedText)); + error(exportedName, Diagnostics.Cannot_export_0_Only_local_declarations_can_be_exported_from_a_module, idText(exportedName)); } else { markExportAsReferenced(node); @@ -25022,7 +25017,7 @@ namespace ts { function checkESModuleMarker(name: Identifier | BindingPattern): boolean { if (name.kind === SyntaxKind.Identifier) { - if (unescapeLeadingUnderscores(name.escapedText) === "__esModule") { + if (idText(name) === "__esModule") { return grammarErrorOnNode(name, Diagnostics.Identifier_expected_esModule_is_reserved_as_an_exported_marker_when_transforming_ECMAScript_modules); } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d458e7e5ef5..8083b3841c8 100755 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2766,7 +2766,7 @@ namespace ts { return generateName(node); } else if (isIdentifier(node) && (nodeIsSynthesized(node) || !node.parent)) { - return unescapeLeadingUnderscores(node.escapedText); + return idText(node); } else if (node.kind === SyntaxKind.StringLiteral && (node).textSourceNode) { return getTextOfNode((node).textSourceNode, includeTrivia); @@ -2986,7 +2986,7 @@ namespace ts { case GeneratedIdentifierKind.Loop: return makeTempVariableName(TempFlags._i); case GeneratedIdentifierKind.Unique: - return makeUniqueName(unescapeLeadingUnderscores(name.escapedText)); + return makeUniqueName(idText(name)); } Debug.fail("Unsupported GeneratedIdentifierKind."); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index 2ba2408225e..6dcf9fab067 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -126,7 +126,7 @@ namespace ts { export function updateIdentifier(node: Identifier, typeArguments: NodeArray | undefined): Identifier { return node.typeArguments !== typeArguments - ? updateNode(createIdentifier(unescapeLeadingUnderscores(node.escapedText), typeArguments), node) + ? updateNode(createIdentifier(idText(node), typeArguments), node) : node; } @@ -2951,12 +2951,12 @@ namespace ts { function createJsxFactoryExpressionFromEntityName(jsxFactory: EntityName, parent: JsxOpeningLikeElement): Expression { if (isQualifiedName(jsxFactory)) { const left = createJsxFactoryExpressionFromEntityName(jsxFactory.left, parent); - const right = createIdentifier(unescapeLeadingUnderscores(jsxFactory.right.escapedText)); + const right = createIdentifier(idText(jsxFactory.right)); right.escapedText = jsxFactory.right.escapedText; return createPropertyAccess(left, right); } else { - return createReactNamespace(unescapeLeadingUnderscores(jsxFactory.escapedText), parent); + return createReactNamespace(idText(jsxFactory), parent); } } diff --git a/src/compiler/transformers/destructuring.ts b/src/compiler/transformers/destructuring.ts index 0fea3e41c87..a283275d066 100644 --- a/src/compiler/transformers/destructuring.ts +++ b/src/compiler/transformers/destructuring.ts @@ -418,7 +418,7 @@ namespace ts { return createElementAccess(value, argumentExpression); } else { - const name = createIdentifier(unescapeLeadingUnderscores(propertyName.escapedText)); + const name = createIdentifier(idText(propertyName)); return createPropertyAccess(value, name); } } diff --git a/src/compiler/transformers/es2015.ts b/src/compiler/transformers/es2015.ts index e9c84be5176..9d10120471e 100644 --- a/src/compiler/transformers/es2015.ts +++ b/src/compiler/transformers/es2015.ts @@ -609,7 +609,7 @@ namespace ts { // - break/continue is non-labeled and located in non-converted loop/switch statement const jump = node.kind === SyntaxKind.BreakStatement ? Jump.Break : Jump.Continue; const canUseBreakOrContinue = - (node.label && convertedLoopState.labels && convertedLoopState.labels.get(unescapeLeadingUnderscores(node.label.escapedText))) || + (node.label && convertedLoopState.labels && convertedLoopState.labels.get(idText(node.label))) || (!node.label && (convertedLoopState.allowedNonLabeledJumps & jump)); if (!canUseBreakOrContinue) { @@ -628,11 +628,11 @@ namespace ts { else { if (node.kind === SyntaxKind.BreakStatement) { labelMarker = `break-${node.label.escapedText}`; - setLabeledJump(convertedLoopState, /*isBreak*/ true, unescapeLeadingUnderscores(node.label.escapedText), labelMarker); + setLabeledJump(convertedLoopState, /*isBreak*/ true, idText(node.label), labelMarker); } else { labelMarker = `continue-${node.label.escapedText}`; - setLabeledJump(convertedLoopState, /*isBreak*/ false, unescapeLeadingUnderscores(node.label.escapedText), labelMarker); + setLabeledJump(convertedLoopState, /*isBreak*/ false, idText(node.label), labelMarker); } } let returnExpression: Expression = createLiteral(labelMarker); @@ -2187,11 +2187,11 @@ namespace ts { } function recordLabel(node: LabeledStatement) { - convertedLoopState.labels.set(unescapeLeadingUnderscores(node.label.escapedText), true); + convertedLoopState.labels.set(idText(node.label), true); } function resetLabel(node: LabeledStatement) { - convertedLoopState.labels.set(unescapeLeadingUnderscores(node.label.escapedText), false); + convertedLoopState.labels.set(idText(node.label), false); } function visitLabeledStatement(node: LabeledStatement): VisitResult { @@ -3004,7 +3004,7 @@ namespace ts { else { loopParameters.push(createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotDotDotToken*/ undefined, name)); if (resolver.getNodeCheckFlags(decl) & NodeCheckFlags.NeedsLoopOutParameter) { - const outParamName = createUniqueName("out_" + unescapeLeadingUnderscores(name.escapedText)); + const outParamName = createUniqueName("out_" + idText(name)); loopOutParameters.push({ originalName: name, outParamName }); } } diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index 90c9063c140..d98f227e8c7 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -363,7 +363,7 @@ namespace ts { function substitutePropertyAccessExpression(node: PropertyAccessExpression) { if (node.expression.kind === SyntaxKind.SuperKeyword) { return createSuperAccessInAsyncMethod( - createLiteral(unescapeLeadingUnderscores(node.name.escapedText)), + createLiteral(idText(node.name)), node ); } diff --git a/src/compiler/transformers/es5.ts b/src/compiler/transformers/es5.ts index 290c9ae77c8..bc6ee13520f 100644 --- a/src/compiler/transformers/es5.ts +++ b/src/compiler/transformers/es5.ts @@ -111,7 +111,7 @@ namespace ts { * @param name An Identifier */ function trySubstituteReservedName(name: Identifier) { - const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(unescapeLeadingUnderscores(name.escapedText)) : undefined); + const token = name.originalKeywordKind || (nodeIsSynthesized(name) ? stringToToken(idText(name)) : undefined); if (token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord) { return setTextRange(createLiteral(name), name); } diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 0fca09b4540..97b555e01b7 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -790,7 +790,7 @@ namespace ts { function substitutePropertyAccessExpression(node: PropertyAccessExpression) { if (node.expression.kind === SyntaxKind.SuperKeyword) { return createSuperAccessInAsyncMethod( - createLiteral(unescapeLeadingUnderscores(node.name.escapedText)), + createLiteral(idText(node.name)), node ); } diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 06ecae3a63f..84ed997a70e 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -1634,7 +1634,7 @@ namespace ts { } function transformAndEmitContinueStatement(node: ContinueStatement): void { - const label = findContinueTarget(node.label ? unescapeLeadingUnderscores(node.label.escapedText) : undefined); + const label = findContinueTarget(node.label ? idText(node.label) : undefined); if (label > 0) { emitBreak(label, /*location*/ node); } @@ -1646,7 +1646,7 @@ namespace ts { function visitContinueStatement(node: ContinueStatement): Statement { if (inStatementContainingYield) { - const label = findContinueTarget(node.label && unescapeLeadingUnderscores(node.label.escapedText)); + const label = findContinueTarget(node.label && idText(node.label)); if (label > 0) { return createInlineBreak(label, /*location*/ node); } @@ -1656,7 +1656,7 @@ namespace ts { } function transformAndEmitBreakStatement(node: BreakStatement): void { - const label = findBreakTarget(node.label ? unescapeLeadingUnderscores(node.label.escapedText) : undefined); + const label = findBreakTarget(node.label ? idText(node.label) : undefined); if (label > 0) { emitBreak(label, /*location*/ node); } @@ -1668,7 +1668,7 @@ namespace ts { function visitBreakStatement(node: BreakStatement): Statement { if (inStatementContainingYield) { - const label = findBreakTarget(node.label && unescapeLeadingUnderscores(node.label.escapedText)); + const label = findBreakTarget(node.label && idText(node.label)); if (label > 0) { return createInlineBreak(label, /*location*/ node); } @@ -1847,7 +1847,7 @@ namespace ts { // /*body*/ // .endlabeled // .mark endLabel - beginLabeledBlock(unescapeLeadingUnderscores(node.label.escapedText)); + beginLabeledBlock(idText(node.label)); transformAndEmitEmbeddedStatement(node.statement); endLabeledBlock(); } @@ -1858,7 +1858,7 @@ namespace ts { function visitLabeledStatement(node: LabeledStatement) { if (inStatementContainingYield) { - beginScriptLabeledBlock(unescapeLeadingUnderscores(node.label.escapedText)); + beginScriptLabeledBlock(idText(node.label)); } node = visitEachChild(node, visitor, context); @@ -1959,7 +1959,7 @@ namespace ts { } function substituteExpressionIdentifier(node: Identifier) { - if (!isGeneratedIdentifier(node) && renamedCatchVariables && renamedCatchVariables.has(unescapeLeadingUnderscores(node.escapedText))) { + if (!isGeneratedIdentifier(node) && renamedCatchVariables && renamedCatchVariables.has(idText(node))) { const original = getOriginalNode(node); if (isIdentifier(original) && original.parent) { const declaration = resolver.getReferencedValueDeclaration(original); @@ -2128,7 +2128,7 @@ namespace ts { hoistVariableDeclaration(variable.name); } else { - const text = unescapeLeadingUnderscores((variable.name).escapedText); + const text = idText(variable.name); name = declareLocal(text); if (!renamedCatchVariables) { renamedCatchVariables = createMap(); diff --git a/src/compiler/transformers/jsx.ts b/src/compiler/transformers/jsx.ts index 0d00f4d48a3..bbe05afe878 100644 --- a/src/compiler/transformers/jsx.ts +++ b/src/compiler/transformers/jsx.ts @@ -253,7 +253,7 @@ namespace ts { else { const name = (node).tagName; if (isIdentifier(name) && isIntrinsicJsxName(name.escapedText)) { - return createLiteral(unescapeLeadingUnderscores(name.escapedText)); + return createLiteral(idText(name)); } else { return createExpressionFromEntityName(name); @@ -268,11 +268,12 @@ namespace ts { */ function getAttributeName(node: JsxAttribute): StringLiteral | Identifier { const name = node.name; - if (/^[A-Za-z_]\w*$/.test(unescapeLeadingUnderscores(name.escapedText))) { + const text = idText(name); + if (/^[A-Za-z_]\w*$/.test(text)) { return name; } else { - return createLiteral(unescapeLeadingUnderscores(name.escapedText)); + return createLiteral(text); } } diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 08c1fccfe16..145ac07c2f2 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -1231,7 +1231,7 @@ namespace ts { */ function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration): Statement[] | undefined { const name = getDeclarationName(decl); - const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(unescapeLeadingUnderscores(name.escapedText)); + const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(idText(name)); if (exportSpecifiers) { for (const exportSpecifier of exportSpecifiers) { statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index 8c47ec82f70..af77c499e8d 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -353,7 +353,7 @@ namespace ts { // write name of indirectly exported entry, i.e. 'export {x} from ...' exportedNames.push( createPropertyAssignment( - createLiteral(unescapeLeadingUnderscores((element.name || element.propertyName).escapedText)), + createLiteral(idText(element.name || element.propertyName)), createTrue() ) ); @@ -504,10 +504,10 @@ namespace ts { for (const e of (entry).exportClause.elements) { properties.push( createPropertyAssignment( - createLiteral(unescapeLeadingUnderscores(e.name.escapedText)), + createLiteral(idText(e.name)), createElementAccess( parameterName, - createLiteral(unescapeLeadingUnderscores((e.propertyName || e.name).escapedText)) + createLiteral(idText(e.propertyName || e.name)) ) ) ); @@ -1028,7 +1028,7 @@ namespace ts { let excludeName: string; if (exportSelf) { statements = appendExportStatement(statements, decl.name, getLocalName(decl)); - excludeName = unescapeLeadingUnderscores(decl.name.escapedText); + excludeName = idText(decl.name); } statements = appendExportsOfDeclaration(statements, decl, excludeName); @@ -1080,7 +1080,7 @@ namespace ts { } const name = getDeclarationName(decl); - const exportSpecifiers = moduleInfo.exportSpecifiers.get(unescapeLeadingUnderscores(name.escapedText)); + const exportSpecifiers = moduleInfo.exportSpecifiers.get(idText(name)); if (exportSpecifiers) { for (const exportSpecifier of exportSpecifiers) { if (exportSpecifier.name.escapedText !== excludeName) { diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 8640c642675..4feb3ca0c26 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -2038,7 +2038,7 @@ namespace ts { : (name).expression; } else if (isIdentifier(name)) { - return createLiteral(unescapeLeadingUnderscores(name.escapedText)); + return createLiteral(idText(name)); } else { return getSynthesizedClone(name); @@ -3240,7 +3240,7 @@ namespace ts { function getClassAliasIfNeeded(node: ClassDeclaration) { if (resolver.getNodeCheckFlags(node) & NodeCheckFlags.ClassWithConstructorReference) { enableSubstitutionForClassAliases(); - const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? unescapeLeadingUnderscores(node.name.escapedText) : "default"); + const classAlias = createUniqueName(node.name && !isGeneratedIdentifier(node.name) ? idText(node.name) : "default"); classAliases[getOriginalNodeId(node)] = classAlias; hoistVariableDeclaration(classAlias); return classAlias; diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index f641ee49faf..00a0753eaa1 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -58,9 +58,9 @@ namespace ts { else { // export { x, y } for (const specifier of (node).exportClause.elements) { - if (!uniqueExports.get(unescapeLeadingUnderscores(specifier.name.escapedText))) { + if (!uniqueExports.get(idText(specifier.name))) { const name = specifier.propertyName || specifier.name; - exportSpecifiers.add(unescapeLeadingUnderscores(name.escapedText), specifier); + exportSpecifiers.add(idText(name), specifier); const decl = resolver.getReferencedImportDeclaration(name) || resolver.getReferencedValueDeclaration(name); @@ -69,7 +69,7 @@ namespace ts { multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(decl), specifier.name); } - uniqueExports.set(unescapeLeadingUnderscores(specifier.name.escapedText), true); + uniqueExports.set(idText(specifier.name), true); exportedNames = append(exportedNames, specifier.name); } } @@ -103,9 +103,9 @@ namespace ts { else { // export function x() { } const name = (node).name; - if (!uniqueExports.get(unescapeLeadingUnderscores(name.escapedText))) { + if (!uniqueExports.get(idText(name))) { multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports.set(unescapeLeadingUnderscores(name.escapedText), true); + uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } } @@ -124,9 +124,9 @@ namespace ts { else { // export class x { } const name = (node).name; - if (name && !uniqueExports.get(unescapeLeadingUnderscores(name.escapedText))) { + if (name && !uniqueExports.get(idText(name))) { multiMapSparseArrayAdd(exportedBindings, getOriginalNodeId(node), name); - uniqueExports.set(unescapeLeadingUnderscores(name.escapedText), true); + uniqueExports.set(idText(name), true); exportedNames = append(exportedNames, name); } } @@ -158,8 +158,9 @@ namespace ts { } } else if (!isGeneratedIdentifier(decl.name)) { - if (!uniqueExports.get(unescapeLeadingUnderscores(decl.name.escapedText))) { - uniqueExports.set(unescapeLeadingUnderscores(decl.name.escapedText), true); + const text = idText(decl.name); + if (!uniqueExports.get(text)) { + uniqueExports.set(text, true); exportedNames = append(exportedNames, decl.name); } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index e7d973865fb..23888f0b6d5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -560,7 +560,7 @@ namespace ts { export function entityNameToString(name: EntityNameOrEntityNameExpression): string { switch (name.kind) { case SyntaxKind.Identifier: - return getFullWidth(name) === 0 ? unescapeLeadingUnderscores(name.escapedText) : getTextOfNode(name); + return getFullWidth(name) === 0 ? idText(name) : getTextOfNode(name); case SyntaxKind.QualifiedName: return entityNameToString(name.left) + "." + entityNameToString(name.right); case SyntaxKind.PropertyAccessExpression: @@ -1976,8 +1976,7 @@ namespace ts { if (name.kind === SyntaxKind.ComputedPropertyName) { const nameExpression = name.expression; if (isWellKnownSymbolSyntactically(nameExpression)) { - const rightHandSideName = (nameExpression).name.escapedText; - return getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores(rightHandSideName)); + return getPropertyNameForKnownSymbolName(idText((nameExpression).name)); } else if (nameExpression.kind === SyntaxKind.StringLiteral || nameExpression.kind === SyntaxKind.NumericLiteral) { return escapeLeadingUnderscores((nameExpression).text); @@ -1990,7 +1989,7 @@ namespace ts { export function getTextOfIdentifierOrLiteral(node: Identifier | LiteralLikeNode) { if (node) { if (node.kind === SyntaxKind.Identifier) { - return unescapeLeadingUnderscores((node as Identifier).escapedText); + return idText(node as Identifier); } if (node.kind === SyntaxKind.StringLiteral || node.kind === SyntaxKind.NumericLiteral) { @@ -3946,6 +3945,13 @@ namespace ts { return id.length >= 3 && id.charCodeAt(0) === CharacterCodes._ && id.charCodeAt(1) === CharacterCodes._ && id.charCodeAt(2) === CharacterCodes._ ? id.substr(1) : id; } + export function idText(identifier: Identifier): string { + return unescapeLeadingUnderscores(identifier.escapedText); + } + export function symbolName(symbol: Symbol): string { + return unescapeLeadingUnderscores(symbol.escapedName); + } + /** * Remove extra underscore from escaped identifier text content. * @deprecated Use `id.text` for the unescaped text. diff --git a/src/services/services.ts b/src/services/services.ts index 90f9acd82ea..0dfbd56f8d8 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -327,7 +327,7 @@ namespace ts { } get name(): string { - return unescapeLeadingUnderscores(this.escapedName); + return symbolName(this); } getEscapedName(): __String { @@ -383,7 +383,7 @@ namespace ts { } get text(): string { - return unescapeLeadingUnderscores(this.escapedText); + return idText(this); } } IdentifierObject.prototype.kind = SyntaxKind.Identifier; From f71930c4731b8930dd897f8325c7fec444ba015b Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 29 Sep 2017 11:08:45 -0700 Subject: [PATCH 45/56] Print list of available refactors when we can't find one (#18843) --- src/harness/fourslash.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index c2bab0a5a69..e84d8c60950 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2824,7 +2824,7 @@ Actual: ${stringify(fullActual)}`); const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range); const refactor = refactors.find(r => r.name === refactorName); if (!refactor) { - this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.`); + this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.\nAvailable refactors: ${refactors.map(r => r.name)}`); } const action = refactor.actions.find(a => a.name === actionName); From 936f98d9adca8530da7af3f7ccbb4ce1b27b6a93 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 29 Sep 2017 16:01:14 -0700 Subject: [PATCH 46/56] Addressing CR feedback --- src/compiler/types.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c34f0ad5499..01036862980 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3344,6 +3344,7 @@ namespace ts { typeArguments?: Type[]; // Type reference type arguments (undefined if none) } + /* @internal */ export const enum Variance { Invariant = 0, // Neither covariant nor contravariant Covariant = 1, // Covariant @@ -3356,6 +3357,7 @@ namespace ts { export interface GenericType extends InterfaceType, TypeReference { /* @internal */ instantiations: Map; // Generic instantiation cache + /* @internal */ variances?: Variance[]; // Variance of each type parameter } From d821bbf3f3e6f628f13baf5e485e20d3db9dcc84 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 08:36:09 -0700 Subject: [PATCH 47/56] Simplify RulesMap construction (#18858) --- src/services/formatting/rulesMap.ts | 34 ++++++------------------ src/services/formatting/rulesProvider.ts | 2 +- 2 files changed, 9 insertions(+), 27 deletions(-) diff --git a/src/services/formatting/rulesMap.ts b/src/services/formatting/rulesMap.ts index 5b4eacd2c2a..d1f6e4724f7 100644 --- a/src/services/formatting/rulesMap.ts +++ b/src/services/formatting/rulesMap.ts @@ -6,38 +6,20 @@ namespace ts.formatting { public map: RulesBucket[]; public mapRowLength: number; - constructor() { - this.map = []; - this.mapRowLength = 0; - } - - static create(rules: Rule[]): RulesMap { - const result = new RulesMap(); - result.Initialize(rules); - return result; - } - - public Initialize(rules: Rule[]) { + constructor(rules: ReadonlyArray) { this.mapRowLength = SyntaxKind.LastToken + 1; - this.map = new Array(this.mapRowLength * this.mapRowLength); // new Array(this.mapRowLength * this.mapRowLength); + this.map = new Array(this.mapRowLength * this.mapRowLength); // This array is used only during construction of the rulesbucket in the map - const rulesBucketConstructionStateList: RulesBucketConstructionState[] = new Array(this.map.length); // new Array(this.map.length); - - this.FillRules(rules, rulesBucketConstructionStateList); - return this.map; - } - - public FillRules(rules: Rule[], rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { - rules.forEach((rule) => { + const rulesBucketConstructionStateList: RulesBucketConstructionState[] = new Array(this.map.length); + for (const rule of rules) { this.FillRule(rule, rulesBucketConstructionStateList); - }); + } } private GetRuleBucketIndex(row: number, column: number): number { Debug.assert(row <= SyntaxKind.LastKeyword && column <= SyntaxKind.LastKeyword, "Must compute formatting context from tokens"); - const rulesBucketIndex = (row * this.mapRowLength) + column; - return rulesBucketIndex; + return (row * this.mapRowLength) + column; } private FillRule(rule: Rule, rulesBucketConstructionStateList: RulesBucketConstructionState[]): void { @@ -57,7 +39,7 @@ namespace ts.formatting { }); } - public GetRule(context: FormattingContext): Rule { + public GetRule(context: FormattingContext): Rule | undefined { const bucketIndex = this.GetRuleBucketIndex(context.currentTokenSpan.kind, context.nextTokenSpan.kind); const bucket = this.map[bucketIndex]; if (bucket) { @@ -74,7 +56,7 @@ namespace ts.formatting { const MaskBitSize = 5; const Mask = 0x1f; - export enum RulesPosition { + enum RulesPosition { IgnoreRulesSpecific = 0, IgnoreRulesAny = MaskBitSize * 1, ContextRulesSpecific = MaskBitSize * 2, diff --git a/src/services/formatting/rulesProvider.ts b/src/services/formatting/rulesProvider.ts index 1dd7acbdc64..fcf08541890 100644 --- a/src/services/formatting/rulesProvider.ts +++ b/src/services/formatting/rulesProvider.ts @@ -10,7 +10,7 @@ namespace ts.formatting { constructor() { this.globalRules = new Rules(); const activeRules = this.globalRules.HighPriorityCommonRules.concat(this.globalRules.UserConfigurableRules).concat(this.globalRules.LowPriorityCommonRules); - this.rulesMap = RulesMap.create(activeRules); + this.rulesMap = new RulesMap(activeRules); } public getRulesMap() { From b883fcbfddcbaab6764166fc6abd3c72f11ba577 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 08:36:26 -0700 Subject: [PATCH 48/56] Enable "space-within-parens" lint rule (#18856) --- src/compiler/transformers/esnext.ts | 2 +- src/harness/unittests/cachingInServerLSHost.ts | 2 +- src/lib/es2015.core.d.ts | 2 +- tslint.json | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 97b555e01b7..5b4c12b4f23 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -378,7 +378,7 @@ namespace ts { const catchVariable = getGeneratedNameForNode(errorRecord); const returnMethod = createTempVariable(/*recordTempVariable*/ undefined); const callValues = createAsyncValuesHelper(context, expression, /*location*/ node.expression); - const callNext = createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, []); + const callNext = createCall(createPropertyAccess(iterator, "next"), /*typeArguments*/ undefined, []); const getDone = createPropertyAccess(result, "done"); const getValue = createPropertyAccess(result, "value"); const callReturn = createFunctionCall(returnMethod, iterator, []); diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 7c832b210b7..489c3c5fdd3 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -64,7 +64,7 @@ namespace ts { const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, /*containingProject*/ undefined); const project = projectService.createInferredProjectWithRootFileIfNecessary(rootScriptInfo); - project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } ); + project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true }); return { project, rootScriptInfo diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index e7c0d479711..5c2438d9052 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -162,7 +162,7 @@ interface Math { * If any argument is NaN, the result is NaN. * If all arguments are either +0 or −0, the result is +0. */ - hypot(...values: number[] ): number; + hypot(...values: number[]): number; /** * Returns the integral part of the a numeric expression, x, removing any fractional digits. diff --git a/tslint.json b/tslint.json index 7e1830dc736..d5a182ff0cc 100644 --- a/tslint.json +++ b/tslint.json @@ -41,6 +41,7 @@ "avoid-escape" ], "semicolon": [true, "always", "ignore-bound-class-methods"], + "space-within-parens": true, "triple-equals": true, "type-operator-spacing": true, "typedef-whitespace": [ From d11172c86e3534f6c6bfc6bf2c7f57dc5f51f050 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 08:37:02 -0700 Subject: [PATCH 49/56] Use idText in a few more places (#18842) --- src/compiler/checker.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f6aeec2f704..bff7d8d4189 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19792,7 +19792,7 @@ namespace ts { if (!getParameterSymbolFromJSDoc(node)) { error(node.name, Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name, - unescapeLeadingUnderscores((node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name).escapedText)); + idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name)); } } @@ -19808,9 +19808,7 @@ namespace ts { if (extend) { const className = getIdentifierFromEntityNameExpression(extend.expression); if (className && name.escapedText !== className.escapedText) { - error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, - unescapeLeadingUnderscores(name.escapedText), - unescapeLeadingUnderscores(className.escapedText)); + error(name, Diagnostics.JSDoc_augments_0_does_not_match_the_extends_1_clause, idText(name), idText(className)); } } } From a075ba98289ba293545451145a27a872c1aa672c Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 10:23:37 -0700 Subject: [PATCH 50/56] getSuggestionForNonexistentSymbol: Add comment (#18885) --- src/compiler/checker.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bff7d8d4189..9c65bc84c18 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14866,6 +14866,7 @@ namespace ts { function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): string { const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ false, (symbols, name, meaning) => { + // `name` from the callback === the outer `name` const symbol = getSymbol(symbols, name, meaning); // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function // So the table *contains* `x` but `x` isn't actually in scope. From e6980722a6fdab29393663055cb2c87ed39b415b Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 10:33:53 -0700 Subject: [PATCH 51/56] Fix bug: Report errors on `extends` expression in JS even if an `@augments` tag is present (#18854) --- src/compiler/checker.ts | 6 ++++++ .../jsdocAugments_errorInExtendsExpression.errors.txt | 10 ++++++++++ .../jsdocAugments_errorInExtendsExpression.symbols | 8 ++++++++ .../jsdocAugments_errorInExtendsExpression.types | 10 ++++++++++ .../compiler/jsdocAugments_errorInExtendsExpression.ts | 8 ++++++++ 5 files changed, 42 insertions(+) create mode 100644 tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt create mode 100644 tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols create mode 100644 tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types create mode 100644 tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9c65bc84c18..300928f229d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4917,6 +4917,8 @@ namespace ts { */ function getBaseConstructorTypeOfClass(type: InterfaceType): Type { if (!type.resolvedBaseConstructorType) { + const decl = type.symbol.valueDeclaration; + const extended = getClassExtendsHeritageClauseElement(decl); const baseTypeNode = getBaseTypeNodeOfClass(type); if (!baseTypeNode) { return type.resolvedBaseConstructorType = undefinedType; @@ -4925,6 +4927,10 @@ namespace ts { return unknownType; } const baseConstructorType = checkExpression(baseTypeNode.expression); + if (extended && baseTypeNode !== extended) { + Debug.assert(!extended.typeArguments); // Because this is in a JS file, and baseTypeNode is in an @extends tag + checkExpression(extended.expression); + } if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection)) { // Resolving the members of a class requires us to resolve the base class of that class. // We force resolution here such that we catch circularities now. diff --git a/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt new file mode 100644 index 00000000000..11a7311fc30 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.errors.txt @@ -0,0 +1,10 @@ +/a.js(3,17): error TS2304: Cannot find name 'err'. + + +==== /a.js (1 errors) ==== + class A {} + /** @augments A */ + class B extends err() {} + ~~~ +!!! error TS2304: Cannot find name 'err'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols new file mode 100644 index 00000000000..d78feca9143 --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.symbols @@ -0,0 +1,8 @@ +=== /a.js === +class A {} +>A : Symbol(A, Decl(a.js, 0, 0)) + +/** @augments A */ +class B extends err() {} +>B : Symbol(B, Decl(a.js, 0, 10)) + diff --git a/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types new file mode 100644 index 00000000000..1a0fa09d3cd --- /dev/null +++ b/tests/baselines/reference/jsdocAugments_errorInExtendsExpression.types @@ -0,0 +1,10 @@ +=== /a.js === +class A {} +>A : A + +/** @augments A */ +class B extends err() {} +>B : B +>err() : A +>err : any + diff --git a/tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts b/tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts new file mode 100644 index 00000000000..2fba59d4461 --- /dev/null +++ b/tests/cases/compiler/jsdocAugments_errorInExtendsExpression.ts @@ -0,0 +1,8 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @Filename: /a.js +class A {} +/** @augments A */ +class B extends err() {} From 637ed57451ced2b4e01a41334d6525aeb0f95f63 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 10:39:03 -0700 Subject: [PATCH 52/56] Fix crash when extending non-EntityNameExpression (#18853) --- src/compiler/checker.ts | 6 +++-- .../classExtendsInterface_not.errors.txt | 8 +++++++ .../reference/classExtendsInterface_not.js | 22 +++++++++++++++++++ .../classExtendsInterface_not.symbols | 4 ++++ .../reference/classExtendsInterface_not.types | 7 ++++++ .../compiler/classExtendsInterface_not.ts | 1 + 6 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/classExtendsInterface_not.errors.txt create mode 100644 tests/baselines/reference/classExtendsInterface_not.js create mode 100644 tests/baselines/reference/classExtendsInterface_not.symbols create mode 100644 tests/baselines/reference/classExtendsInterface_not.types create mode 100644 tests/cases/compiler/classExtendsInterface_not.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 300928f229d..d405784ed60 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1272,8 +1272,10 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; case SyntaxKind.ExpressionWithTypeArguments: - Debug.assert(isEntityNameExpression((node).expression)); - return (node).expression; + if (isEntityNameExpression((node).expression)) { + return (node).expression; + } + // falls through default: return undefined; } diff --git a/tests/baselines/reference/classExtendsInterface_not.errors.txt b/tests/baselines/reference/classExtendsInterface_not.errors.txt new file mode 100644 index 00000000000..6a4b250cb3e --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.errors.txt @@ -0,0 +1,8 @@ +tests/cases/compiler/classExtendsInterface_not.ts(1,20): error TS2339: Property 'bogus' does not exist on type '""'. + + +==== tests/cases/compiler/classExtendsInterface_not.ts (1 errors) ==== + class C extends "".bogus {} + ~~~~~ +!!! error TS2339: Property 'bogus' does not exist on type '""'. + \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsInterface_not.js b/tests/baselines/reference/classExtendsInterface_not.js new file mode 100644 index 00000000000..3bbd6c9ffba --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.js @@ -0,0 +1,22 @@ +//// [classExtendsInterface_not.ts] +class C extends "".bogus {} + + +//// [classExtendsInterface_not.js] +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 __()); + }; +})(); +var C = /** @class */ (function (_super) { + __extends(C, _super); + function C() { + return _super !== null && _super.apply(this, arguments) || this; + } + return C; +}("".bogus)); diff --git a/tests/baselines/reference/classExtendsInterface_not.symbols b/tests/baselines/reference/classExtendsInterface_not.symbols new file mode 100644 index 00000000000..de4af3a5c16 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.symbols @@ -0,0 +1,4 @@ +=== tests/cases/compiler/classExtendsInterface_not.ts === +class C extends "".bogus {} +>C : Symbol(C, Decl(classExtendsInterface_not.ts, 0, 0)) + diff --git a/tests/baselines/reference/classExtendsInterface_not.types b/tests/baselines/reference/classExtendsInterface_not.types new file mode 100644 index 00000000000..cd1d6272476 --- /dev/null +++ b/tests/baselines/reference/classExtendsInterface_not.types @@ -0,0 +1,7 @@ +=== tests/cases/compiler/classExtendsInterface_not.ts === +class C extends "".bogus {} +>C : C +>"".bogus : any +>"" : "" +>bogus : any + diff --git a/tests/cases/compiler/classExtendsInterface_not.ts b/tests/cases/compiler/classExtendsInterface_not.ts new file mode 100644 index 00000000000..25a93e787e6 --- /dev/null +++ b/tests/cases/compiler/classExtendsInterface_not.ts @@ -0,0 +1 @@ +class C extends "".bogus {} From bf75a3f4ac391bd4c928b84d5a660c89151dfc5e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 Oct 2017 12:54:04 -0700 Subject: [PATCH 53/56] Emit .d.ts file in test --- .../baselines/reference/strictFunctionTypes1.js | 17 +++++++++++++++++ tests/cases/compiler/strictFunctionTypes1.ts | 1 + 2 files changed, 18 insertions(+) diff --git a/tests/baselines/reference/strictFunctionTypes1.js b/tests/baselines/reference/strictFunctionTypes1.js index 28a7a7f61d6..e802c443561 100644 --- a/tests/baselines/reference/strictFunctionTypes1.js +++ b/tests/baselines/reference/strictFunctionTypes1.js @@ -23,3 +23,20 @@ var x1 = f1(fo, fs); // (x: string) => void var x2 = f2("abc", fo, fs); // "abc" var x3 = f3("abc", fo, fx); // "abc" | "def" var x4 = f4(fo, fs); // Func + + +//// [strictFunctionTypes1.d.ts] +declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; +declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; +declare function f3(obj: T, f1: (x: T) => void, f2: (f: (x: T) => void) => void): T; +interface Func { + (x: T): void; +} +declare function f4(f1: Func, f2: Func): Func; +declare function fo(x: Object): void; +declare function fs(x: string): void; +declare function fx(f: (x: "def") => void): void; +declare const x1: (x: string) => void; +declare const x2 = "abc"; +declare const x3: string; +declare const x4: Func; diff --git a/tests/cases/compiler/strictFunctionTypes1.ts b/tests/cases/compiler/strictFunctionTypes1.ts index 7d8ffc689ff..6c07bc51ffe 100644 --- a/tests/cases/compiler/strictFunctionTypes1.ts +++ b/tests/cases/compiler/strictFunctionTypes1.ts @@ -1,4 +1,5 @@ // @strict: true +// @declaration: true declare function f1(f1: (x: T) => void, f2: (x: T) => void): (x: T) => void; declare function f2(obj: T, f1: (x: T) => void, f2: (x: T) => void): T; From eefe5c970602a6bf90c92046e7423c8e99cbff27 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 2 Oct 2017 13:26:35 -0700 Subject: [PATCH 54/56] Fix acquiring format options for getEditsForRefactor (#18848) * Fix acquiring format options for getEditsForRefactor * Add test * Fix test description * Use `executeCommandSeq` --- .../unittests/tsserverProjectSystem.ts | 56 +++++++++++++++++++ src/server/client.ts | 3 +- src/server/protocol.ts | 1 - src/server/session.ts | 2 +- .../convertFunctionToEs6Class-server.ts | 17 +++--- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 651dbae7142..dcefa14bcb8 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -4245,4 +4245,60 @@ namespace ts.projectSystem { } }); }); + + describe("refactors", () => { + it("use formatting options", () => { + const file = { + path: "/a.ts", + content: "function f() {\n 1;\n}", + }; + const host = createServerHost([file]); + const session = createSession(host); + openFilesForSession([file], session); + + const response0 = session.executeCommandSeq({ + command: server.protocol.CommandTypes.Configure, + arguments: { + formatOptions: { + indentSize: 2, + }, + }, + }).response; + assert.deepEqual(response0, /*expected*/ undefined); + + const response1 = session.executeCommandSeq({ + command: server.protocol.CommandTypes.GetEditsForRefactor, + arguments: { + refactor: "Extract Symbol", + action: "function_scope_1", + file: "/a.ts", + startLine: 2, + startOffset: 3, + endLine: 2, + endOffset: 4, + }, + }).response; + assert.deepEqual(response1, { + edits: [ + { + fileName: "/a.ts", + textChanges: [ + { + start: { line: 2, offset: 1 }, + end: { line: 3, offset: 1 }, + newText: " newFunction();\n", + }, + { + start: { line: 3, offset: 2 }, + end: { line: 3, offset: 2 }, + newText: "\nfunction newFunction() {\n 1;\n}\n", + }, + ] + } + ], + renameFilename: "/a.ts", + renameLocation: { line: 2, offset: 3 }, + }); + }); + }); } diff --git a/src/server/client.ts b/src/server/client.ts index 9e472a83e2b..0ffac42dae4 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -573,7 +573,7 @@ namespace ts.server { getEditsForRefactor( fileName: string, - formatOptions: FormatCodeSettings, + _formatOptions: FormatCodeSettings, positionOrRange: number | TextRange, refactorName: string, actionName: string): RefactorEditInfo { @@ -581,7 +581,6 @@ namespace ts.server { const args = this.createFileLocationOrRangeRequestArgs(positionOrRange, fileName) as protocol.GetEditsForRefactorRequestArgs; args.refactor = refactorName; args.action = actionName; - args.formatOptions = formatOptions; const request = this.processRequest(CommandNames.GetEditsForRefactor, args); const response = this.processResponse(request); diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 44862c738cc..3fdbd8fd7f7 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -494,7 +494,6 @@ namespace ts.server.protocol { refactor: string; /* The 'name' property from the refactoring action */ action: string; - formatOptions?: FormatCodeSettings, }; diff --git a/src/server/session.ts b/src/server/session.ts index 02eb76eeae8..5d71d1b11f9 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1488,7 +1488,7 @@ namespace ts.server { const result = project.getLanguageService().getEditsForRefactor( file, - args.formatOptions ? convertFormatOptions(args.formatOptions) : this.projectService.getFormatCodeOptions(), + this.projectService.getFormatCodeOptions(file), position || textRange, args.refactor, args.action diff --git a/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts b/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts index 437bf6dadf4..e7da28a2770 100644 --- a/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts +++ b/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts @@ -14,13 +14,14 @@ verify.applicableRefactorAvailableAtMarker('1'); // NOTE: '// Comment' should be included, but due to incorrect handling of trivia, // it's omitted right now. +// TODO: GH#18445 verify.fileAfterApplyingRefactorAtMarker('1', -`class fn { - constructor() { - this.baz = 10; - } - bar() { - console.log('hello world'); - } -} +`class fn {\r + constructor() {\r + this.baz = 10;\r + }\r + bar() {\r + console.log('hello world');\r + }\r +}\r `, 'Convert to ES2015 class', 'convert'); From bff843a9c9def253257a0290cb590d4e89897e25 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 Oct 2017 13:39:55 -0700 Subject: [PATCH 55/56] Improve error elaboration for invariant generic types --- src/compiler/checker.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index de1b28f6a56..92abee8b9d1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9415,7 +9415,16 @@ namespace ts { // with a structural comparison. Otherwise, we know for certain the instantiations aren't // related and we can return here. if (variances !== emptyArray && !hasCovariantVoidArgument(target, variances)) { - return Ternary.False; + // In some cases generic types that are covariant in regular type checking mode become + // invariant in --strictFunctionTypes mode because one or more type parameters are used in + // both co- and contravariant positions. In order to make it easier to diagnose *why* such + // types are invariant, if any of the type parameters are invariant we reset the reported + // errors and instead force a structural comparison (which will include elaborations that + // reveal the reason). + if (!(reportErrors && some(variances, v => v === Variance.Invariant))) { + return Ternary.False; + } + errorInfo = saveErrorInfo; } } // Even if relationship doesn't hold for unions, intersections, or generic type references, From c2344e07a4d02d5cc453c6e7cc5eda6d0d373de6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 2 Oct 2017 13:40:26 -0700 Subject: [PATCH 56/56] Add error elaboration test --- .../strictFunctionTypesErrors.errors.txt | 35 ++++++++++++++++- .../reference/strictFunctionTypesErrors.js | 18 +++++++++ .../strictFunctionTypesErrors.symbols | 36 ++++++++++++++++++ .../reference/strictFunctionTypesErrors.types | 38 +++++++++++++++++++ .../compiler/strictFunctionTypesErrors.ts | 15 ++++++++ 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/strictFunctionTypesErrors.errors.txt b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt index eb0c2d00233..2827983bd5a 100644 --- a/tests/baselines/reference/strictFunctionTypesErrors.errors.txt +++ b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt @@ -79,9 +79,17 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(84,1): error TS2322: Type 'Fun tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Comparer2' is not assignable to type 'Comparer2'. Type 'Animal' is not assignable to type 'Dog'. Property 'dog' is missing in type 'Animal'. +tests/cases/compiler/strictFunctionTypesErrors.ts(126,1): error TS2322: Type 'Crate' is not assignable to type 'Crate'. + Types of property 'onSetItem' are incompatible. + Type '(item: Dog) => void' is not assignable to type '(item: Animal) => void'. + Types of parameters 'item' and 'item' are incompatible. + Type 'Animal' is not assignable to type 'Dog'. +tests/cases/compiler/strictFunctionTypesErrors.ts(127,1): error TS2322: Type 'Crate' is not assignable to type 'Crate'. + Types of property 'item' are incompatible. + Type 'Animal' is not assignable to type 'Dog'. -==== tests/cases/compiler/strictFunctionTypesErrors.ts (29 errors) ==== +==== tests/cases/compiler/strictFunctionTypesErrors.ts (31 errors) ==== export {} @@ -304,4 +312,29 @@ tests/cases/compiler/strictFunctionTypesErrors.ts(111,1): error TS2322: Type 'Co !!! error TS2322: Type 'Animal' is not assignable to type 'Dog'. !!! error TS2322: Property 'dog' is missing in type 'Animal'. dogComparer2 = animalComparer2; // Ok + + // Crate is invariant in --strictFunctionTypes mode + + interface Crate { + item: T; + onSetItem: (item: T) => void; + } + + declare let animalCrate: Crate; + declare let dogCrate: Crate; + + // Errors below should elaborate the reason for invariance + + animalCrate = dogCrate; // Error + ~~~~~~~~~~~ +!!! error TS2322: Type 'Crate' is not assignable to type 'Crate'. +!!! error TS2322: Types of property 'onSetItem' are incompatible. +!!! error TS2322: Type '(item: Dog) => void' is not assignable to type '(item: Animal) => void'. +!!! error TS2322: Types of parameters 'item' and 'item' are incompatible. +!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'. + dogCrate = animalCrate; // Error + ~~~~~~~~ +!!! error TS2322: Type 'Crate' is not assignable to type 'Crate'. +!!! error TS2322: Types of property 'item' are incompatible. +!!! error TS2322: Type 'Animal' is not assignable to type 'Dog'. \ No newline at end of file diff --git a/tests/baselines/reference/strictFunctionTypesErrors.js b/tests/baselines/reference/strictFunctionTypesErrors.js index 0049fe970ce..2be598f0ef9 100644 --- a/tests/baselines/reference/strictFunctionTypesErrors.js +++ b/tests/baselines/reference/strictFunctionTypesErrors.js @@ -111,6 +111,21 @@ declare let dogComparer2: Comparer2; animalComparer2 = dogComparer2; // Error dogComparer2 = animalComparer2; // Ok + +// Crate is invariant in --strictFunctionTypes mode + +interface Crate { + item: T; + onSetItem: (item: T) => void; +} + +declare let animalCrate: Crate; +declare let dogCrate: Crate; + +// Errors below should elaborate the reason for invariance + +animalCrate = dogCrate; // Error +dogCrate = animalCrate; // Error //// [strictFunctionTypesErrors.js] @@ -168,3 +183,6 @@ animalComparer1 = dogComparer1; // Ok dogComparer1 = animalComparer1; // Ok animalComparer2 = dogComparer2; // Error dogComparer2 = animalComparer2; // Ok +// Errors below should elaborate the reason for invariance +animalCrate = dogCrate; // Error +dogCrate = animalCrate; // Error diff --git a/tests/baselines/reference/strictFunctionTypesErrors.symbols b/tests/baselines/reference/strictFunctionTypesErrors.symbols index ce3a81f524f..30faf83d87e 100644 --- a/tests/baselines/reference/strictFunctionTypesErrors.symbols +++ b/tests/baselines/reference/strictFunctionTypesErrors.symbols @@ -364,3 +364,39 @@ dogComparer2 = animalComparer2; // Ok >dogComparer2 : Symbol(dogComparer2, Decl(strictFunctionTypesErrors.ts, 108, 11)) >animalComparer2 : Symbol(animalComparer2, Decl(strictFunctionTypesErrors.ts, 107, 11)) +// Crate is invariant in --strictFunctionTypes mode + +interface Crate { +>Crate : Symbol(Crate, Decl(strictFunctionTypesErrors.ts, 111, 31)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 115, 16)) + + item: T; +>item : Symbol(Crate.item, Decl(strictFunctionTypesErrors.ts, 115, 20)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 115, 16)) + + onSetItem: (item: T) => void; +>onSetItem : Symbol(Crate.onSetItem, Decl(strictFunctionTypesErrors.ts, 116, 12)) +>item : Symbol(item, Decl(strictFunctionTypesErrors.ts, 117, 16)) +>T : Symbol(T, Decl(strictFunctionTypesErrors.ts, 115, 16)) +} + +declare let animalCrate: Crate; +>animalCrate : Symbol(animalCrate, Decl(strictFunctionTypesErrors.ts, 120, 11)) +>Crate : Symbol(Crate, Decl(strictFunctionTypesErrors.ts, 111, 31)) +>Animal : Symbol(Animal, Decl(strictFunctionTypesErrors.ts, 87, 8)) + +declare let dogCrate: Crate; +>dogCrate : Symbol(dogCrate, Decl(strictFunctionTypesErrors.ts, 121, 11)) +>Crate : Symbol(Crate, Decl(strictFunctionTypesErrors.ts, 111, 31)) +>Dog : Symbol(Dog, Decl(strictFunctionTypesErrors.ts, 89, 33)) + +// Errors below should elaborate the reason for invariance + +animalCrate = dogCrate; // Error +>animalCrate : Symbol(animalCrate, Decl(strictFunctionTypesErrors.ts, 120, 11)) +>dogCrate : Symbol(dogCrate, Decl(strictFunctionTypesErrors.ts, 121, 11)) + +dogCrate = animalCrate; // Error +>dogCrate : Symbol(dogCrate, Decl(strictFunctionTypesErrors.ts, 121, 11)) +>animalCrate : Symbol(animalCrate, Decl(strictFunctionTypesErrors.ts, 120, 11)) + diff --git a/tests/baselines/reference/strictFunctionTypesErrors.types b/tests/baselines/reference/strictFunctionTypesErrors.types index f2d3b52a5b9..e4372d4b8fa 100644 --- a/tests/baselines/reference/strictFunctionTypesErrors.types +++ b/tests/baselines/reference/strictFunctionTypesErrors.types @@ -416,3 +416,41 @@ dogComparer2 = animalComparer2; // Ok >dogComparer2 : Comparer2 >animalComparer2 : Comparer2 +// Crate is invariant in --strictFunctionTypes mode + +interface Crate { +>Crate : Crate +>T : T + + item: T; +>item : T +>T : T + + onSetItem: (item: T) => void; +>onSetItem : (item: T) => void +>item : T +>T : T +} + +declare let animalCrate: Crate; +>animalCrate : Crate +>Crate : Crate +>Animal : Animal + +declare let dogCrate: Crate; +>dogCrate : Crate +>Crate : Crate +>Dog : Dog + +// Errors below should elaborate the reason for invariance + +animalCrate = dogCrate; // Error +>animalCrate = dogCrate : Crate +>animalCrate : Crate +>dogCrate : Crate + +dogCrate = animalCrate; // Error +>dogCrate = animalCrate : Crate +>dogCrate : Crate +>animalCrate : Crate + diff --git a/tests/cases/compiler/strictFunctionTypesErrors.ts b/tests/cases/compiler/strictFunctionTypesErrors.ts index 32029364d76..fbf1c0fa6da 100644 --- a/tests/cases/compiler/strictFunctionTypesErrors.ts +++ b/tests/cases/compiler/strictFunctionTypesErrors.ts @@ -111,3 +111,18 @@ declare let dogComparer2: Comparer2; animalComparer2 = dogComparer2; // Error dogComparer2 = animalComparer2; // Ok + +// Crate is invariant in --strictFunctionTypes mode + +interface Crate { + item: T; + onSetItem: (item: T) => void; +} + +declare let animalCrate: Crate; +declare let dogCrate: Crate; + +// Errors below should elaborate the reason for invariance + +animalCrate = dogCrate; // Error +dogCrate = animalCrate; // Error