Skip to content

Commit 59ad375

Browse files
Kingwlsandersn
andauthored
Add deprecated related feature (#38523)
* Add deprecated related feature * Add more support * fix navtree * Add identifier check * Add more deprecated * fix crash * fix more crash * fix crash * improve diagnostic * avoid new tag * avoid tags * accept baseline * Check deprecated in binder * fix baseline * fix jsdoc cache * fix incorrect fix * Avoid useless changes * Accept baseline * Add tests * fix perf * fix public api * Adjust deprecated mark on qualifed name * Revolve alias symbol * Use modifier flags insted of symbol props * Fix modifier flag resolve * Make lint happy * Fix crash * fix crash * Add cached utils function * Accept baseline * Add more tests * try pinning octokit again * Avoid tests * Use utils some * Deprecated perf test (#3) * check valueDeclaration only * check without modifierFlags * donot check alias * use cached tag * remove call to jsdoc * use deprecated tag * revert changes * Revert mission changes * use node flags * cache result * cache * avoid modifier flags * Opts * fix jsdoc include modifier * fix tests * fix again * use symbol flag * set @octokit/rest back to latest * fix trailing spacel int Co-authored-by: Nathan Shively-Sanders <[email protected]>
1 parent 520103f commit 59ad375

32 files changed

+495
-107
lines changed

Diff for: scripts/processDiagnosticMessages.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ interface DiagnosticDetails {
55
category: string;
66
code: number;
77
reportsUnnecessary?: {};
8+
reportsDeprecated?: {};
89
isEarly?: boolean;
910
elidedInCompatabilityPyramid?: boolean;
1011
}
@@ -64,15 +65,17 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFil
6465
"// generated from '" + inputFilePathRel + "' by '" + thisFilePathRel.replace(/\\/g, "/") + "'\r\n" +
6566
"/* @internal */\r\n" +
6667
"namespace ts {\r\n" +
67-
" function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean): DiagnosticMessage {\r\n" +
68-
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid };\r\n" +
68+
" function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {\r\n" +
69+
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };\r\n" +
6970
" }\r\n" +
7071
" export const Diagnostics = {\r\n";
71-
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid }, name) => {
72+
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated }, name) => {
7273
const propName = convertPropertyName(name);
7374
const argReportsUnnecessary = reportsUnnecessary ? `, /*reportsUnnecessary*/ ${reportsUnnecessary}` : "";
7475
const argElidedInCompatabilityPyramid = elidedInCompatabilityPyramid ? `${!reportsUnnecessary ? ", /*reportsUnnecessary*/ undefined" : ""}, /*elidedInCompatabilityPyramid*/ ${elidedInCompatabilityPyramid}` : "";
75-
result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}),\r\n`;
76+
const argReportsDeprecated = reportsDeprecated ? `${!argElidedInCompatabilityPyramid ? ", /*reportsUnnecessary*/ undefined, /*elidedInCompatabilityPyramid*/ undefined" : ""}, /*reportsDeprecated*/ ${reportsDeprecated}` : "";
77+
78+
result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),\r\n`;
7679
});
7780

7881
result += " };\r\n}";

Diff for: src/compiler/binder.ts

+4
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@ namespace ts {
537537
symbol.parent = parent;
538538
}
539539

540+
if (node.flags & NodeFlags.Deprecated) {
541+
symbol.flags |= SymbolFlags.Deprecated;
542+
}
543+
540544
return symbol;
541545
}
542546

Diff for: src/compiler/builder.ts

