Skip to content

Commit 2e7bec9

Browse files
committed
fix(checker): mark getter/setter as deprecated when any declaration has @deprecated
1 parent 3472548 commit 2e7bec9

5 files changed

Lines changed: 123 additions & 26 deletions

File tree

src/compiler/checker.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2605,14 +2605,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
26052605
return diagnostic;
26062606
}
26072607

2608-
function isDeprecatedSymbol(symbol: Symbol) {
2609-
const parentSymbol = getParentOfSymbol(symbol);
2610-
if (parentSymbol && length(symbol.declarations) > 1) {
2611-
return parentSymbol.flags & SymbolFlags.Interface ? some(symbol.declarations, isDeprecatedDeclaration) : every(symbol.declarations, isDeprecatedDeclaration);
2612-
}
2613-
return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration)
2614-
|| length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration);
2615-
}
2608+
function isDeprecatedSymbol(symbol: Symbol) {
2609+
const parentSymbol = getParentOfSymbol(symbol);
2610+
if (parentSymbol && length(symbol.declarations) > 1) {
2611+
if (parentSymbol.flags & SymbolFlags.Interface || symbol.flags & SymbolFlags.Accessor) {
2612+
return some(symbol.declarations, isDeprecatedDeclaration);
2613+
}
2614+
return every(symbol.declarations, isDeprecatedDeclaration);
2615+
}
2616+
return !!symbol.valueDeclaration && isDeprecatedDeclaration(symbol.valueDeclaration)
2617+
|| length(symbol.declarations) && every(symbol.declarations, isDeprecatedDeclaration);
2618+
}
26162619

26172620
function isDeprecatedDeclaration(declaration: Declaration) {
26182621
return !!(getCombinedNodeFlagsCached(declaration) & NodeFlags.Deprecated);

src/services/completions.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6084,10 +6084,14 @@ function symbolCanBeReferencedAtTypeLocation(symbol: Symbol, checker: TypeChecke
60846084
}
60856085
}
60866086

6087-
function isDeprecated(symbol: Symbol, checker: TypeChecker) {
6088-
const declarations = skipAlias(symbol, checker).declarations;
6089-
return !!length(declarations) && every(declarations, isDeprecatedDeclaration);
6090-
}
6087+
function isDeprecated(symbol: Symbol, checker: TypeChecker) {
6088+
const resolvedSymbol = skipAlias(symbol, checker);
6089+
const declarations = resolvedSymbol.declarations;
6090+
if (resolvedSymbol.flags & SymbolFlags.Accessor) {
6091+
return !!length(declarations) && some(declarations, isDeprecatedDeclaration);
6092+
}
6093+
return !!length(declarations) && every(declarations, isDeprecatedDeclaration);
6094+
}
60916095

60926096
/**
60936097
* True if the first character of `lowercaseCharacters` is the first character

src/services/symbolDisplay.ts

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -213,20 +213,25 @@ function getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeCheck
213213
return ScriptElementKind.unknown;
214214
}
215215

216-
function getNormalizedSymbolModifiers(symbol: Symbol) {
217-
if (symbol.declarations && symbol.declarations.length) {
218-
const [declaration, ...declarations] = symbol.declarations;
219-
// omit deprecated flag if some declarations are not deprecated
220-
const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d))
221-
? ModifierFlags.Deprecated
222-
: ModifierFlags.None;
223-
const modifiers = getNodeModifiers(declaration, excludeFlags);
224-
if (modifiers) {
225-
return modifiers.split(",");
226-
}
227-
}
228-
return [];
229-
}
216+
function getNormalizedSymbolModifiers(symbol: Symbol) {
217+
if (symbol.declarations && symbol.declarations.length) {
218+
const [declaration, ...declarations] = symbol.declarations;
219+
// omit deprecated flag if some declarations are not deprecated
220+
const excludeFlags = length(declarations) && isDeprecatedDeclaration(declaration) && some(declarations, d => !isDeprecatedDeclaration(d))
221+
? ModifierFlags.Deprecated
222+
: ModifierFlags.None;
223+
const modifiers = getNodeModifiers(declaration, excludeFlags);
224+
if (modifiers) {
225+
const result = modifiers.split(",");
226+
// For getter/setter pairs, include deprecated if any accessor has @deprecated
227+
if (symbol.flags & SymbolFlags.Accessor && !result.includes("deprecated") && some(symbol.declarations, isDeprecatedDeclaration)) {
228+
result.push("deprecated");
229+
}
230+
return result;
231+
}
232+
}
233+
return [];
234+
}
230235

231236
/** @internal */
232237
export function getSymbolModifiers(typeChecker: TypeChecker, symbol: Symbol): string {
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// Tests that @deprecated on getter/setter pairs correctly marks the property as deprecated in completions.
4+
// Regression from #41941, reported in #62965.
5+
6+
//// class Test {
7+
//// /** @deprecated */
8+
//// public get test1() { return 0; }
9+
//// public set test1(_value: number) {}
10+
////
11+
//// public get test2() { return 0; }
12+
//// /** @deprecated */
13+
//// public set test2(_value: number) {}
14+
////
15+
//// /** @deprecated */
16+
//// public get test3() { return 0; }
17+
//// /** @deprecated */
18+
//// public set test3(_value: number) {}
19+
////
20+
//// public get test4() { return 0; }
21+
//// public set test4(_value: number) {}
22+
//// }
23+
////
24+
//// const t = new Test();
25+
//// t./*1*/;
26+
27+
verify.completions({
28+
marker: "1",
29+
includes: [
30+
{ name: "test1", kind: "getter", kindModifiers: "public,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) },
31+
{ name: "test2", kind: "getter", kindModifiers: "public,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) },
32+
{ name: "test3", kind: "getter", kindModifiers: "public,deprecated", sortText: completion.SortText.Deprecated(completion.SortText.LocationPriority) },
33+
{ name: "test4", kind: "getter", kindModifiers: "public", sortText: completion.SortText.LocationPriority },
34+
],
35+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// Tests that @deprecated on getter/setter pairs produces suggestion diagnostics.
4+
// Regression from #41941, reported in #62965.
5+
6+
//// class Test {
7+
//// /** @deprecated */
8+
//// public get test1() { return 0; }
9+
//// public set test1(_value: number) {}
10+
////
11+
//// public get test2() { return 0; }
12+
//// /** @deprecated */
13+
//// public set test2(_value: number) {}
14+
////
15+
//// /** @deprecated */
16+
//// public get test3() { return 0; }
17+
//// /** @deprecated */
18+
//// public set test3(_value: number) {}
19+
////
20+
//// public get test4() { return 0; }
21+
//// public set test4(_value: number) {}
22+
//// }
23+
////
24+
//// const t = new Test();
25+
//// const a = t.[|test1|];
26+
//// const b = t.[|test2|];
27+
//// const c = t.[|test3|];
28+
//// const d = t.test4;
29+
30+
const ranges = test.ranges();
31+
verify.getSuggestionDiagnostics([
32+
{
33+
"code": 6385,
34+
"message": "'test1' is deprecated.",
35+
"reportsDeprecated": true,
36+
"range": ranges[0],
37+
},
38+
{
39+
"code": 6385,
40+
"message": "'test2' is deprecated.",
41+
"reportsDeprecated": true,
42+
"range": ranges[1],
43+
},
44+
{
45+
"code": 6385,
46+
"message": "'test3' is deprecated.",
47+
"reportsDeprecated": true,
48+
"range": ranges[2],
49+
},
50+
]);

0 commit comments

Comments
 (0)