fix(41027): handle unused static members (#41103)

This commit is contained in:
Oleksandr T 2021-03-11 16:39:20 +02:00 committed by GitHub
parent 5053b0b987
commit 3c576f108c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 427 additions and 7 deletions

View File

@ -14334,7 +14334,7 @@ namespace ts {
addDeprecatedSuggestion(deprecatedNode, prop.declarations, propName as string);
}
if (accessExpression) {
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
markPropertyAsReferenced(prop, accessExpression, isSelfTypeAccess(accessExpression.expression, objectType.symbol));
if (isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression))) {
error(accessExpression.argumentExpression, Diagnostics.Cannot_assign_to_0_because_it_is_a_read_only_property, symbolToString(prop));
return undefined;
@ -26796,7 +26796,7 @@ namespace ts {
addDeprecatedSuggestion(right, prop.declarations, right.escapedText as string);
}
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
markPropertyAsReferenced(prop, node, isSelfTypeAccess(left, parentSymbol));
getNodeLinks(node).resolvedSymbol = prop;
checkPropertyAccessibility(node, left.kind === SyntaxKind.SuperKeyword, apparentType, prop);
if (isAssignmentToReadonlyEntity(node as Expression, prop, assignmentKind)) {
@ -27125,7 +27125,7 @@ namespace ts {
}
}
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isThisAccess: boolean) {
function markPropertyAsReferenced(prop: Symbol, nodeForCheckWriteOnly: Node | undefined, isSelfTypeAccess: boolean) {
const valueDeclaration = prop && (prop.flags & SymbolFlags.ClassMember) && prop.valueDeclaration;
if (!valueDeclaration) {
return;
@ -27138,8 +27138,7 @@ namespace ts {
if (nodeForCheckWriteOnly && isWriteOnlyAccess(nodeForCheckWriteOnly) && !(prop.flags & SymbolFlags.SetAccessor)) {
return;
}
if (isThisAccess) {
if (isSelfTypeAccess) {
// Find any FunctionLikeDeclaration because those create a new 'this' binding. But this should only matter for methods (or getters/setters).
const containingMethod = findAncestor(nodeForCheckWriteOnly, isFunctionLikeDeclaration);
if (containingMethod && containingMethod.symbol === prop) {
@ -27150,6 +27149,11 @@ namespace ts {
(getCheckFlags(prop) & CheckFlags.Instantiated ? getSymbolLinks(prop).target : prop)!.isReferenced = SymbolFlags.All;
}
function isSelfTypeAccess(name: Expression | QualifiedName, parent: Symbol | undefined) {
return name.kind === SyntaxKind.ThisKeyword
|| !!parent && isEntityNameExpression(name) && parent === getResolvedSymbol(getFirstIdentifier(name));
}
function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName | ImportTypeNode, propertyName: __String): boolean {
switch (node.kind) {
case SyntaxKind.PropertyAccessExpression:
@ -34700,7 +34704,7 @@ namespace ts {
const nameText = getPropertyNameFromType(exprType);
const property = getPropertyOfType(parentType, nameText);
if (property) {
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isSelfTypeAccess*/ false); // A destructuring is never a write-only reference.
checkPropertyAccessibility(node, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType, property);
}
}
@ -41504,5 +41508,4 @@ namespace ts {
export function signatureHasLiteralTypes(s: Signature) {
return !!(s.flags & SignatureFlags.HasLiteralTypes);
}
}

View File

@ -0,0 +1,58 @@
tests/cases/compiler/unusedPrivateStaticMembers.ts(16,20): error TS6133: 'p1' is declared but its value is never read.
tests/cases/compiler/unusedPrivateStaticMembers.ts(17,20): error TS6133: 'm1' is declared but its value is never read.
tests/cases/compiler/unusedPrivateStaticMembers.ts(21,20): error TS6133: 'm1' is declared but its value is never read.
tests/cases/compiler/unusedPrivateStaticMembers.ts(25,20): error TS6133: 'm2' is declared but its value is never read.
==== tests/cases/compiler/unusedPrivateStaticMembers.ts (4 errors) ====
class Test1 {
private static m1() {}
public static test() {
Test1.m1();
}
}
class Test2 {
private static p1 = 0
public static test() {
Test2.p1;
}
}
class Test3 {
private static p1 = 0;
~~
!!! error TS6133: 'p1' is declared but its value is never read.
private static m1() {}
~~
!!! error TS6133: 'm1' is declared but its value is never read.
}
class Test4 {
private static m1(n: number): number {
~~
!!! error TS6133: 'm1' is declared but its value is never read.
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
}
private static m2(n: number): number {
~~
!!! error TS6133: 'm2' is declared but its value is never read.
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
}
}
class Test5 {
private static m1() {}
public static test() {
Test5["m1"]();
}
}
class Test6 {
private static p1 = 0;
public static test() {
Test6["p1"];
}
}

View File

@ -0,0 +1,82 @@
//// [unusedPrivateStaticMembers.ts]
class Test1 {
private static m1() {}
public static test() {
Test1.m1();
}
}
class Test2 {
private static p1 = 0
public static test() {
Test2.p1;
}
}
class Test3 {
private static p1 = 0;
private static m1() {}
}
class Test4 {
private static m1(n: number): number {
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
}
private static m2(n: number): number {
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
}
}
class Test5 {
private static m1() {}
public static test() {
Test5["m1"]();
}
}
class Test6 {
private static p1 = 0;
public static test() {
Test6["p1"];
}
}
//// [unusedPrivateStaticMembers.js]
class Test1 {
static m1() { }
static test() {
Test1.m1();
}
}
class Test2 {
static test() {
Test2.p1;
}
}
Test2.p1 = 0;
class Test3 {
static m1() { }
}
Test3.p1 = 0;
class Test4 {
static m1(n) {
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
}
static m2(n) {
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
}
}
class Test5 {
static m1() { }
static test() {
Test5["m1"]();
}
}
class Test6 {
static test() {
Test6["p1"];
}
}
Test6.p1 = 0;

View File

@ -0,0 +1,102 @@
=== tests/cases/compiler/unusedPrivateStaticMembers.ts ===
class Test1 {
>Test1 : Symbol(Test1, Decl(unusedPrivateStaticMembers.ts, 0, 0))
private static m1() {}
>m1 : Symbol(Test1.m1, Decl(unusedPrivateStaticMembers.ts, 0, 13))
public static test() {
>test : Symbol(Test1.test, Decl(unusedPrivateStaticMembers.ts, 1, 26))
Test1.m1();
>Test1.m1 : Symbol(Test1.m1, Decl(unusedPrivateStaticMembers.ts, 0, 13))
>Test1 : Symbol(Test1, Decl(unusedPrivateStaticMembers.ts, 0, 0))
>m1 : Symbol(Test1.m1, Decl(unusedPrivateStaticMembers.ts, 0, 13))
}
}
class Test2 {
>Test2 : Symbol(Test2, Decl(unusedPrivateStaticMembers.ts, 5, 1))
private static p1 = 0
>p1 : Symbol(Test2.p1, Decl(unusedPrivateStaticMembers.ts, 7, 13))
public static test() {
>test : Symbol(Test2.test, Decl(unusedPrivateStaticMembers.ts, 8, 25))
Test2.p1;
>Test2.p1 : Symbol(Test2.p1, Decl(unusedPrivateStaticMembers.ts, 7, 13))
>Test2 : Symbol(Test2, Decl(unusedPrivateStaticMembers.ts, 5, 1))
>p1 : Symbol(Test2.p1, Decl(unusedPrivateStaticMembers.ts, 7, 13))
}
}
class Test3 {
>Test3 : Symbol(Test3, Decl(unusedPrivateStaticMembers.ts, 12, 1))
private static p1 = 0;
>p1 : Symbol(Test3.p1, Decl(unusedPrivateStaticMembers.ts, 14, 13))
private static m1() {}
>m1 : Symbol(Test3.m1, Decl(unusedPrivateStaticMembers.ts, 15, 26))
}
class Test4 {
>Test4 : Symbol(Test4, Decl(unusedPrivateStaticMembers.ts, 17, 1))
private static m1(n: number): number {
>m1 : Symbol(Test4.m1, Decl(unusedPrivateStaticMembers.ts, 19, 13))
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
>Test4.m1 : Symbol(Test4.m1, Decl(unusedPrivateStaticMembers.ts, 19, 13))
>Test4 : Symbol(Test4, Decl(unusedPrivateStaticMembers.ts, 17, 1))
>m1 : Symbol(Test4.m1, Decl(unusedPrivateStaticMembers.ts, 19, 13))
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 20, 22))
}
private static m2(n: number): number {
>m2 : Symbol(Test4.m2, Decl(unusedPrivateStaticMembers.ts, 22, 5))
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
>Test4 : Symbol(Test4, Decl(unusedPrivateStaticMembers.ts, 17, 1))
>"m2" : Symbol(Test4.m2, Decl(unusedPrivateStaticMembers.ts, 22, 5))
>n : Symbol(n, Decl(unusedPrivateStaticMembers.ts, 24, 22))
}
}
class Test5 {
>Test5 : Symbol(Test5, Decl(unusedPrivateStaticMembers.ts, 27, 1))
private static m1() {}
>m1 : Symbol(Test5.m1, Decl(unusedPrivateStaticMembers.ts, 29, 13))
public static test() {
>test : Symbol(Test5.test, Decl(unusedPrivateStaticMembers.ts, 30, 26))
Test5["m1"]();
>Test5 : Symbol(Test5, Decl(unusedPrivateStaticMembers.ts, 27, 1))
>"m1" : Symbol(Test5.m1, Decl(unusedPrivateStaticMembers.ts, 29, 13))
}
}
class Test6 {
>Test6 : Symbol(Test6, Decl(unusedPrivateStaticMembers.ts, 34, 1))
private static p1 = 0;
>p1 : Symbol(Test6.p1, Decl(unusedPrivateStaticMembers.ts, 36, 13))
public static test() {
>test : Symbol(Test6.test, Decl(unusedPrivateStaticMembers.ts, 37, 26))
Test6["p1"];
>Test6 : Symbol(Test6, Decl(unusedPrivateStaticMembers.ts, 34, 1))
>"p1" : Symbol(Test6.p1, Decl(unusedPrivateStaticMembers.ts, 36, 13))
}
}

View File

@ -0,0 +1,130 @@
=== tests/cases/compiler/unusedPrivateStaticMembers.ts ===
class Test1 {
>Test1 : Test1
private static m1() {}
>m1 : () => void
public static test() {
>test : () => void
Test1.m1();
>Test1.m1() : void
>Test1.m1 : () => void
>Test1 : typeof Test1
>m1 : () => void
}
}
class Test2 {
>Test2 : Test2
private static p1 = 0
>p1 : number
>0 : 0
public static test() {
>test : () => void
Test2.p1;
>Test2.p1 : number
>Test2 : typeof Test2
>p1 : number
}
}
class Test3 {
>Test3 : Test3
private static p1 = 0;
>p1 : number
>0 : 0
private static m1() {}
>m1 : () => void
}
class Test4 {
>Test4 : Test4
private static m1(n: number): number {
>m1 : (n: number) => number
>n : number
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
>(n === 0) ? 1 : (n * Test4.m1(n - 1)) : number
>(n === 0) : boolean
>n === 0 : boolean
>n : number
>0 : 0
>1 : 1
>(n * Test4.m1(n - 1)) : number
>n * Test4.m1(n - 1) : number
>n : number
>Test4.m1(n - 1) : number
>Test4.m1 : (n: number) => number
>Test4 : typeof Test4
>m1 : (n: number) => number
>n - 1 : number
>n : number
>1 : 1
}
private static m2(n: number): number {
>m2 : (n: number) => number
>n : number
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
>(n === 0) ? 1 : (n * Test4["m2"](n - 1)) : number
>(n === 0) : boolean
>n === 0 : boolean
>n : number
>0 : 0
>1 : 1
>(n * Test4["m2"](n - 1)) : number
>n * Test4["m2"](n - 1) : number
>n : number
>Test4["m2"](n - 1) : number
>Test4["m2"] : (n: number) => number
>Test4 : typeof Test4
>"m2" : "m2"
>n - 1 : number
>n : number
>1 : 1
}
}
class Test5 {
>Test5 : Test5
private static m1() {}
>m1 : () => void
public static test() {
>test : () => void
Test5["m1"]();
>Test5["m1"]() : void
>Test5["m1"] : () => void
>Test5 : typeof Test5
>"m1" : "m1"
}
}
class Test6 {
>Test6 : Test6
private static p1 = 0;
>p1 : number
>0 : 0
public static test() {
>test : () => void
Test6["p1"];
>Test6["p1"] : number
>Test6 : typeof Test6
>"p1" : "p1"
}
}

View File

@ -0,0 +1,45 @@
// @noUnusedLocals: true
// @target: esnext
class Test1 {
private static m1() {}
public static test() {
Test1.m1();
}
}
class Test2 {
private static p1 = 0
public static test() {
Test2.p1;
}
}
class Test3 {
private static p1 = 0;
private static m1() {}
}
class Test4 {
private static m1(n: number): number {
return (n === 0) ? 1 : (n * Test4.m1(n - 1));
}
private static m2(n: number): number {
return (n === 0) ? 1 : (n * Test4["m2"](n - 1));
}
}
class Test5 {
private static m1() {}
public static test() {
Test5["m1"]();
}
}
class Test6 {
private static p1 = 0;
public static test() {
Test6["p1"];
}
}