diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index ea6ccd68fbc35..52ac86263fda3 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -997,18 +997,43 @@ namespace FourSlash { definition: string | { text: string, range: ts.TextSpan }; references: ts.ReferenceEntry[]; } + interface RangeMarkerData { + id?: string; + isWriteAccess?: boolean, + isDefinition?: boolean, + isInString?: true, + contextRangeIndex?: number, + contextRangeDelta?: number, + contextRangeId?: string + } const fullExpected = ts.map(parts, ({ definition, ranges }) => ({ definition: typeof definition === "string" ? definition : { ...definition, range: ts.createTextSpanFromRange(definition.range) }, references: ranges.map(r => { - const { isWriteAccess = false, isDefinition = false, isInString, contextRangeIndex } = (r.marker && r.marker.data || {}) as { isWriteAccess?: boolean, isDefinition?: boolean, isInString?: true, contextRangeIndex?: number }; + const { isWriteAccess = false, isDefinition = false, isInString, contextRangeIndex, contextRangeDelta, contextRangeId } = (r.marker && r.marker.data || {}) as RangeMarkerData; + let contextSpan: ts.TextSpan | undefined; + if (contextRangeDelta !== undefined) { + const allRanges = this.getRanges(); + const index = allRanges.indexOf(r); + if (index !== -1) { + contextSpan = ts.createTextSpanFromRange(allRanges[index + contextRangeDelta]); + } + } + else if (contextRangeId !== undefined) { + const allRanges = this.getRanges(); + const contextRange = ts.find(allRanges, range => (range.marker?.data as RangeMarkerData)?.id === contextRangeId); + if (contextRange) { + contextSpan = ts.createTextSpanFromRange(contextRange); + } + } + else if (contextRangeIndex !== undefined) { + contextSpan = ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]); + } return { - fileName: r.fileName, textSpan: ts.createTextSpanFromRange(r), + fileName: r.fileName, + ...(contextSpan ? { contextSpan } : undefined), isWriteAccess, isDefinition, - ...(contextRangeIndex !== undefined ? - { contextSpan: ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]) } : - undefined), ...(isInString ? { isInString: true } : undefined), }; }), @@ -1032,7 +1057,7 @@ namespace FourSlash { } public verifyNoReferences(markerNameOrRange?: string | Range) { - if (markerNameOrRange) this.goToMarkerOrRange(markerNameOrRange); + if (markerNameOrRange !== undefined) this.goToMarkerOrRange(markerNameOrRange); const refs = this.getReferencesAtCaret(); if (refs && refs.length) { this.raiseError(`Expected getReferences to fail, but saw references: ${stringify(refs)}`); @@ -1233,6 +1258,12 @@ namespace FourSlash { } public verifyRenameLocations(startRanges: ArrayOrSingle, options: FourSlashInterface.RenameLocationsOptions) { + interface RangeMarkerData { + id?: string; + contextRangeIndex?: number, + contextRangeDelta?: number + contextRangeId?: string; + } const { findInStrings = false, findInComments = false, ranges = this.getRanges(), providePrefixAndSuffixTextForRename = true } = ts.isArray(options) ? { findInStrings: false, findInComments: false, ranges: options, providePrefixAndSuffixTextForRename: true } : options; const _startRanges = toArray(startRanges); @@ -1253,13 +1284,29 @@ namespace FourSlash { locations && ts.sort(locations, (r1, r2) => ts.compareStringsCaseSensitive(r1.fileName, r2.fileName) || r1.textSpan.start - r2.textSpan.start); assert.deepEqual(sort(references), sort(ranges.map((rangeOrOptions): ts.RenameLocation => { const { range, ...prefixSuffixText } = "range" in rangeOrOptions ? rangeOrOptions : { range: rangeOrOptions }; // eslint-disable-line no-in-operator - const { contextRangeIndex } = (range.marker && range.marker.data || {}) as { contextRangeIndex?: number; }; + const { contextRangeIndex, contextRangeDelta, contextRangeId } = (range.marker && range.marker.data || {}) as RangeMarkerData; + let contextSpan: ts.TextSpan | undefined; + if (contextRangeDelta !== undefined) { + const allRanges = this.getRanges(); + const index = allRanges.indexOf(range); + if (index !== -1) { + contextSpan = ts.createTextSpanFromRange(allRanges[index + contextRangeDelta]); + } + } + else if (contextRangeId !== undefined) { + const allRanges = this.getRanges(); + const contextRange = ts.find(allRanges, range => (range.marker?.data as RangeMarkerData)?.id === contextRangeId); + if (contextRange) { + contextSpan = ts.createTextSpanFromRange(contextRange); + } + } + else if (contextRangeIndex !== undefined) { + contextSpan = ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]); + } return { fileName: range.fileName, textSpan: ts.createTextSpanFromRange(range), - ...(contextRangeIndex !== undefined ? - { contextSpan: ts.createTextSpanFromRange(this.getRanges()[contextRangeIndex]) } : - undefined), + ...(contextSpan ? { contextSpan } : undefined), ...prefixSuffixText }; }))); @@ -3589,19 +3636,41 @@ namespace FourSlash { // Parse out the files and their metadata const testData = parseTestData(absoluteBasePath, content, absoluteFileName); const state = new TestState(absoluteFileName, absoluteBasePath, testType, testData); - const output = ts.transpileModule(content, { reportDiagnostics: true, compilerOptions: { target: ts.ScriptTarget.ES2015 } }); + const actualFileName = Harness.IO.resolvePath(fileName) || absoluteFileName; + const output = ts.transpileModule(content, { reportDiagnostics: true, fileName: actualFileName, compilerOptions: { target: ts.ScriptTarget.ES2015, inlineSourceMap: true } }); if (output.diagnostics!.length > 0) { throw new Error(`Syntax error in ${absoluteBasePath}: ${output.diagnostics![0].messageText}`); } - runCode(output.outputText, state); + runCode(output.outputText, state, actualFileName); } - function runCode(code: string, state: TestState): void { + function runCode(code: string, state: TestState, fileName: string): void { // Compile and execute the test - const wrappedCode = - `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) { -${code} -})`; + const generatedFile = ts.changeExtension(fileName, ".js"); + const wrappedCode = `(function(test, goTo, plugins, verify, edit, debug, format, cancellation, classification, completion, verifyOperationIsCancelled) {${code}\n//# sourceURL=${generatedFile}\n})`; + + type SourceMapSupportModule = typeof import("source-map-support") & { + // TODO(rbuckton): This is missing from the DT definitions and needs to be added. + resetRetrieveHandlers(): void + }; + + // Provide the content of the current test to 'source-map-support' so that it can give us the correct source positions + // for test failures. + let sourceMapSupportModule: SourceMapSupportModule | undefined; + try { + sourceMapSupportModule = require("source-map-support"); + } + catch { + // do nothing + } + + sourceMapSupportModule?.install({ + retrieveFile: path => { + return path === generatedFile ? wrappedCode : + undefined!; + } + }); + try { const test = new FourSlashInterface.Test(state); const goTo = new FourSlashInterface.GoTo(state); @@ -3616,8 +3685,13 @@ ${code} f(test, goTo, plugins, verify, edit, debug, format, cancellation, FourSlashInterface.Classification, FourSlashInterface.Completion, verifyOperationIsCancelled); } catch (err) { + // ensure 'source-map-support' is triggered while we still have the handler attached by accessing `error.stack`. + err.stack?.toString(); throw err; } + finally { + sourceMapSupportModule?.resetRetrieveHandlers(); + } } function chompLeadingSpace(content: string) { diff --git a/src/services/callHierarchy.ts b/src/services/callHierarchy.ts index 838f9355d3dca..a924bc2809108 100644 --- a/src/services/callHierarchy.ts +++ b/src/services/callHierarchy.ts @@ -293,7 +293,7 @@ namespace ts.CallHierarchy { return []; } const location = getCallHierarchyDeclarationReferenceNode(declaration); - const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, /*options*/ undefined, convertEntryToCallSite), isDefined); + const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, { use: FindAllReferences.FindReferencesUse.References }, convertEntryToCallSite), isDefined); return calls ? group(calls, getCallSiteGroupKey, entries => convertCallSiteGroupToIncomingCall(program, entries)) : []; } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index b4f1c19837dd1..920d2dcc573ea 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -171,14 +171,27 @@ namespace ts.FindAllReferences { undefined; } + export const enum FindReferencesUse { + /** + * When searching for references to a symbol, the location will not be adjusted (this is the default behavior when not specified). + */ + Other, + /** + * When searching for references to a symbol, the location will be adjusted if the cursor was on a keyword. + */ + References, + /** + * When searching for references to a symbol, the location will be adjusted if the cursor was on a keyword. + * Unlike `References`, the location will only be adjusted keyword belonged to a declaration with a valid name. + * If set, we will find fewer references -- if it is referenced by several different names, we still only find references for the original name. + */ + Rename, + } + export interface Options { readonly findInStrings?: boolean; readonly findInComments?: boolean; - /** - * True if we are renaming the symbol. - * If so, we will find fewer references -- if it is referenced by several different names, we still only find references for the original name. - */ - readonly isForRename?: boolean; + readonly use?: FindReferencesUse; /** True if we are searching for implementations. We will have a different method of adding references if so. */ readonly implementations?: boolean; /** @@ -191,7 +204,7 @@ namespace ts.FindAllReferences { export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined { const node = getTouchingPropertyName(sourceFile, position); - const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken); + const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, { use: FindReferencesUse.References }); const checker = program.getTypeChecker(); return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined(referencedSymbols, ({ definition, references }) => // Only include referenced symbols that have a valid definition. @@ -229,7 +242,7 @@ namespace ts.FindAllReferences { } else { // Perform "Find all References" and retrieve only those that are implementations - return getReferenceEntriesForNode(position, node, program, sourceFiles, cancellationToken, { implementations: true }); + return getReferenceEntriesForNode(position, node, program, sourceFiles, cancellationToken, { implementations: true, use: FindReferencesUse.References }); } } @@ -502,6 +515,7 @@ namespace ts.FindAllReferences { case SyntaxKind.ModuleDeclaration: case SyntaxKind.NamespaceExportDeclaration: case SyntaxKind.NamespaceImport: + case SyntaxKind.NamespaceExport: case SyntaxKind.Parameter: case SyntaxKind.ShorthandPropertyAssignment: case SyntaxKind.TypeAliasDeclaration: @@ -539,6 +553,12 @@ namespace ts.FindAllReferences { export namespace Core { /** Core find-all-references algorithm. Handles special cases before delegating to `getReferencedSymbolsForSymbol`. */ export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap = arrayToSet(sourceFiles, f => f.fileName)): readonly SymbolAndEntries[] | undefined { + if (options.use === FindReferencesUse.References) { + node = getAdjustedReferenceLocation(node); + } + else if (options.use === FindReferencesUse.Rename) { + node = getAdjustedRenameLocation(node); + } if (isSourceFile(node)) { const reference = GoToDefinition.getReferenceAtPosition(node, position, program); const moduleSymbol = reference && program.getTypeChecker().getMergedSymbol(reference.file.symbol); @@ -723,6 +743,11 @@ namespace ts.FindAllReferences { /** getReferencedSymbols for special node kinds. */ function getReferencedSymbolsSpecial(node: Node, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] | undefined { if (isTypeKeyword(node.kind)) { + // A void expression (i.e., `void foo()`) is not special, but the `void` type is. + if (node.kind === SyntaxKind.VoidKeyword && isVoidExpression(node.parent)) { + return undefined; + } + // A modifier readonly (like on a property declaration) is not special; // a readonly type keyword (like `readonly string[]`) is. if (node.kind === SyntaxKind.ReadonlyKeyword && !isReadonlyTypeOperator(node)) { @@ -779,7 +804,7 @@ namespace ts.FindAllReferences { searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.assertDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state); } else { - const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, !!options.isForRename, !!options.providePrefixAndSuffixTextForRename, !!options.implementations) : [symbol] }); + const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, options.use === FindReferencesUse.Rename, !!options.providePrefixAndSuffixTextForRename, !!options.implementations) : [symbol] }); getReferencesInContainerOrFiles(symbol, state, search); } @@ -920,7 +945,7 @@ namespace ts.FindAllReferences { /** 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.sourceFilesSet, this.checker, this.cancellationToken); - return this.importTracker(exportSymbol, exportInfo, !!this.options.isForRename); + return this.importTracker(exportSymbol, exportInfo, this.options.use === FindReferencesUse.Rename); } /** @param allSearchSymbols set of additional symbols for use by `includes`. */ @@ -1001,7 +1026,7 @@ namespace ts.FindAllReferences { break; case ExportKind.Default: // Search for a property access to '.default'. This can't be renamed. - indirectSearch = state.options.isForRename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" }); + indirectSearch = state.options.use === FindReferencesUse.Rename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" }); break; case ExportKind.ExportEquals: break; @@ -1041,7 +1066,7 @@ namespace ts.FindAllReferences { function shouldAddSingleReference(singleRef: Identifier | StringLiteral, state: State): boolean { if (!hasMatchingMeaning(singleRef, state)) return false; - if (!state.options.isForRename) return true; + if (state.options.use !== FindReferencesUse.Rename) return true; // Don't rename an import type `import("./module-name")` when renaming `name` in `export = name;` if (!isIdentifier(singleRef)) return false; // At `default` in `import { default as x }` or `export { default as x }`, do add a reference, but do not rename. @@ -1373,7 +1398,7 @@ namespace ts.FindAllReferences { if (!propertyName) { // Don't rename at `export { default } from "m";`. (but do continue to search for imports of the re-export) - if (!(state.options.isForRename && (name.escapedText === InternalSymbolName.Default))) { + if (!(state.options.use === FindReferencesUse.Rename && (name.escapedText === InternalSymbolName.Default))) { addRef(); } } @@ -1384,7 +1409,7 @@ namespace ts.FindAllReferences { addRef(); } - if (addReferencesHere && !state.options.isForRename && state.markSeenReExportRHS(name)) { + if (addReferencesHere && state.options.use !== FindReferencesUse.Rename && state.markSeenReExportRHS(name)) { addReference(name, Debug.assertDefined(exportSpecifier.symbol), state); } } @@ -1503,7 +1528,7 @@ namespace ts.FindAllReferences { function addClassStaticThisReferences(referenceLocation: Node, search: Search, state: State): void { addReference(referenceLocation, search.symbol, state); const classLike = referenceLocation.parent; - if (state.options.isForRename || !isClassLike(classLike)) return; + if (state.options.use === FindReferencesUse.Rename || !isClassLike(classLike)) return; Debug.assert(classLike.name === referenceLocation); const addRef = state.referenceAdder(search.symbol); for (const member of classLike.members) { @@ -1961,7 +1986,7 @@ namespace ts.FindAllReferences { function getRelatedSymbol(search: Search, referenceSymbol: Symbol, referenceLocation: Node, state: State): RelatedSymbol | undefined { const { checker } = state; return forEachRelatedSymbol(referenceSymbol, referenceLocation, checker, /*isForRenamePopulateSearchSymbolSet*/ false, - /*onlyIncludeBindingElementAtReferenceLocation*/ !state.options.isForRename || !!state.options.providePrefixAndSuffixTextForRename, + /*onlyIncludeBindingElementAtReferenceLocation*/ state.options.use !== FindReferencesUse.Rename || !!state.options.providePrefixAndSuffixTextForRename, (sym, rootSymbol, baseSymbol, kind): RelatedSymbol | undefined => search.includes(baseSymbol || rootSymbol || sym) // For a base type, use the symbol for the derived type. For a synthetic (e.g. union) property, use the union symbol. ? { symbol: rootSymbol && !(getCheckFlags(sym) & CheckFlags.Synthetic) ? rootSymbol : sym, kind } @@ -2054,7 +2079,7 @@ namespace ts.FindAllReferences { } function isForRenameWithPrefixAndSuffixText(options: Options) { - return options.isForRename && options.providePrefixAndSuffixTextForRename; + return options.use === FindReferencesUse.Rename && options.providePrefixAndSuffixTextForRename; } } } diff --git a/src/services/rename.ts b/src/services/rename.ts index 775051a8364c3..3251dee6cd4a8 100644 --- a/src/services/rename.ts +++ b/src/services/rename.ts @@ -1,11 +1,14 @@ /* @internal */ namespace ts.Rename { export function getRenameInfo(program: Program, sourceFile: SourceFile, position: number, options?: RenameInfoOptions): RenameInfo { - const node = getTouchingPropertyName(sourceFile, position); - const renameInfo = node && nodeIsEligibleForRename(node) - ? getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()), options) - : undefined; - return renameInfo || getRenameInfoError(Diagnostics.You_cannot_rename_this_element); + const node = getAdjustedRenameLocation(getTouchingPropertyName(sourceFile, position)); + if (nodeIsEligibleForRename(node)) { + const renameInfo = getRenameInfoForNode(node, program.getTypeChecker(), sourceFile, declaration => program.isSourceFileDefaultLibrary(declaration.getSourceFile()), options); + if (renameInfo) { + return renameInfo; + } + } + return getRenameInfoError(Diagnostics.You_cannot_rename_this_element); } function getRenameInfoForNode(node: Node, typeChecker: TypeChecker, sourceFile: SourceFile, isDefinedInLibraryFile: (declaration: Node) => boolean, options?: RenameInfoOptions): RenameInfo | undefined { diff --git a/src/services/services.ts b/src/services/services.ts index 1a3eae675e62c..0c45e3f2d6ba5 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1608,7 +1608,7 @@ namespace ts { function findRenameLocations(fileName: string, position: number, findInStrings: boolean, findInComments: boolean, providePrefixAndSuffixTextForRename?: boolean): RenameLocation[] | undefined { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); - const node = getTouchingPropertyName(sourceFile, position); + const node = getAdjustedRenameLocation(getTouchingPropertyName(sourceFile, position)); if (isIdentifier(node) && (isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) && isIntrinsicJsxName(node.escapedText)) { const { openingElement, closingElement } = node.parent.parent; return [openingElement, closingElement].map((node): RenameLocation => { @@ -1621,21 +1621,21 @@ namespace ts { }); } else { - return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, isForRename: true }, + return getReferencesWorker(node, position, { findInStrings, findInComments, providePrefixAndSuffixTextForRename, use: FindAllReferences.FindReferencesUse.Rename }, (entry, originalNode, checker) => FindAllReferences.toRenameLocation(entry, originalNode, checker, providePrefixAndSuffixTextForRename || false)); } } function getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined { synchronizeHostData(); - return getReferencesWorker(getTouchingPropertyName(getValidSourceFile(fileName), position), position, {}, FindAllReferences.toReferenceEntry); + return getReferencesWorker(getTouchingPropertyName(getValidSourceFile(fileName), position), position, { use: FindAllReferences.FindReferencesUse.References }, FindAllReferences.toReferenceEntry); } function getReferencesWorker(node: Node, position: number, options: FindAllReferences.Options, cb: FindAllReferences.ToReferenceOrRenameEntry): T[] | undefined { synchronizeHostData(); // Exclude default library when renaming as commonly user don't want to change that file. - const sourceFiles = options && options.isForRename + const sourceFiles = options && options.use === FindAllReferences.FindReferencesUse.Rename ? program.getSourceFiles().filter(sourceFile => !program.isSourceFileDefaultLibrary(sourceFile)) : program.getSourceFiles(); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 69010b4611fd1..356ea22dbaa1b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -88,6 +88,7 @@ namespace ts { } export function getMeaningFromLocation(node: Node): SemanticMeaning { + node = getAdjustedReferenceLocation(node); if (node.kind === SyntaxKind.SourceFile) { return SemanticMeaning.Value; } @@ -720,6 +721,337 @@ namespace ts { return syntaxList; } + function isDefaultModifier(node: Node) { + return node.kind === SyntaxKind.DefaultKeyword; + } + + function isClassKeyword(node: Node) { + return node.kind === SyntaxKind.ClassKeyword; + } + + function isFunctionKeyword(node: Node) { + return node.kind === SyntaxKind.FunctionKeyword; + } + + function getAdjustedLocationForClass(node: ClassDeclaration | ClassExpression) { + if (isNamedDeclaration(node)) { + return node.name; + } + if (isClassDeclaration(node)) { + // for class and function declarations, use the `default` modifier + // when the declaration is unnamed. + const defaultModifier = find(node.modifiers!, isDefaultModifier); + if (defaultModifier) return defaultModifier; + } + if (isClassExpression(node)) { + // for class expressions, use the `class` keyword when the class is unnamed + const classKeyword = find(node.getChildren(), isClassKeyword); + if (classKeyword) return classKeyword; + } + } + + function getAdjustedLocationForFunction(node: FunctionDeclaration | FunctionExpression) { + if (isNamedDeclaration(node)) { + return node.name; + } + if (isFunctionDeclaration(node)) { + // for class and function declarations, use the `default` modifier + // when the declaration is unnamed. + const defaultModifier = find(node.modifiers!, isDefaultModifier); + if (defaultModifier) return defaultModifier; + } + if (isFunctionExpression(node)) { + // for function expressions, use the `function` keyword when the function is unnamed + const functionKeyword = find(node.getChildren(), isFunctionKeyword); + if (functionKeyword) return functionKeyword; + } + } + + function getAdjustedLocationForDeclaration(node: Node, forRename: boolean) { + if (!forRename) { + switch (node.kind) { + case SyntaxKind.ClassDeclaration: + case SyntaxKind.ClassExpression: + return getAdjustedLocationForClass(node as ClassDeclaration | ClassExpression); + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + return getAdjustedLocationForFunction(node as FunctionDeclaration | FunctionExpression); + } + } + if (isNamedDeclaration(node)) { + return node.name; + } + } + + function getAdjustedLocationForImportDeclaration(node: ImportDeclaration, forRename: boolean) { + if (node.importClause) { + // /**/import [|name|] from ...; + // import /**/type [|name|] from ...; + if (node.importClause.name && !node.importClause.namedBindings) { + return node.importClause.name; + } + // /**/import { [|name|] } from ...; + // /**/import { propertyName as [|name|] } from ...; + // /**/import * as [|name|] from ...; + // import /**/type { [|name|] } from ...; + // import /**/type { propertyName as [|name|] } from ...; + // import /**/type * as [|name|] from ...; + if (!node.importClause.name && node.importClause.namedBindings) { + if (isNamedImports(node.importClause.namedBindings)) { + if (node.importClause.namedBindings.elements.length === 1) { + return node.importClause.namedBindings.elements[0].name; + } + } + else if (isNamespaceImport(node.importClause.namedBindings)) { + return node.importClause.namedBindings.name; + } + } + + } + if (!forRename) { + // /**/import "[|module|]"; + // /**/import ... from "[|module|]"; + // import /**/type ... from "[|module|]"; + return node.moduleSpecifier; + } + } + + function getAdjustedLocationForExportDeclaration(node: ExportDeclaration, forRename: boolean) { + if (node.exportClause) { + // /**/export { [|name|] } ... + // /**/export { propertyName as [|name|] } ... + // /**/export * as [|name|] ... + // export /**/type { [|name|] } from ... + // export /**/type { propertyName as [|name|] } from ... + // export /**/type * as [|name|] ... + if (isNamedExports(node.exportClause)) { + if (node.exportClause.elements.length === 1) { + return node.exportClause.elements[0].name; + } + } + else if (isNamespaceExport(node.exportClause)) { + return node.exportClause.name; + } + } + if (!forRename) { + // /**/export * from "[|module|]"; + // export /**/type * from "[|module|]"; + return node.moduleSpecifier; + } + } + + function getAdjustedLocationForHeritageClause(node: HeritageClause) { + // /**/extends [|name|] + // /**/implements [|name|] + if (node.types.length === 1) { + return node.types[0].expression; + } + + // /**/extends name1, name2 ... + // /**/implements name1, name2 ... + } + + function getAdjustedLocation(node: Node, forRename: boolean): Node { + const { parent } = node; + // /**/ [|name|] ... + // /**/ [|name|] ... + // /**/ [|name|] ... + // /**/import [|name|] = ... + // + // NOTE: If the node is a modifier, we don't adjust its location if it is the `default` modifier as that is handled + // specially by `getSymbolAtLocation`. + if (isModifier(node) && (forRename || node.kind !== SyntaxKind.DefaultKeyword) ? contains(parent.modifiers, node) : + node.kind === SyntaxKind.ClassKeyword ? isClassDeclaration(parent) || isClassExpression(node) : + node.kind === SyntaxKind.FunctionKeyword ? isFunctionDeclaration(parent) || isFunctionExpression(node) : + node.kind === SyntaxKind.InterfaceKeyword ? isInterfaceDeclaration(parent) : + node.kind === SyntaxKind.EnumKeyword ? isEnumDeclaration(parent) : + node.kind === SyntaxKind.TypeKeyword ? isTypeAliasDeclaration(parent) : + node.kind === SyntaxKind.NamespaceKeyword || node.kind === SyntaxKind.ModuleKeyword ? isModuleDeclaration(parent) : + node.kind === SyntaxKind.ImportKeyword ? isImportEqualsDeclaration(parent) : + node.kind === SyntaxKind.GetKeyword ? isGetAccessorDeclaration(parent) : + node.kind === SyntaxKind.SetKeyword && isSetAccessorDeclaration(parent)) { + const location = getAdjustedLocationForDeclaration(parent, forRename); + if (location) { + return location; + } + } + // /**/ [|name|] ... + if ((node.kind === SyntaxKind.VarKeyword || node.kind === SyntaxKind.ConstKeyword || node.kind === SyntaxKind.LetKeyword) && + isVariableDeclarationList(parent) && parent.declarations.length === 1) { + const decl = parent.declarations[0]; + if (isIdentifier(decl.name)) { + return decl.name; + } + } + if (node.kind === SyntaxKind.TypeKeyword) { + // import /**/type [|name|] from ...; + // import /**/type { [|name|] } from ...; + // import /**/type { propertyName as [|name|] } from ...; + // import /**/type ... from "[|module|]"; + if (isImportClause(parent) && parent.isTypeOnly) { + const location = getAdjustedLocationForImportDeclaration(parent.parent, forRename); + if (location) { + return location; + } + } + // export /**/type { [|name|] } from ...; + // export /**/type { propertyName as [|name|] } from ...; + // export /**/type * from "[|module|]"; + // export /**/type * as ... from "[|module|]"; + if (isExportDeclaration(parent) && parent.isTypeOnly) { + const location = getAdjustedLocationForExportDeclaration(parent, forRename); + if (location) { + return location; + } + } + } + // import { propertyName /**/as [|name|] } ... + // import * /**/as [|name|] ... + // export { propertyName /**/as [|name|] } ... + // export * /**/as [|name|] ... + if (node.kind === SyntaxKind.AsKeyword) { + if (isImportSpecifier(parent) && parent.propertyName || + isExportSpecifier(parent) && parent.propertyName || + isNamespaceImport(parent) || + isNamespaceExport(parent)) { + return parent.name; + } + if (isExportDeclaration(parent) && parent.exportClause && isNamespaceExport(parent.exportClause)) { + return parent.exportClause.name; + } + } + // /**/import [|name|] from ...; + // /**/import { [|name|] } from ...; + // /**/import { propertyName as [|name|] } from ...; + // /**/import ... from "[|module|]"; + // /**/import "[|module|]"; + if (node.kind === SyntaxKind.ImportKeyword && isImportDeclaration(parent)) { + const location = getAdjustedLocationForImportDeclaration(parent, forRename); + if (location) { + return location; + } + } + if (node.kind === SyntaxKind.ExportKeyword) { + // /**/export { [|name|] } ...; + // /**/export { propertyName as [|name|] } ...; + // /**/export * from "[|module|]"; + // /**/export * as ... from "[|module|]"; + if (isExportDeclaration(parent)) { + const location = getAdjustedLocationForExportDeclaration(parent, forRename); + if (location) { + return location; + } + } + // NOTE: We don't adjust the location of the `default` keyword as that is handled specially by `getSymbolAtLocation`. + // /**/export default [|name|]; + // /**/export = [|name|]; + if (isExportAssignment(parent)) { + return skipOuterExpressions(parent.expression); + } + } + // import name = /**/require("[|module|]"); + if (node.kind === SyntaxKind.RequireKeyword && isExternalModuleReference(parent)) { + return parent.expression; + } + // import ... /**/from "[|module|]"; + // export ... /**/from "[|module|]"; + if (node.kind === SyntaxKind.FromKeyword && (isImportDeclaration(parent) || isExportDeclaration(parent)) && parent.moduleSpecifier) { + return parent.moduleSpecifier; + } + // class ... /**/extends [|name|] ... + // class ... /**/implements [|name|] ... + // class ... /**/implements name1, name2 ... + // interface ... /**/extends [|name|] ... + // interface ... /**/extends name1, name2 ... + if ((node.kind === SyntaxKind.ExtendsKeyword || node.kind === SyntaxKind.ImplementsKeyword) && isHeritageClause(parent) && parent.token === node.kind) { + const location = getAdjustedLocationForHeritageClause(parent); + if (location) { + return location; + } + } + if (node.kind === SyntaxKind.ExtendsKeyword) { + // ... ... + if (isTypeParameterDeclaration(parent) && parent.constraint && isTypeReferenceNode(parent.constraint)) { + return parent.constraint.typeName; + } + // ... T /**/extends [|U|] ? ... + if (isConditionalTypeNode(parent) && isTypeReferenceNode(parent.extendsType)) { + return parent.extendsType.typeName; + } + } + // ... T extends /**/infer [|U|] ? ... + if (node.kind === SyntaxKind.InferKeyword && isInferTypeNode(parent)) { + return parent.typeParameter.name; + } + // { [ [|K|] /**/in keyof T]: ... } + if (node.kind === SyntaxKind.InKeyword && isTypeParameterDeclaration(parent) && isMappedTypeNode(parent.parent)) { + return parent.name; + } + // /**/keyof [|T|] + if (node.kind === SyntaxKind.KeyOfKeyword && isTypeOperatorNode(parent) && parent.operator === SyntaxKind.KeyOfKeyword && + isTypeReferenceNode(parent.type)) { + return parent.type.typeName; + } + // /**/readonly [|name|][] + if (node.kind === SyntaxKind.ReadonlyKeyword && isTypeOperatorNode(parent) && parent.operator === SyntaxKind.ReadonlyKeyword && + isArrayTypeNode(parent.type) && isTypeReferenceNode(parent.type.elementType)) { + return parent.type.elementType.typeName; + } + if (!forRename) { + // /**/new [|name|] + // /**/void [|name|] + // /**/void obj.[|name|] + // /**/typeof [|name|] + // /**/typeof obj.[|name|] + // /**/await [|name|] + // /**/await obj.[|name|] + // /**/yield [|name|] + // /**/yield obj.[|name|] + // /**/delete obj.[|name|] + if (node.kind === SyntaxKind.NewKeyword && isNewExpression(parent) || + node.kind === SyntaxKind.VoidKeyword && isVoidExpression(parent) || + node.kind === SyntaxKind.TypeOfKeyword && isTypeOfExpression(parent) || + node.kind === SyntaxKind.AwaitKeyword && isAwaitExpression(parent) || + node.kind === SyntaxKind.YieldKeyword && isYieldExpression(parent) || + node.kind === SyntaxKind.DeleteKeyword && isDeleteExpression(parent)) { + if (parent.expression) { + return skipOuterExpressions(parent.expression); + } + } + // left /**/in [|name|] + // left /**/instanceof [|name|] + if ((node.kind === SyntaxKind.InKeyword || node.kind === SyntaxKind.InstanceOfKeyword) && isBinaryExpression(parent) && parent.operatorToken === node) { + return skipOuterExpressions(parent.right); + } + // left /**/as [|name|] + if (node.kind === SyntaxKind.AsKeyword && isAsExpression(parent) && isTypeReferenceNode(parent.type)) { + return parent.type.typeName; + } + // for (... /**/in [|name|]) + // for (... /**/of [|name|]) + if (node.kind === SyntaxKind.InKeyword && isForInStatement(parent) || + node.kind === SyntaxKind.OfKeyword && isForOfStatement(parent)) { + return skipOuterExpressions(parent.expression); + } + } + return node; + } + + /** + * Adjusts the location used for "find references" and "go to definition" when the cursor was not + * on a property name. + */ + export function getAdjustedReferenceLocation(node: Node): Node { + return getAdjustedLocation(node, /*forRename*/ false); + } + + /** + * Adjusts the location used for "rename" when the cursor was not on a property name. + */ + export function getAdjustedRenameLocation(node: Node): Node { + return getAdjustedLocation(node, /*forRename*/ true); + } + /** * Gets the token whose text has range [start, end) and * position >= start and (position < end or (position === end && token is literal or keyword or identifier)) diff --git a/tests/cases/fourslash/findAllRefsExportEquals.ts b/tests/cases/fourslash/findAllRefsExportEquals.ts index 647023b43ff60..f0fb88e5ab3d6 100644 --- a/tests/cases/fourslash/findAllRefsExportEquals.ts +++ b/tests/cases/fourslash/findAllRefsExportEquals.ts @@ -14,4 +14,4 @@ const b = { definition: '(alias) type T = number\nimport T = require("./a")', ra verify.referenceGroups([r0, r2], [a, b]); verify.referenceGroups(r3, [b, a]); verify.referenceGroups(r4, [mod, a, b]); -verify.referenceGroups(r1, [mod]); +verify.referenceGroups(r1, [a, b]); diff --git a/tests/cases/fourslash/findAllRefsInExport1.ts b/tests/cases/fourslash/findAllRefsInExport1.ts deleted file mode 100644 index 02ec901177f33..0000000000000 --- a/tests/cases/fourslash/findAllRefsInExport1.ts +++ /dev/null @@ -1,10 +0,0 @@ -/// - -//// class C {} -//// /*1*/export { C /*2*/as D }; - -goTo.marker("1"); -verify.noReferences(); - -goTo.marker("2"); -verify.noReferences(); \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts b/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts index 61e66de94cac5..59967af0d9ce5 100644 --- a/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts +++ b/tests/cases/fourslash/findAllRefs_importType_exportEquals.ts @@ -19,7 +19,7 @@ verify.referenceGroups(r1, [{ definition: "namespace T", ranges: [r1, r2] }]); const t: FourSlashInterface.ReferenceGroup = { definition: "type T = number\nnamespace T", ranges: [r0, r1, r2, r3] }; verify.referenceGroups(r2, [t]); verify.referenceGroups([r3, r4], [{ definition: 'module "/a"', ranges: [r4, rExport] }, t]); -verify.referenceGroups(rExport, [{ definition: 'module "/a"', ranges: [r3, r4, rExport] }]); +verify.referenceGroups(rExport, [t]); verify.renameLocations(r0, [r0, r2]); verify.renameLocations(r1, [r1, r2]); diff --git a/tests/cases/fourslash/getRenameInfoTests2.ts b/tests/cases/fourslash/getRenameInfoTests2.ts index 93e8e451b4a16..880bca7378a62 100644 --- a/tests/cases/fourslash/getRenameInfoTests2.ts +++ b/tests/cases/fourslash/getRenameInfoTests2.ts @@ -1,6 +1,6 @@ /// -/////**/class C { +////class C /**/extends null { //// ////} diff --git a/tests/cases/fourslash/referencesForDeclarationKeywords.ts b/tests/cases/fourslash/referencesForDeclarationKeywords.ts new file mode 100644 index 0000000000000..aeb8870ee2388 --- /dev/null +++ b/tests/cases/fourslash/referencesForDeclarationKeywords.ts @@ -0,0 +1,89 @@ +/// +////[|{| "id": "baseDecl" |}class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "baseDecl" |}Base|] {}|] +////[|{| "id": "implemented1Decl" |}interface [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "implemented1Decl" |}Implemented1|] {}|] +////[|{| "id": "classDecl1" |}[|class|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "classDecl1" |}C1|] [|extends|] [|Base|] [|implements|] [|Implemented1|] { +//// [|{| "id": "getDecl" |}[|get|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "getDecl" |}e|]() { return 1; }|] +//// [|{| "id": "setDecl" |}[|set|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "setDecl" |}e|](v) {}|] +////}|] +////[|{| "id": "interfaceDecl1" |}[|interface|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "interfaceDecl1" |}I1|] [|extends|] [|Base|] { }|] +////[|{| "id": "typeDecl" |}[|type|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "typeDecl" |}T|] = { }|] +////[|{| "id": "enumDecl" |}[|enum|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "enumDecl" |}E|] { }|] +////[|{| "id": "namespaceDecl" |}[|namespace|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "namespaceDecl" |}N|] { }|] +////[|{| "id": "moduleDecl" |}[|module|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "moduleDecl" |}M|] { }|] +////[|{| "id": "functionDecl" |}[|function|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "functionDecl" |}fn|]() {}|] +////[|{| "id": "varDecl" |}[|var|] [|{| "isWriteAccess": false, "isDefinition": true, "contextRangeId": "varDecl" |}x|];|] +////[|{| "id": "letDecl" |}[|let|] [|{| "isWriteAccess": false, "isDefinition": true, "contextRangeId": "letDecl" |}y|];|] +////[|{| "id": "constDecl" |}[|const|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "constDecl" |}z|] = 1;|] +////interface Implemented2 {} +////interface Implemented3 {} +////class C2 [|implements|] Implemented2, Implemented3 {} +////interface I2 [|extends|] Implemented2, Implemented3 {} + +const [ + baseDecl, + baseDecl_name, + implemented1Decl, + implemented1Decl_name, + classDecl1, + classDecl1_classKeyword, + classDecl1_name, + classDecl1_extendsKeyword, + classDecl1_extendsName, + classDecl1_implementsKeyword, + classDecl1_implementsName, + getDecl, + getDecl_getKeyword, + getDecl_name, + setDecl, + setDecl_setKeyword, + setDecl_name, + interfaceDecl1, + interfaceDecl1_interfaceKeyword, + interfaceDecl1_name, + interfaceDecl1_extendsKeyword, + interfaceDecl1_extendsName, + typeDecl, + typeDecl_typeKeyword, + typeDecl_name, + enumDecl, + enumDecl_enumKeyword, + enumDecl_name, + namespaceDecl, + namespaceDecl_namespaceKeyword, + namespaceDecl_name, + moduleDecl, + moduleDecl_moduleKeyword, + moduleDecl_name, + functionDecl, + functionDecl_functionKeyword, + functionDecl_name, + varDecl, + varDecl_varKeyword, + varDecl_name, + letDecl, + letDecl_letKeyword, + letDecl_name, + constDecl, + constDecl_constKeyword, + constDecl_name, + classDecl2_implementsKeyword, + interfaceDecl2_extendsKeyword, +] = test.ranges(); +verify.referenceGroups(classDecl1_classKeyword, [{ definition: "class C1", ranges: [classDecl1_name] }]); +verify.referenceGroups(classDecl1_extendsKeyword, [{ definition: "class Base", ranges: [baseDecl_name, classDecl1_extendsName, interfaceDecl1_extendsName] }]); +verify.referenceGroups(classDecl1_implementsKeyword, [{ definition: "interface Implemented1", ranges: [implemented1Decl_name, classDecl1_implementsName] }]); +for (const keyword of [getDecl_getKeyword, setDecl_setKeyword]) { + verify.referenceGroups(keyword, [{ definition: "(property) C1.e: number", ranges: [getDecl_name, setDecl_name] }]); +} +verify.referenceGroups(interfaceDecl1_interfaceKeyword, [{ definition: "interface I1", ranges: [interfaceDecl1_name] }]); +verify.referenceGroups(interfaceDecl1_extendsKeyword, [{ definition: "class Base", ranges: [baseDecl_name, classDecl1_extendsName, interfaceDecl1_extendsName] }]); +verify.referenceGroups(typeDecl_typeKeyword, [{ definition: "type T = {}", ranges: [typeDecl_name] }]); +verify.referenceGroups(enumDecl_enumKeyword, [{ definition: "enum E", ranges: [enumDecl_name] }]); +verify.referenceGroups(namespaceDecl_namespaceKeyword, [{ definition: "namespace N", ranges: [namespaceDecl_name] }]); +verify.referenceGroups(moduleDecl_moduleKeyword, [{ definition: "namespace M", ranges: [moduleDecl_name] }]); +verify.referenceGroups(functionDecl_functionKeyword, [{ definition: "function fn(): void", ranges: [functionDecl_name] }]); +verify.referenceGroups(varDecl_varKeyword, [{ definition: "var x: any", ranges: [varDecl_name] }]); +verify.referenceGroups(letDecl_letKeyword, [{ definition: "let y: any", ranges: [letDecl_name] }]); +verify.referenceGroups(constDecl_constKeyword, [{ definition: "const z: 1", ranges: [constDecl_name] }]); +verify.noReferences(classDecl2_implementsKeyword); +verify.noReferences(interfaceDecl2_extendsKeyword); \ No newline at end of file diff --git a/tests/cases/fourslash/referencesForExpressionKeywords.ts b/tests/cases/fourslash/referencesForExpressionKeywords.ts new file mode 100644 index 0000000000000..c64919b765941 --- /dev/null +++ b/tests/cases/fourslash/referencesForExpressionKeywords.ts @@ -0,0 +1,44 @@ +/// + +////[|class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}C|] { +//// [|static [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}x|] = 1;|] +////}|] +////[|new|] [|C|](); +////[|void|] [|C|]; +////[|typeof|] [|C|]; +////[|delete|] [|C|].[|x|]; +////async function* f() { +//// [|yield|] [|C|]; +//// [|await|] [|C|]; +////} +////"x" [|in|] [|C|]; +////undefined [|instanceof|] [|C|]; +////undefined [|as|] [|C|]; + +const [ + classDecl, + classDecl_name, + fieldDecl, + fieldDecl_name, + newKeyword, + newC, + voidKeyword, + voidC, + typeofKeyword, + typeofC, + deleteKeyword, + deleteC, + deleteCx, + yieldKeyword, + yieldC, + awaitKeyword, + awaitC, + inKeyword, + inC, + instanceofKeyword, + instanceofC, + asKeyword, + asC, +] = test.ranges(); +verify.referenceGroups([newKeyword, voidKeyword, typeofKeyword, yieldKeyword, awaitKeyword, inKeyword, instanceofKeyword, asKeyword], [{ definition: "class C", ranges: [classDecl_name, newC, voidC, typeofC, deleteC, yieldC, awaitC, inC, instanceofC, asC] }]); +verify.referenceGroups(deleteKeyword, [{ definition: "(property) C.x: number", ranges: [fieldDecl_name, deleteCx] }]); \ No newline at end of file diff --git a/tests/cases/fourslash/referencesForModifiers.ts b/tests/cases/fourslash/referencesForModifiers.ts new file mode 100644 index 0000000000000..2cfa7eebce77b --- /dev/null +++ b/tests/cases/fourslash/referencesForModifiers.ts @@ -0,0 +1,27 @@ +/// + +////[|/*declareModifier*/declare /*abstractModifier*/abstract class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}C1|] { +//// [|/*staticModifier*/static [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}a|];|] +//// [|/*readonlyModifier*/readonly [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}b|];|] +//// [|/*publicModifier*/public [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}c|];|] +//// [|/*protectedModifier*/protected [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}d|];|] +//// [|/*privateModifier*/private [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}e|];|] +////}|] +////[|/*constModifier*/const enum [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}E|] { +////}|] +////[|/*asyncModifier*/async function [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}fn|]() {}|] +////[|/*exportModifier*/export /*defaultModifier*/[|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -1 |}default|] class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}C2|] {}|] + +const [, classDef1,, aDef,, bDef,, cDef,, dDef,, eDef,, enumDef,, functionDef,, classDef2Default, classDef2Name] = test.ranges(); +for (const modifier of ["declareModifier", "abstractModifier"]) { + verify.referenceGroups(modifier, [{ definition: "class C1", ranges: [classDef1] }]); +} +verify.referenceGroups("staticModifier", [{ definition: "(property) C1.a: any", ranges: [aDef] }]); +verify.referenceGroups("readonlyModifier", [{ definition: "(property) C1.b: any", ranges: [bDef] }]); +verify.referenceGroups("publicModifier", [{ definition: "(property) C1.c: any", ranges: [cDef] }]); +verify.referenceGroups("protectedModifier", [{ definition: "(property) C1.d: any", ranges: [dDef] }]); +verify.referenceGroups("privateModifier", [{ definition: "(property) C1.e: any", ranges: [eDef] }]); +verify.referenceGroups("constModifier", [{ definition: "const enum E", ranges: [enumDef] }]); +verify.referenceGroups("asyncModifier", [{ definition: "function fn(): Promise", ranges: [functionDef] }]); +verify.referenceGroups("exportModifier", [{ definition: "class C2", ranges: [classDef2Name] }]); +verify.referenceGroups("defaultModifier", [{ definition: "class C2", ranges: [classDef2Default] }]); diff --git a/tests/cases/fourslash/referencesForStatementKeywords.ts b/tests/cases/fourslash/referencesForStatementKeywords.ts new file mode 100644 index 0000000000000..bcd9aa0f48a3c --- /dev/null +++ b/tests/cases/fourslash/referencesForStatementKeywords.ts @@ -0,0 +1,267 @@ +/// + +// @filename: /main.ts +////// import ... = ... +////[|{| "id": "importEqualsDecl1" |}[|import|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importEqualsDecl1" |}A|] = [|require|]("[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "importEqualsDecl1" |}./a|]");|] +////[|{| "id": "namespaceDecl1" |}namespace [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "namespaceDecl1" |}N|] { }|] +////[|{| "id": "importEqualsDecl2" |}[|import|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importEqualsDecl2" |}N2|] = [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importEqualsDecl2" |}N|];|] +//// +////// import ... from ... +////[|{| "id": "importDecl1" |}[|import|] [|type|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importDecl1" |}B|] [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "importDecl1" |}./b|]";|] +////[|{| "id": "importDecl2" |}[|import|] [|type|] * [|as|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importDecl2" |}C|] [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "importDecl2" |}./c|]";|] +////[|{| "id": "importDecl3" |}[|import|] [|type|] { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importDecl3" |}D|] } [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "importDecl3" |}./d|]";|] +////[|{| "id": "importDecl4" |}[|import|] [|type|] { e1, e2 [|as|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "importDecl4" |}e3|] } [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "importDecl4" |}./e|]";|] +//// +////// import "module" +////[|{| "id": "importDecl5" |}[|import|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "importDecl5" |}./f|]";|] +//// +////// export ... from ... +////[|{| "id": "exportDecl1" |}[|export|] [|type|] * [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "exportDecl1" |}./g|]";|] +////[|{| "id": "exportDecl2" |}[|export|] [|type|] * [|as|] [|{| "isWriteAccess": true, "isDefinition": true |}H|] [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "exportDecl2" |}./h|]";|] +////[|{| "id": "exportDecl3" |}[|export|] [|type|] { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "exportDecl3" |}I|] } [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "exportDecl3" |}./i|]";|] +////[|{| "id": "exportDecl4" |}[|export|] [|type|] { j1, j2 [|as|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "exportDecl4" |}j3|] } [|from|] "[|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "exportDecl4" |}./j|]";|] +////[|{| "id": "typeDecl1" |}type [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "typeDecl1" |}Z1|] = 1;|] +////[|{| "id": "exportDecl5" |}[|export|] [|type|] { [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "exportDecl5" |}Z1|] };|] +////type Z2 = 2; +////type Z3 = 3; +////[|{| "id": "exportDecl6" |}[|export|] [|type|] { z2, z3 [|as|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "exportDecl6" |}z4|] };|] + +// @filename: /main2.ts +////[|{| "id": "varDecl1" |}const [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "varDecl1" |}x|] = {};|] +////[|{| "id": "exportAssignment1" |}[|export|] = [|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "exportAssignment1"|}x|];|] + +// @filename: /main3.ts +////[|{| "id": "varDecl3" |}const [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "varDecl3" |}y|] = {};|] +////[|{| "id": "exportAssignment2" |}[|export|] [|default|] [|{| "isWriteAccess": false, "isDefinition": false, "contextRangeId": "exportAssignment2"|}y|];|] + +// @filename: /a.ts +////export const a = 1; + +// @filename: /b.ts +////[|{| "id": "classDecl1" |}export default class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "classDecl1" |}B|] {}|] + +// @filename: /c.ts +////export const c = 1; + +// @filename: /d.ts +////[|{| "id": "classDecl2" |}export class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "classDecl2" |}D|] {}|] + +// @filename: /e.ts +////export const e1 = 1; +////export const e2 = 2; + +// @filename: /f.ts +////export const f = 1; + +// @filename: /g.ts +////export const g = 1; + +// @filename: /h.ts +////export const h = 1; + +// @filename: /i.ts +////[|{| "id": "classDecl3" |}export class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "classDecl3" |}I|] {}|] + +// @filename: /j.ts +////export const j1 = 1; +////export const j2 = 2; + +const [ + // main.ts + importEqualsDecl1, + importEqualsDecl1_importKeyword, + importEqualsDecl1_name, + importEqualsDecl1_requireKeyword, + importEqualsDecl1_module, + + namespaceDecl1, + namespaceDecl1_name, + + importEqualsDecl2, + importEqualsDecl2_importKeyword, + importEqualsDecl2_name, + importEqualsDecl2_reference, + + importDecl1, + importDecl1_importKeyword, + importDecl1_typeKeyword, + importDecl1_name, + importDecl1_fromKeyword, + importDecl1_module, + + importDecl2, + importDecl2_importKeyword, + importDecl2_typeKeyword, + importDecl2_asKeyword, + importDecl2_name, + importDecl2_fromKeyword, + importDecl2_module, + + importDecl3, + importDecl3_importKeyword, + importDecl3_typeKeyword, + importDecl3_name, + importDecl3_fromKeyword, + importDecl3_module, + + importDecl4, + importDecl4_importKeyword, + importDecl4_typeKeyword, + importDecl4_asKeyword, + importDecl4_name, + importDecl4_fromKeyword, + importDecl4_module, + + importDecl5, + importDecl5_importKeyword, + importDecl5_module, + + exportDecl1, + exportDecl1_exportKeyword, + exportDecl1_typeKeyword, + exportDecl1_fromKeyword, + exportDecl1_module, + + exportDecl2, + exportDecl2_exportKeyword, + exportDecl2_typeKeyword, + exportDecl2_asKeyword, + exportDecl2_name, + exportDecl2_fromKeyword, + exportDecl2_module, + + exportDecl3, + exportDecl3_exportKeyword, + exportDecl3_typeKeyword, + exportDecl3_name, + exportDecl3_fromKeyword, + exportDecl3_module, + + exportDecl4, + exportDecl4_exportKeyword, + exportDecl4_typeKeyword, + exportDecl4_asKeyword, + exportDecl4_name, + exportDecl4_fromKeyword, + exportDecl4_module, + + typeDecl1, + typeDecl1_name, + + exportDecl5, + exportDecl5_exportKeyword, + exportDecl5_typeKeyword, + exportDecl5_name, + + exportDecl6, + exportDecl6_exportKeyword, + exportDecl6_typeKeyword, + exportDecl6_asKeyword, + exportDecl6_name, + + // main2.ts + varDecl1, + varDecl1_name, + + exportAssignment1, + exportAssignment1_exportKeyword, + exportAssignment1_name, + + // main3.ts + varDecl2, + varDecl2_name, + + exportAssignment2, + exportAssignment2_exportKeyword, + exportAssignment2_defaultKeyword, + exportAssignment2_name, + + // a.ts + // b.ts + classDecl1, + classDecl1_name, + + // c.ts + // d.ts + classDecl2, + classDecl2_name, + + // e.ts + // f.ts + // g.ts + // h.ts + // i.ts + classDecl3, + classDecl3_name, + // j.ts + +] = test.ranges(); + + +// importEqualsDecl1: +verify.referenceGroups(importEqualsDecl1_importKeyword, [{ definition: "import A = require(\"./a\")", ranges: [importEqualsDecl1_name] }]); +verify.referenceGroups(importEqualsDecl1_requireKeyword, [{ definition: "module \"/a\"", ranges: [importEqualsDecl1_module] }]); + +// importEqualsDecl2: +verify.referenceGroups(importEqualsDecl2_importKeyword, [{ definition: "(alias) namespace N2\nimport N2 = N", ranges: [importEqualsDecl2_name] }]); + +// importDecl1: +verify.referenceGroups([importDecl1_importKeyword, importDecl1_typeKeyword], [ + { definition: "(alias) class B\nimport B", ranges: [importDecl1_name] }, + { definition: "class B", ranges: [classDecl1_name] } +]); +verify.referenceGroups(importDecl1_fromKeyword, [{ definition: "module \"/b\"", ranges: [importDecl1_module] }]); + +// importDecl2: +verify.referenceGroups([importDecl2_importKeyword, importDecl2_typeKeyword, importDecl2_asKeyword], [{ definition: "import C", ranges: [importDecl2_name] }]); +verify.referenceGroups([importDecl2_fromKeyword], [{ definition: "module \"/c\"", ranges: [importDecl2_module] }]); + +// importDecl3: +verify.referenceGroups([importDecl3_importKeyword, importDecl3_typeKeyword], [ + { definition: "(alias) class D\nimport D", ranges: [importDecl3_name] }, + { definition: "class D", ranges: [classDecl2_name] } +]); +verify.referenceGroups(importDecl3_fromKeyword, [{ definition: "module \"/d\"", ranges: [importDecl3_module] }]); + +// importDecl4: +verify.referenceGroups([importDecl4_importKeyword, importDecl4_typeKeyword, importDecl4_fromKeyword], [{ definition: "module \"/e\"", ranges: [importDecl4_module] }]); +verify.referenceGroups(importDecl4_asKeyword, [{ definition: "(alias) const e3: 2\nimport e3", ranges: [importDecl4_name] }]); + +// importDecl5 +verify.referenceGroups(importDecl5_importKeyword, [{ definition: "module \"/f\"", ranges: [importDecl5_module] }]); + +// exportDecl1: +verify.referenceGroups([exportDecl1_exportKeyword, exportDecl1_typeKeyword, exportDecl1_fromKeyword], [{ definition: "module \"/g\"", ranges: [exportDecl1_module] }]); + +// exportDecl2: +verify.referenceGroups([exportDecl2_exportKeyword, exportDecl2_typeKeyword, exportDecl2_asKeyword], [{ definition: "import H", ranges: [exportDecl2_name] }]); +verify.referenceGroups([exportDecl2_fromKeyword], [{ definition: "module \"/h\"", ranges: [exportDecl2_module] }]); + +// exportDecl3: +verify.referenceGroups([exportDecl3_exportKeyword, exportDecl3_typeKeyword], [ + { definition: "(alias) class I\nexport I", ranges: [exportDecl3_name] }, + { definition: "class I", ranges: [classDecl3_name] } +]); +verify.referenceGroups(exportDecl3_fromKeyword, [{ definition: "module \"/i\"", ranges: [exportDecl3_module] }]); + +// exportDecl4: +verify.referenceGroups([exportDecl4_exportKeyword, exportDecl4_typeKeyword, exportDecl4_fromKeyword], [{ definition: "module \"/j\"", ranges: [exportDecl4_module] }]); +verify.referenceGroups(exportDecl4_asKeyword, [{ definition: "(alias) const j3: 2\nexport j3", ranges: [exportDecl4_name] }]); + +// exportDecl5: +verify.referenceGroups([exportDecl5_exportKeyword, exportDecl5_typeKeyword], [{ definition: "type Z1 = 1", ranges: [typeDecl1_name, exportDecl5_name] }]); + +// exportDecl6: +verify.noReferences(exportDecl6_exportKeyword); +verify.noReferences(exportDecl6_typeKeyword); +verify.referenceGroups(exportDecl6_asKeyword, [{ definition: "export z4", ranges: [exportDecl6_name] }]); + +// exportAssignment1: +verify.referenceGroups(exportAssignment1_exportKeyword, [ + { definition: "const x: {}", ranges: [varDecl1_name, exportAssignment1_name] } +]); + +// exportAssignment2: +verify.referenceGroups(exportAssignment2_exportKeyword, [ + { definition: "const y: {}", ranges: [varDecl2_name, exportAssignment2_name] } +]); diff --git a/tests/cases/fourslash/referencesForTypeKeywords.ts b/tests/cases/fourslash/referencesForTypeKeywords.ts new file mode 100644 index 0000000000000..cfd37a43960c0 --- /dev/null +++ b/tests/cases/fourslash/referencesForTypeKeywords.ts @@ -0,0 +1,43 @@ +/// + +////[|{| "id": "interfaceDecl" |}interface [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "interfaceDecl" |}I|] {}|] +////function f() {} +////type A1 = T [|extends|] [|U|] ? 1 : 0; +////type A2 = T extends [|infer|] [|{| "isWriteAccess": true, "isDefinition": true |}U|] ? 1 : 0; +////type A3 = { [[|{| "id": "mappedType_param" |}[|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "mappedType_param" |}P|] [|in|] keyof T|]]: 1 }; +////type A4<[|{| "isWriteAccess": true, "isDefinition": true |}T|]> = [|keyof|] [|T|]; +////type A5<[|{| "isWriteAccess": true, "isDefinition": true |}T|]> = [|readonly|] [|T|][]; + +const [ + interfaceDecl, + interfaceDecl_name, + + typeParam_extendsKeyword, + typeParam_constraint, + + typeParamA1_name, + conditionalType_extendsKeyword, + conditionalType_extendsType, + + inferType_inferKeyword, + inferType_type, + + mappedType_param, + mappedType_name, + mappedType_inOperator, + + typeParamA4_name, + keyofOperator_keyofKeyword, + keyofOperator_type, + + typeParamA5_name, + readonlyOperator_readonlyKeyword, + readonlyOperator_elementType, +] = test.ranges(); + +verify.referenceGroups(typeParam_extendsKeyword, [{ definition: "interface I", ranges: [interfaceDecl_name, typeParam_constraint] }]); +verify.referenceGroups(conditionalType_extendsKeyword, [{ definition: "(type parameter) U in type A1", ranges: [typeParamA1_name, conditionalType_extendsType] }]); +verify.referenceGroups(inferType_inferKeyword, [{ definition: "(type parameter) U", ranges: [inferType_type] }]); +verify.referenceGroups(mappedType_inOperator, [{ definition: "(type parameter) P", ranges: [mappedType_name] }]); +verify.referenceGroups(keyofOperator_keyofKeyword, [{ definition: "(type parameter) T in type A4", ranges: [typeParamA4_name, keyofOperator_type] }]); +verify.referenceGroups(readonlyOperator_readonlyKeyword, [{ definition: "(type parameter) T in type A5", ranges: [typeParamA5_name, readonlyOperator_elementType] }]); \ No newline at end of file diff --git a/tests/cases/fourslash/renameDeclarationKeywords.ts b/tests/cases/fourslash/renameDeclarationKeywords.ts new file mode 100644 index 0000000000000..0a762150ff65a --- /dev/null +++ b/tests/cases/fourslash/renameDeclarationKeywords.ts @@ -0,0 +1,95 @@ +/// + +////[|{| "id": "baseDecl" |}class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "baseDecl" |}Base|] {}|] +////[|{| "id": "implemented1Decl" |}interface [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "implemented1Decl" |}Implemented1|] {}|] +////[|{| "id": "classDecl1" |}[|class|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "classDecl1" |}C1|] [|extends|] [|Base|] [|implements|] [|Implemented1|] { +//// [|{| "id": "getDecl" |}[|get|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "getDecl" |}e|]() { return 1; }|] +//// [|{| "id": "setDecl" |}[|set|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "setDecl" |}e|](v) {}|] +////}|] +////[|{| "id": "interfaceDecl1" |}[|interface|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "interfaceDecl1" |}I1|] [|extends|] [|Base|] { }|] +////[|{| "id": "typeDecl" |}[|type|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "typeDecl" |}T|] = { }|] +////[|{| "id": "enumDecl" |}[|enum|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "enumDecl" |}E|] { }|] +////[|{| "id": "namespaceDecl" |}[|namespace|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "namespaceDecl" |}N|] { }|] +////[|{| "id": "moduleDecl" |}[|module|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "moduleDecl" |}M|] { }|] +////[|{| "id": "functionDecl" |}[|function|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "functionDecl" |}fn|]() {}|] +////[|{| "id": "varDecl" |}[|var|] [|{| "isWriteAccess": false, "isDefinition": true, "contextRangeId": "varDecl" |}x|];|] +////[|{| "id": "letDecl" |}[|let|] [|{| "isWriteAccess": false, "isDefinition": true, "contextRangeId": "letDecl" |}y|];|] +////[|{| "id": "constDecl" |}[|const|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeId": "constDecl" |}z|] = 1;|] + +const [ + baseDecl, + baseDecl_name, + + implemented1Decl, + implemented1Decl_name, + + classDecl1, + classDecl1_classKeyword, + classDecl1_name, + classDecl1_extendsKeyword, + classDecl1_extendsName, + classDecl1_implementsKeyword, + classDecl1_implementsName, + + getDecl, + getDecl_getKeyword, + getDecl_name, + + setDecl, + setDecl_setKeyword, + setDecl_name, + + interfaceDecl1, + interfaceDecl1_interfaceKeyword, + interfaceDecl1_name, + interfaceDecl1_extendsKeyword, + interfaceDecl1_extendsName, + + typeDecl, + typeDecl_typeKeyword, + typeDecl_name, + + enumDecl, + enumDecl_enumKeyword, + enumDecl_name, + + namespaceDecl, + namespaceDecl_namespaceKeyword, + namespaceDecl_name, + + moduleDecl, + moduleDecl_moduleKeyword, + moduleDecl_name, + + functionDecl, + functionDecl_functionKeyword, + functionDecl_name, + + varDecl, + varDecl_varKeyword, + varDecl_name, + + letDecl, + letDecl_letKeyword, + letDecl_name, + + constDecl, + constDecl_constKeyword, + constDecl_name, +] = test.ranges(); +verify.renameLocations(classDecl1_classKeyword, [{ range: classDecl1_name }]); +verify.renameLocations(classDecl1_extendsKeyword, [{ range: baseDecl_name }, { range: classDecl1_extendsName }, { range: interfaceDecl1_extendsName }]); +verify.renameLocations(classDecl1_implementsKeyword, [{ range: implemented1Decl_name }, { range: classDecl1_implementsName }]); +for (const keyword of [getDecl_getKeyword, setDecl_setKeyword]) { + verify.renameLocations(keyword, [{ range: getDecl_name }, { range: setDecl_name }]); +} +verify.renameLocations(interfaceDecl1_interfaceKeyword, [{ range: interfaceDecl1_name }]); +verify.renameLocations(interfaceDecl1_extendsKeyword, [{ range: baseDecl_name }, { range: classDecl1_extendsName }, { range: interfaceDecl1_extendsName }]); +verify.renameLocations(typeDecl_typeKeyword, [{ range: typeDecl_name }]); +verify.renameLocations(enumDecl_enumKeyword, [{ range: enumDecl_name }]); +verify.renameLocations(namespaceDecl_namespaceKeyword, [{ range: namespaceDecl_name }]); +verify.renameLocations(moduleDecl_moduleKeyword, [{ range: moduleDecl_name }]); +verify.renameLocations(functionDecl_functionKeyword, [{ range: functionDecl_name }]); +verify.renameLocations(varDecl_varKeyword, [{ range: varDecl_name }]); +verify.renameLocations(letDecl_letKeyword, [{ range: letDecl_name }]); +verify.renameLocations(constDecl_constKeyword, [{ range: constDecl_name }]); \ No newline at end of file diff --git a/tests/cases/fourslash/renameModifiers.ts b/tests/cases/fourslash/renameModifiers.ts new file mode 100644 index 0000000000000..ab2258a1d08cf --- /dev/null +++ b/tests/cases/fourslash/renameModifiers.ts @@ -0,0 +1,34 @@ +/// + +////[|[|declare|] [|abstract|] class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -3 |}C1|] { +//// [|[|static|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}a|];|] +//// [|[|readonly|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}b|];|] +//// [|[|public|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}c|];|] +//// [|[|protected|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}d|];|] +//// [|[|private|] [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}e|];|] +////}|] +////[|[|const|] enum [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}E|] { +////}|] +////[|[|async|] function [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -2 |}fn|]() {}|] +////[|[|export|] [|default|] class [|{| "isWriteAccess": true, "isDefinition": true, "contextRangeDelta": -3 |}C2|] {}|] + +const [ + class1Def, declareKeyword, abstractKeyword, class1Name, + aDef, staticKeyword, aName, + bDef, readonlyKeyword, bName, + cDef, publicKeyword, cName, + dDef, protectedKeyword, dName, + eDef, privateKeyword, eName, + enumDef, constKeyword, enumName, + functionDef, asyncKeyword, functionName, + class2Def, exportKeyword, defaultKeyword, class2Name, +] = test.ranges(); +verify.renameLocations([declareKeyword, abstractKeyword], [{ range: class1Name }]); +verify.renameLocations([staticKeyword], [{ range: aName }]); +verify.renameLocations([readonlyKeyword], [{ range: bName }]); +verify.renameLocations([publicKeyword], [{ range: cName }]); +verify.renameLocations([protectedKeyword], [{ range: dName }]); +verify.renameLocations([privateKeyword], [{ range: eName }]); +verify.renameLocations([constKeyword], [{ range: enumName }]); +verify.renameLocations([asyncKeyword], [{ range: functionName }]); +verify.renameLocations([exportKeyword, defaultKeyword], [{ range: class2Name }]);