Skip to content

Commit 93ccc17

Browse files
authored
Node-based @deprecated checks (microsoft#39323)
* 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. * remove in-progress comment * remove unused isTypeDeclaration * ❤️ lint * support jsx and tagged template functions * Support decorators too
1 parent 652a1c5 commit 93ccc17

File tree

8 files changed

+504
-108
lines changed

8 files changed

+504
-108
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: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13284,13 +13284,19 @@ namespace ts {
1328413284
undefined;
1328513285
}
1328613286

13287+
function isUncalledFunctionReference(node: Node, symbol: Symbol) {
13288+
return !(symbol.flags & (SymbolFlags.Function | SymbolFlags.Method))
13289+
|| !isCallLikeExpression(findAncestor(node, n => !isAccessExpression(n)) || node.parent)
13290+
&& every(symbol.declarations, d => !isFunctionLike(d) || !!(d.flags & NodeFlags.Deprecated));
13291+
}
13292+
1328713293
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
1328813294
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
1328913295
const propName = accessNode && isPrivateIdentifier(accessNode) ? undefined : getPropertyNameFromIndex(indexType, accessNode);
1329013296
if (propName !== undefined) {
1329113297
const prop = getPropertyOfType(objectType, propName);
1329213298
if (prop) {
13293-
if (accessNode && prop.flags & SymbolFlags.Deprecated) {
13299+
if (accessNode && prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(accessNode, prop)) {
1329413300
const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
1329513301
errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string);
1329613302
}
@@ -22055,9 +22061,8 @@ namespace ts {
2205522061
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
2205622062
let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration;
2205722063

22058-
const target = (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
22059-
if (target.flags & SymbolFlags.Deprecated) {
22060-
errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);
22064+
if (declaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node.parent, localOrExportSymbol)) {
22065+
errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);;
2206122066
}
2206222067
if (localOrExportSymbol.flags & SymbolFlags.Class) {
2206322068
// Due to the emit for class decorators, any reference to the class from inside of the class body
@@ -24604,6 +24609,7 @@ namespace ts {
2460424609
if (isNodeOpeningLikeElement) {
2460524610
const jsxOpeningLikeNode = node as JsxOpeningLikeElement;
2460624611
const sig = getResolvedSignature(jsxOpeningLikeNode);
24612+
checkDeprecatedSignature(sig, node);
2460724613
checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(jsxOpeningLikeNode), getReturnTypeOfSignature(sig), jsxOpeningLikeNode);
2460824614
}
2460924615
}
@@ -25032,7 +25038,7 @@ namespace ts {
2503225038
propType = indexInfo.type;
2503325039
}
2503425040
else {
25035-
if (prop.flags & SymbolFlags.Deprecated) {
25041+
if (prop.valueDeclaration?.flags & NodeFlags.Deprecated && isUncalledFunctionReference(node, prop)) {
2503625042
errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string);
2503725043
}
2503825044

@@ -27424,6 +27430,8 @@ namespace ts {
2742427430
return nonInferrableType;
2742527431
}
2742627432

27433+
checkDeprecatedSignature(signature, node);
27434+
2742727435
if (node.expression.kind === SyntaxKind.SuperKeyword) {
2742827436
return voidType;
2742927437
}
@@ -27483,6 +27491,12 @@ namespace ts {
2748327491
return returnType;
2748427492
}
2748527493

27494+
function checkDeprecatedSignature(signature: Signature, node: Node) {
27495+
if (signature.declaration && signature.declaration.flags & NodeFlags.Deprecated) {
27496+
errorOrSuggestion(/*isError*/ false, node, Diagnostics._0_is_deprecated, signatureToString(signature));
27497+
}
27498+
}
27499+
2748627500
function isSymbolOrSymbolForCall(node: Node) {
2748727501
if (!isCallExpression(node)) return false;
2748827502
let left = node.expression;
@@ -27591,7 +27605,9 @@ namespace ts {
2759127605
if (languageVersion < ScriptTarget.ES2015) {
2759227606
checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject);
2759327607
}
27594-
return getReturnTypeOfSignature(getResolvedSignature(node));
27608+
const signature = getResolvedSignature(node);
27609+
checkDeprecatedSignature(signature, node);
27610+
return getReturnTypeOfSignature(signature);
2759527611
}
2759627612

2759727613
function checkAssertion(node: AssertionExpression) {
@@ -30867,7 +30883,7 @@ namespace ts {
3086730883
}
3086830884
const symbol = getNodeLinks(node).resolvedSymbol;
3086930885
if (symbol) {
30870-
if (symbol.flags & SymbolFlags.Deprecated) {
30886+
if (every(symbol.declarations, d => !isTypeDeclaration(d) || !!(d.flags & NodeFlags.Deprecated))) {
3087130887
const diagLocation = isTypeReferenceNode(node) && isQualifiedName(node.typeName) ? node.typeName.right : node;
3087230888
errorOrSuggestion(/* isError */ false, diagLocation, Diagnostics._0_is_deprecated, symbol.escapedName as string);
3087330889
}
@@ -31719,6 +31735,7 @@ namespace ts {
3171931735
/** Check a decorator */
3172031736
function checkDecorator(node: Decorator): void {
3172131737
const signature = getResolvedSignature(node);
31738+
checkDeprecatedSignature(signature, node);
3172231739
const returnType = getReturnTypeOfSignature(signature);
3172331740
if (returnType.flags & TypeFlags.Any) {
3172431741
return;
@@ -35212,7 +35229,9 @@ namespace ts {
3521235229
}
3521335230
}
3521435231

35215-
if (isImportSpecifier(node) && target.flags & SymbolFlags.Deprecated) {
35232+
if (isImportSpecifier(node) &&
35233+
(target.valueDeclaration && target.valueDeclaration.flags & NodeFlags.Deprecated
35234+
|| every(target.declarations, d => !!(d.flags & NodeFlags.Deprecated)))) {
3521635235
errorOrSuggestion(/* isError */ false, node.name, Diagnostics._0_is_deprecated, symbol.escapedName as string);
3521735236
}
3521835237
}

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,

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2343,7 +2343,6 @@ declare namespace ts {
23432343
Transient = 33554432,
23442344
Assignment = 67108864,
23452345
ModuleExports = 134217728,
2346-
Deprecated = 268435456,
23472346
Enum = 384,
23482347
Variable = 3,
23492348
Value = 111551,

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2343,7 +2343,6 @@ declare namespace ts {
23432343
Transient = 33554432,
23442344
Assignment = 67108864,
23452345
ModuleExports = 134217728,
2346-
Deprecated = 268435456,
23472346
Enum = 384,
23482347
Variable = 3,
23492348
Value = 111551,

0 commit comments

Comments
 (0)