Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deprecated related feature #38523

Merged
merged 45 commits into from
Jun 19, 2020
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
bb09d38
Add deprecated related feature
Kingwl May 13, 2020
5a8da16
Add more support
Kingwl May 13, 2020
2deadaa
fix navtree
Kingwl May 13, 2020
9847ffb
Add identifier check
Kingwl May 14, 2020
06c8d22
Add more deprecated
Kingwl May 14, 2020
03d1ffe
fix crash
Kingwl May 14, 2020
30cea12
fix more crash
Kingwl May 14, 2020
13bdb75
fix crash
Kingwl May 14, 2020
1b3e477
improve diagnostic
Kingwl May 16, 2020
f794e11
avoid new tag
Kingwl May 17, 2020
9d27ab3
avoid tags
Kingwl May 17, 2020
36f4226
accept baseline
Kingwl May 17, 2020
6df251f
Check deprecated in binder
Kingwl May 18, 2020
1f15136
fix baseline
Kingwl May 18, 2020
d856ad5
fix jsdoc cache
Kingwl May 18, 2020
9c577e2
fix incorrect fix
Kingwl May 18, 2020
8abcf7e
Avoid useless changes
Kingwl May 18, 2020
e7be909
Accept baseline
Kingwl May 20, 2020
e796f20
Add tests
Kingwl May 26, 2020
7fcd23f
Merge branch 'master' into deprecated_support
Kingwl May 26, 2020
70b0445
fix perf
Kingwl May 28, 2020
8d5bcc0
fix public api
Kingwl May 28, 2020
0022d54
Merge branch 'master' into deprecated_support
Kingwl Jun 12, 2020
3c75d8e
Merge branch 'deprecated_support' of github.com:Kingwl/TypeScript int…
Kingwl Jun 12, 2020
b0311e3
Adjust deprecated mark on qualifed name
Kingwl Jun 12, 2020
182ed1c
Revolve alias symbol
Kingwl Jun 12, 2020
8b2f1f8
Use modifier flags insted of symbol props
Kingwl Jun 13, 2020
ed18e4a
Fix modifier flag resolve
Kingwl Jun 13, 2020
69ddd5f
Make lint happy
Kingwl Jun 13, 2020
8fdceab
Fix crash
Kingwl Jun 13, 2020
a30a430
fix crash
Kingwl Jun 13, 2020
dc97c66
Merge branch 'master' into deprecated_support
Kingwl Jun 13, 2020
7e18beb
Add cached utils function
Kingwl Jun 15, 2020
22410c1
Accept baseline
Kingwl Jun 15, 2020
b1eb8e0
Add more tests
Kingwl Jun 15, 2020
c89ee76
try pinning octokit again
sandersn Jun 15, 2020
1e00d36
Avoid tests
Kingwl Jun 15, 2020
f3e425c
Use utils some
Kingwl Jun 15, 2020
30fe85c
Merge branch 'master' into deprecated_support
Kingwl Jun 17, 2020
1472263
Deprecated perf test (#3)
Kingwl Jun 18, 2020
2d189dc
use symbol flag
Kingwl Jun 18, 2020
b8142f9
Merge branch 'master' into deprecated_support
Kingwl Jun 18, 2020
7714dbf
Merge branch 'master' into deprecated_support
sandersn Jun 18, 2020
6b30216
set @octokit/rest back to latest
sandersn Jun 18, 2020
6434447
fix trailing spacel int
sandersn Jun 19, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions scripts/processDiagnosticMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ interface DiagnosticDetails {
category: string;
code: number;
reportsUnnecessary?: {};
reportsDeprecated?: {};
isEarly?: boolean;
elidedInCompatabilityPyramid?: boolean;
}
Expand Down Expand Up @@ -64,15 +65,17 @@ function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, inputFil
"// generated from '" + inputFilePathRel + "' by '" + thisFilePathRel.replace(/\\/g, "/") + "'\r\n" +
"/* @internal */\r\n" +
"namespace ts {\r\n" +
" function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean): DiagnosticMessage {\r\n" +
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid };\r\n" +
" function diag(code: number, category: DiagnosticCategory, key: string, message: string, reportsUnnecessary?: {}, elidedInCompatabilityPyramid?: boolean, reportsDeprecated?: {}): DiagnosticMessage {\r\n" +
" return { code, category, key, message, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated };\r\n" +
" }\r\n" +
" export const Diagnostics = {\r\n";
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid }, name) => {
messageTable.forEach(({ code, category, reportsUnnecessary, elidedInCompatabilityPyramid, reportsDeprecated }, name) => {
const propName = convertPropertyName(name);
const argReportsUnnecessary = reportsUnnecessary ? `, /*reportsUnnecessary*/ ${reportsUnnecessary}` : "";
const argElidedInCompatabilityPyramid = elidedInCompatabilityPyramid ? `${!reportsUnnecessary ? ", /*reportsUnnecessary*/ undefined" : ""}, /*elidedInCompatabilityPyramid*/ ${elidedInCompatabilityPyramid}` : "";
result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}),\r\n`;
const argReportsDeprecated = reportsDeprecated ? `${!argElidedInCompatabilityPyramid ? ", /*reportsUnnecessary*/ undefined, /*elidedInCompatabilityPyramid*/ undefined" : ""}, /*reportsDeprecated*/ ${reportsDeprecated}` : "";

result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}${argReportsUnnecessary}${argElidedInCompatabilityPyramid}${argReportsDeprecated}),\r\n`;
});

