Skip to content

Commit e82d0af

Browse files
authored
Fix readonly occurrences highlighting (microsoft#32583)
* Fix readonly occurrences highlighting * Rename function * Rename again * Apply suggestions from code review Remove unused function
1 parent 6b4f730 commit e82d0af

File tree

6 files changed

+63
-7
lines changed

6 files changed

+63
-7
lines changed

src/services/documentHighlights.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ namespace ts.DocumentHighlights {
193193

194194
function getNodesToSearchForModifier(declaration: Node, modifierFlag: ModifierFlags): ReadonlyArray<Node> | undefined {
195195
// Types of node whose children might have modifiers.
196-
const container = declaration.parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ClassLikeDeclaration;
196+
const container = declaration.parent as ModuleBlock | SourceFile | Block | CaseClause | DefaultClause | ConstructorDeclaration | MethodDeclaration | FunctionDeclaration | ObjectTypeDeclaration;
197197
switch (container.kind) {
198198
case SyntaxKind.ModuleBlock:
199199
case SyntaxKind.SourceFile:
@@ -213,11 +213,13 @@ namespace ts.DocumentHighlights {
213213
return [...container.parameters, ...(isClassLike(container.parent) ? container.parent.members : [])];
214214
case SyntaxKind.ClassDeclaration:
215215
case SyntaxKind.ClassExpression:
216+
case SyntaxKind.InterfaceDeclaration:
217+
case SyntaxKind.TypeLiteral:
216218
const nodes = container.members;
217219

218220
// If we're an accessibility modifier, we're in an instance member and should search
219221
// the constructor's parameter list for instance members as well.
220-
if (modifierFlag & ModifierFlags.AccessibilityModifier) {
222+
if (modifierFlag & (ModifierFlags.AccessibilityModifier | ModifierFlags.Readonly)) {
221223
const constructor = find(container.members, isConstructorDeclaration);
222224
if (constructor) {
223225
return [...nodes, ...constructor.parameters];

src/services/findAllReferences.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -709,10 +709,28 @@ namespace ts.FindAllReferences.Core {
709709
return references.length ? [{ definition: { type: DefinitionKind.Symbol, symbol }, references }] : emptyArray;
710710
}
711711

712+
/** As in a `readonly prop: any` or `constructor(readonly prop: any)`, not a `readonly any[]`. */
713+
function isReadonlyTypeOperator(node: Node): boolean {
714+
return node.kind === SyntaxKind.ReadonlyKeyword
715+
&& isTypeOperatorNode(node.parent)
716+
&& node.parent.operator === SyntaxKind.ReadonlyKeyword;
717+
}
718+
712719
/** getReferencedSymbols for special node kinds. */
713720
function getReferencedSymbolsSpecial(node: Node, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken): SymbolAndEntries[] | undefined {
714721
if (isTypeKeyword(node.kind)) {
715-
return getAllReferencesForKeyword(sourceFiles, node.kind, cancellationToken);
722+
// A modifier readonly (like on a property declaration) is not special;
723+
// a readonly type keyword (like `readonly string[]`) is.
724+
if (node.kind === SyntaxKind.ReadonlyKeyword && !isReadonlyTypeOperator(node)) {
725+
return undefined;
726+
}
727+
// Likewise, when we *are* looking for a special keyword, make sure we
728+
// *don’t* include readonly member modifiers.
729+
return getAllReferencesForKeyword(
730+
sourceFiles,
731+
node.kind,
732+
cancellationToken,
733+
node.kind === SyntaxKind.ReadonlyKeyword ? isReadonlyTypeOperator : undefined);
716734
}
717735

718736
// Labels
@@ -1235,11 +1253,14 @@ namespace ts.FindAllReferences.Core {
12351253
}
12361254
}
12371255

1238-
function getAllReferencesForKeyword(sourceFiles: ReadonlyArray<SourceFile>, keywordKind: SyntaxKind, cancellationToken: CancellationToken): SymbolAndEntries[] | undefined {
1256+
function getAllReferencesForKeyword(sourceFiles: ReadonlyArray<SourceFile>, keywordKind: SyntaxKind, cancellationToken: CancellationToken, filter?: (node: Node) => boolean): SymbolAndEntries[] | undefined {
12391257
const references = flatMap(sourceFiles, sourceFile => {
12401258
cancellationToken.throwIfCancellationRequested();
1241-
return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, tokenToString(keywordKind)!, sourceFile), referenceLocation =>
1242-
referenceLocation.kind === keywordKind ? nodeEntry(referenceLocation) : undefined);
1259+
return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, tokenToString(keywordKind)!, sourceFile), referenceLocation => {
1260+
if (referenceLocation.kind === keywordKind && (!filter || filter(referenceLocation))) {
1261+
return nodeEntry(referenceLocation);
1262+
}
1263+
});
12431264
});
12441265
return references.length ? [{ definition: { type: DefinitionKind.Keyword, node: references[0].node }, references }] : undefined;
12451266
}

tests/cases/fourslash/documentHighlightsInvalidModifierLocations.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44
//// m([|readonly|] p) {}
55
////}
66
////function f([|readonly|] p) {}
7+
////
8+
////class D {
9+
//// m([|public|] p) {}
10+
////}
11+
////function g([|public|] p) {}
712

813
for (const r of test.ranges()) {
9-
verify.documentHighlightsOf(r, test.ranges());
14+
verify.documentHighlightsOf(r, [r]);
1015
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////interface I {
4+
//// [|readonly|] prop: string;
5+
////}
6+
7+
verify.rangesAreOccurrences(false);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////type T = {
4+
//// [|readonly|] prop: string;
5+
////}
6+
7+
verify.rangesAreOccurrences(false);
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
////class C {
4+
//// [|readonly|] prop: /**/readonly string[] = [];
5+
//// constructor([|readonly|] prop2: string) {
6+
//// class D {
7+
//// readonly prop: string = "";
8+
//// }
9+
//// }
10+
////}
11+
12+
verify.rangesAreOccurrences(false);
13+
goTo.marker();
14+
verify.occurrencesAtPositionCount(1);

0 commit comments

Comments
 (0)