diff --git a/Jakefile.js b/Jakefile.js index 7b4991b74b6..8a4c67ac84b 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -138,7 +138,10 @@ var harnessSources = harnessCoreSources.concat([ "projectErrors.ts", "matchFiles.ts", "initializeTSConfig.ts", - "extractMethods.ts", + "extractConstants.ts", + "extractFunctions.ts", + "extractRanges.ts", + "extractTestHelpers.ts", "printer.ts", "textChanges.ts", "telemetry.ts", 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 d89247b70f9..e3ba62bf3be 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; @@ -227,8 +228,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); @@ -281,6 +282,11 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const circularConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); + 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); const resolvingSignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false); @@ -1144,11 +1150,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) { @@ -1272,8 +1278,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; } @@ -2508,7 +2516,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); } @@ -2904,7 +2912,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 +3126,7 @@ namespace ts { return `"${escapeString(stringValue, CharacterCodes.doubleQuote)}"`; } } - return unescapeLeadingUnderscores(symbol.escapedName); + return symbolName(symbol); } function getSymbolDisplayBuilder(): SymbolDisplayBuilder { @@ -3577,7 +3585,7 @@ namespace ts { continue; } if (getDeclarationModifierFlagsFromSymbol(p) & (ModifierFlags.Private | ModifierFlags.Protected)) { - writer.reportPrivateInBaseOfClassExpression(unescapeLeadingUnderscores(p.escapedName)); + writer.reportPrivateInBaseOfClassExpression(symbolName(p)); } } const t = getTypeOfSymbol(p); @@ -4882,7 +4890,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[] { @@ -4908,6 +4925,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; @@ -4916,6 +4935,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. @@ -4986,15 +5009,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 +5017,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; } @@ -5252,6 +5266,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 +5288,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. @@ -6712,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 { @@ -6872,17 +6890,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 +6926,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 { @@ -7060,11 +7077,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; @@ -7577,7 +7594,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) { @@ -7616,7 +7633,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); @@ -8525,6 +8542,9 @@ namespace ts { source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes); } + 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); @@ -8532,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) { @@ -8566,12 +8586,12 @@ 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, - 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; } @@ -8725,7 +8745,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); @@ -9074,7 +9094,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, @@ -9178,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) { @@ -9187,11 +9207,45 @@ 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; + // 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 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; } @@ -9324,8 +9378,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)) { @@ -9354,11 +9406,34 @@ 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)) { + 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 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; } + // 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)) { + // 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, // it may hold in a structural comparison. @@ -9769,6 +9844,69 @@ namespace ts { } } + // 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 + // 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; + } + 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) { + // 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); + 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); + } + } + type.variances = variances; + } + 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); } @@ -10044,6 +10182,11 @@ 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); + } + function isArrayType(type: Type): boolean { return getObjectFlags(type) & ObjectFlags.Reference && (type).target === globalArrayType; } @@ -10285,7 +10428,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; } @@ -10570,8 +10713,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 = getVariances((source).target); for (let i = 0; i < count; i++) { - inferFromTypes(sourceTypes[i], targetTypes[i]); + if (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) { @@ -10642,6 +10791,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) { @@ -10719,7 +10879,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); @@ -10792,11 +10952,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 = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.ReturnType ? - getUnionType(baseCandidates, /*subtypeReduction*/ true) : getCommonSupertype(baseCandidates); - inferredType = getWidenedType(unionOrSuperType); + // 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(unwidenedType); } else if (context.flags & InferenceFlags.NoDefault) { // We use silentNeverType as the wildcard that signals no inferences. @@ -10889,7 +11051,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; @@ -10906,7 +11068,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: @@ -14002,7 +14164,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 { @@ -14257,8 +14419,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); } @@ -14266,7 +14428,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; @@ -14560,7 +14722,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; } @@ -14573,7 +14735,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; } @@ -14759,7 +14921,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; } } @@ -14785,13 +14947,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)); } } @@ -14848,7 +15010,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)); @@ -14856,25 +15018,21 @@ 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) => { + // `name` from the callback === the outer `name` 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); } /** @@ -14904,7 +15062,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) { @@ -15712,7 +15870,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); @@ -18590,7 +18748,7 @@ namespace ts { memberName = member.name.text; break; case SyntaxKind.Identifier: - memberName = unescapeLeadingUnderscores(member.name.escapedText); + memberName = idText(member.name); break; default: continue; @@ -18846,7 +19004,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); } @@ -19569,7 +19727,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; } @@ -19789,6 +19947,45 @@ 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, + idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name)); + } + } + + 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, idText(name), idText(className)); + } + } + } + + 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); @@ -19927,11 +20124,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))); } } }); @@ -19966,7 +20163,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 { @@ -19975,13 +20172,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)); } } } @@ -20002,7 +20199,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)); } } } @@ -20015,7 +20212,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)); } } } @@ -22310,7 +22507,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); @@ -22483,10 +22680,12 @@ 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: - return checkSourceElement((node as JSDocParameterTag).typeExpression); + return checkJSDocParameterTag(node as JSDocParameterTag); case SyntaxKind.JSDocFunctionType: checkSignatureDeclaration(node as JSDocFunctionType); // falls through @@ -24976,7 +25175,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/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 6cb05c697c2..ab90d47f2a4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3314,6 +3314,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 @@ -3511,6 +3515,18 @@ "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 + }, + "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 @@ -3702,22 +3718,28 @@ "category": "Message", "code": 95002 }, - - "Extract function": { + "Extract symbol": { "category": "Message", "code": 95003 }, - "Extract to {0}": { "category": "Message", "code": 95004 }, - "Annotate with type from JSDoc": { + "Extract function": { "category": "Message", "code": 95005 }, - "Annotate with return type from JSDoc": { + "Extract constant": { "category": "Message", "code": 95006 + }, + "Annotate with type from JSDoc": { + "category": "Message", + "code": 95007 + }, + "Annotate with return type from JSDoc": { + "category": "Message", + "code": 95008 } } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 808e8aeaf81..c5659b75154 100755 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2817,7 +2817,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); @@ -3037,7 +3037,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/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/program.ts b/src/compiler/program.ts old mode 100644 new mode 100755 index 7934b26f388..76a0b618a35 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1587,7 +1587,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; 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/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..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, []); @@ -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/types.ts b/src/compiler/types.ts index 7a120064a64..01036862980 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 { @@ -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, @@ -3343,10 +3344,21 @@ 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 + Contravariant = 2, // Contravariant + Bivariant = 3, // Both covariant and contravariant + Independent = 4, // Unwitnessed type parameter + } + // Generic class and interface types export interface GenericType extends InterfaceType, TypeReference { /* @internal */ - instantiations: Map; // Generic instantiation cache + instantiations: Map; // Generic instantiation cache + /* @internal */ + variances?: Variance[]; // Variance of each type parameter } export interface UnionOrIntersectionType extends Type { @@ -3522,9 +3534,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, // 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 } export interface InferenceInfo { @@ -3707,6 +3720,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; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 2fee52f1e3f..2f8f9583f29 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: @@ -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; } @@ -1581,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; } @@ -1591,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); @@ -1973,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); @@ -1987,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) { @@ -3943,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/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); 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/harness/tsconfig.json b/src/harness/tsconfig.json index c6e78138638..88999b2d979 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -128,7 +128,10 @@ "./unittests/printer.ts", "./unittests/transform.ts", "./unittests/customTransforms.ts", - "./unittests/extractMethods.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/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/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts new file mode 100644 index 00000000000..e2c64afb526 --- /dev/null +++ b/src/harness/unittests/extractConstants.ts @@ -0,0 +1,87 @@ +/// + +namespace ts { + 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|]; + } +}`); + + testExtractConstant("extractConstant_Parameters", + `function F() { + let w = 1; + let x = [#|w + 1|]; +}`); + + testExtractConstant("extractConstant_TypeParameters", + `function F(t: T) { + 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++) { + let x = [#|i + 1|]; + } +}`); + }); + + function testExtractConstant(caption: string, text: string) { + testExtractSymbol(caption, text, "extractConstant", Diagnostics.Extract_constant); + } + + function testExtractConstantFailed(caption: string, text: string) { + testExtractSymbolFailed(caption, text, Diagnostics.Extract_constant); + } +} diff --git a/src/harness/unittests/extractFunctions.ts b/src/harness/unittests/extractFunctions.ts new file mode 100644 index 00000000000..27c21ce3fb6 --- /dev/null +++ b/src/harness/unittests/extractFunctions.ts @@ -0,0 +1,369 @@ +/// + +namespace ts { + describe("extractFunctions", () => { + testExtractFunction("extractFunction1", + `namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + foo();|] + } + } +}`); + testExtractFunction("extractFunction2", + `namespace A { + let x = 1; + function foo() { + } + namespace B { + function a() { + [#| + let y = 5; + let z = x; + return foo();|] + } + } +}`); + testExtractFunction("extractFunction3", + `namespace A { + function foo() { + } + namespace B { + function* a(z: number) { + [#| + let y = 5; + yield z; + return foo();|] + } + } +}`); + testExtractFunction("extractFunction4", + `namespace A { + function foo() { + } + namespace B { + async function a(z: number, z1: any) { + [#| + let y = 5; + if (z) { + await z1; + } + return foo();|] + } + } +}`); + testExtractFunction("extractFunction5", + `namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + foo();|] + } + } +}`); + testExtractFunction("extractFunction6", + `namespace A { + let x = 1; + export function foo() { + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + return foo();|] + } + } +}`); + testExtractFunction("extractFunction7", + `namespace A { + let x = 1; + export namespace C { + export function foo() { + } + } + namespace B { + function a() { + let a = 1; + [#| + let y = 5; + let z = x; + a = y; + return C.foo();|] + } + } +}`); + testExtractFunction("extractFunction9", + `namespace A { + export interface I { x: number }; + namespace B { + function a() { + [#|let a1: I = { x: 1 }; + return a1.x + 10;|] + } + } +}`); + testExtractFunction("extractFunction10", + `namespace A { + export interface I { x: number }; + class C { + a() { + let z = 1; + [#|let a1: I = { x: 1 }; + return a1.x + 10;|] + } + } +}`); + testExtractFunction("extractFunction11", + `namespace A { + let y = 1; + class C { + a() { + let z = 1; + [#|let a1 = { x: 1 }; + y = 10; + z = 42; + return a1.x + 10;|] + } + } +}`); + testExtractFunction("extractFunction12", + `namespace A { + let y = 1; + class C { + b() {} + a() { + let z = 1; + [#|let a1 = { x: 1 }; + y = 10; + z = 42; + this.b(); + return a1.x + 10;|] + } + } +}`); + // The "b" type parameters aren't used and shouldn't be passed to the extracted function. + // Type parameters should be in syntactic order (i.e. in order or character offset from BOF). + // 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. + testExtractFunction("extractFunction13", + `(u1a: U1a, u1b: U1b) => { + function F1(t1a: T1a, t1b: T1b) { + (u2a: U2a, u2b: U2b) => { + function F2(t2a: T2a, t2b: T2b) { + (u3a: U3a, u3b: U3b) => { + [#|t1a.toString(); + t2a.toString(); + u1a.toString(); + u2a.toString(); + u3a.toString();|] + } + } + } + } +}`); + // This test is descriptive, rather than normative. The current implementation + // doesn't handle type parameter shadowing. + testExtractFunction("extractFunction14", + `function F(t1: T) { + function G(t2: T) { + [#|t1.toString(); + t2.toString();|] + } +}`); + // Confirm that the constraint is preserved. + 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. + testExtractFunction("extractFunction16", + `function F() { + const array: T[] = [#|[]|]; +}`); + // Class type parameter + testExtractFunction("extractFunction17", + `class C { + M(t1: T1, t2: T2) { + [#|t1.toString()|]; + } +}`); + // Function type parameter + testExtractFunction("extractFunction18", + `class C { + M(t1: T1, t2: T2) { + [#|t1.toString()|]; + } +}`); + // Coupled constraints + testExtractFunction("extractFunction19", + `function F(v: V) { + [#|v.toString()|]; +}`); + + testExtractFunction("extractFunction20", + `const _ = class { + a() { + [#|let a1 = { x: 1 }; + return a1.x + 10;|] + } +}`); + // Write + void return + testExtractFunction("extractFunction21", + `function foo() { + let x = 10; + [#|x++; + return;|] +}`); + // Return in finally block + testExtractFunction("extractFunction22", + `function test() { + try { + } + finally { + [#|return 1;|] + } +}`); + // Extraction position - namespace + testExtractFunction("extractFunction23", + `namespace NS { + function M1() { } + function M2() { + [#|return 1;|] + } + function M3() { } +}`); + // Extraction position - function + testExtractFunction("extractFunction24", + `function Outer() { + function M1() { } + function M2() { + [#|return 1;|] + } + function M3() { } +}`); + // Extraction position - file + testExtractFunction("extractFunction25", + `function M1() { } +function M2() { + [#|return 1;|] +} +function M3() { }`); + // Extraction position - class without ctor + testExtractFunction("extractFunction26", + `class C { + M1() { } + M2() { + [#|return 1;|] + } + M3() { } +}`); + // Extraction position - class with ctor in middle + testExtractFunction("extractFunction27", + `class C { + M1() { } + M2() { + [#|return 1;|] + } + constructor() { } + M3() { } +}`); + // Extraction position - class with ctor at end + testExtractFunction("extractFunction28", + `class C { + M1() { } + M2() { + [#|return 1;|] + } + M3() { } + constructor() { } +}`); + // Shorthand property names + testExtractFunction("extractFunction29", + `interface UnaryExpression { + kind: "Unary"; + operator: string; + operand: any; +} + +function parseUnaryExpression(operator: string): UnaryExpression { + [#|return { + kind: "Unary", + operator, + operand: parsePrimaryExpression(), + };|] +} + +function parsePrimaryExpression(): any { + throw "Not implemented"; +}`); + // Type parameter as declared type + testExtractFunction("extractFunction30", + `function F() { + [#|let t: T;|] +}`); + // Return in nested function + testExtractFunction("extractFunction31", + `namespace N { + + export const value = 1; + + () => { + var f: () => number; + [#|f = function (): number { + return value; + }|] + } +}`); + // Return in nested class + testExtractFunction("extractFunction32", + `namespace N { + + export const value = 1; + + () => { + [#|var c = class { + M() { + return value; + } + }|] + } +}`); + // Selection excludes leading trivia of declaration + testExtractFunction("extractFunction33", + `function F() { + [#|function G() { }|] +}`); + +// TODO (acasey): handle repeated substitution +// testExtractFunction("extractFunction_RepeatedSubstitution", +// `namespace X { +// export const j = 10; +// export const y = [#|j * j|]; +// }`); + }); + + function testExtractFunction(caption: string, text: string) { + testExtractSymbol(caption, text, "extractFunction", Diagnostics.Extract_function); + } +} diff --git a/src/harness/unittests/extractMethods.ts b/src/harness/unittests/extractMethods.ts deleted file mode 100644 index 190cd1d5be0..00000000000 --- a/src/harness/unittests/extractMethods.ts +++ /dev/null @@ -1,823 +0,0 @@ -/// -/// - -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.extractMethod.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.extractMethod.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);`, - [ - "Statement or expression expected." - ]); - - testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, ["Select more than a single identifier."]); - - testExtractMethod("extractMethod1", - `namespace A { - let x = 1; - function foo() { - } - namespace B { - function a() { - let a = 1; - [#| - let y = 5; - let z = x; - a = y; - foo();|] - } - } -}`); - testExtractMethod("extractMethod2", - `namespace A { - let x = 1; - function foo() { - } - namespace B { - function a() { - [#| - let y = 5; - let z = x; - return foo();|] - } - } -}`); - testExtractMethod("extractMethod3", - `namespace A { - function foo() { - } - namespace B { - function* a(z: number) { - [#| - let y = 5; - yield z; - return foo();|] - } - } -}`); - testExtractMethod("extractMethod4", - `namespace A { - function foo() { - } - namespace B { - async function a(z: number, z1: any) { - [#| - let y = 5; - if (z) { - await z1; - } - return foo();|] - } - } -}`); - testExtractMethod("extractMethod5", - `namespace A { - let x = 1; - export function foo() { - } - namespace B { - function a() { - let a = 1; - [#| - let y = 5; - let z = x; - a = y; - foo();|] - } - } -}`); - testExtractMethod("extractMethod6", - `namespace A { - let x = 1; - export function foo() { - } - namespace B { - function a() { - let a = 1; - [#| - let y = 5; - let z = x; - a = y; - return foo();|] - } - } -}`); - testExtractMethod("extractMethod7", - `namespace A { - let x = 1; - export namespace C { - export function foo() { - } - } - namespace B { - function a() { - let a = 1; - [#| - let y = 5; - let z = x; - a = y; - return C.foo();|] - } - } -}`); - testExtractMethod("extractMethod8", - `namespace A { - let x = 1; - namespace B { - function a() { - let a1 = 1; - return 1 + [#|a1 + x|] + 100; - } - } -}`); - testExtractMethod("extractMethod9", - `namespace A { - export interface I { x: number }; - namespace B { - function a() { - [#|let a1: I = { x: 1 }; - return a1.x + 10;|] - } - } -}`); - testExtractMethod("extractMethod10", - `namespace A { - export interface I { x: number }; - class C { - a() { - let z = 1; - [#|let a1: I = { x: 1 }; - return a1.x + 10;|] - } - } -}`); - testExtractMethod("extractMethod11", - `namespace A { - let y = 1; - class C { - a() { - let z = 1; - [#|let a1 = { x: 1 }; - y = 10; - z = 42; - return a1.x + 10;|] - } - } -}`); - testExtractMethod("extractMethod12", - `namespace A { - let y = 1; - class C { - b() {} - a() { - let z = 1; - [#|let a1 = { x: 1 }; - y = 10; - z = 42; - this.b(); - return a1.x + 10;|] - } - } -}`); - // The "b" type parameters aren't used and shouldn't be passed to the extracted function. - // Type parameters should be in syntactic order (i.e. in order or character offset from BOF). - // 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", - `(u1a: U1a, u1b: U1b) => { - function F1(t1a: T1a, t1b: T1b) { - (u2a: U2a, u2b: U2b) => { - function F2(t2a: T2a, t2b: T2b) { - (u3a: U3a, u3b: U3b) => { - [#|t1a.toString(); - t2a.toString(); - u1a.toString(); - u2a.toString(); - u3a.toString();|] - } - } - } - } -}`); - // This test is descriptive, rather than normative. The current implementation - // doesn't handle type parameter shadowing. - testExtractMethod("extractMethod14", - `function F(t1: T) { - function F(t2: T) { - [#|t1.toString(); - t2.toString();|] - } -}`); - // Confirm that the constraint is preserved. - testExtractMethod("extractMethod15", - `function F(t1: T) { - function F(t2: U) { - [#|t2.toString();|] - } -}`); - // Confirm that the contextual type of an extracted expression counts as a use. - testExtractMethod("extractMethod16", - `function F() { - const array: T[] = [#|[]|]; -}`); - // Class type parameter - testExtractMethod("extractMethod17", - `class C { - M(t1: T1, t2: T2) { - [#|t1.toString()|]; - } -}`); - // Method type parameter - testExtractMethod("extractMethod18", - `class C { - M(t1: T1, t2: T2) { - [#|t1.toString()|]; - } -}`); - // Coupled constraints - testExtractMethod("extractMethod19", - `function F(v: V) { - [#|v.toString()|]; -}`); - - testExtractMethod("extractMethod20", - `const _ = class { - a() { - [#|let a1 = { x: 1 }; - return a1.x + 10;|] - } -}`); - // Write + void return - testExtractMethod("extractMethod21", - `function foo() { - let x = 10; - [#|x++; - return;|] -}`); - // Return in finally block - testExtractMethod("extractMethod22", - `function test() { - try { - } - finally { - [#|return 1;|] - } -}`); - // Extraction position - namespace - testExtractMethod("extractMethod23", - `namespace NS { - function M1() { } - function M2() { - [#|return 1;|] - } - function M3() { } -}`); - // Extraction position - function - testExtractMethod("extractMethod24", - `function Outer() { - function M1() { } - function M2() { - [#|return 1;|] - } - function M3() { } -}`); - // Extraction position - file - testExtractMethod("extractMethod25", - `function M1() { } -function M2() { - [#|return 1;|] -} -function M3() { }`); - // Extraction position - class without ctor - testExtractMethod("extractMethod26", - `class C { - M1() { } - M2() { - [#|return 1;|] - } - M3() { } -}`); - // Extraction position - class with ctor in middle - testExtractMethod("extractMethod27", - `class C { - M1() { } - M2() { - [#|return 1;|] - } - constructor() { } - M3() { } -}`); - // Extraction position - class with ctor at end - testExtractMethod("extractMethod28", - `class C { - M1() { } - M2() { - [#|return 1;|] - } - M3() { } - constructor() { } -}`); - // Shorthand property names - testExtractMethod("extractMethod29", - `interface UnaryExpression { - kind: "Unary"; - operator: string; - operand: any; -} - -function parseUnaryExpression(operator: string): UnaryExpression { - [#|return { - kind: "Unary", - operator, - operand: parsePrimaryExpression(), - };|] -} - -function parsePrimaryExpression(): any { - throw "Not implemented"; -}`); - // Type parameter as declared type - testExtractMethod("extractMethod30", - `function F() { - [#|let t: T;|] -}`); - // Return in nested function - testExtractMethod("extractMethod31", - `namespace N { - - export const value = 1; - - () => { - var f: () => number; - [#|f = function (): number { - return value; - }|] - } -}`); - // Return in nested class - testExtractMethod("extractMethod32", - `namespace N { - - export const value = 1; - - () => { - [#|var c = class { - M() { - return value; - } - }|] - } -}`); - // Selection excludes leading trivia of declaration - testExtractMethod("extractMethod33", - `function F() { - [#|function G() { }|] -}`); - }); - - - 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: -1, - 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 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)); - assert.lengthOf(edits, 1); - data.push(`// ==SCOPE::${r.scopeDescription}==`); - 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); - }); - }); - } -} diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts new file mode 100644 index 00000000000..2ddcac482a9 --- /dev/null +++ b/src/harness/unittests/extractRanges.ts @@ -0,0 +1,318 @@ +/// + +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; + } + } + `); + }); + + testExtractRangeFailed("extractRangeFailed1", + ` +namespace A { +function f() { + [#| + let x = 1 + if (x) { + return 10; + } + |] +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message + ]); + + testExtractRangeFailed("extractRangeFailed2", + ` +namespace A { +function f() { + while (true) { + [#| + let x = 1 + if (x) { + break; + } + |] + } +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message + ]); + + testExtractRangeFailed("extractRangeFailed3", + ` +namespace A { +function f() { + while (true) { + [#| + let x = 1 + if (x) { + continue; + } + |] + } +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message + ]); + + testExtractRangeFailed("extractRangeFailed4", + ` +namespace A { +function f() { + l1: { + [#| + let x = 1 + if (x) { + break l1; + } + |] + } +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingLabeledBreakOrContinueStatementWithTargetOutsideOfTheRange.message + ]); + + testExtractRangeFailed("extractRangeFailed5", + ` +namespace A { +function f() { + [#| + try { + f2() + return 10; + } + catch (e) { + } + |] +} +function f2() { +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message + ]); + + testExtractRangeFailed("extractRangeFailed6", + ` +namespace A { +function f() { + [#| + try { + f2() + } + catch (e) { + return 10; + } + |] +} +function f2() { +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalReturnStatement.message + ]); + + testExtractRangeFailed("extractRangeFailed7", + ` +function test(x: number) { +while (x) { + x--; + [#|break;|] +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message + ]); + + testExtractRangeFailed("extractRangeFailed8", + ` +function test(x: number) { +switch (x) { + case 1: + [#|break;|] +} +} + `, + [ + refactor.extractSymbol.Messages.CannotExtractRangeContainingConditionalBreakOrContinueStatements.message + ]); + + testExtractRangeFailed("extractRangeFailed9", + `var x = ([#||]1 + 2);`, + [ + 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]); + }); +} \ 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..ea1cccd32ea --- /dev/null +++ b/src/harness/unittests/extractTestHelpers.ts @@ -0,0 +1,199 @@ +/// +/// + +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) { + 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 path = "/a" + extension; + const program = makeProgram({ path, content: t.source }); + + 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(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); + 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); + + 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) { + 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 diff --git a/src/harness/unittests/jsDocParsing.ts b/src/harness/unittests/jsDocParsing.ts index 2116d87dc6f..b7215f5ea35 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("less-than and greater-than characters", +`/** + * @param x hi +< > still part of the previous comment + */`); }); }); describe("getFirstToken", () => { 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/lib/dom.generated.d.ts b/src/lib/dom.generated.d.ts index d4ee81e4974..420be2f0f5d 100644 --- a/src/lib/dom.generated.d.ts +++ b/src/lib/dom.generated.d.ts @@ -4233,11 +4233,7 @@ interface HTMLBodyElement extends HTMLElement { 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; @@ -4246,7 +4242,6 @@ interface HTMLBodyElement extends HTMLElement { 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; @@ -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. */ @@ -4970,17 +4961,10 @@ interface HTMLFrameSetElement extends HTMLElement { 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; /** * 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; @@ -4989,7 +4973,6 @@ interface HTMLFrameSetElement extends HTMLElement { 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; /** @@ -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. 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/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/project.ts b/src/server/project.ts index 9ef79530e51..28d029cf580 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -771,9 +771,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() }; } } 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/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/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 5f26990d3a4..3808cb78940 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -663,9 +663,10 @@ 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 && rangeContainsRange(originalRange, parent)) { childIndentationAmount = tryComputeIndentationForListItem(childStartPos, child.end, parentStartLine, originalRange, inheritedIndentation); if (childIndentationAmount !== Constants.Unknown) { inheritedIndentation = childIndentationAmount; 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() { diff --git a/src/services/refactors/extractMethod.ts b/src/services/refactors/extractSymbol.ts similarity index 72% rename from src/services/refactors/extractMethod.ts rename to src/services/refactors/extractSymbol.ts index 481834c5a09..b88a013f52b 100644 --- a/src/services/refactors/extractMethod.ts +++ b/src/services/refactors/extractSymbol.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,63 +30,103 @@ 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 - namespace Messages { + export namespace Messages { function createMessage(message: string): DiagnosticMessage { 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,14 @@ 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"); + export const CannotExtractToJSClass = createMessage("Cannot extract constant to a class scope in JS"); } enum RangeFacts { @@ -150,14 +196,14 @@ 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. // 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[] = []; @@ -167,39 +213,18 @@ 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) { - // 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 createErrorResult(sourceFile, span.start, length, Messages.CannotExtractFunction); - } + // 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)) { - 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 +241,17 @@ 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 } }; - } - 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)]; + return [createDiagnosticForNode(node, Messages.CannotExtractIdentifier)]; } return undefined; } @@ -309,7 +329,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 +338,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 +348,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 +459,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 +487,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 +504,44 @@ 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(); + const expression = isExpression(target) + ? target + : (target.statements[0] as ExpressionStatement).expression; + return extractConstantInScope(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 +567,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 +614,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 +637,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 +729,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 +815,126 @@ 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)) { + Debug.assert(!isJS); // See CannotExtractToJSClass + const modifiers: Modifier[] = []; + modifiers.push(createToken(SyntaxKind.PrivateKeyword)); + if (rangeFacts & RangeFacts.InStaticRegion) { + modifiers.push(createToken(SyntaxKind.StaticKeyword)); + } + modifiers.push(createToken(SyntaxKind.ReadonlyKeyword)); + + const newVariable = createProperty( + /*decorators*/ undefined, + modifiers, + 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 +1039,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 +1048,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 +1083,31 @@ 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)); + } + + // 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. + + const isClassLikeScope = isClassLike(scope); + let prevChild: Statement | ClassElement | undefined = undefined; + for (const child of children) { + if (child.pos >= maxPos) { + break; + } + prevChild = child; + if (isClassLikeScope && !isPropertyDeclaration(child)) { + break; + } + } + + Debug.assert(prevChild !== undefined); + return prevChild; } function getPropertyAssignmentsForWrites(writes: ReadonlyArray): ShorthandPropertyAssignment[] { @@ -982,7 +1155,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 +1169,33 @@ 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 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) + : undefined; + // 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)] + : []); + + const constantErrors = []; + if (expressionDiagnostic) { + constantErrors.push(expressionDiagnostic); + } + if (isClassLike(scope) && isInJavaScriptFile(scope)) { + constantErrors.push(createDiagnosticForNode(scope, Messages.CannotExtractToJSClass)); + } + constantErrorsPerScope.push(constantErrors); } const seenUsages = createMap(); @@ -1054,6 +1247,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 +1268,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 +1285,7 @@ namespace ts.refactor.extractMethod { forEachChild(containingLexicalScopeOfExtraction, checkForUsedDeclarations); } - return { target, usagesPerScope, errorsPerScope }; + return { target, usagesPerScope, functionErrorsPerScope, constantErrorsPerScope }; function hasTypeParameters(node: Node) { return isDeclarationWithTypeParameters(node) && @@ -1157,9 +1361,9 @@ namespace ts.refactor.extractMethod { 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); } } } @@ -1211,8 +1415,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 +1438,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 +1460,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/src/services/refactors/refactors.ts b/src/services/refactors/refactors.ts index a536d5a8b62..21aeda7ae52 100644 --- a/src/services/refactors/refactors.ts +++ b/src/services/refactors/refactors.ts @@ -1,3 +1,3 @@ /// /// -/// +/// 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; diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than characters.json new file mode 100644 index 00000000000..93d7cf14737 --- /dev/null +++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.less-than and greater-than 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/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/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_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.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_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.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_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.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_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.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_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.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_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.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_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_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_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_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/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 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 diff --git a/tests/baselines/reference/extractMethod/extractMethod1.ts b/tests/baselines/reference/extractFunction/extractFunction1.ts similarity index 88% rename from tests/baselines/reference/extractMethod/extractMethod1.ts rename to tests/baselines/reference/extractFunction/extractFunction1.ts index 86c28b5f4d2..74303bbf3e1 100644 --- a/tests/baselines/reference/extractMethod/extractMethod1.ts +++ b/tests/baselines/reference/extractFunction/extractFunction1.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/extractFunction/extractFunction10.ts similarity index 80% rename from tests/baselines/reference/extractMethod/extractMethod10.ts rename to tests/baselines/reference/extractFunction/extractFunction10.ts index e3eb73b661e..65fbc9fb4c9 100644 --- a/tests/baselines/reference/extractMethod/extractMethod10.ts +++ b/tests/baselines/reference/extractFunction/extractFunction10.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/extractFunction/extractFunction11.ts similarity index 83% rename from tests/baselines/reference/extractMethod/extractMethod11.ts rename to tests/baselines/reference/extractFunction/extractFunction11.ts index 43fdd75b76f..55472063055 100644 --- a/tests/baselines/reference/extractMethod/extractMethod11.ts +++ b/tests/baselines/reference/extractFunction/extractFunction11.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/extractFunction/extractFunction12.ts similarity index 79% rename from tests/baselines/reference/extractMethod/extractMethod12.ts rename to tests/baselines/reference/extractFunction/extractFunction12.ts index 2f3082cf280..c8f066657a2 100644 --- a/tests/baselines/reference/extractMethod/extractMethod12.ts +++ b/tests/baselines/reference/extractFunction/extractFunction12.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/extractFunction/extractFunction13.ts similarity index 93% rename from tests/baselines/reference/extractMethod/extractMethod13.ts rename to tests/baselines/reference/extractFunction/extractFunction13.ts index 121d7eeecfa..c40f29711f4 100644 --- a/tests/baselines/reference/extractMethod/extractMethod13.ts +++ b/tests/baselines/reference/extractFunction/extractFunction13.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/extractFunction/extractFunction14.ts similarity index 67% rename from tests/baselines/reference/extractMethod/extractMethod14.ts rename to tests/baselines/reference/extractFunction/extractFunction14.ts index d3dcded2c42..943c8250edd 100644 --- a/tests/baselines/reference/extractMethod/extractMethod14.ts +++ b/tests/baselines/reference/extractFunction/extractFunction14.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/extractFunction/extractFunction15.ts similarity index 60% rename from tests/baselines/reference/extractMethod/extractMethod15.ts rename to tests/baselines/reference/extractFunction/extractFunction15.ts index 50516445c87..b6e3e0f04b6 100644 --- a/tests/baselines/reference/extractMethod/extractMethod15.ts +++ b/tests/baselines/reference/extractFunction/extractFunction15.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/extractFunction/extractFunction16.ts similarity index 74% rename from tests/baselines/reference/extractMethod/extractMethod16.ts rename to tests/baselines/reference/extractFunction/extractFunction16.ts index e58bbf576c6..223307ff349 100644 --- a/tests/baselines/reference/extractMethod/extractMethod16.ts +++ b/tests/baselines/reference/extractFunction/extractFunction16.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/extractFunction/extractFunction17.ts similarity index 65% rename from tests/baselines/reference/extractMethod/extractMethod17.ts rename to tests/baselines/reference/extractFunction/extractFunction17.ts index f79abcca792..e1f20af6867 100644 --- a/tests/baselines/reference/extractMethod/extractMethod17.ts +++ b/tests/baselines/reference/extractFunction/extractFunction17.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/extractFunction/extractFunction18.ts similarity index 64% rename from tests/baselines/reference/extractMethod/extractMethod18.ts rename to tests/baselines/reference/extractFunction/extractFunction18.ts index 122eced75d5..775a1f3ed15 100644 --- a/tests/baselines/reference/extractMethod/extractMethod18.ts +++ b/tests/baselines/reference/extractFunction/extractFunction18.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/extractFunction/extractFunction19.ts similarity index 79% rename from tests/baselines/reference/extractMethod/extractMethod19.ts rename to tests/baselines/reference/extractFunction/extractFunction19.ts index 61d35f97db5..e9107198b18 100644 --- a/tests/baselines/reference/extractMethod/extractMethod19.ts +++ b/tests/baselines/reference/extractFunction/extractFunction19.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/extractFunction/extractFunction2.ts similarity index 86% rename from tests/baselines/reference/extractMethod/extractMethod2.ts rename to tests/baselines/reference/extractFunction/extractFunction2.ts index b83cc6f32c6..201dd173adc 100644 --- a/tests/baselines/reference/extractMethod/extractMethod2.ts +++ b/tests/baselines/reference/extractFunction/extractFunction2.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/extractFunction/extractFunction20.js similarity index 66% rename from tests/baselines/reference/extractMethod/extractMethod20.ts rename to tests/baselines/reference/extractFunction/extractFunction20.js index 6e0148e0991..455fee113fb 100644 --- a/tests/baselines/reference/extractMethod/extractMethod20.ts +++ b/tests/baselines/reference/extractFunction/extractFunction20.js @@ -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() { + 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/extractFunction/extractFunction20.ts b/tests/baselines/reference/extractFunction/extractFunction20.ts new file mode 100644 index 00000000000..723d8a99ca6 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction20.ts @@ -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(); + } + + private 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/extractMethod/extractMethod21.ts b/tests/baselines/reference/extractFunction/extractFunction21.ts similarity index 76% rename from tests/baselines/reference/extractMethod/extractMethod21.ts rename to tests/baselines/reference/extractFunction/extractFunction21.ts index 2c4ffd1bdb8..b0469d81384 100644 --- a/tests/baselines/reference/extractMethod/extractMethod21.ts +++ b/tests/baselines/reference/extractFunction/extractFunction21.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/extractFunction/extractFunction22.js similarity index 77% rename from tests/baselines/reference/extractMethod/extractMethod22.ts rename to tests/baselines/reference/extractFunction/extractFunction22.js index 990bfdf0575..09fbc7a5b82 100644 --- a/tests/baselines/reference/extractMethod/extractMethod22.ts +++ b/tests/baselines/reference/extractFunction/extractFunction22.js @@ -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/extractFunction/extractFunction22.ts b/tests/baselines/reference/extractFunction/extractFunction22.ts new file mode 100644 index 00000000000..09fbc7a5b82 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction22.ts @@ -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/extractMethod/extractMethod23.ts b/tests/baselines/reference/extractFunction/extractFunction23.ts similarity index 80% rename from tests/baselines/reference/extractMethod/extractMethod23.ts rename to tests/baselines/reference/extractFunction/extractFunction23.ts index b9bc4264ea6..1a0eaf098fd 100644 --- a/tests/baselines/reference/extractMethod/extractMethod23.ts +++ b/tests/baselines/reference/extractFunction/extractFunction23.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/extractFunction/extractFunction24.js similarity index 80% rename from tests/baselines/reference/extractMethod/extractMethod24.ts rename to tests/baselines/reference/extractFunction/extractFunction24.js index a9dc25d32ea..7b80180c5d3 100644 --- a/tests/baselines/reference/extractMethod/extractMethod24.ts +++ b/tests/baselines/reference/extractFunction/extractFunction24.js @@ -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/extractFunction/extractFunction24.ts b/tests/baselines/reference/extractFunction/extractFunction24.ts new file mode 100644 index 00000000000..7b80180c5d3 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction24.ts @@ -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/extractMethod/extractMethod25.ts b/tests/baselines/reference/extractFunction/extractFunction25.js similarity index 77% rename from tests/baselines/reference/extractMethod/extractMethod25.ts rename to tests/baselines/reference/extractFunction/extractFunction25.js index dc376781346..3233c404307 100644 --- a/tests/baselines/reference/extractMethod/extractMethod25.ts +++ b/tests/baselines/reference/extractFunction/extractFunction25.js @@ -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/extractFunction/extractFunction25.ts b/tests/baselines/reference/extractFunction/extractFunction25.ts new file mode 100644 index 00000000000..3233c404307 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction25.ts @@ -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/extractMethod/extractMethod26.ts b/tests/baselines/reference/extractFunction/extractFunction26.js similarity index 66% rename from tests/baselines/reference/extractMethod/extractMethod26.ts rename to tests/baselines/reference/extractFunction/extractFunction26.js index b49e8ab9508..ccc4bacb477 100644 --- a/tests/baselines/reference/extractMethod/extractMethod26.ts +++ b/tests/baselines/reference/extractFunction/extractFunction26.js @@ -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() { + 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/extractFunction/extractFunction26.ts b/tests/baselines/reference/extractFunction/extractFunction26.ts new file mode 100644 index 00000000000..6f4d85c7801 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction26.ts @@ -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(); + } + private 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/extractMethod/extractMethod27.ts b/tests/baselines/reference/extractFunction/extractFunction27.js similarity index 70% rename from tests/baselines/reference/extractMethod/extractMethod27.ts rename to tests/baselines/reference/extractFunction/extractFunction27.js index 0ec214bb5ed..1f894d17bad 100644 --- a/tests/baselines/reference/extractMethod/extractMethod27.ts +++ b/tests/baselines/reference/extractFunction/extractFunction27.js @@ -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() { + 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/extractFunction/extractFunction27.ts b/tests/baselines/reference/extractFunction/extractFunction27.ts new file mode 100644 index 00000000000..3426c4ec5c3 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction27.ts @@ -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() { } + private 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/extractMethod/extractMethod28.ts b/tests/baselines/reference/extractFunction/extractFunction28.js similarity index 70% rename from tests/baselines/reference/extractMethod/extractMethod28.ts rename to tests/baselines/reference/extractFunction/extractFunction28.js index ab6ce220bd7..d205edbd4bf 100644 --- a/tests/baselines/reference/extractMethod/extractMethod28.ts +++ b/tests/baselines/reference/extractFunction/extractFunction28.js @@ -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() { + 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/extractFunction/extractFunction28.ts b/tests/baselines/reference/extractFunction/extractFunction28.ts new file mode 100644 index 00000000000..b6e94cb47c5 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction28.ts @@ -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(); + } + private 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/extractMethod/extractMethod29.ts b/tests/baselines/reference/extractFunction/extractFunction29.ts similarity index 90% rename from tests/baselines/reference/extractMethod/extractMethod29.ts rename to tests/baselines/reference/extractFunction/extractFunction29.ts index aa7004d254e..9baea5870f5 100644 --- a/tests/baselines/reference/extractMethod/extractMethod29.ts +++ b/tests/baselines/reference/extractFunction/extractFunction29.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/extractFunction/extractFunction3.ts similarity index 86% rename from tests/baselines/reference/extractMethod/extractMethod3.ts rename to tests/baselines/reference/extractFunction/extractFunction3.ts index 0af79791fe7..b5ae7592157 100644 --- a/tests/baselines/reference/extractMethod/extractMethod3.ts +++ b/tests/baselines/reference/extractFunction/extractFunction3.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/extractFunction/extractFunction30.ts similarity index 70% rename from tests/baselines/reference/extractMethod/extractMethod30.ts rename to tests/baselines/reference/extractFunction/extractFunction30.ts index 67dc1208abc..35b86b668ff 100644 --- a/tests/baselines/reference/extractMethod/extractMethod30.ts +++ b/tests/baselines/reference/extractFunction/extractFunction30.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/extractFunction/extractFunction31.ts similarity index 87% rename from tests/baselines/reference/extractMethod/extractMethod31.ts rename to tests/baselines/reference/extractFunction/extractFunction31.ts index 754814dcc26..77120091828 100644 --- a/tests/baselines/reference/extractMethod/extractMethod31.ts +++ b/tests/baselines/reference/extractFunction/extractFunction31.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/extractFunction/extractFunction32.ts similarity index 85% rename from tests/baselines/reference/extractMethod/extractMethod32.ts rename to tests/baselines/reference/extractFunction/extractFunction32.ts index b9b870a08fa..529b81e8f9b 100644 --- a/tests/baselines/reference/extractMethod/extractMethod32.ts +++ b/tests/baselines/reference/extractFunction/extractFunction32.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/extractFunction/extractFunction33.js similarity index 70% rename from tests/baselines/reference/extractMethod/extractMethod33.ts rename to tests/baselines/reference/extractFunction/extractFunction33.js index 79a6626a535..fd716646e66 100644 --- a/tests/baselines/reference/extractMethod/extractMethod33.ts +++ b/tests/baselines/reference/extractFunction/extractFunction33.js @@ -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/extractFunction/extractFunction33.ts b/tests/baselines/reference/extractFunction/extractFunction33.ts new file mode 100644 index 00000000000..fd716646e66 --- /dev/null +++ b/tests/baselines/reference/extractFunction/extractFunction33.ts @@ -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() { } +} diff --git a/tests/baselines/reference/extractMethod/extractMethod4.ts b/tests/baselines/reference/extractFunction/extractFunction4.ts similarity index 88% rename from tests/baselines/reference/extractMethod/extractMethod4.ts rename to tests/baselines/reference/extractFunction/extractFunction4.ts index e0a636135d4..6ef3e843ee1 100644 --- a/tests/baselines/reference/extractMethod/extractMethod4.ts +++ b/tests/baselines/reference/extractFunction/extractFunction4.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/extractFunction/extractFunction5.ts similarity index 88% rename from tests/baselines/reference/extractMethod/extractMethod5.ts rename to tests/baselines/reference/extractFunction/extractFunction5.ts index fc15f6762e5..e2f72721b95 100644 --- a/tests/baselines/reference/extractMethod/extractMethod5.ts +++ b/tests/baselines/reference/extractFunction/extractFunction5.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/extractFunction/extractFunction6.ts similarity index 89% rename from tests/baselines/reference/extractMethod/extractMethod6.ts rename to tests/baselines/reference/extractFunction/extractFunction6.ts index 37112b80e9e..6b4852dc722 100644 --- a/tests/baselines/reference/extractMethod/extractMethod6.ts +++ b/tests/baselines/reference/extractFunction/extractFunction6.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/extractFunction/extractFunction7.ts similarity index 90% rename from tests/baselines/reference/extractMethod/extractMethod7.ts rename to tests/baselines/reference/extractFunction/extractFunction7.ts index 8859b7b4fdd..9712cedda8d 100644 --- a/tests/baselines/reference/extractMethod/extractMethod7.ts +++ b/tests/baselines/reference/extractFunction/extractFunction7.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/extractMethod9.ts b/tests/baselines/reference/extractFunction/extractFunction9.ts similarity index 85% rename from tests/baselines/reference/extractMethod/extractMethod9.ts rename to tests/baselines/reference/extractFunction/extractFunction9.ts index 022dab82363..9c1f81edf45 100644 --- a/tests/baselines/reference/extractMethod/extractMethod9.ts +++ b/tests/baselines/reference/extractFunction/extractFunction9.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/baselines/reference/extractMethod/extractMethod8.ts b/tests/baselines/reference/extractMethod/extractMethod8.ts deleted file mode 100644 index cb06470d385..00000000000 --- a/tests/baselines/reference/extractMethod/extractMethod8.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::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::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::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::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; -} 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 { 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/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_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/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/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/baselines/reference/strictFunctionTypes1.js b/tests/baselines/reference/strictFunctionTypes1.js new file mode 100644 index 00000000000..e802c443561 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypes1.js @@ -0,0 +1,42 @@ +//// [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 + + +//// [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/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..2827983bd5a --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.errors.txt @@ -0,0 +1,340 @@ +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'. + 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'. + 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'. + 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'. + 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'. + 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>'. + 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>'. + 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>'. + 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>'. + 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>'. + 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>'. + 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>'. + 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>'. + 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(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 (31 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'. + + type 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: 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: Types of parameters 'x' and 'x' are incompatible. +!!! 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: 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: Types of parameters 'x' and 'x' are incompatible. +!!! 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: 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: 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: 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: 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>'. +!!! 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'. +!!! 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: 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: 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: 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: Types of parameters 'x' and 'x' are incompatible. +!!! 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'. +!!! 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 new file mode 100644 index 00000000000..2be598f0ef9 --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.js @@ -0,0 +1,188 @@ +//// [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 + +type 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 + +// 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] +"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 +// 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 new file mode 100644 index 00000000000..30faf83d87e --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.symbols @@ -0,0 +1,402 @@ +=== 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)) + +// 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 new file mode 100644 index 00000000000..e4372d4b8fa --- /dev/null +++ b/tests/baselines/reference/strictFunctionTypesErrors.types @@ -0,0 +1,456 @@ +=== 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 + +// 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/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index a1bc2185da5..08887fc6c94 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 12ce62fdefb..ca2b4aa4087 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 ea94f857fa4..9437685c295 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 40181bc2553..d2e7e85ad55 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 228c332c1e3..3f4100033d0 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 a1bc2185da5..08887fc6c94 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 a3c6771965d..22cb0444209 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 96b15f67792..fc3321600fe 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. */ 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 {} 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; 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() {} 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/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) {} diff --git a/tests/cases/compiler/strictFunctionTypes1.ts b/tests/cases/compiler/strictFunctionTypes1.ts new file mode 100644 index 00000000000..6c07bc51ffe --- /dev/null +++ b/tests/cases/compiler/strictFunctionTypes1.ts @@ -0,0 +1,19 @@ +// @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; +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..fbf1c0fa6da --- /dev/null +++ b/tests/cases/compiler/strictFunctionTypesErrors.ts @@ -0,0 +1,128 @@ +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 + +type 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 + +// 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 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"); 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 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(); 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'); 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": [