From 98893efa118f89009bed5afdc77f67849a8b517e Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 25 May 2017 09:00:52 -0700 Subject: [PATCH 1/2] findAllRefs: Replace 'interface State' and 'createState' with just 'class State' --- src/services/findAllReferences.ts | 108 +++++++++++++++--------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index efc6a6f6ed..833d9ec95f 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -386,7 +386,7 @@ namespace ts.FindAllReferences.Core { const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations); const result: SymbolAndEntries[] = []; - const state = createState(sourceFiles, node, checker, cancellationToken, searchMeaning, options, result); + const state = new State(sourceFiles, node, checker, cancellationToken, searchMeaning, options, result); const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) }); // Try to get the smallest valid scope that we can limit our search to; @@ -446,35 +446,18 @@ namespace ts.FindAllReferences.Core { * Holds all state needed for the finding references. * Unlike `Search`, there is only one `State`. */ - interface State extends Options { + class State { /** True if we're searching for constructor references. */ readonly isForConstructor: boolean; - readonly sourceFiles: SourceFile[]; - readonly checker: TypeChecker; - readonly cancellationToken: CancellationToken; - readonly searchMeaning: SemanticMeaning; - /** Cache for `explicitlyinheritsFrom`. */ - readonly inheritsFromCache: Map; + readonly inheritsFromCache = createMap(); - /** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */ - getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult; + private readonly symbolIdToReferences: Entry[][] = []; + // Source file ID → symbol ID → Whether the symbol has been searched for in the source file. + private readonly sourceFileToSeenSymbols: Array> = []; - /** @param allSearchSymbols set of additinal symbols for use by `includes`. */ - createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions?: { text?: string, allSearchSymbols?: Symbol[] }): Search; - - /** - * Callback to add references for a particular searched symbol. - * This initializes a reference group, so only call this if you will add at least one reference. - */ - referenceAdder(searchSymbol: Symbol, searchLocation: Node): (node: Node) => void; - - /** Add a reference with no associated definition. */ - addStringOrCommentReference(fileName: string, textSpan: TextSpan): void; - - /** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */ - markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean; + private importTracker: ImportTracker | undefined; /** * Type nodes can contain multiple references to the same type. For example: @@ -483,7 +466,7 @@ namespace ts.FindAllReferences.Core { * duplicate entries would be returned here as each of the type references is part of * the same implementation. For that reason, check before we add a new entry. */ - markSeenContainingTypeReference(containingTypeReference: Node): boolean; + readonly markSeenContainingTypeReference: (containingTypeReference: Node) => boolean; /** * It's possible that we will encounter the right side of `export { foo as bar } from "x";` more than once. @@ -496,33 +479,44 @@ namespace ts.FindAllReferences.Core { * But another reference to it may appear in the same source file. * See `tests/cases/fourslash/transitiveExportImports3.ts`. */ - markSeenReExportRHS(rhs: Identifier): boolean; - } + readonly markSeenReExportRHS: (rhs: Identifier) => boolean; - function createState(sourceFiles: SourceFile[], originalLocation: Node, checker: TypeChecker, cancellationToken: CancellationToken, searchMeaning: SemanticMeaning, options: Options, result: Push): State { - const symbolIdToReferences: Entry[][] = []; - const inheritsFromCache = createMap(); - // Source file ID → symbol ID → Whether the symbol has been searched for in the source file. - const sourceFileToSeenSymbols: Array> = []; - const isForConstructor = originalLocation.kind === SyntaxKind.ConstructorKeyword; - let importTracker: ImportTracker | undefined; + readonly findInStrings?: boolean; + readonly findInComments?: boolean; + readonly isForRename?: boolean; + readonly implementations?: boolean; - return { - ...options, - sourceFiles, isForConstructor, checker, cancellationToken, searchMeaning, inheritsFromCache, getImportSearches, createSearch, referenceAdder, addStringOrCommentReference, - markSearchedSymbol, markSeenContainingTypeReference: nodeSeenTracker(), markSeenReExportRHS: nodeSeenTracker(), - }; + constructor( + readonly sourceFiles: SourceFile[], + originalLocation: Node, + readonly checker: TypeChecker, + readonly cancellationToken: CancellationToken, + readonly searchMeaning: SemanticMeaning, + options: Options, + private readonly result: Push) { - function getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult { - if (!importTracker) importTracker = createImportTracker(sourceFiles, checker, cancellationToken); - return importTracker(exportSymbol, exportInfo, options.isForRename); + this.findInStrings = options.findInStrings; + this.findInComments = options.findInComments; + this.isForRename = options.isForRename; + this.implementations = options.implementations; + + this.isForConstructor = originalLocation.kind === SyntaxKind.ConstructorKeyword; + this.markSeenContainingTypeReference = nodeSeenTracker(); + this.markSeenReExportRHS = nodeSeenTracker(); } - function createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search { + /** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */ + getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult { + if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.checker, this.cancellationToken); + return this.importTracker(exportSymbol, exportInfo, this.isForRename); + } + + /** @param allSearchSymbols set of additinal symbols for use by `includes`. */ + createSearch(location: Node, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search { // Note: if this is an external module symbol, the name doesn't include quotes. - const { text = stripQuotes(getDeclaredName(checker, symbol, location)), allSearchSymbols = undefined } = searchOptions; + const { text = stripQuotes(getDeclaredName(this.checker, symbol, location)), allSearchSymbols = undefined } = searchOptions; const escapedText = escapeIdentifier(text); - const parents = options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, checker); + const parents = this.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker); return { location, symbol, comingFrom, text, escapedText, parents, includes }; function includes(referenceSymbol: Symbol): boolean { @@ -530,27 +524,33 @@ namespace ts.FindAllReferences.Core { } } - function referenceAdder(referenceSymbol: Symbol, searchLocation: Node): (node: Node) => void { - const symbolId = getSymbolId(referenceSymbol); - let references = symbolIdToReferences[symbolId]; + /** + * Callback to add references for a particular searched symbol. + * This initializes a reference group, so only call this if you will add at least one reference. + */ + referenceAdder(searchSymbol: Symbol, searchLocation: Node): (node: Node) => void { + const symbolId = getSymbolId(searchSymbol); + let references = this.symbolIdToReferences[symbolId]; if (!references) { - references = symbolIdToReferences[symbolId] = []; - result.push({ definition: { type: "symbol", symbol: referenceSymbol, node: searchLocation }, references }); + references = this.symbolIdToReferences[symbolId] = []; + this.result.push({ definition: { type: "symbol", symbol: searchSymbol, node: searchLocation }, references }); } return node => references.push(nodeEntry(node)); } - function addStringOrCommentReference(fileName: string, textSpan: TextSpan): void { - result.push({ + /** Add a reference with no associated definition. */ + addStringOrCommentReference(fileName: string, textSpan: TextSpan): void { + this.result.push({ definition: undefined, references: [{ type: "span", fileName, textSpan }] }); } - function markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean { + /** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */ + markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean { const sourceId = getNodeId(sourceFile); const symbolId = getSymbolId(symbol); - const seenSymbols = sourceFileToSeenSymbols[sourceId] || (sourceFileToSeenSymbols[sourceId] = []); + const seenSymbols = this.sourceFileToSeenSymbols[sourceId] || (this.sourceFileToSeenSymbols[sourceId] = []); return !seenSymbols[symbolId] && (seenSymbols[symbolId] = true); } } From 528a59fdde73c3d435dbb9bdce1163fe1303b8ad Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 25 May 2017 10:23:04 -0700 Subject: [PATCH 2/2] Clean up instance variables --- src/services/findAllReferences.ts | 59 +++++++++++-------------------- 1 file changed, 20 insertions(+), 39 deletions(-) diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 833d9ec95f..f3b20637ac 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -386,7 +386,7 @@ namespace ts.FindAllReferences.Core { const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations); const result: SymbolAndEntries[] = []; - const state = new State(sourceFiles, node, checker, cancellationToken, searchMeaning, options, result); + const state = new State(sourceFiles, /*isForConstructor*/ node.kind === SyntaxKind.ConstructorKeyword, checker, cancellationToken, searchMeaning, options, result); const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: populateSearchSymbolSet(symbol, node, checker, options.implementations) }); // Try to get the smallest valid scope that we can limit our search to; @@ -447,18 +447,9 @@ namespace ts.FindAllReferences.Core { * Unlike `Search`, there is only one `State`. */ class State { - /** True if we're searching for constructor references. */ - readonly isForConstructor: boolean; - /** Cache for `explicitlyinheritsFrom`. */ readonly inheritsFromCache = createMap(); - private readonly symbolIdToReferences: Entry[][] = []; - // Source file ID → symbol ID → Whether the symbol has been searched for in the source file. - private readonly sourceFileToSeenSymbols: Array> = []; - - private importTracker: ImportTracker | undefined; - /** * Type nodes can contain multiple references to the same type. For example: * let x: Foo & (Foo & Bar) = ... @@ -466,7 +457,7 @@ namespace ts.FindAllReferences.Core { * duplicate entries would be returned here as each of the type references is part of * the same implementation. For that reason, check before we add a new entry. */ - readonly markSeenContainingTypeReference: (containingTypeReference: Node) => boolean; + readonly markSeenContainingTypeReference = nodeSeenTracker(); /** * It's possible that we will encounter the right side of `export { foo as bar } from "x";` more than once. @@ -479,36 +470,23 @@ namespace ts.FindAllReferences.Core { * But another reference to it may appear in the same source file. * See `tests/cases/fourslash/transitiveExportImports3.ts`. */ - readonly markSeenReExportRHS: (rhs: Identifier) => boolean; - - readonly findInStrings?: boolean; - readonly findInComments?: boolean; - readonly isForRename?: boolean; - readonly implementations?: boolean; + readonly markSeenReExportRHS = nodeSeenTracker(); constructor( readonly sourceFiles: SourceFile[], - originalLocation: Node, + /** True if we're searching for constructor references. */ + readonly isForConstructor: boolean, readonly checker: TypeChecker, readonly cancellationToken: CancellationToken, readonly searchMeaning: SemanticMeaning, - options: Options, - private readonly result: Push) { - - this.findInStrings = options.findInStrings; - this.findInComments = options.findInComments; - this.isForRename = options.isForRename; - this.implementations = options.implementations; - - this.isForConstructor = originalLocation.kind === SyntaxKind.ConstructorKeyword; - this.markSeenContainingTypeReference = nodeSeenTracker(); - this.markSeenReExportRHS = nodeSeenTracker(); - } + readonly options: Options, + private readonly result: Push) {} + private importTracker: ImportTracker | undefined; /** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */ getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult { if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.checker, this.cancellationToken); - return this.importTracker(exportSymbol, exportInfo, this.isForRename); + return this.importTracker(exportSymbol, exportInfo, this.options.isForRename); } /** @param allSearchSymbols set of additinal symbols for use by `includes`. */ @@ -516,7 +494,7 @@ namespace ts.FindAllReferences.Core { // Note: if this is an external module symbol, the name doesn't include quotes. const { text = stripQuotes(getDeclaredName(this.checker, symbol, location)), allSearchSymbols = undefined } = searchOptions; const escapedText = escapeIdentifier(text); - const parents = this.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker); + const parents = this.options.implementations && getParentSymbolsOfPropertyAccess(location, symbol, this.checker); return { location, symbol, comingFrom, text, escapedText, parents, includes }; function includes(referenceSymbol: Symbol): boolean { @@ -524,6 +502,7 @@ namespace ts.FindAllReferences.Core { } } + private readonly symbolIdToReferences: Entry[][] = []; /** * Callback to add references for a particular searched symbol. * This initializes a reference group, so only call this if you will add at least one reference. @@ -546,6 +525,8 @@ namespace ts.FindAllReferences.Core { }); } + // Source file ID → symbol ID → Whether the symbol has been searched for in the source file. + private readonly sourceFileToSeenSymbols: Array> = []; /** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */ markSearchedSymbol(sourceFile: SourceFile, symbol: Symbol): boolean { const sourceId = getNodeId(sourceFile); @@ -580,7 +561,7 @@ namespace ts.FindAllReferences.Core { break; case ExportKind.Default: // Search for a property access to '.default'. This can't be renamed. - indirectSearch = state.isForRename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" }); + indirectSearch = state.options.isForRename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" }); break; case ExportKind.ExportEquals: break; @@ -806,7 +787,7 @@ namespace ts.FindAllReferences.Core { return; } - for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.findInComments || container.jsDoc !== undefined)) { + for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container, /*fullStart*/ state.options.findInComments || container.jsDoc !== undefined)) { getReferencesAtLocation(sourceFile, position, search, state); } } @@ -818,7 +799,7 @@ namespace ts.FindAllReferences.Core { // This wasn't the start of a token. Check to see if it might be a // match in a comment or string if that's what the caller is asking // for. - if (!state.implementations && (state.findInStrings && isInString(sourceFile, position) || state.findInComments && isInNonReferenceComment(sourceFile, position))) { + if (!state.options.implementations && (state.options.findInStrings && isInString(sourceFile, position) || state.options.findInComments && isInNonReferenceComment(sourceFile, position))) { // In the case where we're looking inside comments/strings, we don't have // an actual definition. So just use 'undefined' here. Features like // 'Rename' won't care (as they ignore the definitions), and features like @@ -884,7 +865,7 @@ namespace ts.FindAllReferences.Core { addRef(); } - if (!state.isForRename && state.markSeenReExportRHS(name)) { + if (!state.options.isForRename && state.markSeenReExportRHS(name)) { addReference(name, referenceSymbol, name, state); } } @@ -895,7 +876,7 @@ namespace ts.FindAllReferences.Core { } // For `export { foo as bar }`, rename `foo`, but not `bar`. - if (!(referenceLocation === propertyName && state.isForRename)) { + if (!(referenceLocation === propertyName && state.options.isForRename)) { const exportKind = (referenceLocation as Identifier).originalKeywordKind === ts.SyntaxKind.DefaultKeyword ? ExportKind.Default : ExportKind.Named; const exportInfo = getExportInfo(referenceSymbol, exportKind, state.checker); Debug.assert(!!exportInfo); @@ -937,7 +918,7 @@ namespace ts.FindAllReferences.Core { const { symbol } = importOrExport; if (importOrExport.kind === ImportExport.Import) { - if (!state.isForRename || importOrExport.isNamedImport) { + if (!state.options.isForRename || importOrExport.isNamedImport) { searchForImportedSymbol(symbol, state); } } @@ -963,7 +944,7 @@ namespace ts.FindAllReferences.Core { function addReference(referenceLocation: Node, relatedSymbol: Symbol, searchLocation: Node, state: State): void { const addRef = state.referenceAdder(relatedSymbol, searchLocation); - if (state.implementations) { + if (state.options.implementations) { addImplementationReferences(referenceLocation, addRef, state); } else {