From c4362ac4ba6d19b5ee7fc6f2bb316211b95fe37e Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 7 Feb 2018 13:25:36 -0800 Subject: [PATCH] Simplify use of FindAllReferences in inferFromUsage (#20551) --- src/services/codefixes/inferFromUsage.ts | 109 +++++++++++------------ src/services/documentHighlights.ts | 6 +- src/services/findAllReferences.ts | 82 ++++++++--------- src/services/services.ts | 2 +- 4 files changed, 96 insertions(+), 103 deletions(-) diff --git a/src/services/codefixes/inferFromUsage.ts b/src/services/codefixes/inferFromUsage.ts index 2f3dae01acf..2abd82fa7a7 100644 --- a/src/services/codefixes/inferFromUsage.ts +++ b/src/services/codefixes/inferFromUsage.ts @@ -74,11 +74,11 @@ namespace ts.codefix { // Variable and Property declarations case Diagnostics.Member_0_implicitly_has_an_1_type.code: case Diagnostics.Variable_0_implicitly_has_type_1_in_some_locations_where_its_type_cannot_be_determined.code: - return getCodeActionForVariableDeclaration(token.parent, sourceFile, program, cancellationToken); + return getCodeActionForVariableDeclaration(token.parent, program, cancellationToken); case Diagnostics.Variable_0_implicitly_has_an_1_type.code: { const symbol = program.getTypeChecker().getSymbolAtLocation(token); - return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(symbol.valueDeclaration, sourceFile, program, cancellationToken); + return symbol && symbol.valueDeclaration && getCodeActionForVariableDeclaration(symbol.valueDeclaration, program, cancellationToken); } } @@ -91,7 +91,7 @@ namespace ts.codefix { // Parameter declarations case Diagnostics.Parameter_0_implicitly_has_an_1_type.code: if (isSetAccessor(containingFunction)) { - return getCodeActionForSetAccessor(containingFunction, sourceFile, program, cancellationToken); + return getCodeActionForSetAccessor(containingFunction, program, cancellationToken); } // falls through case Diagnostics.Rest_parameter_0_implicitly_has_an_any_type.code: @@ -106,7 +106,7 @@ namespace ts.codefix { // Set Accessor declarations case Diagnostics.Property_0_implicitly_has_type_any_because_its_set_accessor_lacks_a_parameter_type_annotation.code: - return isSetAccessor(containingFunction) ? getCodeActionForSetAccessor(containingFunction, sourceFile, program, cancellationToken) : undefined; + return isSetAccessor(containingFunction) ? getCodeActionForSetAccessor(containingFunction, program, cancellationToken) : undefined; default: throw Debug.fail(String(errorCode)); @@ -127,9 +127,9 @@ namespace ts.codefix { } } - function getCodeActionForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Fix | undefined { + function getCodeActionForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration | PropertySignature, program: Program, cancellationToken: CancellationToken): Fix | undefined { if (!isIdentifier(declaration.name)) return undefined; - const type = inferTypeForVariableFromUsage(declaration.name, sourceFile, program, cancellationToken); + const type = inferTypeForVariableFromUsage(declaration.name, program, cancellationToken); return makeFix(declaration, declaration.name.getEnd(), type, program); } @@ -151,7 +151,7 @@ namespace ts.codefix { } const types = inferTypeForParametersFromUsage(containingFunction, sourceFile, program, cancellationToken) || - containingFunction.parameters.map(p => isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, sourceFile, program, cancellationToken) : undefined); + containingFunction.parameters.map(p => isIdentifier(p.name) ? inferTypeForVariableFromUsage(p.name, program, cancellationToken) : undefined); if (!types) return undefined; // We didn't actually find a set of type inference positions matching each parameter position @@ -164,14 +164,14 @@ namespace ts.codefix { return textChanges.length ? { declaration: parameterDeclaration, textChanges } : undefined; } - function getCodeActionForSetAccessor(setAccessorDeclaration: SetAccessorDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Fix | undefined { + function getCodeActionForSetAccessor(setAccessorDeclaration: SetAccessorDeclaration, program: Program, cancellationToken: CancellationToken): Fix | undefined { const setAccessorParameter = setAccessorDeclaration.parameters[0]; if (!setAccessorParameter || !isIdentifier(setAccessorDeclaration.name) || !isIdentifier(setAccessorParameter.name)) { return undefined; } - const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, sourceFile, program, cancellationToken) || - inferTypeForVariableFromUsage(setAccessorParameter.name, sourceFile, program, cancellationToken); + const type = inferTypeForVariableFromUsage(setAccessorDeclaration.name, program, cancellationToken) || + inferTypeForVariableFromUsage(setAccessorParameter.name, program, cancellationToken); return makeFix(setAccessorParameter, setAccessorParameter.name.getEnd(), type, program); } @@ -180,7 +180,7 @@ namespace ts.codefix { return undefined; } - const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name, sourceFile, program, cancellationToken); + const type = inferTypeForVariableFromUsage(getAccessorDeclaration.name, program, cancellationToken); const closeParenToken = findChildOfKind(getAccessorDeclaration, SyntaxKind.CloseParenToken, sourceFile); return makeFix(getAccessorDeclaration, closeParenToken.getEnd(), type, program); } @@ -194,23 +194,14 @@ namespace ts.codefix { return typeString === undefined ? undefined : createTextChangeFromStartLength(start, 0, `: ${typeString}`); } - function getReferences(token: PropertyName | Token, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Identifier[] { - const references = FindAllReferences.findReferencedSymbols( - program, - cancellationToken, - program.getSourceFiles(), - sourceFile, - token.getStart(sourceFile)); - - if (!references || references.length !== 1) { - return []; - } - - return references[0].references.map(r => getTokenAtPosition(program.getSourceFile(r.fileName), r.textSpan.start, /*includeJsDocComment*/ false)); + function getReferences(token: PropertyName | Token, program: Program, cancellationToken: CancellationToken): ReadonlyArray { + // Position shouldn't matter since token is not a SourceFile. + return mapDefined(FindAllReferences.getReferenceEntriesForNode(-1, token, program, program.getSourceFiles(), cancellationToken), entry => + entry.type === "node" ? tryCast(entry.node, isIdentifier) : undefined); } - function inferTypeForVariableFromUsage(token: Identifier, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): Type | undefined { - return InferFromReference.inferTypeFromReferences(getReferences(token, sourceFile, program, cancellationToken), program.getTypeChecker(), cancellationToken); + function inferTypeForVariableFromUsage(token: Identifier, program: Program, cancellationToken: CancellationToken): Type | undefined { + return InferFromReference.inferTypeFromReferences(getReferences(token, program, cancellationToken), program.getTypeChecker(), cancellationToken); } function inferTypeForParametersFromUsage(containingFunction: FunctionLikeDeclaration, sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): (Type | undefined)[] | undefined { @@ -224,7 +215,7 @@ namespace ts.codefix { findChildOfKind>(containingFunction, SyntaxKind.ConstructorKeyword, sourceFile) : containingFunction.name; if (searchToken) { - return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, sourceFile, program, cancellationToken), containingFunction, program.getTypeChecker(), cancellationToken); + return InferFromReference.inferTypeForParametersFromReferences(getReferences(searchToken, program, cancellationToken), containingFunction, program.getTypeChecker(), cancellationToken); } } } @@ -292,7 +283,7 @@ namespace ts.codefix { stringIndexContext?: UsageContext; } - export function inferTypeFromReferences(references: Identifier[], checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined { + export function inferTypeFromReferences(references: ReadonlyArray, checker: TypeChecker, cancellationToken: CancellationToken): Type | undefined { const usageContext: UsageContext = {}; for (const reference of references) { cancellationToken.throwIfCancellationRequested(); @@ -301,43 +292,45 @@ namespace ts.codefix { return getTypeFromUsageContext(usageContext, checker); } - export function inferTypeForParametersFromReferences(references: Identifier[], declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | undefined { + export function inferTypeForParametersFromReferences(references: ReadonlyArray, declaration: FunctionLikeDeclaration, checker: TypeChecker, cancellationToken: CancellationToken): (Type | undefined)[] | undefined { if (references.length === 0) { return undefined; } - if (declaration.parameters) { - const usageContext: UsageContext = {}; - for (const reference of references) { - cancellationToken.throwIfCancellationRequested(); - inferTypeFromContext(reference, checker, usageContext); - } - const isConstructor = declaration.kind === SyntaxKind.Constructor; - const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts; - if (callContexts) { - const paramTypes: Type[] = []; - for (let parameterIndex = 0; parameterIndex < declaration.parameters.length; parameterIndex++) { - let types: Type[] = []; - const isRestParameter = ts.isRestParameter(declaration.parameters[parameterIndex]); - for (const callContext of callContexts) { - if (callContext.argumentTypes.length > parameterIndex) { - if (isRestParameter) { - types = concatenate(types, map(callContext.argumentTypes.slice(parameterIndex), a => checker.getBaseTypeOfLiteralType(a))); - } - else { - types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex])); - } - } - } - if (types.length) { - const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype)); - paramTypes[parameterIndex] = isRestParameter ? checker.createArrayType(type) : type; + if (!declaration.parameters) { + return undefined; + } + + const usageContext: UsageContext = {}; + for (const reference of references) { + cancellationToken.throwIfCancellationRequested(); + inferTypeFromContext(reference, checker, usageContext); + } + const isConstructor = declaration.kind === SyntaxKind.Constructor; + const callContexts = isConstructor ? usageContext.constructContexts : usageContext.callContexts; + return callContexts && declaration.parameters.map((parameter, parameterIndex) => { + const types: Type[] = []; + const isRestParameter = ts.isRestParameter(parameter); + for (const callContext of callContexts) { + if (callContext.argumentTypes.length <= parameterIndex) { + continue; + } + + if (isRestParameter) { + for (let i = parameterIndex; i < callContext.argumentTypes.length; i++) { + types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[i])); } } - return paramTypes; + else { + types.push(checker.getBaseTypeOfLiteralType(callContext.argumentTypes[parameterIndex])); + } } - } - return undefined; + if (!types.length) { + return undefined; + } + const type = checker.getWidenedType(checker.getUnionType(types, UnionReduction.Subtype)); + return isRestParameter ? checker.createArrayType(type) : type; + }); } function inferTypeFromContext(node: Expression, checker: TypeChecker, usageContext: UsageContext): void { diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index 8e2c0d1900f..2a8b594d77a 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts.DocumentHighlights { - export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] | undefined { + export function getDocumentHighlights(program: Program, cancellationToken: CancellationToken, sourceFile: SourceFile, position: number, sourceFilesToSearch: ReadonlyArray): DocumentHighlights[] | undefined { const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true); if (node.parent && (isJsxOpeningElement(node.parent) && node.parent.tagName === node || isJsxClosingElement(node.parent))) { @@ -21,12 +21,12 @@ namespace ts.DocumentHighlights { }; } - function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] { + function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: ReadonlyArray): DocumentHighlights[] { const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken); return referenceEntries && convertReferencedSymbols(referenceEntries); } - function convertReferencedSymbols(referenceEntries: FindAllReferences.Entry[]): DocumentHighlights[] { + function convertReferencedSymbols(referenceEntries: ReadonlyArray): DocumentHighlights[] { const fileNameToDocumentHighlights = createMap(); for (const entry of referenceEntries) { const { fileName, span } = FindAllReferences.toHighlightSpan(entry); diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 07cd2c683a9..8b9937394f1 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -703,7 +703,7 @@ namespace ts.FindAllReferences.Core { return exposedByParent ? scope.getSourceFile() : scope; } - function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): number[] { + function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): ReadonlyArray { const positions: number[] = []; /// TODO: Cache symbol existence for files to save text search @@ -1344,63 +1344,63 @@ namespace ts.FindAllReferences.Core { const references: Entry[] = []; - let possiblePositions: number[]; + let possiblePositions: ReadonlyArray; if (searchSpaceNode.kind === SyntaxKind.SourceFile) { forEach(sourceFiles, sourceFile => { cancellationToken.throwIfCancellationRequested(); possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this"); - getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, references); + getThisReferencesInFile(sourceFile, sourceFile, possiblePositions, staticFlag, references); }); } else { const sourceFile = searchSpaceNode.getSourceFile(); possiblePositions = getPossibleSymbolReferencePositions(sourceFile, "this", searchSpaceNode); - getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, references); + getThisReferencesInFile(sourceFile, searchSpaceNode, possiblePositions, staticFlag, references); } return [{ definition: { type: "this", node: thisOrSuperKeyword }, references }]; + } - function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: number[], result: Entry[]): void { - forEach(possiblePositions, position => { - const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false); - if (!node || !isThis(node)) { - return; - } + function getThisReferencesInFile(sourceFile: SourceFile, searchSpaceNode: Node, possiblePositions: ReadonlyArray, staticFlag: ModifierFlags, result: Push): void { + forEach(possiblePositions, position => { + const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false); + if (!node || !isThis(node)) { + return; + } - const container = getThisContainer(node, /* includeArrowFunctions */ false); + const container = getThisContainer(node, /* includeArrowFunctions */ false); - switch (searchSpaceNode.kind) { - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - if (searchSpaceNode.symbol === container.symbol) { - result.push(nodeEntry(node)); - } - break; - case SyntaxKind.MethodDeclaration: - case SyntaxKind.MethodSignature: - if (isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) { - result.push(nodeEntry(node)); - } - break; - case SyntaxKind.ClassExpression: - case SyntaxKind.ClassDeclaration: - // Make sure the container belongs to the same class - // and has the appropriate static modifier from the original container. - if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) { - result.push(nodeEntry(node)); - } - break; - case SyntaxKind.SourceFile: - if (container.kind === SyntaxKind.SourceFile && !isExternalModule(container)) { - result.push(nodeEntry(node)); - } - break; - } - }); - } + switch (searchSpaceNode.kind) { + case SyntaxKind.FunctionExpression: + case SyntaxKind.FunctionDeclaration: + if (searchSpaceNode.symbol === container.symbol) { + result.push(nodeEntry(node)); + } + break; + case SyntaxKind.MethodDeclaration: + case SyntaxKind.MethodSignature: + if (isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol) { + result.push(nodeEntry(node)); + } + break; + case SyntaxKind.ClassExpression: + case SyntaxKind.ClassDeclaration: + // Make sure the container belongs to the same class + // and has the appropriate static modifier from the original container. + if (container.parent && searchSpaceNode.symbol === container.parent.symbol && (getModifierFlags(container) & ModifierFlags.Static) === staticFlag) { + result.push(nodeEntry(node)); + } + break; + case SyntaxKind.SourceFile: + if (container.kind === SyntaxKind.SourceFile && !isExternalModule(container)) { + result.push(nodeEntry(node)); + } + break; + } + }); } function getReferencesForStringLiteral(node: StringLiteral, sourceFiles: ReadonlyArray, cancellationToken: CancellationToken): SymbolAndEntries[] { @@ -1417,7 +1417,7 @@ namespace ts.FindAllReferences.Core { references }]; - function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchText: string, possiblePositions: number[], references: Push): void { + function getReferencesForStringLiteralInFile(sourceFile: SourceFile, searchText: string, possiblePositions: ReadonlyArray, references: Push): void { for (const position of possiblePositions) { const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ false); if (node && node.kind === SyntaxKind.StringLiteral && (node as StringLiteral).text === searchText) { diff --git a/src/services/services.ts b/src/services/services.ts index 422248465c9..d8bf5f56ed6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1580,7 +1580,7 @@ namespace ts { return results; } - function getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] { + function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray): DocumentHighlights[] { synchronizeHostData(); const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f))); const sourceFile = getValidSourceFile(fileName);