+3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ namespace ts {
33
export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation {
44
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
55
reportsUnnecessary?: {};
6+
reportDeprecated?: {}
67
source?: string;
78
relatedInformation?: ReusableDiagnosticRelatedInformation[];
89
skippedOn?: keyof CompilerOptions;
@@ -268,6 +269,7 @@ namespace ts {
268269
return diagnostics.map(diagnostic => {
269270
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
270271
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
272+
result.reportsDeprecated = diagnostic.reportDeprecated;
271273
result.source = diagnostic.source;
272274
result.skippedOn = diagnostic.skippedOn;
273275
const { relatedInformation } = diagnostic;
@@ -817,6 +819,7 @@ namespace ts {
817819
return diagnostics.map(diagnostic => {
818820
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
819821
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
822+
result.reportDeprecated = diagnostic.reportsDeprecated;
820823
result.source = diagnostic.source;
821824
result.skippedOn = diagnostic.skippedOn;
822825
const { relatedInformation } = diagnostic;

Diff for: src/compiler/checker.ts

+55-29
Original file line numberDiff line numberDiff line change
@@ -13168,6 +13168,10 @@ namespace ts {
1316813168
if (propName !== undefined) {
1316913169
const prop = getPropertyOfType(objectType, propName);
1317013170
if (prop) {
13171+
if (accessNode && prop.flags & SymbolFlags.Deprecated) {
13172+
const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
13173+
errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string);
13174+
}
1317113175
if (accessExpression) {
1317213176
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
1317313177
if (isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression))) {
@@ -21698,6 +21702,10 @@ namespace ts {
2169821702
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
2169921703
let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration;
2170021704

21705+
const target = (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
21706+
if (target.flags & SymbolFlags.Deprecated) {
21707+
errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);
21708+
}
2170121709
if (localOrExportSymbol.flags & SymbolFlags.Class) {
2170221710
// Due to the emit for class decorators, any reference to the class from inside of the class body
2170321711
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
@@ -24679,6 +24687,10 @@ namespace ts {
2467924687
propType = indexInfo.type;
2468024688
}
2468124689
else {
24690+
if (prop.flags & SymbolFlags.Deprecated) {
24691+
errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string);
24692+
}
24693+
2468224694
checkPropertyNotUsedBeforeDeclaration(prop, node, right);
2468324695
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
2468424696
getNodeLinks(node).resolvedSymbol = prop;
@@ -30499,8 +30511,15 @@ namespace ts {
3049930511
checkTypeArgumentConstraints(node, typeParameters);
3050030512
}
3050130513
}
30502-
if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol!.flags & SymbolFlags.EnumMember) {
30503-
error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type));
30514+
const symbol = getNodeLinks(node).resolvedSymbol;
30515+
if (symbol) {
30516+
if (symbol.flags & SymbolFlags.Deprecated) {
30517+
const diagLocation = isTypeReferenceNode(node) && isQualifiedName(node.typeName) ? node.typeName.right : node;
30518+
errorOrSuggestion(/* isError */ false, diagLocation, Diagnostics._0_is_deprecated, symbol.escapedName as string);
30519+
}
30520+
if (type.flags & TypeFlags.Enum && symbol.flags & SymbolFlags.EnumMember) {
30521+
error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type));
30522+
}
3050430523
}
3050530524
}
3050630525
}
@@ -31644,6 +31663,7 @@ namespace ts {
3164431663
error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName));
3164531664
}
3164631665
}
31666+
3164731667
function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void {
3164831668
const classLike = getEffectiveJSDocHost(node);
3164931669
if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) {
@@ -34803,33 +34823,39 @@ namespace ts {
3480334823
let symbol = getSymbolOfNode(node);
3480434824
const target = resolveAlias(symbol);
3480534825

34806-
const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment;
34807-
if (!shouldSkipWithJSExpandoTargets && target !== unknownSymbol) {
34808-
// For external modules symbol represents local symbol for an alias.
34809-
// This local symbol will merge any other local declarations (excluding other aliases)
34810-
// and symbol.flags will contains combined representation for all merged declaration.
34811-
// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
34812-
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
34813-
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
34814-
symbol = getMergedSymbol(symbol.exportSymbol || symbol);
34815-
const excludedMeanings =
34816-
(symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
34817-
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
34818-
(symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0);
34819-
if (target.flags & excludedMeanings) {
34820-
const message = node.kind === SyntaxKind.ExportSpecifier ?
34821-
Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 :
34822-
Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0;
34823-
error(node, message, symbolToString(symbol));
34824-
}
34825-
34826-
// Don't allow to re-export something with no value side when `--isolatedModules` is set.
34827-
if (compilerOptions.isolatedModules
34828-
&& node.kind === SyntaxKind.ExportSpecifier
34829-
&& !node.parent.parent.isTypeOnly
34830-
&& !(target.flags & SymbolFlags.Value)
34831-
&& !(node.flags & NodeFlags.Ambient)) {
34832-
error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type);
34826+
if (target !== unknownSymbol) {
34827+
const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment;
34828+
if (!shouldSkipWithJSExpandoTargets) {
34829+
// For external modules symbol represents local symbol for an alias.
34830+
// This local symbol will merge any other local declarations (excluding other aliases)
34831+
// and symbol.flags will contains combined representation for all merged declaration.
34832+
// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
34833+
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
34834+
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
34835+
symbol = getMergedSymbol(symbol.exportSymbol || symbol);
34836+
const excludedMeanings =
34837+
(symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
34838+
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
34839+
(symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0);
34840+
if (target.flags & excludedMeanings) {
34841+
const message = node.kind === SyntaxKind.ExportSpecifier ?
34842+
Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 :
34843+
Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0;
34844+
error(node, message, symbolToString(symbol));
34845+
}
34846+
34847+
// Don't allow to re-export something with no value side when `--isolatedModules` is set.
34848+
if (compilerOptions.isolatedModules
34849+
&& node.kind === SyntaxKind.ExportSpecifier
34850+
&& !node.parent.parent.isTypeOnly
34851+
&& !(target.flags & SymbolFlags.Value)
34852+
&& !(node.flags & NodeFlags.Ambient)) {
34853+
error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type);
34854+
}
34855+
}
34856+
34857+
if (isImportSpecifier(node) && target.flags & SymbolFlags.Deprecated) {
34858+
errorOrSuggestion(/* isError */ false, node.name, Diagnostics._0_is_deprecated, symbol.escapedName as string);
3483334859
}
3483434860
}
3483534861
}

Diff for: src/compiler/diagnosticMessages.json

+5
Original file line numberDiff line numberDiff line change
@@ -4602,6 +4602,11 @@
46024602
"category": "Message",
46034603
"code": 6384
46044604
},
4605+
"'{0}' is deprecated": {
4606+
"category": "Suggestion",
4607+
"code": 6385,
4608+
"reportsDeprecated": true
4609+
},
46054610

46064611
"The expected type comes from property '{0}' which is declared here on type '{1}'": {
46074612
"category": "Message",

Diff for: src/compiler/factory/nodeFactory.ts

+4
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,8 @@ namespace ts {
358358
get updateJSDocProtectedTag() { return getJSDocSimpleTagUpdateFunction<JSDocProtectedTag>(SyntaxKind.JSDocProtectedTag); },
359359
get createJSDocReadonlyTag() { return getJSDocSimpleTagCreateFunction<JSDocReadonlyTag>(SyntaxKind.JSDocReadonlyTag); },
360360
get updateJSDocReadonlyTag() { return getJSDocSimpleTagUpdateFunction<JSDocReadonlyTag>(SyntaxKind.JSDocReadonlyTag); },
361+
get createJSDocDeprecatedTag() { return getJSDocSimpleTagCreateFunction<JSDocDeprecatedTag>(SyntaxKind.JSDocDeprecatedTag); },
362+
get updateJSDocDeprecatedTag() { return getJSDocSimpleTagUpdateFunction<JSDocDeprecatedTag>(SyntaxKind.JSDocDeprecatedTag); },
361363
createJSDocUnknownTag,
362364
updateJSDocUnknownTag,
363365
createJSDocComment,
@@ -4225,6 +4227,7 @@ namespace ts {
42254227
// createJSDocPrivateTag
42264228
// createJSDocProtectedTag
42274229
// createJSDocReadonlyTag
4230+
// createJSDocDeprecatedTag
42284231
function createJSDocSimpleTagWorker<T extends JSDocTag>(kind: T["kind"], tagName: Identifier | undefined, comment?: string) {
42294232
const node = createBaseJSDocTag<T>(kind, tagName ?? createIdentifier(getDefaultTagNameForKind(kind)), comment);
42304233
return node;
@@ -4237,6 +4240,7 @@ namespace ts {
42374240
// updateJSDocPrivateTag
42384241
// updateJSDocProtectedTag
42394242
// updateJSDocReadonlyTag
4243+
// updateJSDocDeprecatedTag
42404244
function updateJSDocSimpleTagWorker<T extends JSDocTag>(kind: T["kind"], node: T, tagName: Identifier = getDefaultTagName(node), comment: string | undefined) {
42414245
return node.tagName !== tagName
42424246
|| node.comment !== comment

Diff for: src/compiler/factory/nodeTests.ts

+4
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,10 @@ namespace ts {
769769
return node.kind === SyntaxKind.JSDocReadonlyTag;
770770
}
771771

772+
export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag {
773+
return node.kind === SyntaxKind.JSDocDeprecatedTag;
774+
775+
}
772776
export function isJSDocEnumTag(node: Node): node is JSDocEnumTag {
773777
return node.kind === SyntaxKind.JSDocEnumTag;
774778
}

Diff for: src/compiler/parser.ts

+9
Original file line numberDiff line numberDiff line change
@@ -1014,10 +1014,15 @@ namespace ts {
10141014
return hasJSDoc ? addJSDocComment(node) : node;
10151015
}
10161016

1017+
let hasDeprecatedTag = false;
10171018
function addJSDocComment<T extends HasJSDoc>(node: T): T {
10181019
Debug.assert(!node.jsDoc); // Should only be called once per node
10191020
const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos));
10201021
if (jsDoc.length) node.jsDoc = jsDoc;
1022+
if (hasDeprecatedTag) {
1023+
hasDeprecatedTag = false;
1024+
(node as Mutable<T>).flags |= NodeFlags.Deprecated;
1025+
}
10211026
return node;
10221027
}
10231028

@@ -7178,6 +7183,10 @@ namespace ts {
71787183
case "readonly":
71797184
tag = parseSimpleTag(start, factory.createJSDocReadonlyTag, tagName, margin, indentText);
71807185
break;
7186+
case "deprecated":
7187+
hasDeprecatedTag = true;
7188+
tag = parseSimpleTag(start, factory.createJSDocDeprecatedTag, tagName, margin, indentText);
7189+
break;
71817190
case "this":
71827191
tag = parseThisTag(start, tagName, margin, indentText);
71837192
break;

0 commit comments

Comments
 (0)