result += " };\r\n}";
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,10 @@ namespace ts {
symbol.parent = parent;
}

if (node.flags & NodeFlags.Deprecated) {
symbol.flags |= SymbolFlags.Deprecated;
}

return symbol;
}

Expand Down
3 changes: 3 additions & 0 deletions src/compiler/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace ts {
export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation {
/** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */
reportsUnnecessary?: {};
reportDeprecated?: {}
source?: string;
relatedInformation?: ReusableDiagnosticRelatedInformation[];
skippedOn?: keyof CompilerOptions;
Expand Down Expand Up @@ -268,6 +269,7 @@ namespace ts {
return diagnostics.map(diagnostic => {
const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram, toPath);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.reportsDeprecated = diagnostic.reportDeprecated;
result.source = diagnostic.source;
result.skippedOn = diagnostic.skippedOn;
const { relatedInformation } = diagnostic;
Expand Down Expand Up @@ -817,6 +819,7 @@ namespace ts {
return diagnostics.map(diagnostic => {
const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic, relativeToBuildInfo);
result.reportsUnnecessary = diagnostic.reportsUnnecessary;
result.reportDeprecated = diagnostic.reportsDeprecated;
result.source = diagnostic.source;
result.skippedOn = diagnostic.skippedOn;
const { relatedInformation } = diagnostic;
Expand Down
84 changes: 55 additions & 29 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13168,6 +13168,10 @@ namespace ts {
if (propName !== undefined) {
const prop = getPropertyOfType(objectType, propName);
if (prop) {
if (accessNode && prop.flags & SymbolFlags.Deprecated) {
const deprecatedNode = accessExpression?.argumentExpression ?? (isIndexedAccessTypeNode(accessNode) ? accessNode.indexType : accessNode);
errorOrSuggestion(/* isError */ false, deprecatedNode, Diagnostics._0_is_deprecated, propName as string);
}
if (accessExpression) {
markPropertyAsReferenced(prop, accessExpression, /*isThisAccess*/ accessExpression.expression.kind === SyntaxKind.ThisKeyword);
if (isAssignmentToReadonlyEntity(accessExpression, prop, getAssignmentTargetKind(accessExpression))) {
Expand Down Expand Up @@ -21698,6 +21702,10 @@ namespace ts {
const localOrExportSymbol = getExportSymbolOfValueSymbolIfExported(symbol);
let declaration: Declaration | undefined = localOrExportSymbol.valueDeclaration;

const target = (symbol.flags & SymbolFlags.Alias ? resolveAlias(symbol) : symbol);
if (target.flags & SymbolFlags.Deprecated) {
errorOrSuggestion(/* isError */ false, node, Diagnostics._0_is_deprecated, node.escapedText as string);
}
if (localOrExportSymbol.flags & SymbolFlags.Class) {
// Due to the emit for class decorators, any reference to the class from inside of the class body
// must instead be rewritten to point to a temporary variable to avoid issues with the double-bind
Expand Down Expand Up @@ -24679,6 +24687,10 @@ namespace ts {
propType = indexInfo.type;
}
else {
if (prop.flags & SymbolFlags.Deprecated) {
errorOrSuggestion(/* isError */ false, right, Diagnostics._0_is_deprecated, right.escapedText as string);
}

checkPropertyNotUsedBeforeDeclaration(prop, node, right);
markPropertyAsReferenced(prop, node, left.kind === SyntaxKind.ThisKeyword);
getNodeLinks(node).resolvedSymbol = prop;
Expand Down Expand Up @@ -30499,8 +30511,15 @@ namespace ts {
checkTypeArgumentConstraints(node, typeParameters);
}
}
if (type.flags & TypeFlags.Enum && getNodeLinks(node).resolvedSymbol!.flags & SymbolFlags.EnumMember) {
error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type));
const symbol = getNodeLinks(node).resolvedSymbol;
if (symbol) {
if (symbol.flags & SymbolFlags.Deprecated) {
const diagLocation = isTypeReferenceNode(node) && isQualifiedName(node.typeName) ? node.typeName.right : node;
errorOrSuggestion(/* isError */ false, diagLocation, Diagnostics._0_is_deprecated, symbol.escapedName as string);
}
if (type.flags & TypeFlags.Enum && symbol.flags & SymbolFlags.EnumMember) {
error(node, Diagnostics.Enum_type_0_has_members_with_initializers_that_are_not_literals, typeToString(type));
}
}
}
}
Expand Down Expand Up @@ -31644,6 +31663,7 @@ namespace ts {
error(classLike, Diagnostics.JSDoc_0_is_not_attached_to_a_class, idText(node.tagName));
}
}

function checkJSDocAugmentsTag(node: JSDocAugmentsTag): void {
const classLike = getEffectiveJSDocHost(node);
if (!classLike || !isClassDeclaration(classLike) && !isClassExpression(classLike)) {
Expand Down Expand Up @@ -34803,33 +34823,39 @@ namespace ts {
let symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);

const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment;
if (!shouldSkipWithJSExpandoTargets && target !== unknownSymbol) {
// For external modules symbol represents local symbol for an alias.
// This local symbol will merge any other local declarations (excluding other aliases)
// and symbol.flags will contains combined representation for all merged declaration.
// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
symbol = getMergedSymbol(symbol.exportSymbol || symbol);
const excludedMeanings =
(symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
(symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0);
if (target.flags & excludedMeanings) {
const message = node.kind === SyntaxKind.ExportSpecifier ?
Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 :
Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0;
error(node, message, symbolToString(symbol));
}

// Don't allow to re-export something with no value side when `--isolatedModules` is set.
if (compilerOptions.isolatedModules
&& node.kind === SyntaxKind.ExportSpecifier
&& !node.parent.parent.isTypeOnly
&& !(target.flags & SymbolFlags.Value)
&& !(node.flags & NodeFlags.Ambient)) {
error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type);
if (target !== unknownSymbol) {
const shouldSkipWithJSExpandoTargets = symbol.flags & SymbolFlags.Assignment;
if (!shouldSkipWithJSExpandoTargets) {
// For external modules symbol represents local symbol for an alias.
// This local symbol will merge any other local declarations (excluding other aliases)
// and symbol.flags will contains combined representation for all merged declaration.
// Based on symbol.flags we can compute a set of excluded meanings (meaning that resolved alias should not have,
// otherwise it will conflict with some local declaration). Note that in addition to normal flags we include matching SymbolFlags.Export*
// in order to prevent collisions with declarations that were exported from the current module (they still contribute to local names).
symbol = getMergedSymbol(symbol.exportSymbol || symbol);
const excludedMeanings =
(symbol.flags & (SymbolFlags.Value | SymbolFlags.ExportValue) ? SymbolFlags.Value : 0) |
(symbol.flags & SymbolFlags.Type ? SymbolFlags.Type : 0) |
(symbol.flags & SymbolFlags.Namespace ? SymbolFlags.Namespace : 0);
if (target.flags & excludedMeanings) {
const message = node.kind === SyntaxKind.ExportSpecifier ?
Diagnostics.Export_declaration_conflicts_with_exported_declaration_of_0 :
Diagnostics.Import_declaration_conflicts_with_local_declaration_of_0;
error(node, message, symbolToString(symbol));
}

// Don't allow to re-export something with no value side when `--isolatedModules` is set.
if (compilerOptions.isolatedModules
&& node.kind === SyntaxKind.ExportSpecifier
&& !node.parent.parent.isTypeOnly
&& !(target.flags & SymbolFlags.Value)
&& !(node.flags & NodeFlags.Ambient)) {
error(node, Diagnostics.Re_exporting_a_type_when_the_isolatedModules_flag_is_provided_requires_using_export_type);
}
}

if (isImportSpecifier(node) && target.flags & SymbolFlags.Deprecated) {
errorOrSuggestion(/* isError */ false, node.name, Diagnostics._0_is_deprecated, symbol.escapedName as string);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4602,6 +4602,11 @@
"category": "Message",
"code": 6384
},
"'{0}' is deprecated": {
"category": "Suggestion",
"code": 6385,
"reportsDeprecated": true
},
Kingwl marked this conversation as resolved.
Show resolved Hide resolved

"The expected type comes from property '{0}' which is declared here on type '{1}'": {
"category": "Message",
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ namespace ts {
get updateJSDocProtectedTag() { return getJSDocSimpleTagUpdateFunction<JSDocProtectedTag>(SyntaxKind.JSDocProtectedTag); },
get createJSDocReadonlyTag() { return getJSDocSimpleTagCreateFunction<JSDocReadonlyTag>(SyntaxKind.JSDocReadonlyTag); },
get updateJSDocReadonlyTag() { return getJSDocSimpleTagUpdateFunction<JSDocReadonlyTag>(SyntaxKind.JSDocReadonlyTag); },
get createJSDocDeprecatedTag() { return getJSDocSimpleTagCreateFunction<JSDocDeprecatedTag>(SyntaxKind.JSDocDeprecatedTag); },
get updateJSDocDeprecatedTag() { return getJSDocSimpleTagUpdateFunction<JSDocDeprecatedTag>(SyntaxKind.JSDocDeprecatedTag); },
createJSDocUnknownTag,
updateJSDocUnknownTag,
createJSDocComment,
Expand Down Expand Up @@ -4225,6 +4227,7 @@ namespace ts {
// createJSDocPrivateTag
// createJSDocProtectedTag
// createJSDocReadonlyTag
// createJSDocDeprecatedTag
function createJSDocSimpleTagWorker<T extends JSDocTag>(kind: T["kind"], tagName: Identifier | undefined, comment?: string) {
const node = createBaseJSDocTag<T>(kind, tagName ?? createIdentifier(getDefaultTagNameForKind(kind)), comment);
return node;
Expand All @@ -4237,6 +4240,7 @@ namespace ts {
// updateJSDocPrivateTag
// updateJSDocProtectedTag
// updateJSDocReadonlyTag
// updateJSDocDeprecatedTag
function updateJSDocSimpleTagWorker<T extends JSDocTag>(kind: T["kind"], node: T, tagName: Identifier = getDefaultTagName(node), comment: string | undefined) {
return node.tagName !== tagName
|| node.comment !== comment
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/factory/nodeTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,10 @@ namespace ts {
return node.kind === SyntaxKind.JSDocReadonlyTag;
}

export function isJSDocDeprecatedTag(node: Node): node is JSDocDeprecatedTag {
return node.kind === SyntaxKind.JSDocDeprecatedTag;

}
export function isJSDocEnumTag(node: Node): node is JSDocEnumTag {
return node.kind === SyntaxKind.JSDocEnumTag;
}
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1014,10 +1014,15 @@ namespace ts {
return hasJSDoc ? addJSDocComment(node) : node;
}

let hasDeprecatedTag = false;
function addJSDocComment<T extends HasJSDoc>(node: T): T {
Debug.assert(!node.jsDoc); // Should only be called once per node
const jsDoc = mapDefined(getJSDocCommentRanges(node, sourceText), comment => JSDocParser.parseJSDocComment(node, comment.pos, comment.end - comment.pos));
if (jsDoc.length) node.jsDoc = jsDoc;
if (hasDeprecatedTag) {
hasDeprecatedTag = false;
(node as Mutable<T>).flags |= NodeFlags.Deprecated;
}
return node;
}

Expand Down Expand Up @@ -7178,6 +7183,10 @@ namespace ts {
case "readonly":
tag = parseSimpleTag(start, factory.createJSDocReadonlyTag, tagName, margin, indentText);
break;
case "deprecated":
hasDeprecatedTag = true;
tag = parseSimpleTag(start, factory.createJSDocDeprecatedTag, tagName, margin, indentText);
break;
case "this":
tag = parseThisTag(start, tagName, margin, indentText);
break;
Expand Down
Loading