Skip to content

Commit f419bb2

Browse files
committed
Node-based @deprecated checks
Switch the checker to syntactic checks for `@deprecated` on declarations. This requires a bit more checking of declarations in the checker at times, but it 1. Gets rid of work, and a symbol flag, in the binder. 2. Skips work in the checker unless there is a `@deprecated` tag. 3. Makes it fairly simple to only issue errors on particular signatures of overloaded functions.
1 parent 4601a78 commit f419bb2

File tree

9 files changed

+386
-107
lines changed

9 files changed

+386
-107
lines changed

src/compiler/binder.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -537,10 +537,6 @@ namespace ts {
537537
symbol.parent = parent;
538538
}
539539

540-
if (node.flags & NodeFlags.Deprecated) {
541-
symbol.flags |= SymbolFlags.Deprecated;
542-
}
543-
544540
return symbol;
545541
}
546542

src/compiler/checker.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13274,13 +13274,23 @@ namespace ts {
1327413274
undefined;
1327513275
}
1327613276

13277+
function isUncalledFunctionReference(node: Node, symbol: Symbol) {
13278+
// (!(localOrExportSymbol.flags & SymbolFlags.Function)
13279+
// || !isCallExpression(findAncestor(node.parent, n => !isPropertyAccessExpression(n)) ?? node.parent)
13280+
// && localOrExportSymbol.declarations.every(d => !isFunctionLike(d) || d.flags & NodeFlags.Deprecated))
13281+
// TODO: Needs to use isELement or isProperty, variously
13282+
return !(symbol.flags & SymbolFlags.Function)
13283+
|| !isCallExpression(findAncestor(node, n => !isAccessExpression(n)) ?? node.parent)
13284+
&& every(symbol.declarations, d => !isFunctionLike(d) || !!(d.flags & NodeFlags.Deprecated));
13285+
}
13286+
1327713287
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
1327813288
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
1327913289
const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode);
1328013290
if (propName !== undefined) {
1328113291
const prop = getPropertyOfType(objectType, propName);
1328213292
if (prop) {
13283-
if (accessNode && prop.flags & SymbolFlags.Deprecated) {
13293+
if (accessNode && prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
1328413294
const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
1328513295
errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string);
1328613296
}
@@ -22043,9 +22053,8 @@ namespace ts {
2204322053
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
2204422054
let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration;
2204522055

22046-
const target = (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
22047-
if (target.flags & SymbolFlags.Deprecated) {
22048-
errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);
22056+
if (declaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node.parent, localOrExportSymbol) ) {
22057+
errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);;
2204922058
}
2205022059
if (localOrExportSymbol.flags & SymbolFlags.Class) {
2205122060
// Due to the emit for class decorators, any reference to the class from inside of the class body
@@ -25013,7 +25022,7 @@ namespace ts {
2501325022
propType = indexInfo.type;
2501425023
}
2501525024
else {
25016-
if (prop.flags & SymbolFlags.Deprecated) {
25025+
if (prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) {
2501725026
errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string);
2501825027
}
2501925028

@@ -27404,6 +27413,10 @@ namespace ts {
2740427413
return nonInferrableType;
2740527414
}
2740627415

27416+
if (signature.declaration && signature.declaration.flags & NodeFlags.Deprecated) {
27417+
errorOrSuggestion(/*isError*/ false, node, Diagnostics._0_is_deprecated, signatureToString(signature));
27418+
}
27419+
2740727420
if (node.expression.kind === SyntaxKind.SuperKeyword) {
2740827421
return voidType;
2740927422
}
@@ -30847,7 +30860,7 @@ namespace ts {
3084730860
}
3084830861
const symbol = getNodeLinks(node).resolvedSymbol;
3084930862
if (symbol) {
30850-
if (symbol.flags & SymbolFlags.Deprecated) {
30863+
if (every(symbol.declarations, d => !isTypeDeclaration(d) || !!(d.flags & NodeFlags.Deprecated))) {
3085130864
const diagLocation = isTypeReferenceNode(node) && isQualifiedName(node.typeName) ? node.typeName.right : node;
3085230865
errorOrSuggestion(/* isError */ false, diagLocation, Diagnostics._0_is_deprecated, symbol.escapedName as string);
3085330866
}
@@ -35192,7 +35205,9 @@ namespace ts {
3519235205
}
3519335206
}
3519435207

35195-
if (isImportSpecifier(node) && target.flags & SymbolFlags.Deprecated) {
35208+
if (isImportSpecifier(node) &&
35209+
(target.valueDeclaration && target.valueDeclaration.flags & NodeFlags.Deprecated
35210+
|| every(target.declarations, d => !!(d.flags & NodeFlags.Deprecated)))) {
3519635211
errorOrSuggestion(/* isError */ false, node.name, Diagnostics._0_is_deprecated, symbol.escapedName as string);
3519735212
}
3519835213
}

src/compiler/types.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4512,10 +4512,9 @@ namespace ts {
45124512
Transient = 1 << 25, // Transient symbol (created during type check)
45134513
Assignment = 1 << 26, // Assignment treated as declaration (eg `this.prop = 1`)
45144514
ModuleExports = 1 << 27, // Symbol for CommonJS `module` of `module.exports`
4515-
Deprecated = 1 << 28, // Symbol has Deprecated declaration tag (eg `@deprecated`)
45164515
/* @internal */
45174516
All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral
4518-
| ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient | Deprecated,
4517+
| ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient,
45194518

45204519
Enum = RegularEnum | ConstEnum,
45214520
Variable = FunctionScopedVariable | BlockScopedVariable,

src/compiler/utilities.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5395,6 +5395,16 @@ namespace ts {
53955395
return isClassLike(node) || isInterfaceDeclaration(node) || isTypeLiteralNode(node);
53965396
}
53975397

5398+
export function isTypeDeclaration(node: Node) {
5399+
switch (node.kind) {
5400+
case SyntaxKind.InterfaceDeclaration:
5401+
case SyntaxKind.ClassDeclaration:
5402+
case SyntaxKind.TypeAliasDeclaration:
5403+
return true;
5404+
}
5405+
return false;
5406+
}
5407+
53985408
export function isTypeNodeKind(kind: SyntaxKind): kind is TypeNodeSyntaxKind {
53995409
return (kind >= SyntaxKind.FirstTypeNode && kind <= SyntaxKind.LastTypeNode)
54005410
|| kind === SyntaxKind.AnyKeyword

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2331,7 +2331,6 @@ declare namespace ts {
23312331
Transient = 33554432,
23322332
Assignment = 67108864,
23332333
ModuleExports = 134217728,
2334-
Deprecated = 268435456,
23352334
Enum = 384,
23362335
Variable = 3,
23372336
Value = 111551,

tests/baselines/reference/api/typescript.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2331,7 +2331,6 @@ declare namespace ts {
23312331
Transient = 33554432,
23322332
Assignment = 67108864,
23332333
ModuleExports = 134217728,
2334-
Deprecated = 268435456,
23352334
Enum = 384,
23362335
Variable = 3,
23372336
Value = 111551,
Lines changed: 100 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1+
///<reference path="fourslash.ts" />
2+
13
// @Filename: a.ts
24
//// export namespace foo {
35
//// /** @deprecated */
46
//// export function faff () { }
5-
//// [|faff|]()
7+
//// [|faff()|]
68
//// }
7-
//// const [|a|] = foo.[|faff|]()
9+
//// const [|a|] = [|foo.faff()|]
810
//// foo[[|"faff"|]]
911
//// const { [|faff|] } = foo
10-
//// faff()
12+
//// [|faff()|]
1113
//// /** @deprecated */
1214
//// export function bar () {
13-
//// foo?.[|faff|]()
15+
//// [|foo?.faff()|]
1416
//// }
15-
//// foo?.[[|"faff"|]]?.()
16-
//// [|bar|]();
17+
//// [|foo?.["faff"]?.()|]
18+
//// [|bar()|];
1719
//// /** @deprecated */
1820
//// export interface Foo {
1921
//// /** @deprecated */
@@ -26,9 +28,9 @@
2628
// @Filename: b.ts
2729
//// import * as f from './a';
2830
//// import { [|bar|], [|QW|] } from './a';
29-
//// f.[|bar|]();
30-
//// f.foo.[|faff|]();
31-
//// [|bar|]();
31+
//// [|f.bar()|];
32+
//// [|f.foo.faff()|];
33+
//// [|bar()|];
3234
//// type Z = [|QW|];
3335
//// type A = f.[|Foo|];
3436
//// type B = f.[|QW|];
@@ -40,127 +42,133 @@ const ranges = test.ranges();
4042

4143
verify.getSuggestionDiagnostics([
4244
{
43-
message: "'faff' is deprecated",
44-
code: 6385,
45-
range: ranges[0],
46-
reportsDeprecated: true,
45+
"code": 6385,
46+
"message": "'(): void' is deprecated",
47+
"reportsDeprecated": true,
48+
"range": ranges[0]
4749
},
4850
{
49-
message: "'a' is declared but its value is never read.",
50-
code: 6133,
51-
range: ranges[1],
52-
reportsUnnecessary: true
51+
"code": 6133,
52+
"message": "'a' is declared but its value is never read.",
53+
"reportsUnnecessary": true,
54+
"range": ranges[1]
5355
},
5456
{
55-
message: "'faff' is deprecated",
56-
code: 6385,
57-
range: ranges[2],
58-
reportsDeprecated: true,
57+
"code": 6385,
58+
"message": "'(): void' is deprecated",
59+
"reportsDeprecated": true,
60+
"range": ranges[2]
5961
},
6062
{
61-
message: "'faff' is deprecated",
62-
code: 6385,
63-
range: ranges[3],
64-
reportsDeprecated: true,
63+
"code": 6385,
64+
"message": "'faff' is deprecated",
65+
"reportsDeprecated": true,
66+
"range": ranges[3]
6567
},
6668
{
67-
message: "'faff' is deprecated",
68-
code: 6385,
69-
range: ranges[4],
70-
reportsDeprecated: true,
69+
"code": 6385,
70+
"message": "'faff' is deprecated",
71+
"reportsDeprecated": true,
72+
"range": ranges[4]
7173
},
7274
{
73-
message: "'faff' is deprecated",
74-
code: 6385,
75-
range: ranges[5],
76-
reportsDeprecated: true,
75+
"code": 6385,
76+
"message": "'(): void' is deprecated",
77+
"reportsDeprecated": true,
78+
"range": ranges[5]
7779
},
7880
{
79-
message: "'faff' is deprecated",
80-
code: 6385,
81-
range: ranges[6],
82-
reportsDeprecated: true,
81+
"code": 6385,
82+
"message": "'(): void' is deprecated",
83+
"reportsDeprecated": true,
84+
"range": ranges[6]
8385
},
8486
{
85-
message: "'bar' is deprecated",
86-
code: 6385,
87-
range: ranges[7],
88-
reportsDeprecated: true,
87+
"code": 6385,
88+
"message": "'(): void' is deprecated",
89+
"reportsDeprecated": true,
90+
"range": ranges[7]
8991
},
9092
{
91-
message: "'Foo' is deprecated",
92-
code: 6385,
93-
range: ranges[8],
94-
reportsDeprecated: true,
93+
"code": 6385,
94+
"message": "'(): void' is deprecated",
95+
"reportsDeprecated": true,
96+
"range": ranges[8]
9597
},
9698
{
97-
message: "'zzz' is deprecated",
98-
code: 6385,
99-
range: ranges[9],
100-
reportsDeprecated: true,
99+
"code": 6385,
100+
"message": "'Foo' is deprecated",
101+
"reportsDeprecated": true,
102+
"range": ranges[9]
101103
},
102104
{
103-
message: "'QW' is deprecated",
104-
code: 6385,
105-
range: ranges[10],
106-
reportsDeprecated: true,
107-
}
108-
])
105+
"code": 6385,
106+
"message": "'zzz' is deprecated",
107+
"reportsDeprecated": true,
108+
"range": ranges[10]
109+
},
110+
{
111+
"code": 6385,
112+
"message": "'QW' is deprecated",
113+
"reportsDeprecated": true,
114+
"range": ranges[11]
115+
},
116+
]);
109117

110118
goTo.file('b.ts')
111119
verify.getSuggestionDiagnostics([
112120
{
113-
message: "'bar' is deprecated",
114-
code: 6385,
115-
range: ranges[11],
116-
reportsDeprecated: true,
121+
"code": 6385,
122+
"message": "'bar' is deprecated",
123+
"reportsDeprecated": true,
124+
"range": ranges[12]
117125
},
118126
{
119-
message: "'QW' is deprecated",
120-
code: 6385,
121-
range: ranges[12],
122-
reportsDeprecated: true,
127+
"code": 6385,
128+
"message": "'QW' is deprecated",
129+
"reportsDeprecated": true,
130+
"range": ranges[13]
123131
},
124132
{
125-
message: "'bar' is deprecated",
126-
code: 6385,
127-
range: ranges[13],
128-
reportsDeprecated: true,
133+
"code": 6385,
134+
"message": "'(): void' is deprecated",
135+
"reportsDeprecated": true,
136+
"range": ranges[14]
129137
},
130138
{
131-
message: "'faff' is deprecated",
132-
code: 6385,
133-
range: ranges[14],
134-
reportsDeprecated: true,
139+
"code": 6385,
140+
"message": "'(): void' is deprecated",
141+
"reportsDeprecated": true,
142+
"range": ranges[15]
135143
},
136144
{
137-
message: "'bar' is deprecated",
138-
code: 6385,
139-
range: ranges[15],
140-
reportsDeprecated: true,
145+
"code": 6385,
146+
"message": "'(): void' is deprecated",
147+
"reportsDeprecated": true,
148+
"range": ranges[16]
141149
},
142150
{
143-
message: "'QW' is deprecated",
144-
code: 6385,
145-
range: ranges[16],
146-
reportsDeprecated: true,
151+
"code": 6385,
152+
"message": "'QW' is deprecated",
153+
"reportsDeprecated": true,
154+
"range": ranges[17]
147155
},
148156
{
149-
message: "'Foo' is deprecated",
150-
code: 6385,
151-
range: ranges[17],
152-
reportsDeprecated: true,
157+
"code": 6385,
158+
"message": "'Foo' is deprecated",
159+
"reportsDeprecated": true,
160+
"range": ranges[18]
153161
},
154162
{
155-
message: "'QW' is deprecated",
156-
code: 6385,
157-
range: ranges[18],
158-
reportsDeprecated: true,
163+
"code": 6385,
164+
"message": "'QW' is deprecated",
165+
"reportsDeprecated": true,
166+
"range": ranges[19]
159167
},
160168
{
161-
message: "'O' is declared but never used.",
162-
code: 6196,
163-
range: ranges[19],
164-
reportsUnnecessary: true
169+
"code": 6196,
170+
"message": "'O' is declared but never used.",
171+
"reportsUnnecessary": true,
172+
"range": ranges[20]
165173
}
166-
])
174+
])

0 commit comments

Comments
 (0)