Skip to content

Commit 3c576f1

Browse files
authored
fix(41027): handle unused static members (microsoft#41103)
1 parent 5053b0b commit 3c576f1

File tree

6 files changed

+427
-7
lines changed

6 files changed

+427
-7
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14334,7 +14334,7 @@ namespace ts {
1433414334
addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName as string);
1433514335
}
1433614336
if (accessExpression) {
14337-
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
14337+
markPropertyAsReferenced(prop, accessExpression, isSelfTypeAccess(accessExpression.expression, objectType.symbol));
1433814338
if (isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression))) {
1433914339
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, symbolToString(prop));
1434014340
return undefined;
@@ -26796,7 +26796,7 @@ namespace ts {
2679626796
addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string);
2679726797
}
2679826798
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
26799-
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
26799+
markPropertyAsReferenced(prop, node, isSelfTypeAccess(left, parentSymbol));
2680026800
getNodeLinks(node).resolvedSymbol = prop;
2680126801
checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop);
2680226802
if (isAssignmentToReadonlyEntity(node as Expression, prop, assignmentKind)) {
@@ -27125,7 +27125,7 @@ namespace ts {
2712527125
}
2712627126
}
2712727127

27128-
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) {
27128+
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isSelfTypeAccess: boolean) {
2712927129
const valueDeclaration = prop && (prop.flags & SymbolFlags.ClassMember) && prop.valueDeclaration;
2713027130
if (!valueDeclaration) {
2713127131
return;
@@ -27138,8 +27138,7 @@ namespace ts {
2713827138
if (nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly) && !(prop.flags & SymbolFlags.SetAccessor)) {
2713927139
return;
2714027140
}
27141-
27142-
if (isThisAccess) {
27141+
if (isSelfTypeAccess) {
2714327142
// Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters).
2714427143
const containingMethod = findAncestor(nodeForCheckWriteOnly, isFunctionLikeDeclaration);
2714527144
if (containingMethod && containingMethod.symbol === prop) {
@@ -27150,6 +27149,11 @@ namespace ts {
2715027149
(getCheckFlags(prop) & CheckFlags.Instantiated ? getSymbolLinks(prop).target : prop)!.isReferenced = SymbolFlags.All;
2715127150
}
2715227151

27152+
function isSelfTypeAccess(name: Expression | QualifiedName, parent: Symbol | undefined) {
27153+
return name.kind === SyntaxKind.ThisKeyword
27154+
|| !!parent && isEntityNameExpression(name) && parent === getResolvedSymbol(getFirstIdentifier(name));
27155+
}
27156+
2715327157
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: __String): boolean {
2715427158
switch (node.kind) {
2715527159
case SyntaxKind.PropertyAccessExpression:
@@ -34700,7 +34704,7 @@ namespace ts {
3470034704
const nameText = getPropertyNameFromType(exprType);
3470134705
const property = getPropertyOfType(parentType, nameText);
3470234706
if (property) {
34703-
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
34707+
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isSelfTypeAccess*/ false); // A destructuring is never a write-only reference.
3470434708
checkPropertyAccessibility(node, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType, property);
3470534709
}
3470634710
}
@@ -41504,5 +41508,4 @@ namespace ts {
4150441508
export function signatureHasLiteralTypes(s: Signature) {
4150541509
return !!(s.flags & SignatureFlags.HasLiteralTypes);
4150641510
}
41507-
4150841511
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
tests/cases/compiler/unusedPrivateStaticMembers.ts(16,20): error TS6133: 'p1' is declared but its value is never read.
2+
tests/cases/compiler/unusedPrivateStaticMembers.ts(17,20): error TS6133: 'm1' is declared but its value is never read.
3+
tests/cases/compiler/unusedPrivateStaticMembers.ts(21,20): error TS6133: 'm1' is declared but its value is never read.
4+
tests/cases/compiler/unusedPrivateStaticMembers.ts(25,20): error TS6133: 'm2' is declared but its value is never read.
5+
6+
7+
==== tests/cases/compiler/unusedPrivateStaticMembers.ts (4 errors) ====
8+
class Test1 {
9+
private static m1() {}
10+
public static test() {
11+
Test1.m1();
12+
}
13+
}
14+
15+
class Test2 {
16+
private static p1 = 0
17+
public static test() {
18+
Test2.p1;
19+
}
20+
}
21+
22+
class Test3 {
23+
private static p1 = 0;
24+
~~
25+
!!! error TS6133: 'p1' is declared but its value is never read.
26+
private static m1() {}
27+
~~
28+
!!! error TS6133: 'm1' is declared but its value is never read.
29+
}
30+
31+
class Test4 {
32+
private static m1(n: number): number {
33+
~~
34+
!!! error TS6133: 'm1' is declared but its value is never read.
35+
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
36+
}
37+
38+
private static m2(n: number): number {
39+
~~
40+
!!! error TS6133: 'm2' is declared but its value is never read.
41+
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
42+
}
43+
}
44+
45+
class Test5 {
46+
private static m1() {}
47+
public static test() {
48+
Test5["m1"]();
49+
}
50+
}
51+
52+
class Test6 {
53+
private static p1 = 0;
54+
public static test() {
55+
Test6["p1"];
56+
}
57+
}
58+
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//// [unusedPrivateStaticMembers.ts]
2+
class Test1 {
3+
private static m1() {}
4+
public static test() {
5+
Test1.m1();
6+
}
7+
}
8+
9+
class Test2 {
10+
private static p1 = 0
11+
public static test() {
12+
Test2.p1;
13+
}
14+
}
15+
16+
class Test3 {
17+
private static p1 = 0;
18+
private static m1() {}
19+
}
20+
21+
class Test4 {
22+
private static m1(n: number): number {
23+
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
24+
}
25+
26+
private static m2(n: number): number {
27+
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
28+
}
29+
}
30+
31+
class Test5 {
32+
private static m1() {}
33+
public static test() {
34+
Test5["m1"]();
35+
}
36+
}
37+
38+
class Test6 {
39+
private static p1 = 0;
40+
public static test() {
41+
Test6["p1"];
42+
}
43+
}
44+
45+
46+
//// [unusedPrivateStaticMembers.js]
47+
class Test1 {
48+
static m1() { }
49+
static test() {
50+
Test1.m1();
51+
}
52+
}
53+
class Test2 {
54+
static test() {
55+
Test2.p1;
56+
}
57+
}
58+
Test2.p1 = 0;
59+
class Test3 {
60+
static m1() { }
61+
}
62+
Test3.p1 = 0;
63+
class Test4 {
64+
static m1(n) {
65+
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
66+
}
67+
static m2(n) {
68+
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
69+
}
70+
}
71+
class Test5 {
72+
static m1() { }
73+
static test() {
74+
Test5["m1"]();
75+
}
76+
}
77+
class Test6 {
78+
static test() {
79+
Test6["p1"];
80+
}
81+
}
82+
Test6.p1 = 0;
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
=== tests/cases/compiler/unusedPrivateStaticMembers.ts ===
2+
class Test1 {
3+
>Test1 : Symbol(Test1, Decl(unusedPrivateStaticMembers.ts, 0, 0))
4+
5+
private static m1() {}
6+
>m1 : Symbol(Test1.m1, Decl(unusedPrivateStaticMembers.ts, 0, 13))
7+
8+
public static test() {
9+
>test : Symbol(Test1.test, Decl(unusedPrivateStaticMembers.ts, 1, 26))
10+
11+
Test1.m1();
12+
>Test1.m1 : Symbol(Test1.m1, Decl(unusedPrivateStaticMembers.ts, 0, 13))
13+
>Test1 : Symbol(Test1, Decl(unusedPrivateStaticMembers.ts, 0, 0))
14+
>m1 : Symbol(Test1.m1, Decl(unusedPrivateStaticMembers.ts, 0, 13))
15+
}
16+
}
17+
18+
class Test2 {
19+
>Test2 : Symbol(Test2, Decl(unusedPrivateStaticMembers.ts, 5, 1))
20+
21+
private static p1 = 0
22+
>p1 : Symbol(Test2.p1, Decl(unusedPrivateStaticMembers.ts, 7, 13))
23+
24+
public static test() {
25+
>test : Symbol(Test2.test, Decl(unusedPrivateStaticMembers.ts, 8, 25))
26+
27+
Test2.p1;
28+
>Test2.p1 : Symbol(Test2.p1, Decl(unusedPrivateStaticMembers.ts, 7, 13))
29+
>Test2 : Symbol(Test2, Decl(unusedPrivateStaticMembers.ts, 5, 1))
30+
>p1 : Symbol(Test2.p1, Decl(unusedPrivateStaticMembers.ts, 7, 13))
31+
}
32+
}
33+
34+
class Test3 {
35+
>Test3 : Symbol(Test3, Decl(unusedPrivateStaticMembers.ts, 12, 1))
36+
37+
private static p1 = 0;
38+
>p1 : Symbol(Test3.p1, Decl(unusedPrivateStaticMembers.ts, 14, 13))
39+
40+
private static m1() {}
41+
>m1 : Symbol(Test3.m1, Decl(unusedPrivateStaticMembers.ts, 15, 26))
42+
}
43+
44+
class Test4 {
45+
>Test4 : Symbol(Test4, Decl(unusedPrivateStaticMembers.ts, 17, 1))
46+
47+
private static m1(n: number): number {
48+
>m1 : Symbol(Test4.m1, Decl(unusedPrivateStaticMembers.ts, 19, 13))
49+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
50+
51+
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
52+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
53+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
54+
>Test4.m1 : Symbol(Test4.m1, Decl(unusedPrivateStaticMembers.ts, 19, 13))
55+
>Test4 : Symbol(Test4, Decl(unusedPrivateStaticMembers.ts, 17, 1))
56+
>m1 : Symbol(Test4.m1, Decl(unusedPrivateStaticMembers.ts, 19, 13))
57+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
58+
}
59+
60+
private static m2(n: number): number {
61+
>m2 : Symbol(Test4.m2, Decl(unusedPrivateStaticMembers.ts, 22, 5))
62+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
63+
64+
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
65+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
66+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
67+
>Test4 : Symbol(Test4, Decl(unusedPrivateStaticMembers.ts, 17, 1))
68+
>"m2" : Symbol(Test4.m2, Decl(unusedPrivateStaticMembers.ts, 22, 5))
69+
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
70+
}
71+
}
72+
73+
class Test5 {
74+
>Test5 : Symbol(Test5, Decl(unusedPrivateStaticMembers.ts, 27, 1))
75+
76+
private static m1() {}
77+
>m1 : Symbol(Test5.m1, Decl(unusedPrivateStaticMembers.ts, 29, 13))
78+
79+
public static test() {
80+
>test : Symbol(Test5.test, Decl(unusedPrivateStaticMembers.ts, 30, 26))
81+
82+
Test5["m1"]();
83+
>Test5 : Symbol(Test5, Decl(unusedPrivateStaticMembers.ts, 27, 1))
84+
>"m1" : Symbol(Test5.m1, Decl(unusedPrivateStaticMembers.ts, 29, 13))
85+
}
86+
}
87+
88+
class Test6 {
89+
>Test6 : Symbol(Test6, Decl(unusedPrivateStaticMembers.ts, 34, 1))
90+
91+
private static p1 = 0;
92+
>p1 : Symbol(Test6.p1, Decl(unusedPrivateStaticMembers.ts, 36, 13))
93+
94+
public static test() {
95+
>test : Symbol(Test6.test, Decl(unusedPrivateStaticMembers.ts, 37, 26))
96+
97+
Test6["p1"];
98+
>Test6 : Symbol(Test6, Decl(unusedPrivateStaticMembers.ts, 34, 1))
99+
>"p1" : Symbol(Test6.p1, Decl(unusedPrivateStaticMembers.ts, 36, 13))
100+
}
101+
}
102+

0 commit comments

Comments
 (0)