mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-11 10:46:28 -05:00
Merge branch 'master' into fail-spec-lambda-parsing-on-parameter-initialiser-missing-=
This commit is contained in:
@@ -230,7 +230,7 @@ namespace ts {
|
||||
getSuggestionForNonexistentSymbol: (location, name, meaning) => unescapeLeadingUnderscores(getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning)),
|
||||
getBaseConstraintOfType,
|
||||
resolveName(name, location, meaning) {
|
||||
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
return resolveName(location, escapeLeadingUnderscores(name), meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
|
||||
},
|
||||
getJsxNamespace: () => unescapeLeadingUnderscores(getJsxNamespace()),
|
||||
};
|
||||
@@ -865,17 +865,22 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
|
||||
// the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
|
||||
// the given name can be found.
|
||||
/**
|
||||
* Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
|
||||
* the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
|
||||
* the given name can be found.
|
||||
*
|
||||
* @param isUse If true, this will count towards --noUnusedLocals / --noUnusedParameters.
|
||||
*/
|
||||
function resolveName(
|
||||
location: Node | undefined,
|
||||
name: __String,
|
||||
meaning: SymbolFlags,
|
||||
nameNotFoundMessage: DiagnosticMessage | undefined,
|
||||
nameArg: __String | Identifier,
|
||||
isUse: boolean,
|
||||
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
|
||||
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, getSymbol, suggestedNameNotFoundMessage);
|
||||
return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, getSymbol, suggestedNameNotFoundMessage);
|
||||
}
|
||||
|
||||
function resolveNameHelper(
|
||||
@@ -884,6 +889,7 @@ namespace ts {
|
||||
meaning: SymbolFlags,
|
||||
nameNotFoundMessage: DiagnosticMessage,
|
||||
nameArg: __String | Identifier,
|
||||
isUse: boolean,
|
||||
lookup: typeof getSymbol,
|
||||
suggestedNameNotFoundMessage?: DiagnosticMessage): Symbol {
|
||||
const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location
|
||||
@@ -1114,7 +1120,7 @@ namespace ts {
|
||||
// We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`.
|
||||
// If `result === lastLocation.symbol`, that means that we are somewhere inside `lastLocation` looking up a name, and resolving to `lastLocation` itself.
|
||||
// That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used.
|
||||
if (result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) {
|
||||
if (isUse && result && nameNotFoundMessage && noUnusedIdentifiers && result !== lastLocation.symbol) {
|
||||
result.isReferenced = true;
|
||||
}
|
||||
|
||||
@@ -1267,7 +1273,7 @@ namespace ts {
|
||||
|
||||
function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
|
||||
if (meaning === SymbolFlags.Namespace) {
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
|
||||
const parent = errorLocation.parent;
|
||||
if (symbol) {
|
||||
if (isQualifiedName(parent)) {
|
||||
@@ -1298,7 +1304,7 @@ namespace ts {
|
||||
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name));
|
||||
return true;
|
||||
}
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
|
||||
if (symbol && !(symbol.flags & SymbolFlags.NamespaceModule)) {
|
||||
error(errorLocation, Diagnostics._0_only_refers_to_a_type_but_is_being_used_as_a_value_here, unescapeLeadingUnderscores(name));
|
||||
return true;
|
||||
@@ -1309,14 +1315,14 @@ namespace ts {
|
||||
|
||||
function checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
|
||||
if (meaning & (SymbolFlags.Value & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Type)) {
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Value, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
|
||||
if (symbol) {
|
||||
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_value, unescapeLeadingUnderscores(name));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (meaning & (SymbolFlags.Type & ~SymbolFlags.NamespaceModule & ~SymbolFlags.Value)) {
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined));
|
||||
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.NamespaceModule & ~SymbolFlags.Type, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
|
||||
if (symbol) {
|
||||
error(errorLocation, Diagnostics.Cannot_use_namespace_0_as_a_type, unescapeLeadingUnderscores(name));
|
||||
return true;
|
||||
@@ -1640,7 +1646,7 @@ namespace ts {
|
||||
if (name.kind === SyntaxKind.Identifier) {
|
||||
const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
|
||||
|
||||
symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name);
|
||||
symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name, /*isUse*/ true);
|
||||
if (!symbol) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -2314,7 +2320,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
const firstIdentifier = getFirstIdentifier(entityName);
|
||||
const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
|
||||
const symbol = resolveName(enclosingDeclaration, firstIdentifier.escapedText, meaning, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
|
||||
|
||||
// Verify if the symbol is accessible
|
||||
return (symbol && hasVisibleDeclarations(symbol, /*shouldComputeAliasToMakeVisible*/ true)) || {
|
||||
@@ -3971,7 +3977,7 @@ namespace ts {
|
||||
function collectLinkedAliases(node: Identifier): Node[] {
|
||||
let exportSymbol: Symbol;
|
||||
if (node.parent && node.parent.kind === SyntaxKind.ExportAssignment) {
|
||||
exportSymbol = resolveName(node.parent, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node);
|
||||
exportSymbol = resolveName(node.parent, node.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias, Diagnostics.Cannot_find_name_0, node, /*isUse*/ false);
|
||||
}
|
||||
else if (node.parent.kind === SyntaxKind.ExportSpecifier) {
|
||||
exportSymbol = getTargetOfExportSpecifier(<ExportSpecifier>node.parent, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias);
|
||||
@@ -3993,7 +3999,7 @@ namespace ts {
|
||||
const internalModuleReference = <Identifier | QualifiedName>(<ImportEqualsDeclaration>declaration).moduleReference;
|
||||
const firstIdentifier = getFirstIdentifier(internalModuleReference);
|
||||
const importSymbol = resolveName(declaration, firstIdentifier.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace,
|
||||
undefined, undefined);
|
||||
undefined, undefined, /*isUse*/ false);
|
||||
if (importSymbol) {
|
||||
buildVisibleNodeList(importSymbol.declarations);
|
||||
}
|
||||
@@ -6408,7 +6414,7 @@ namespace ts {
|
||||
let paramSymbol = param.symbol;
|
||||
// Include parameter symbol instead of property symbol in the signature
|
||||
if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) {
|
||||
const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined);
|
||||
const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined, /*isUse*/ false);
|
||||
paramSymbol = resolvedSymbol;
|
||||
}
|
||||
if (i === 0 && paramSymbol.escapedName === "this") {
|
||||
@@ -7085,7 +7091,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getGlobalSymbol(name: __String, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol {
|
||||
return resolveName(undefined, name, meaning, diagnostic, name);
|
||||
// Don't track references for global symbols anyway, so value if `isReference` is arbitrary
|
||||
return resolveName(undefined, name, meaning, diagnostic, name, /*isUse*/ false);
|
||||
}
|
||||
|
||||
function getGlobalType(name: __String, arity: 0, reportErrors: boolean): ObjectType;
|
||||
@@ -10832,7 +10839,15 @@ namespace ts {
|
||||
function getResolvedSymbol(node: Identifier): Symbol {
|
||||
const links = getNodeLinks(node);
|
||||
if (!links.resolvedSymbol) {
|
||||
links.resolvedSymbol = !nodeIsMissing(node) && resolveName(node, node.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue, Diagnostics.Cannot_find_name_0, node, Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol;
|
||||
links.resolvedSymbol = !nodeIsMissing(node) &&
|
||||
resolveName(
|
||||
node,
|
||||
node.escapedText,
|
||||
SymbolFlags.Value | SymbolFlags.ExportValue,
|
||||
Diagnostics.Cannot_find_name_0,
|
||||
node,
|
||||
!isWriteOnlyAccess(node),
|
||||
Diagnostics.Cannot_find_name_0_Did_you_mean_1) || unknownSymbol;
|
||||
}
|
||||
return links.resolvedSymbol;
|
||||
}
|
||||
@@ -14428,7 +14443,7 @@ namespace ts {
|
||||
// And if there is no reactNamespace/jsxFactory's symbol in scope when targeting React emit, we should issue an error.
|
||||
const reactRefErr = diagnostics && compilerOptions.jsx === JsxEmit.React ? Diagnostics.Cannot_find_name_0 : undefined;
|
||||
const reactNamespace = getJsxNamespace();
|
||||
const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace);
|
||||
const reactSym = resolveName(node.tagName, reactNamespace, SymbolFlags.Value, reactRefErr, reactNamespace, /*isUse*/ true);
|
||||
if (reactSym) {
|
||||
// Mark local symbol as referenced here because it might not have been marked
|
||||
// if jsx emit was not react as there wont be error being emitted
|
||||
@@ -14704,7 +14719,7 @@ namespace ts {
|
||||
|
||||
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
|
||||
|
||||
markPropertyAsReferenced(prop);
|
||||
markPropertyAsReferenced(prop, node);
|
||||
|
||||
getNodeLinks(node).resolvedSymbol = prop;
|
||||
|
||||
@@ -14804,7 +14819,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function getSuggestionForNonexistentSymbol(location: Node, name: __String, meaning: SymbolFlags): __String {
|
||||
const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, (symbols, name, meaning) => {
|
||||
const result = resolveNameHelper(location, name, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ false, (symbols, name, meaning) => {
|
||||
const symbol = getSymbol(symbols, name, meaning);
|
||||
if (symbol) {
|
||||
// Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function
|
||||
@@ -14884,11 +14899,12 @@ namespace ts {
|
||||
return bestCandidate;
|
||||
}
|
||||
|
||||
function markPropertyAsReferenced(prop: Symbol) {
|
||||
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined) {
|
||||
if (prop &&
|
||||
noUnusedIdentifiers &&
|
||||
(prop.flags & SymbolFlags.ClassMember) &&
|
||||
prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)) {
|
||||
prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Private)
|
||||
&& !(nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly))) {
|
||||
if (getCheckFlags(prop) & CheckFlags.Instantiated) {
|
||||
getSymbolLinks(prop).target.isReferenced = true;
|
||||
}
|
||||
@@ -15153,7 +15169,6 @@ namespace ts {
|
||||
let argCount: number; // Apparent number of arguments we will have in this call
|
||||
let typeArguments: NodeArray<TypeNode>; // Type arguments (undefined if none)
|
||||
let callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments
|
||||
let isDecorator: boolean;
|
||||
let spreadArgIndex = -1;
|
||||
|
||||
if (isJsxOpeningLikeElement(node)) {
|
||||
@@ -15187,7 +15202,6 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else if (node.kind === SyntaxKind.Decorator) {
|
||||
isDecorator = true;
|
||||
typeArguments = undefined;
|
||||
argCount = getEffectiveArgumentCount(node, /*args*/ undefined, signature);
|
||||
}
|
||||
@@ -16582,7 +16596,7 @@ namespace ts {
|
||||
}
|
||||
// Make sure require is not a local function
|
||||
if (!isIdentifier(node.expression)) throw Debug.fail();
|
||||
const resolvedRequire = resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
const resolvedRequire = resolveName(node.expression, node.expression.escapedText, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true);
|
||||
if (!resolvedRequire) {
|
||||
// project does not contain symbol named 'require' - assume commonjs require
|
||||
return true;
|
||||
@@ -19579,8 +19593,11 @@ namespace ts {
|
||||
}
|
||||
|
||||
function markEntityNameOrEntityExpressionAsReference(typeName: EntityNameOrEntityNameExpression) {
|
||||
const rootName = typeName && getFirstIdentifier(typeName);
|
||||
const rootSymbol = rootName && resolveName(rootName, rootName.escapedText, (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
if (!typeName) return;
|
||||
|
||||
const rootName = getFirstIdentifier(typeName);
|
||||
const meaning = (typeName.kind === SyntaxKind.Identifier ? SymbolFlags.Type : SymbolFlags.Namespace) | SymbolFlags.Alias;
|
||||
const rootSymbol = resolveName(rootName, rootName.escapedText, meaning, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isRefernce*/ true);
|
||||
if (rootSymbol
|
||||
&& rootSymbol.flags & SymbolFlags.Alias
|
||||
&& symbolIsValue(rootSymbol)
|
||||
@@ -19874,7 +19891,7 @@ namespace ts {
|
||||
!isParameterPropertyDeclaration(parameter) &&
|
||||
!parameterIsThisKeyword(parameter) &&
|
||||
!parameterNameStartsWithUnderscore(name)) {
|
||||
error(name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(local.escapedName));
|
||||
error(name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(local.escapedName));
|
||||
}
|
||||
}
|
||||
else if (compilerOptions.noUnusedLocals) {
|
||||
@@ -19903,7 +19920,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (!isRemovedPropertyFromObjectSpread(node.kind === SyntaxKind.Identifier ? node.parent : node)) {
|
||||
error(node, Diagnostics._0_is_declared_but_never_used, name);
|
||||
error(node, Diagnostics._0_is_declared_but_its_value_is_never_read, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19921,13 +19938,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_never_used, unescapeLeadingUnderscores(member.symbol.escapedName));
|
||||
error(member.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(member.symbol.escapedName));
|
||||
}
|
||||
}
|
||||
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_never_used, unescapeLeadingUnderscores(parameter.symbol.escapedName));
|
||||
error(parameter.name, Diagnostics.Property_0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(parameter.symbol.escapedName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19948,7 +19965,7 @@ namespace ts {
|
||||
}
|
||||
for (const typeParameter of node.typeParameters) {
|
||||
if (!getMergedSymbol(typeParameter.symbol).isReferenced) {
|
||||
error(typeParameter.name, Diagnostics._0_is_declared_but_never_used, unescapeLeadingUnderscores(typeParameter.symbol.escapedName));
|
||||
error(typeParameter.name, Diagnostics._0_is_declared_but_its_value_is_never_read, unescapeLeadingUnderscores(typeParameter.symbol.escapedName));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20179,7 +20196,7 @@ namespace ts {
|
||||
const symbol = getSymbolOfNode(node);
|
||||
if (symbol.flags & SymbolFlags.FunctionScopedVariable) {
|
||||
if (!isIdentifier(node.name)) throw Debug.fail();
|
||||
const localDeclarationSymbol = resolveName(node, node.name.escapedText, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined);
|
||||
const localDeclarationSymbol = resolveName(node, node.name.escapedText, SymbolFlags.Variable, /*nodeNotFoundErrorMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false);
|
||||
if (localDeclarationSymbol &&
|
||||
localDeclarationSymbol !== symbol &&
|
||||
localDeclarationSymbol.flags & SymbolFlags.BlockScopedVariable) {
|
||||
@@ -20234,7 +20251,7 @@ namespace ts {
|
||||
else if (n.kind === SyntaxKind.Identifier) {
|
||||
// check FunctionLikeDeclaration.locals (stores parameters\function local variable)
|
||||
// if it contains entry with a specified name
|
||||
const symbol = resolveName(n, (<Identifier>n).escapedText, SymbolFlags.Value | SymbolFlags.Alias, /*nameNotFoundMessage*/undefined, /*nameArg*/undefined);
|
||||
const symbol = resolveName(n, (<Identifier>n).escapedText, SymbolFlags.Value | SymbolFlags.Alias, /*nameNotFoundMessage*/undefined, /*nameArg*/undefined, /*isUse*/ false);
|
||||
if (!symbol || symbol === unknownSymbol || !symbol.valueDeclaration) {
|
||||
return;
|
||||
}
|
||||
@@ -20318,7 +20335,7 @@ namespace ts {
|
||||
const parentType = getTypeForBindingElementParent(parent);
|
||||
const name = node.propertyName || <Identifier>node.name;
|
||||
const property = getPropertyOfType(parentType, getTextOfPropertyName(name));
|
||||
markPropertyAsReferenced(property);
|
||||
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined); // A destructuring is never a write-only reference.
|
||||
if (parent.initializer && property) {
|
||||
checkPropertyAccessibility(parent, parent.initializer, parentType, property);
|
||||
}
|
||||
@@ -22254,7 +22271,7 @@ namespace ts {
|
||||
const exportedName = node.propertyName || node.name;
|
||||
// find immediate value referenced by exported name (SymbolFlags.Alias is set so we don't chase down aliases)
|
||||
const symbol = resolveName(exportedName, exportedName.escapedText, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias,
|
||||
/*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
/*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));
|
||||
}
|
||||
@@ -23359,7 +23376,7 @@ namespace ts {
|
||||
const container = getEnclosingBlockScopeContainer(symbol.valueDeclaration);
|
||||
if (isStatementWithLocals(container)) {
|
||||
const nodeLinks = getNodeLinks(symbol.valueDeclaration);
|
||||
if (!!resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) {
|
||||
if (resolveName(container.parent, symbol.escapedName, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ false)) {
|
||||
// redeclaration - always should be renamed
|
||||
links.isDeclarationWithCollidingName = true;
|
||||
}
|
||||
@@ -23669,7 +23686,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
return resolveName(location, reference.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined);
|
||||
return resolveName(location, reference.escapedText, SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias, /*nodeNotFoundMessage*/ undefined, /*nameArg*/ undefined, /*isUse*/ true);
|
||||
}
|
||||
|
||||
function getReferencedValueDeclaration(reference: Identifier): Declaration {
|
||||
@@ -23992,7 +24009,7 @@ namespace ts {
|
||||
return quickResult;
|
||||
}
|
||||
|
||||
let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node;
|
||||
let lastStatic: Node, lastDeclare: Node, lastAsync: Node, lastReadonly: Node;
|
||||
let flags = ModifierFlags.None;
|
||||
for (const modifier of node.modifiers) {
|
||||
if (modifier.kind !== SyntaxKind.ReadonlyKeyword) {
|
||||
@@ -24014,13 +24031,6 @@ namespace ts {
|
||||
case SyntaxKind.PrivateKeyword:
|
||||
const text = visibilityToString(modifierToFlag(modifier.kind));
|
||||
|
||||
if (modifier.kind === SyntaxKind.ProtectedKeyword) {
|
||||
lastProtected = modifier;
|
||||
}
|
||||
else if (modifier.kind === SyntaxKind.PrivateKeyword) {
|
||||
lastPrivate = modifier;
|
||||
}
|
||||
|
||||
if (flags & ModifierFlags.AccessibilityModifier) {
|
||||
return grammarErrorOnNode(modifier, Diagnostics.Accessibility_modifier_already_seen);
|
||||
}
|
||||
|
||||
@@ -3102,7 +3102,7 @@
|
||||
"category": "Message",
|
||||
"code": 6132
|
||||
},
|
||||
"'{0}' is declared but never used.": {
|
||||
"'{0}' is declared but its value is never read.": {
|
||||
"category": "Error",
|
||||
"code": 6133
|
||||
},
|
||||
@@ -3122,7 +3122,7 @@
|
||||
"category": "Error",
|
||||
"code": 6137
|
||||
},
|
||||
"Property '{0}' is declared but never used.": {
|
||||
"Property '{0}' is declared but its value is never read.": {
|
||||
"category": "Error",
|
||||
"code": 6138
|
||||
},
|
||||
|
||||
@@ -21,9 +21,6 @@ namespace ts {
|
||||
const compilerOptions = context.getCompilerOptions();
|
||||
const languageVersion = getEmitScriptTarget(compilerOptions);
|
||||
|
||||
// These variables contain state that changes as we descend into the tree.
|
||||
let currentSourceFile: SourceFile;
|
||||
|
||||
/**
|
||||
* Keeps track of whether expression substitution has been enabled for specific edge cases.
|
||||
* They are persisted between each SourceFile transformation and should not be reset.
|
||||
@@ -51,12 +48,8 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
currentSourceFile = node;
|
||||
|
||||
const visited = visitEachChild(node, visitor, context);
|
||||
addEmitHelpers(visited, context.readEmitHelpers());
|
||||
|
||||
currentSourceFile = undefined;
|
||||
return visited;
|
||||
}
|
||||
|
||||
|
||||
@@ -244,7 +244,6 @@ namespace ts {
|
||||
const previousOnSubstituteNode = context.onSubstituteNode;
|
||||
context.onSubstituteNode = onSubstituteNode;
|
||||
|
||||
let currentSourceFile: SourceFile;
|
||||
let renamedCatchVariables: Map<boolean>;
|
||||
let renamedCatchVariableDeclarations: Identifier[];
|
||||
|
||||
@@ -300,12 +299,9 @@ namespace ts {
|
||||
return node;
|
||||
}
|
||||
|
||||
currentSourceFile = node;
|
||||
|
||||
const visited = visitEachChild(node, visitor, context);
|
||||
addEmitHelpers(visited, context.readEmitHelpers());
|
||||
|
||||
currentSourceFile = undefined;
|
||||
return visited;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,6 @@ namespace ts {
|
||||
const commandLine = parseCommandLine(args);
|
||||
let configFileName: string; // Configuration file name (if any)
|
||||
let cachedConfigFileText: string; // Cached configuration file text, used for reparsing (if any)
|
||||
let configFileWatcher: FileWatcher; // Configuration file watcher
|
||||
let directoryWatcher: FileWatcher; // Directory watcher to monitor source file addition/removal
|
||||
let cachedProgram: Program; // Program cached from last compilation
|
||||
let rootFileNames: string[]; // Root fileNames for compilation
|
||||
@@ -189,7 +188,7 @@ namespace ts {
|
||||
return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped);
|
||||
}
|
||||
if (configFileName) {
|
||||
configFileWatcher = sys.watchFile(configFileName, configFileChanged);
|
||||
sys.watchFile(configFileName, configFileChanged);
|
||||
}
|
||||
if (sys.watchDirectory && configFileName) {
|
||||
const directory = ts.getDirectoryPath(configFileName);
|
||||
|
||||
@@ -500,13 +500,11 @@ namespace ts {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
return true;
|
||||
default:
|
||||
staticAssertNever(node);
|
||||
assertTypeIsNever(node);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function staticAssertNever(_: never): void {}
|
||||
|
||||
// Gets the nearest enclosing block scope container that has the provided node
|
||||
// as a descendant, that is not the provided node.
|
||||
export function getEnclosingBlockScopeContainer(node: Node): Node {
|
||||
@@ -3504,6 +3502,46 @@ namespace ts {
|
||||
export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags {
|
||||
return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags;
|
||||
}
|
||||
|
||||
export function isWriteOnlyAccess(node: Node) {
|
||||
return accessKind(node) === AccessKind.Write;
|
||||
}
|
||||
|
||||
export function isWriteAccess(node: Node) {
|
||||
return accessKind(node) !== AccessKind.Read;
|
||||
}
|
||||
|
||||
const enum AccessKind {
|
||||
/** Only reads from a variable. */
|
||||
Read,
|
||||
/** Only writes to a variable without using the result. E.g.: `x++;`. */
|
||||
Write,
|
||||
/** Writes to a variable and uses the result as an expression. E.g.: `f(x++);`. */
|
||||
ReadWrite
|
||||
}
|
||||
function accessKind(node: Node): AccessKind {
|
||||
const { parent } = node;
|
||||
if (!parent) return AccessKind.Read;
|
||||
|
||||
switch (parent.kind) {
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
const { operator } = parent as PrefixUnaryExpression | PostfixUnaryExpression;
|
||||
return operator === SyntaxKind.PlusPlusToken || operator === SyntaxKind.MinusMinusToken ? writeOrReadWrite() : AccessKind.Read;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
const { left, operatorToken } = parent as BinaryExpression;
|
||||
return left === node && isAssignmentOperator(operatorToken.kind) ? writeOrReadWrite() : AccessKind.Read;
|
||||
case SyntaxKind.PropertyAccessExpression:
|
||||
return (parent as PropertyAccessExpression).name !== node ? AccessKind.Read : accessKind(parent);
|
||||
default:
|
||||
return AccessKind.Read;
|
||||
}
|
||||
|
||||
function writeOrReadWrite(): AccessKind {
|
||||
// If grandparent is not an ExpressionStatement, this is used as an expression in addition to having a side effect.
|
||||
return parent.parent && parent.parent.kind === SyntaxKind.ExpressionStatement ? AccessKind.Write : AccessKind.ReadWrite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace ts {
|
||||
|
||||
@@ -11,19 +11,13 @@ const enum CompilerTestType {
|
||||
class CompilerBaselineRunner extends RunnerBase {
|
||||
private basePath = "tests/cases";
|
||||
private testSuiteName: TestRunnerKind;
|
||||
private errors: boolean;
|
||||
private emit: boolean;
|
||||
private decl: boolean;
|
||||
private output: boolean;
|
||||
|
||||
public options: string;
|
||||
|
||||
constructor(public testType: CompilerTestType) {
|
||||
super();
|
||||
this.errors = true;
|
||||
this.emit = true;
|
||||
this.decl = true;
|
||||
this.output = true;
|
||||
if (testType === CompilerTestType.Conformance) {
|
||||
this.testSuiteName = "conformance";
|
||||
}
|
||||
@@ -214,26 +208,14 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
|
||||
private parseOptions() {
|
||||
if (this.options && this.options.length > 0) {
|
||||
this.errors = false;
|
||||
this.emit = false;
|
||||
this.decl = false;
|
||||
this.output = false;
|
||||
|
||||
const opts = this.options.split(",");
|
||||
for (let i = 0; i < opts.length; i++) {
|
||||
switch (opts[i]) {
|
||||
case "error":
|
||||
this.errors = true;
|
||||
break;
|
||||
case "emit":
|
||||
this.emit = true;
|
||||
break;
|
||||
case "decl":
|
||||
this.decl = true;
|
||||
break;
|
||||
case "output":
|
||||
this.output = true;
|
||||
break;
|
||||
default:
|
||||
throw new Error("unsupported flag");
|
||||
}
|
||||
|
||||
@@ -2758,11 +2758,11 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
private getSelection() {
|
||||
return ({
|
||||
private getSelection(): ts.TextRange {
|
||||
return {
|
||||
pos: this.currentCaretPosition,
|
||||
end: this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
public verifyRefactorAvailable(negative: boolean, name: string, actionName?: string) {
|
||||
@@ -2803,7 +2803,7 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public applyRefactor({ refactorName, actionName, actionDescription }: FourSlashInterface.ApplyRefactorOptions) {
|
||||
public applyRefactor({ refactorName, actionName, actionDescription, newContent: newContentWithRenameMarker }: FourSlashInterface.ApplyRefactorOptions) {
|
||||
const range = this.getSelection();
|
||||
const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range);
|
||||
const refactor = refactors.find(r => r.name === refactorName);
|
||||
@@ -2823,6 +2823,35 @@ namespace FourSlash {
|
||||
for (const edit of editInfo.edits) {
|
||||
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
|
||||
}
|
||||
|
||||
const { renamePosition, newContent } = parseNewContent();
|
||||
|
||||
this.verifyCurrentFileContent(newContent);
|
||||
|
||||
if (renamePosition === undefined) {
|
||||
if (editInfo.renameLocation !== undefined) {
|
||||
this.raiseError(`Did not expect a rename location, got ${editInfo.renameLocation}`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// TODO: test editInfo.renameFilename value
|
||||
assert.isDefined(editInfo.renameFilename);
|
||||
if (renamePosition !== editInfo.renameLocation) {
|
||||
this.raiseError(`Expected rename position of ${renamePosition}, but got ${editInfo.renameLocation}`);
|
||||
}
|
||||
}
|
||||
|
||||
function parseNewContent(): { renamePosition: number | undefined, newContent: string } {
|
||||
const renamePosition = newContentWithRenameMarker.indexOf("/*RENAME*/");
|
||||
if (renamePosition === -1) {
|
||||
return { renamePosition: undefined, newContent: newContentWithRenameMarker };
|
||||
}
|
||||
else {
|
||||
const newContent = newContentWithRenameMarker.slice(0, renamePosition) + newContentWithRenameMarker.slice(renamePosition + "/*RENAME*/".length);
|
||||
return { renamePosition, newContent };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public verifyFileAfterApplyingRefactorAtMarker(
|
||||
@@ -4319,6 +4348,7 @@ namespace FourSlashInterface {
|
||||
refactorName: string;
|
||||
actionName: string;
|
||||
actionDescription: string;
|
||||
newContent: string;
|
||||
}
|
||||
|
||||
export interface CompletionsAtOptions {
|
||||
|
||||
@@ -55,7 +55,6 @@ namespace ts.projectSystem {
|
||||
let configFile: FileOrFolder;
|
||||
let changeModuleFile1ShapeRequest1: server.protocol.Request;
|
||||
let changeModuleFile1InternalRequest1: server.protocol.Request;
|
||||
let changeModuleFile1ShapeRequest2: server.protocol.Request;
|
||||
// A compile on save affected file request using file1
|
||||
let moduleFile1FileListRequest: server.protocol.Request;
|
||||
|
||||
@@ -112,16 +111,6 @@ namespace ts.projectSystem {
|
||||
insertString: `var T1: number;`
|
||||
});
|
||||
|
||||
// Change the content of file1 to `export var T: number;export function Foo() { };`
|
||||
changeModuleFile1ShapeRequest2 = makeSessionRequest<server.protocol.ChangeRequestArgs>(CommandNames.Change, {
|
||||
file: moduleFile1.path,
|
||||
line: 1,
|
||||
offset: 1,
|
||||
endLine: 1,
|
||||
endOffset: 1,
|
||||
insertString: `export var T2: number;`
|
||||
});
|
||||
|
||||
moduleFile1FileListRequest = makeSessionRequest<server.protocol.FileRequestArgs>(CommandNames.CompileOnSaveAffectedFileList, { file: moduleFile1.path, projectFileName: configFile.path });
|
||||
});
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace ts {
|
||||
testExtractRange(`
|
||||
function f() {
|
||||
while (true) {
|
||||
[#|
|
||||
[#|
|
||||
if (x) {
|
||||
return;
|
||||
} |]
|
||||
@@ -234,7 +234,7 @@ namespace ts {
|
||||
testExtractRange(`
|
||||
function f() {
|
||||
while (true) {
|
||||
[#|
|
||||
[#|
|
||||
[$|if (x) {
|
||||
}
|
||||
return;|]
|
||||
@@ -655,6 +655,60 @@ function test(x: number) {
|
||||
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() { }
|
||||
}`);
|
||||
});
|
||||
|
||||
@@ -691,9 +745,12 @@ function test(x: number) {
|
||||
data.push(`// ==ORIGINAL==`);
|
||||
data.push(sourceFile.text);
|
||||
for (const r of results) {
|
||||
const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes;
|
||||
const { renameLocation, edits } = refactor.extractMethod.getExtractionAtIndex(result.targetRange, context, results.indexOf(r));
|
||||
assert.lengthOf(edits, 1);
|
||||
data.push(`// ==SCOPE::${r.scopeDescription}==`);
|
||||
data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges));
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -366,13 +366,11 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
const host = createServerHost([file1, file2]);
|
||||
let enqueueIsCalled = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
|
||||
@@ -586,9 +586,7 @@ namespace ts.server {
|
||||
const response = this.processResponse<protocol.GetEditsForRefactorResponse>(request);
|
||||
|
||||
if (!response.body) {
|
||||
return {
|
||||
edits: []
|
||||
};
|
||||
return { edits: [], renameFilename: undefined, renameLocation: undefined };
|
||||
}
|
||||
|
||||
const edits: FileTextChanges[] = this.convertCodeEditsToTextChanges(response.body.edits);
|
||||
|
||||
@@ -585,7 +585,6 @@ namespace ts.server {
|
||||
function createPollingWatchedFileSet(interval = 2500, chunkSize = 30) {
|
||||
const watchedFiles: WatchedFile[] = [];
|
||||
let nextFileToCheck = 0;
|
||||
let watchTimer: any;
|
||||
return { getModifiedTime, poll, startWatchTimer, addFile, removeFile };
|
||||
|
||||
function getModifiedTime(fileName: string): Date {
|
||||
@@ -622,7 +621,7 @@ namespace ts.server {
|
||||
// stat due to inconsistencies of fs.watch
|
||||
// and efficiency of stat on modern filesystems
|
||||
function startWatchTimer() {
|
||||
watchTimer = setInterval(() => {
|
||||
setInterval(() => {
|
||||
let count = 0;
|
||||
let nextToCheck = nextFileToCheck;
|
||||
let firstCheck = -1;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
namespace ts.codefix {
|
||||
registerCodeFix({
|
||||
errorCodes: [
|
||||
Diagnostics._0_is_declared_but_never_used.code,
|
||||
Diagnostics.Property_0_is_declared_but_never_used.code
|
||||
Diagnostics._0_is_declared_but_its_value_is_never_read.code,
|
||||
Diagnostics.Property_0_is_declared_but_its_value_is_never_read.code
|
||||
],
|
||||
getCodeActions: (context: CodeFixContext) => {
|
||||
const sourceFile = context.sourceFile;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace ts.FindAllReferences {
|
||||
return {
|
||||
fileName: node.getSourceFile().fileName,
|
||||
textSpan: getTextSpan(node),
|
||||
isWriteAccess: isWriteAccess(node),
|
||||
isWriteAccess: isWriteAccessForReference(node),
|
||||
isDefinition: node.kind === SyntaxKind.DefaultKeyword
|
||||
|| isAnyDeclarationName(node)
|
||||
|| isLiteralComputedPropertyDeclarationName(node),
|
||||
@@ -224,7 +224,7 @@ namespace ts.FindAllReferences {
|
||||
|
||||
const { node, isInString } = entry;
|
||||
const fileName = entry.node.getSourceFile().fileName;
|
||||
const writeAccess = isWriteAccess(node);
|
||||
const writeAccess = isWriteAccessForReference(node);
|
||||
const span: HighlightSpan = {
|
||||
textSpan: getTextSpan(node),
|
||||
kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
|
||||
@@ -244,21 +244,8 @@ namespace ts.FindAllReferences {
|
||||
}
|
||||
|
||||
/** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
|
||||
function isWriteAccess(node: Node): boolean {
|
||||
if (node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { parent } = node;
|
||||
switch (parent && parent.kind) {
|
||||
case SyntaxKind.PostfixUnaryExpression:
|
||||
case SyntaxKind.PrefixUnaryExpression:
|
||||
return true;
|
||||
case SyntaxKind.BinaryExpression:
|
||||
return (<BinaryExpression>parent).left === node && isAssignmentOperator((<BinaryExpression>parent).operatorToken.kind);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
function isWriteAccessForReference(node: Node): boolean {
|
||||
return node.kind === SyntaxKind.DefaultKeyword || isAnyDeclarationName(node) || isWriteAccess(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -273,13 +273,11 @@ namespace ts {
|
||||
|
||||
// skip open bracket
|
||||
token = nextToken();
|
||||
let i = 0;
|
||||
// scan until ']' or EOF
|
||||
while (token !== SyntaxKind.CloseBracketToken && token !== SyntaxKind.EndOfFileToken) {
|
||||
// record string literals as module names
|
||||
if (token === SyntaxKind.StringLiteral) {
|
||||
recordModuleName();
|
||||
i++;
|
||||
}
|
||||
|
||||
token = nextToken();
|
||||
|
||||
@@ -97,7 +97,9 @@ namespace ts.refactor.convertFunctionToES6Class {
|
||||
}
|
||||
|
||||
return {
|
||||
edits: changeTracker.getChanges()
|
||||
edits: changeTracker.getChanges(),
|
||||
renameFilename: undefined,
|
||||
renameLocation: undefined,
|
||||
};
|
||||
|
||||
function deleteNode(node: Node, inList = false) {
|
||||
|
||||
@@ -31,16 +31,16 @@ namespace ts.refactor.extractMethod {
|
||||
const usedNames: Map<boolean> = createMap();
|
||||
|
||||
let i = 0;
|
||||
for (const extr of extractions) {
|
||||
for (const { scopeDescription, errors } of extractions) {
|
||||
// Skip these since we don't have a way to report errors yet
|
||||
if (extr.errors && extr.errors.length) {
|
||||
if (errors.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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, [extr.scopeDescription]);
|
||||
const description = formatStringFromArgs(Diagnostics.Extract_to_0.message, [scopeDescription]);
|
||||
if (!usedNames.has(description)) {
|
||||
usedNames.set(description, true);
|
||||
actions.push({
|
||||
@@ -75,10 +75,7 @@ namespace ts.refactor.extractMethod {
|
||||
const index = +parsedIndexMatch[1];
|
||||
Debug.assert(isFinite(index), "Expected to parse a finite number from the scope index");
|
||||
|
||||
const extractions = getPossibleExtractions(targetRange, context, index);
|
||||
// Scope is no longer valid from when the user issued the refactor (??)
|
||||
Debug.assert(extractions !== undefined, "The extraction went missing? How?");
|
||||
return ({ edits: extractions[0].changes });
|
||||
return getExtractionAtIndex(targetRange, context, index);
|
||||
}
|
||||
|
||||
// Move these into diagnostic messages if they become user-facing
|
||||
@@ -102,7 +99,7 @@ namespace ts.refactor.extractMethod {
|
||||
export const CannotExtractAmbientBlock = createMessage("Cannot extract code from ambient contexts");
|
||||
}
|
||||
|
||||
export enum RangeFacts {
|
||||
enum RangeFacts {
|
||||
None = 0,
|
||||
HasReturn = 1 << 0,
|
||||
IsGenerator = 1 << 1,
|
||||
@@ -117,7 +114,7 @@ namespace ts.refactor.extractMethod {
|
||||
/**
|
||||
* Represents an expression or a list of statements that should be extracted with some extra information
|
||||
*/
|
||||
export interface TargetRange {
|
||||
interface TargetRange {
|
||||
readonly range: Expression | Statement[];
|
||||
readonly facts: RangeFacts;
|
||||
/**
|
||||
@@ -130,7 +127,7 @@ namespace ts.refactor.extractMethod {
|
||||
/**
|
||||
* Result of 'getRangeToExtract' operation: contains either a range or a list of errors
|
||||
*/
|
||||
export type RangeToExtract = {
|
||||
type RangeToExtract = {
|
||||
readonly targetRange?: never;
|
||||
readonly errors: ReadonlyArray<Diagnostic>;
|
||||
} | {
|
||||
@@ -141,18 +138,7 @@ namespace ts.refactor.extractMethod {
|
||||
/*
|
||||
* Scopes that can store newly extracted method
|
||||
*/
|
||||
export type Scope = FunctionLikeDeclaration | SourceFile | ModuleBlock | ClassLikeDeclaration;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export interface ExtractResultForScope {
|
||||
readonly scope: Scope;
|
||||
readonly scopeDescription: string;
|
||||
readonly changes?: FileTextChanges[];
|
||||
readonly errors?: Diagnostic[];
|
||||
}
|
||||
type Scope = FunctionLikeDeclaration | SourceFile | ModuleBlock | ClassLikeDeclaration;
|
||||
|
||||
/**
|
||||
* getRangeToExtract takes a span inside a text file and returns either an expression or an array
|
||||
@@ -160,6 +146,7 @@ namespace ts.refactor.extractMethod {
|
||||
* process may fail, in which case a set of errors is returned instead (these are currently
|
||||
* not shown to the user, but can be used by us diagnostically)
|
||||
*/
|
||||
// exported only for tests
|
||||
export function getRangeToExtract(sourceFile: SourceFile, span: TextSpan): RangeToExtract {
|
||||
const length = span.length || 0;
|
||||
// Walk up starting from the the start position until we find a non-SourceFile node that subsumes the selected span.
|
||||
@@ -458,7 +445,7 @@ namespace ts.refactor.extractMethod {
|
||||
* you may be able to extract into a class method *or* local closure *or* namespace function,
|
||||
* depending on what's in the extracted body.
|
||||
*/
|
||||
export function collectEnclosingScopes(range: TargetRange): Scope[] | undefined {
|
||||
function collectEnclosingScopes(range: TargetRange): Scope[] | undefined {
|
||||
let current: Node = isReadonlyArray(range.range) ? firstOrUndefined(range.range) : range.range;
|
||||
if (range.facts & RangeFacts.UsesThis) {
|
||||
// if range uses this as keyword or as type inside the class then it can only be extracted to a method of the containing class
|
||||
@@ -494,12 +481,32 @@ 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?");
|
||||
context.cancellationToken.throwIfCancellationRequested();
|
||||
return extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context);
|
||||
}
|
||||
|
||||
interface PossibleExtraction {
|
||||
readonly scopeDescription: string;
|
||||
readonly errors: 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.
|
||||
*/
|
||||
export function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext, requestedChangesIndex: number = undefined): ReadonlyArray<ExtractResultForScope> | undefined {
|
||||
// exported only for tests
|
||||
export function getPossibleExtractions(targetRange: TargetRange, context: RefactorContext): ReadonlyArray<PossibleExtraction> | undefined {
|
||||
const { scopes, readsAndWrites: { errorsPerScope } } = 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] }));
|
||||
}
|
||||
|
||||
function getPossibleExtractionsWorker(targetRange: TargetRange, context: RefactorContext): { readonly scopes: Scope[], readonly readsAndWrites: ReadsAndWrites } {
|
||||
const { file: sourceFile } = context;
|
||||
|
||||
if (targetRange === undefined) {
|
||||
@@ -512,35 +519,14 @@ namespace ts.refactor.extractMethod {
|
||||
}
|
||||
|
||||
const enclosingTextRange = getEnclosingTextRange(targetRange, sourceFile);
|
||||
const { target, usagesPerScope, errorsPerScope } = collectReadsAndWrites(
|
||||
const readsAndWrites = collectReadsAndWrites(
|
||||
targetRange,
|
||||
scopes,
|
||||
enclosingTextRange,
|
||||
sourceFile,
|
||||
context.program.getTypeChecker(),
|
||||
context.cancellationToken);
|
||||
|
||||
context.cancellationToken.throwIfCancellationRequested();
|
||||
|
||||
if (requestedChangesIndex !== undefined) {
|
||||
if (errorsPerScope[requestedChangesIndex].length) {
|
||||
return undefined;
|
||||
}
|
||||
return [extractFunctionInScope(target, scopes[requestedChangesIndex], usagesPerScope[requestedChangesIndex], targetRange, context)];
|
||||
}
|
||||
else {
|
||||
return scopes.map((scope, i) => {
|
||||
const errors = errorsPerScope[i];
|
||||
if (errors.length) {
|
||||
return {
|
||||
scope,
|
||||
scopeDescription: getDescriptionForScope(scope),
|
||||
errors
|
||||
};
|
||||
}
|
||||
return { scope, scopeDescription: getDescriptionForScope(scope) };
|
||||
});
|
||||
}
|
||||
return { scopes, readsAndWrites };
|
||||
}
|
||||
|
||||
function getDescriptionForScope(scope: Scope): string {
|
||||
@@ -583,34 +569,33 @@ namespace ts.refactor.extractMethod {
|
||||
: scope.externalModuleIndicator ? "module scope" : "global scope";
|
||||
}
|
||||
|
||||
function getUniqueName(isNameOkay: (name: string) => boolean) {
|
||||
function getUniqueName(fileText: string): string {
|
||||
let functionNameText = "newFunction";
|
||||
if (isNameOkay(functionNameText)) {
|
||||
return functionNameText;
|
||||
}
|
||||
let i = 1;
|
||||
while (!isNameOkay(functionNameText = `newFunction_${i}`)) {
|
||||
i++;
|
||||
for (let i = 1; fileText.indexOf(functionNameText) !== -1; i++) {
|
||||
functionNameText = `newFunction_${i}`;
|
||||
}
|
||||
return functionNameText;
|
||||
}
|
||||
|
||||
export function extractFunctionInScope(
|
||||
/**
|
||||
* 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 extractFunctionInScope(
|
||||
node: Statement | Expression | Block,
|
||||
scope: Scope,
|
||||
{ usages: usagesInScope, typeParameterUsages, substitutions }: ScopeUsages,
|
||||
range: TargetRange,
|
||||
context: RefactorContext): ExtractResultForScope {
|
||||
context: RefactorContext): RefactorEditInfo {
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
|
||||
// Make a unique name for the extracted function
|
||||
const file = scope.getSourceFile();
|
||||
const functionNameText: string = getUniqueName(n => !file.identifiers.has(n));
|
||||
const functionNameText = getUniqueName(file.text);
|
||||
const isJS = isInJavaScriptFile(scope);
|
||||
|
||||
const functionName = createIdentifier(functionNameText as string);
|
||||
const functionReference = createIdentifier(functionNameText as string);
|
||||
const functionName = createIdentifier(functionNameText);
|
||||
|
||||
let returnType: TypeNode = undefined;
|
||||
const parameters: ParameterDeclaration[] = [];
|
||||
@@ -660,7 +645,7 @@ namespace ts.refactor.extractMethod {
|
||||
returnType = checker.typeToTypeNode(contextualType);
|
||||
}
|
||||
|
||||
const { body, returnValueProperty } = transformFunctionBody(node);
|
||||
const { body, returnValueProperty } = transformFunctionBody(node, writes, substitutions, !!(range.facts & RangeFacts.HasReturn));
|
||||
let newFunction: MethodDeclaration | FunctionDeclaration;
|
||||
|
||||
if (isClassLike(scope)) {
|
||||
@@ -698,13 +683,21 @@ namespace ts.refactor.extractMethod {
|
||||
}
|
||||
|
||||
const changeTracker = textChanges.ChangeTracker.fromContext(context);
|
||||
// insert function at the end of the scope
|
||||
changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, { prefix: context.newLineCharacter, suffix: context.newLineCharacter });
|
||||
const minInsertionPos = (isReadonlyArray(range.range) ? lastOrUndefined(range.range) : range.range).end;
|
||||
const nodeToInsertBefore = getNodeToInsertBefore(minInsertionPos, scope);
|
||||
if (nodeToInsertBefore) {
|
||||
changeTracker.insertNodeBefore(context.file, nodeToInsertBefore, newFunction, { suffix: context.newLineCharacter + context.newLineCharacter });
|
||||
}
|
||||
else {
|
||||
changeTracker.insertNodeBefore(context.file, scope.getLastToken(), newFunction, { prefix: context.newLineCharacter, suffix: context.newLineCharacter });
|
||||
}
|
||||
|
||||
const newNodes: Node[] = [];
|
||||
// replace range with function call
|
||||
const called = getCalledExpression(scope, range, functionNameText);
|
||||
|
||||
let call: Expression = createCall(
|
||||
isClassLike(scope) ? createPropertyAccess(range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.getText()) : createThis(), functionReference) : functionReference,
|
||||
called,
|
||||
callTypeArguments, // Note that no attempt is made to take advantage of type argument inference
|
||||
callArguments);
|
||||
if (range.facts & RangeFacts.IsGenerator) {
|
||||
@@ -773,114 +766,174 @@ namespace ts.refactor.extractMethod {
|
||||
changeTracker.replaceNodeWithNodes(context.file, range.range, newNodes, { nodeSeparator: context.newLineCharacter });
|
||||
}
|
||||
|
||||
return {
|
||||
scope,
|
||||
scopeDescription: getDescriptionForScope(scope),
|
||||
changes: changeTracker.getChanges()
|
||||
};
|
||||
const edits = changeTracker.getChanges();
|
||||
const renameRange = isReadonlyArray(range.range) ? range.range[0] : range.range;
|
||||
|
||||
function getFirstDeclaration(type: Type): Declaration | undefined {
|
||||
let firstDeclaration = undefined;
|
||||
const renameFilename = renameRange.getSourceFile().fileName;
|
||||
const renameLocation = getRenameLocation(edits, renameFilename, functionNameText);
|
||||
return { renameFilename, renameLocation, edits };
|
||||
}
|
||||
|
||||
const symbol = type.symbol;
|
||||
if (symbol && symbol.declarations) {
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) {
|
||||
firstDeclaration = declaration;
|
||||
}
|
||||
function getRenameLocation(edits: ReadonlyArray<FileTextChanges>, renameFilename: string, functionNameText: string): number {
|
||||
let delta = 0;
|
||||
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;
|
||||
}
|
||||
delta += newText.length - span.length;
|
||||
}
|
||||
}
|
||||
throw new Error(); // Didn't find the text we inserted?
|
||||
}
|
||||
|
||||
function getFirstDeclaration(type: Type): Declaration | undefined {
|
||||
let firstDeclaration = undefined;
|
||||
|
||||
const symbol = type.symbol;
|
||||
if (symbol && symbol.declarations) {
|
||||
for (const declaration of symbol.declarations) {
|
||||
if (firstDeclaration === undefined || declaration.pos < firstDeclaration.pos) {
|
||||
firstDeclaration = declaration;
|
||||
}
|
||||
}
|
||||
|
||||
return firstDeclaration;
|
||||
}
|
||||
|
||||
function compareTypesByDeclarationOrder(
|
||||
{type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration},
|
||||
{type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) {
|
||||
return firstDeclaration;
|
||||
}
|
||||
|
||||
if (declaration1) {
|
||||
if (declaration2) {
|
||||
const positionDiff = declaration1.pos - declaration2.pos;
|
||||
if (positionDiff !== 0) {
|
||||
return positionDiff;
|
||||
}
|
||||
function compareTypesByDeclarationOrder(
|
||||
{type: type1, declaration: declaration1}: {type: Type, declaration?: Declaration},
|
||||
{type: type2, declaration: declaration2}: {type: Type, declaration?: Declaration}) {
|
||||
|
||||
if (declaration1) {
|
||||
if (declaration2) {
|
||||
const positionDiff = declaration1.pos - declaration2.pos;
|
||||
if (positionDiff !== 0) {
|
||||
return positionDiff;
|
||||
}
|
||||
else {
|
||||
return 1; // Sort undeclared type parameters to the front.
|
||||
}
|
||||
}
|
||||
else if (declaration2) {
|
||||
return -1; // Sort undeclared type parameters to the front.
|
||||
}
|
||||
|
||||
const name1 = type1.symbol ? type1.symbol.getName() : "";
|
||||
const name2 = type2.symbol ? type2.symbol.getName() : "";
|
||||
const nameDiff = compareStrings(name1, name2);
|
||||
if (nameDiff !== 0) {
|
||||
return nameDiff;
|
||||
}
|
||||
|
||||
// IDs are guaranteed to be unique, so this ensures a total ordering.
|
||||
return type1.id - type2.id;
|
||||
}
|
||||
|
||||
function getPropertyAssignmentsForWrites(writes: UsageEntry[]) {
|
||||
return writes.map(w => createShorthandPropertyAssignment(w.symbol.name));
|
||||
}
|
||||
|
||||
function generateReturnValueProperty() {
|
||||
return "__return";
|
||||
}
|
||||
|
||||
function transformFunctionBody(body: Node) {
|
||||
if (isBlock(body) && !writes && substitutions.size === 0) {
|
||||
// already block, no writes to propagate back, no substitutions - can use node as is
|
||||
return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined };
|
||||
}
|
||||
let returnValueProperty: string;
|
||||
const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(<Expression>body)]);
|
||||
// rewrite body if either there are writes that should be propagated back via return statements or there are substitutions
|
||||
if (writes || substitutions.size) {
|
||||
const rewrittenStatements = visitNodes(statements, visitor).slice();
|
||||
if (writes && !(range.facts & RangeFacts.HasReturn) && isStatement(body)) {
|
||||
// add return at the end to propagate writes back in case if control flow falls out of the function body
|
||||
// it is ok to know that range has at least one return since it we only allow unconditional returns
|
||||
const assignments = getPropertyAssignmentsForWrites(writes);
|
||||
if (assignments.length === 1) {
|
||||
rewrittenStatements.push(createReturn(assignments[0].name));
|
||||
}
|
||||
else {
|
||||
rewrittenStatements.push(createReturn(createObjectLiteral(assignments)));
|
||||
}
|
||||
}
|
||||
return { body: createBlock(rewrittenStatements, /*multiLine*/ true), returnValueProperty };
|
||||
}
|
||||
else {
|
||||
return { body: createBlock(statements, /*multiLine*/ true), returnValueProperty: undefined };
|
||||
}
|
||||
|
||||
function visitor(node: Node): VisitResult<Node> {
|
||||
if (node.kind === SyntaxKind.ReturnStatement && writes) {
|
||||
const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes);
|
||||
if ((<ReturnStatement>node).expression) {
|
||||
if (!returnValueProperty) {
|
||||
returnValueProperty = generateReturnValueProperty();
|
||||
}
|
||||
assignments.unshift(createPropertyAssignment(returnValueProperty, visitNode((<ReturnStatement>node).expression, visitor)));
|
||||
}
|
||||
if (assignments.length === 1) {
|
||||
return createReturn(assignments[0].name as Expression);
|
||||
}
|
||||
else {
|
||||
return createReturn(createObjectLiteral(assignments));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const substitution = substitutions.get(getNodeId(node).toString());
|
||||
return substitution || visitEachChild(node, visitor, nullTransformationContext);
|
||||
}
|
||||
return 1; // Sort undeclared type parameters to the front.
|
||||
}
|
||||
}
|
||||
else if (declaration2) {
|
||||
return -1; // Sort undeclared type parameters to the front.
|
||||
}
|
||||
|
||||
const name1 = type1.symbol ? type1.symbol.getName() : "";
|
||||
const name2 = type2.symbol ? type2.symbol.getName() : "";
|
||||
const nameDiff = compareStrings(name1, name2);
|
||||
if (nameDiff !== 0) {
|
||||
return nameDiff;
|
||||
}
|
||||
|
||||
// IDs are guaranteed to be unique, so this ensures a total ordering.
|
||||
return type1.id - type2.id;
|
||||
}
|
||||
|
||||
function getCalledExpression(scope: Node, range: TargetRange, functionNameText: string): Expression {
|
||||
const functionReference = createIdentifier(functionNameText);
|
||||
if (isClassLike(scope)) {
|
||||
const lhs = range.facts & RangeFacts.InStaticRegion ? createIdentifier(scope.name.text) : createThis();
|
||||
return createPropertyAccess(lhs, functionReference);
|
||||
}
|
||||
else {
|
||||
return functionReference;
|
||||
}
|
||||
}
|
||||
|
||||
function transformFunctionBody(body: Node, writes: ReadonlyArray<UsageEntry>, substitutions: ReadonlyMap<Node>, hasReturn: boolean): { body: Block, returnValueProperty: string } {
|
||||
if (isBlock(body) && !writes && substitutions.size === 0) {
|
||||
// already block, no writes to propagate back, no substitutions - can use node as is
|
||||
return { body: createBlock(body.statements, /*multLine*/ true), returnValueProperty: undefined };
|
||||
}
|
||||
let returnValueProperty: string;
|
||||
const statements = createNodeArray(isBlock(body) ? body.statements.slice(0) : [isStatement(body) ? body : createReturn(<Expression>body)]);
|
||||
// rewrite body if either there are writes that should be propagated back via return statements or there are substitutions
|
||||
if (writes || substitutions.size) {
|
||||
const rewrittenStatements = visitNodes(statements, visitor).slice();
|
||||
if (writes && !hasReturn && isStatement(body)) {
|
||||
// add return at the end to propagate writes back in case if control flow falls out of the function body
|
||||
// it is ok to know that range has at least one return since it we only allow unconditional returns
|
||||
const assignments = getPropertyAssignmentsForWrites(writes);
|
||||
if (assignments.length === 1) {
|
||||
rewrittenStatements.push(createReturn(assignments[0].name));
|
||||
}
|
||||
else {
|
||||
rewrittenStatements.push(createReturn(createObjectLiteral(assignments)));
|
||||
}
|
||||
}
|
||||
return { body: createBlock(rewrittenStatements, /*multiLine*/ true), returnValueProperty };
|
||||
}
|
||||
else {
|
||||
return { body: createBlock(statements, /*multiLine*/ true), returnValueProperty: undefined };
|
||||
}
|
||||
|
||||
function visitor(node: Node): VisitResult<Node> {
|
||||
if (node.kind === SyntaxKind.ReturnStatement && writes) {
|
||||
const assignments: ObjectLiteralElementLike[] = getPropertyAssignmentsForWrites(writes);
|
||||
if ((<ReturnStatement>node).expression) {
|
||||
if (!returnValueProperty) {
|
||||
returnValueProperty = "__return";
|
||||
}
|
||||
assignments.unshift(createPropertyAssignment(returnValueProperty, visitNode((<ReturnStatement>node).expression, visitor)));
|
||||
}
|
||||
if (assignments.length === 1) {
|
||||
return createReturn(assignments[0].name as Expression);
|
||||
}
|
||||
else {
|
||||
return createReturn(createObjectLiteral(assignments));
|
||||
}
|
||||
}
|
||||
else {
|
||||
const substitution = substitutions.get(getNodeId(node).toString());
|
||||
return substitution || visitEachChild(node, visitor, nullTransformationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getStatementsOrClassElements(scope: Scope): ReadonlyArray<Statement> | ReadonlyArray<ClassElement> {
|
||||
if (isFunctionLike(scope)) {
|
||||
const body = scope.body;
|
||||
if (isBlock(body)) {
|
||||
return body.statements;
|
||||
}
|
||||
}
|
||||
else if (isModuleBlock(scope) || isSourceFile(scope)) {
|
||||
return scope.statements;
|
||||
}
|
||||
else if (isClassLike(scope)) {
|
||||
return scope.members;
|
||||
}
|
||||
else {
|
||||
assertTypeIsNever(scope);
|
||||
}
|
||||
|
||||
return emptyArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* If `scope` contains a function after `minPos`, then return the first such function.
|
||||
* Otherwise, return `undefined`.
|
||||
*/
|
||||
function getNodeToInsertBefore(minPos: number, scope: Scope): Node | undefined {
|
||||
const children = getStatementsOrClassElements(scope);
|
||||
for (const child of children) {
|
||||
if (child.pos >= minPos && isFunctionLike(child) && !isConstructorDeclaration(child)) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertyAssignmentsForWrites(writes: ReadonlyArray<UsageEntry>): ShorthandPropertyAssignment[] {
|
||||
return writes.map(w => createShorthandPropertyAssignment(w.symbol.name));
|
||||
}
|
||||
|
||||
function isReadonlyArray(v: any): v is ReadonlyArray<any> {
|
||||
@@ -909,25 +962,30 @@ namespace ts.refactor.extractMethod {
|
||||
Write = 2
|
||||
}
|
||||
|
||||
export interface UsageEntry {
|
||||
interface UsageEntry {
|
||||
readonly usage: Usage;
|
||||
readonly symbol: Symbol;
|
||||
readonly node: Node;
|
||||
}
|
||||
|
||||
export interface ScopeUsages {
|
||||
usages: Map<UsageEntry>;
|
||||
typeParameterUsages: Map<TypeParameter>; // Key is type ID
|
||||
substitutions: Map<Node>;
|
||||
interface ScopeUsages {
|
||||
readonly usages: Map<UsageEntry>;
|
||||
readonly typeParameterUsages: Map<TypeParameter>; // Key is type ID
|
||||
readonly substitutions: Map<Node>;
|
||||
}
|
||||
|
||||
interface ReadsAndWrites {
|
||||
readonly target: Expression | Block;
|
||||
readonly usagesPerScope: ReadonlyArray<ScopeUsages>;
|
||||
readonly errorsPerScope: ReadonlyArray<ReadonlyArray<Diagnostic>>;
|
||||
}
|
||||
function collectReadsAndWrites(
|
||||
targetRange: TargetRange,
|
||||
scopes: Scope[],
|
||||
enclosingTextRange: TextRange,
|
||||
sourceFile: SourceFile,
|
||||
checker: TypeChecker,
|
||||
cancellationToken: CancellationToken) {
|
||||
cancellationToken: CancellationToken): ReadsAndWrites {
|
||||
|
||||
const allTypeParameterUsages = createMap<TypeParameter>(); // Key is type ID
|
||||
const usagesPerScope: ScopeUsages[] = [];
|
||||
|
||||
@@ -414,8 +414,8 @@ namespace ts {
|
||||
*/
|
||||
export interface RefactorEditInfo {
|
||||
edits: FileTextChanges[];
|
||||
renameFilename?: string;
|
||||
renameLocation?: number;
|
||||
renameFilename: string | undefined;
|
||||
renameLocation: number | undefined;
|
||||
}
|
||||
|
||||
export interface TextInsertion {
|
||||
|
||||
Reference in New Issue
Block a user