Merge branch 'master' into refactor-jsdoc-types-to-typescript

This commit is contained in:
Nathan Shively-Sanders
2017-10-02 16:24:00 -07:00
196 changed files with 4942 additions and 1485 deletions
+4 -1
View File
@@ -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",
+3 -3
View File
@@ -244,7 +244,7 @@ namespace ts {
}
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores((<PropertyAccessExpression>nameExpression).name.escapedText));
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
}
return getEscapedTextOfIdentifierOrLiteral(<Identifier | LiteralExpression>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;
+317 -118
View File
@@ -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 = <TypeParameter>createType(TypeFlags.TypeParameter);
const markerSubType = <TypeParameter>createType(TypeFlags.TypeParameter);
markerSubType.constraint = markerSuperType;
const markerOtherType = <TypeParameter>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((<ExpressionWithTypeArguments>node).expression));
return <EntityNameExpression>(<ExpressionWithTypeArguments>node).expression;
if (isEntityNameExpression((<ExpressionWithTypeArguments>node).expression)) {
return <EntityNameExpression>(<ExpressionWithTypeArguments>node).expression;
}
// falls through
default:
return undefined;
}
@@ -2508,7 +2516,7 @@ namespace ts {
return typeReferenceToTypeNode(<TypeReference>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(<ClassLikeDeclaration>type.symbol.valueDeclaration);
const decl = <ClassLikeDeclaration>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<TypeNode>, location: Node): Signature[] {
@@ -4908,6 +4925,8 @@ namespace ts {
*/
function getBaseConstructorTypeOfClass(type: InterfaceType): Type {
if (!type.resolvedBaseConstructorType) {
const decl = <ClassLikeDeclaration>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<TypeParameterDeclaration>(type.symbol, SyntaxKind.TypeParameter).constraint;
return type.symbol && getDeclarationOfKind<TypeParameterDeclaration>(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((<InterfaceType>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 <ObjectType>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("" + (<LiteralType>indexType).value) :
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores((<Identifier>(<PropertyAccessExpression>accessExpression.argumentExpression).name).escapedText)) :
getPropertyNameForKnownSymbolName(idText((<Identifier>(<PropertyAccessExpression>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 && (<TypeReference>source).target === (<TypeReference>target).target) {
// We have type references to same target type, see if relationship holds for all type arguments
if (result = typeArgumentsRelatedTo(<TypeReference>source, <TypeReference>target, reportErrors)) {
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>source).target === (<TypeReference>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((<TypeReference>source).target);
if (result = typeArgumentsRelatedTo(<TypeReference>source, <TypeReference>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(<TypeReference>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(<TypeParameter>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 && (<TypeReference>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 = (<TypeReference>source).typeArguments || emptyArray;
const targetTypes = (<TypeReference>target).typeArguments || emptyArray;
const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
const variances = getVariances((<TypeReference>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((<PropertyAccessExpression>node).expression);
return key && key + "." + unescapeLeadingUnderscores((<PropertyAccessExpression>node).name.escapedText);
return key && key + "." + idText((<PropertyAccessExpression>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 {
// <CustomTag> Hello World </CustomTag>
const intrinsicElementsType = getJsxType(JsxNames.IntrinsicElements);
if (intrinsicElementsType !== unknownType) {
const stringLiteralTypeName = escapeLeadingUnderscores((<StringLiteralType>elementType).value);
const intrinsicProp = getPropertyOfType(intrinsicElementsType, stringLiteralTypeName);
const stringLiteralTypeName = (<StringLiteralType>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(<Expression>node, prop) || isReferenceThroughNamespaceImport(<Expression>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 = <ClassElement>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((<Identifier>node).escapedText).charCodeAt(0) === CharacterCodes._;
return node.kind === SyntaxKind.Identifier && idText(<Identifier>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 (<ConstructorDeclaration>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((<ParenthesizedTypeNode | TypeOperatorNode>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);
}
}
+7
View File
@@ -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",
+27 -5
View File
@@ -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
}
}
+2 -2
View File
@@ -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 && (<StringLiteral>node).textSourceNode) {
return getTextOfNode((<StringLiteral>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.");
+3 -3
View File
@@ -126,7 +126,7 @@ namespace ts {
export function updateIdentifier(node: Identifier, typeArguments: NodeArray<TypeNode> | 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);
}
}
+32 -8
View File
@@ -424,7 +424,7 @@ namespace ts {
case SyntaxKind.JSDocTypeTag:
return visitNode(cbNode, (<JSDocTypeTag>node).typeExpression);
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (<JSDocAugmentsTag>node).typeExpression);
return visitNode(cbNode, (<JSDocAugmentsTag>node).class);
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
@@ -5624,13 +5624,16 @@ namespace ts {
function parseExpressionWithTypeArguments(): ExpressionWithTypeArguments {
const node = <ExpressionWithTypeArguments>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<TypeNode> | 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 = <JSDocAugmentsTag>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 = <JSDocClassTag>createNode(SyntaxKind.JSDocClassTag, atToken.pos);
tag.atToken = atToken;
Regular → Executable
+1 -1
View File
@@ -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;
+6
View File
@@ -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;
+1 -1
View File
@@ -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);
}
}
+6 -6
View File
@@ -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<Statement> {
@@ -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 });
}
}
+1 -1
View File
@@ -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
);
}
+1 -1
View File
@@ -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);
}
+2 -2
View File
@@ -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
);
}
+8 -8
View File
@@ -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((<Identifier>variable.name).escapedText);
const text = idText(<Identifier>variable.name);
name = declareLocal(text);
if (!renamedCatchVariables) {
renamedCatchVariables = createMap<boolean>();
+4 -3
View File
@@ -253,7 +253,7 @@ namespace ts {
else {
const name = (<JsxOpeningLikeElement>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);
}
}
+1 -1
View File
@@ -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);
+5 -5
View File
@@ -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 (<ExportDeclaration>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) {
+2 -2
View File
@@ -2038,7 +2038,7 @@ namespace ts {
: (<ComputedPropertyName>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;
+10 -9
View File
@@ -58,9 +58,9 @@ namespace ts {
else {
// export { x, y }
for (const specifier of (<ExportDeclaration>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 = (<FunctionDeclaration>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 = (<ClassDeclaration>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);
}
}
+19 -5
View File
@@ -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<TypeReference>; // Generic instantiation cache
instantiations: Map<TypeReference>; // 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;
+18 -9
View File
@@ -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 = <BinaryExpression>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 = (<PropertyAccessExpression>nameExpression).name.escapedText;
return getPropertyNameForKnownSymbolName(unescapeLeadingUnderscores(rightHandSideName));
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
}
else if (nameExpression.kind === SyntaxKind.StringLiteral || nameExpression.kind === SyntaxKind.NumericLiteral) {
return escapeLeadingUnderscores((<LiteralExpression>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.
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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<true>();
/* 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];
+4 -1
View File
@@ -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",
@@ -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
+87
View File
@@ -0,0 +1,87 @@
/// <reference path="extractTestHelpers.ts" />
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: 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);
}
}
+369
View File
@@ -0,0 +1,369 @@
/// <reference path="extractTestHelpers.ts" />
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, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(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<T>(t1: T) {
function G<T>(t2: T) {
[#|t1.toString();
t2.toString();|]
}
}`);
// Confirm that the constraint is preserved.
testExtractFunction("extractFunction15",
`function F<T>(t1: T) {
function G<U extends T[]>(t2: U) {
[#|t2.toString();|]
}
}`);
// Confirm that the contextual type of an extracted expression counts as a use.
testExtractFunction("extractFunction16",
`function F<T>() {
const array: T[] = [#|[]|];
}`);
// Class type parameter
testExtractFunction("extractFunction17",
`class C<T1, T2> {
M(t1: T1, t2: T2) {
[#|t1.toString()|];
}
}`);
// Function type parameter
testExtractFunction("extractFunction18",
`class C {
M<T1, T2>(t1: T1, t2: T2) {
[#|t1.toString()|];
}
}`);
// Coupled constraints
testExtractFunction("extractFunction19",
`function F<T, U extends T[], V extends U[]>(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<T>() {
[#|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);
}
}
-823
View File
@@ -1,823 +0,0 @@
/// <reference path="..\harness.ts" />
/// <reference path="tsserverProjectSystem.ts" />
namespace ts {
interface Range {
start: number;
end: number;
name: string;
}
interface Test {
source: string;
ranges: Map<Range>;
}
function extractTest(source: string): Test {
const activeRanges: Range[] = [];
let text = "";
let lastPos = 0;
let pos = 0;
const ranges = createMap<Range>();
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 => <string>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, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
function F2<T2a, T2b>(t2a: T2a, t2b: T2b) {
<U3a, U3b>(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<T>(t1: T) {
function F<T>(t2: T) {
[#|t1.toString();
t2.toString();|]
}
}`);
// Confirm that the constraint is preserved.
testExtractMethod("extractMethod15",
`function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
[#|t2.toString();|]
}
}`);
// Confirm that the contextual type of an extracted expression counts as a use.
testExtractMethod("extractMethod16",
`function F<T>() {
const array: T[] = [#|[]|];
}`);
// Class type parameter
testExtractMethod("extractMethod17",
`class C<T1, T2> {
M(t1: T1, t2: T2) {
[#|t1.toString()|];
}
}`);
// Method type parameter
testExtractMethod("extractMethod18",
`class C {
M<T1, T2>(t1: T1, t2: T2) {
[#|t1.toString()|];
}
}`);
// Coupled constraints
testExtractMethod("extractMethod19",
`function F<T, U extends T[], V extends U[]>(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<T>() {
[#|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);
});
});
}
}
+318
View File
@@ -0,0 +1,318 @@
/// <reference path="extractTestHelpers.ts" />
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 => <string>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]);
});
}
+199
View File
@@ -0,0 +1,199 @@
/// <reference path="..\harness.ts" />
/// <reference path="tsserverProjectSystem.ts" />
namespace ts {
export interface Range {
start: number;
end: number;
name: string;
}
export interface Test {
source: string;
ranges: Map<Range>;
}
export function extractTest(source: string): Test {
const activeRanges: Range[] = [];
let text = "";
let lastPos = 0;
let pos = 0;
const ranges = createMap<Range>();
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));
});
}
}
+5
View File
@@ -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", () => {
@@ -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<server.protocol.ConfigureRequest>({
command: server.protocol.CommandTypes.Configure,
arguments: {
formatOptions: {
indentSize: 2,
},
},
}).response;
assert.deepEqual(response0, /*expected*/ undefined);
const response1 = session.executeCommandSeq<server.protocol.GetEditsForRefactorRequest>({
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 },
});
});
});
}
+1 -21
View File
@@ -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.
+1 -1
View File
@@ -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.
+1 -2
View File
@@ -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<protocol.GetEditsForRefactorRequest>(CommandNames.GetEditsForRefactor, args);
const response = this.processResponse<protocol.GetEditsForRefactorResponse>(request);
+3 -2
View File
@@ -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() };
}
}
-1
View File
@@ -494,7 +494,6 @@ namespace ts.server.protocol {
refactor: string;
/* The 'name' property from the refactoring action */
action: string;
formatOptions?: FormatCodeSettings,
};
+1 -1
View File
@@ -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
+1 -2
View File
@@ -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:
+3 -2
View File
@@ -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;
+8 -26
View File
@@ -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<Rule>) {
this.mapRowLength = SyntaxKind.LastToken + 1;
this.map = <any>new Array(this.mapRowLength * this.mapRowLength); // new Array<RulesBucket>(this.mapRowLength * this.mapRowLength);
this.map = new Array<RulesBucket>(this.mapRowLength * this.mapRowLength);
// This array is used only during construction of the rulesbucket in the map
const rulesBucketConstructionStateList: RulesBucketConstructionState[] = <any>new Array(this.map.length); // new Array<RulesBucketConstructionState>(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<RulesBucketConstructionState>(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,
+1 -1
View File
@@ -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() {
@@ -2,18 +2,21 @@
/// <reference path="../../compiler/checker.ts" />
/* @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<boolean> = createMap();
const functionActions: RefactorActionInfo[] = [];
const usedFunctionNames: Map<boolean> = createMap();
const constantActions: RefactorActionInfo[] = [];
const usedConstantNames: Map<boolean> = 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 (<BlockLike>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<Diagnostic>;
readonly functionDescription: string;
readonly functionErrors: ReadonlyArray<Diagnostic>;
readonly constantDescription: string;
readonly constantErrors: ReadonlyArray<Diagnostic>;
}
/**
* 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<PossibleExtraction> | undefined {
const { scopes, readsAndWrites: { errorsPerScope } } = getPossibleExtractionsWorker(targetRange, context);
function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray<PossibleExtraction> | 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<FileTextChanges>, 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<FileTextChanges>, 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<Node>): Expression {
return substitutions.size
? visitor(initializer) as Expression
: initializer;
function visitor(node: Node): VisitResult<Node> {
const substitution = substitutions.get(getNodeId(node).toString());
return substitution || visitEachChild(node, visitor, nullTransformationContext);
}
}
function getStatementsOrClassElements(scope: Scope): ReadonlyArray<Statement> | ReadonlyArray<ClassElement> {
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<Statement | ClassElement>(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<UsageEntry>): ShorthandPropertyAssignment[] {
@@ -982,7 +1155,8 @@ namespace ts.refactor.extractMethod {
interface ReadsAndWrites {
readonly target: Expression | Block;
readonly usagesPerScope: ReadonlyArray<ScopeUsages>;
readonly errorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
readonly functionErrorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
readonly constantErrorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
}
function collectReadsAndWrites(
targetRange: TargetRange,
@@ -995,14 +1169,33 @@ namespace ts.refactor.extractMethod {
const allTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
const usagesPerScope: ScopeUsages[] = [];
const substitutionsPerScope: Map<Node>[] = [];
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<UsageEntry>(), typeParameterUsages: createMap<TypeParameter>(), substitutions: createMap<Expression>() });
substitutionsPerScope.push(createMap<Expression>());
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<Usage>();
@@ -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<symbolId, subst> to map<nodeId, subst> 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;
}
+1 -1
View File
@@ -1,3 +1,3 @@
/// <reference path="annotateWithTypeFromJSDoc.ts" />
/// <reference path="convertFunctionToEs6Class.ts" />
/// <reference path="extractMethod.ts" />
/// <reference path="extractSymbol.ts" />
+2 -2
View File
@@ -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;
@@ -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
}
}
@@ -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 '""'.
@@ -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));
@@ -0,0 +1,4 @@
=== tests/cases/compiler/classExtendsInterface_not.ts ===
class C extends "".bogus {}
>C : Symbol(C, Decl(classExtendsInterface_not.ts, 0, 0))
@@ -0,0 +1,7 @@
=== tests/cases/compiler/classExtendsInterface_not.ts ===
class C extends "".bogus {}
>C : C
>"".bogus : any
>"" : ""
>bogus : any
@@ -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;
}
}
@@ -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;
}
}
@@ -0,0 +1,10 @@
// ==ORIGINAL==
class C {
x = 1;
}
// ==SCOPE::Extract to constant in global scope==
const newLocal = 1;
class C {
x = /*RENAME*/newLocal;
}
@@ -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;
}
@@ -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;
}
}
@@ -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;
}
}
@@ -0,0 +1,4 @@
// ==ORIGINAL==
"hello";
// ==SCOPE::Extract to constant in global scope==
const /*RENAME*/newLocal = "hello";
@@ -0,0 +1,4 @@
// ==ORIGINAL==
"hello";
// ==SCOPE::Extract to constant in global scope==
const /*RENAME*/newLocal = "hello";
@@ -0,0 +1,4 @@
// ==ORIGINAL==
"hello";
// ==SCOPE::Extract to constant in global scope==
const /*RENAME*/newLocal = "hello";
@@ -0,0 +1,4 @@
// ==ORIGINAL==
"hello";
// ==SCOPE::Extract to constant in global scope==
const /*RENAME*/newLocal = "hello";
@@ -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;
}
@@ -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;
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
}
@@ -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;
}
@@ -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;
}
@@ -0,0 +1,6 @@
// ==ORIGINAL==
let x = 1;
// ==SCOPE::Extract to constant in global scope==
const newLocal = 1;
let x = /*RENAME*/newLocal;
@@ -0,0 +1,6 @@
// ==ORIGINAL==
let x = 1;
// ==SCOPE::Extract to constant in global scope==
const newLocal = 1;
let x = /*RENAME*/newLocal;
@@ -0,0 +1,10 @@
// ==ORIGINAL==
function F<T>(t: T) {
let x = t + 1;
}
// ==SCOPE::Extract to constant in function 'F'==
function F<T>(t: T) {
const newLocal = t + 1;
let x = /*RENAME*/newLocal;
}
@@ -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() {
@@ -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 {
@@ -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 {
@@ -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;
@@ -14,7 +14,7 @@
}
}
}
// ==SCOPE::inner function in function 'F2'==
// ==SCOPE::Extract to inner function in function 'F2'==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
@@ -34,7 +34,7 @@
}
}
}
// ==SCOPE::inner function in function 'F1'==
// ==SCOPE::Extract to inner function in function 'F1'==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
@@ -54,7 +54,7 @@
}
}
}
// ==SCOPE::function in global scope==
// ==SCOPE::Extract to function in global scope==
<U1a, U1b>(u1a: U1a, u1b: U1b) => {
function F1<T1a, T1b>(t1a: T1a, t1b: T1b) {
<U2a, U2b>(u2a: U2a, u2b: U2b) => {
@@ -1,13 +1,13 @@
// ==ORIGINAL==
function F<T>(t1: T) {
function F<T>(t2: T) {
function G<T>(t2: T) {
t1.toString();
t2.toString();
}
}
// ==SCOPE::inner function in function 'F'==
// ==SCOPE::Extract to inner function in function 'G'==
function F<T>(t1: T) {
function F<T>(t2: T) {
function G<T>(t2: T) {
/*RENAME*/newFunction();
function newFunction() {
@@ -16,9 +16,9 @@ function F<T>(t1: T) {
}
}
}
// ==SCOPE::inner function in function 'F'==
// ==SCOPE::Extract to inner function in function 'F'==
function F<T>(t1: T) {
function F<T>(t2: T) {
function G<T>(t2: T) {
/*RENAME*/newFunction<T>(t2);
}
@@ -27,9 +27,9 @@ function F<T>(t1: T) {
t2.toString();
}
}
// ==SCOPE::function in global scope==
// ==SCOPE::Extract to function in global scope==
function F<T>(t1: T) {
function F<T>(t2: T) {
function G<T>(t2: T) {
/*RENAME*/newFunction<T, T>(t1, t2);
}
}
@@ -1,12 +1,12 @@
// ==ORIGINAL==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
function G<U extends T[]>(t2: U) {
t2.toString();
}
}
// ==SCOPE::inner function in function 'F'==
// ==SCOPE::Extract to inner function in function 'G'==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
function G<U extends T[]>(t2: U) {
/*RENAME*/newFunction();
function newFunction() {
@@ -14,9 +14,9 @@ function F<T>(t1: T) {
}
}
}
// ==SCOPE::inner function in function 'F'==
// ==SCOPE::Extract to inner function in function 'F'==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
function G<U extends T[]>(t2: U) {
/*RENAME*/newFunction<U>(t2);
}
@@ -24,9 +24,9 @@ function F<T>(t1: T) {
t2.toString();
}
}
// ==SCOPE::function in global scope==
// ==SCOPE::Extract to function in global scope==
function F<T>(t1: T) {
function F<U extends T[]>(t2: U) {
function G<U extends T[]>(t2: U) {
/*RENAME*/newFunction<T, U>(t2);
}
}
@@ -2,7 +2,7 @@
function F<T>() {
const array: T[] = [];
}
// ==SCOPE::inner function in function 'F'==
// ==SCOPE::Extract to inner function in function 'F'==
function F<T>() {
const array: T[] = /*RENAME*/newFunction();
@@ -10,7 +10,7 @@ function F<T>() {
return [];
}
}
// ==SCOPE::function in global scope==
// ==SCOPE::Extract to function in global scope==
function F<T>() {
const array: T[] = /*RENAME*/newFunction<T>();
}
@@ -4,17 +4,17 @@ class C<T1, T2> {
t1.toString();
}
}
// ==SCOPE::method in class 'C'==
// ==SCOPE::Extract to method in class 'C'==
class C<T1, T2> {
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<T1, T2> {
M(t1: T1, t2: T2) {
/*RENAME*/newFunction<T1>(t1);
@@ -4,17 +4,17 @@ class C {
t1.toString();
}
}
// ==SCOPE::method in class 'C'==
// ==SCOPE::Extract to method in class 'C'==
class C {
M<T1, T2>(t1: T1, t2: T2) {
this./*RENAME*/newFunction<T1>(t1);
this./*RENAME*/newMethod<T1>(t1);
}
private newFunction<T1>(t1: T1) {
private newMethod<T1>(t1: T1) {
t1.toString();
}
}
// ==SCOPE::function in global scope==
// ==SCOPE::Extract to function in global scope==
class C {
M<T1, T2>(t1: T1, t2: T2) {
/*RENAME*/newFunction<T1>(t1);
@@ -2,7 +2,7 @@
function F<T, U extends T[], V extends U[]>(v: V) {
v.toString();
}
// ==SCOPE::inner function in function 'F'==
// ==SCOPE::Extract to inner function in function 'F'==
function F<T, U extends T[], V extends U[]>(v: V) {
/*RENAME*/newFunction();
@@ -10,7 +10,7 @@ function F<T, U extends T[], V extends U[]>(v: V) {
v.toString();
}
}
// ==SCOPE::function in global scope==
// ==SCOPE::Extract to function in global scope==
function F<T, U extends T[], V extends U[]>(v: V) {
/*RENAME*/newFunction<T, U, V>(v);
}
@@ -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() {
@@ -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();
@@ -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;
}
@@ -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;
}
@@ -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);
@@ -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 {
}
@@ -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;
}
@@ -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() {
@@ -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() {
@@ -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;
}
@@ -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();
@@ -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() { }
@@ -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() {
@@ -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;
}
@@ -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() {
@@ -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;
}
@@ -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() {

Some files were not shown because too many files have changed in this diff Show More