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

Fix deprecations, improve messaging #52993

Merged
merged 11 commits into from Mar 7, 2023
12 changes: 10 additions & 2 deletions src/compiler/diagnosticMessages.json
Expand Up @@ -4281,11 +4281,11 @@
"category": "Error",
"code": 5098
},
"Flag '{0}' is deprecated and will stop functioning in TypeScript {1}. Specify compilerOption '\"ignoreDeprecations\": \"{2}\"' to silence this error.": {
"Option '{0}' is deprecated and will stop functioning in TypeScript {1}. Specify compilerOption '\"ignoreDeprecations\": \"{2}\"' to silence this error.": {
"category": "Error",
"code": 5101
},
"Flag '{0}' is deprecated. Please remove it from your configuration.": {
"Option '{0}' has been removed. Please remove it from your configuration.": {
"category": "Error",
"code": 5102
},
Expand All @@ -4305,6 +4305,14 @@
"category": "Message",
"code": 5106
},
"Option '{0}={1}' is deprecated and will stop functioning in TypeScript {2}. Specify compilerOption '\"ignoreDeprecations\": \"{3}\"' to silence this error.": {
"category": "Error",
"code": 5107
},
"Option '{0}={1}' has been removed. Please remove it from your configuration.": {
"category": "Error",
"code": 5108
},

"Generates a sourcemap for each corresponding '.d.ts' file.": {
"category": "Message",
Expand Down
190 changes: 105 additions & 85 deletions src/compiler/program.ts
Expand Up @@ -56,7 +56,6 @@ import {
CustomTransformers,
Debug,
DeclarationWithTypeParameterChildren,
DeprecationVersion,
Diagnostic,
DiagnosticCategory,
diagnosticCategoryName,
Expand Down Expand Up @@ -318,6 +317,7 @@ import {
UnparsedSource,
VariableDeclaration,
VariableStatement,
Version,
versionMajorMinor,
walkUpParenthesizedExpressions,
WriteFileCallback,
Expand Down Expand Up @@ -1445,6 +1445,8 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
const { rootNames, options, configFileParsingDiagnostics, projectReferences, typeScriptVersion } = createProgramOptions;
let { oldProgram } = createProgramOptions;

const reportInvalidIgnoreDeprecations = memoize(() => createOptionValueDiagnostic("ignoreDeprecations", Diagnostics.Invalid_value_for_ignoreDeprecations));

let processingDefaultLibFiles: SourceFile[] | undefined;
let processingOtherFiles: SourceFile[] | undefined;
let files: SourceFile[];
Expand Down Expand Up @@ -4320,97 +4322,115 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}
}

function getVersionForDeprecationDiagnostics(reportInvalidIgnoreDeprecations: boolean) {
const version = typeScriptVersion || versionMajorMinor;
function getTypeScriptVersion(): [version: Version, versionString: string] {
jakebailey marked this conversation as resolved.
Show resolved Hide resolved
const versionString = typeScriptVersion || versionMajorMinor;
return [new Version(versionString), versionString];
}

function getIgnoreDeprecationsVersion(): Version {
const ignoreDeprecations = options.ignoreDeprecations;
if (ignoreDeprecations) {
if (ignoreDeprecations === DeprecationVersion.v5_0 && (version === DeprecationVersion.v5_0 || version === DeprecationVersion.v5_5)) {
return;
}
else if (reportInvalidIgnoreDeprecations) {
createOptionValueDiagnostic("ignoreDeprecations", Diagnostics.Invalid_value_for_ignoreDeprecations);
const parsed = Version.tryParse(ignoreDeprecations);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We talked about this and it seems like the intent is that we are not going to deprecate anything between TS 5.0 and TS 6.0, so this is overkill, and we can just write === "5.0" and error.

if (parsed) {
return parsed;
}
reportInvalidIgnoreDeprecations();
}
return version;
return Version.zero;
}

function verifyDeprecatedCompilerOptions() {
const version = getVersionForDeprecationDiagnostics(/*reportInvalidIgnoreDeprecations*/ true);
if (!version) return;
if (options.target === ScriptTarget.ES3) {
createDeprecatedDiagnosticForOption(version, "target", "ES3");
}
if (options.noImplicitUseStrict) {
createDeprecatedDiagnosticForOption(version, "noImplicitUseStrict");
}
if (options.keyofStringsOnly) {
createDeprecatedDiagnosticForOption(version, "keyofStringsOnly");
}
if (options.suppressExcessPropertyErrors) {
createDeprecatedDiagnosticForOption(version, "suppressExcessPropertyErrors");
}
if (options.suppressImplicitAnyIndexErrors) {
createDeprecatedDiagnosticForOption(version, "suppressImplicitAnyIndexErrors");
}
if (options.noStrictGenericChecks) {
createDeprecatedDiagnosticForOption(version, "noStrictGenericChecks");
}
if (options.charset) {
createDeprecatedDiagnosticForOption(version, "charset");
}
if (options.out) {
createDeprecatedDiagnosticForOption(version, "out");
}
if (options.importsNotUsedAsValues) {
createDeprecatedDiagnosticForOption(version, "importsNotUsedAsValues", /*value*/ undefined, "verbatimModuleSyntax");
}
if (options.preserveValueImports) {
createDeprecatedDiagnosticForOption(version, "preserveValueImports", /*value*/ undefined, "verbatimModuleSyntax");
function checkDeprecations(
deprecatedIn: Version,
removedIn: Version,
createDiagnostic: (name: string, message: DiagnosticMessage, arg0: string, arg1?: string, arg2?: string, arg3?: string, value?: string, useInstead?: string) => void,
fn: (createDeprecatedDiagnostic: (name: string, value?: string, useInstead?: string) => void) => void,
) {
const [typescriptVersion, _typescriptVersionString] = getTypeScriptVersion();
const ignoreDeprecationsVersion = getIgnoreDeprecationsVersion();

const mustBeRemoved = !(removedIn.compareTo(typescriptVersion) === Comparison.GreaterThan);
const canBeSilenced = !mustBeRemoved && ignoreDeprecationsVersion.compareTo(deprecatedIn) === Comparison.LessThan;

if (mustBeRemoved || canBeSilenced) {
const deprecatedInVersion = `${deprecatedIn.major}.${deprecatedIn.minor}`;
const removedInVersion = `${removedIn.major}.${removedIn.minor}`;
fn((name, value, useInstead) => {
if (mustBeRemoved) {
if (value === undefined) {
createDiagnostic(name, Diagnostics.Option_0_has_been_removed_Please_remove_it_from_your_configuration, name, /*arg1*/ undefined, /*arg2*/ undefined, /*arg3*/ undefined, value, useInstead);
}
else {
createDiagnostic(name, Diagnostics.Option_0_1_has_been_removed_Please_remove_it_from_your_configuration, name, value, /*arg2*/ undefined, /*arg3*/ undefined, value, useInstead);
}
}
else {
if (value === undefined) {
createDiagnostic(name, Diagnostics.Option_0_is_deprecated_and_will_stop_functioning_in_TypeScript_1_Specify_compilerOption_ignoreDeprecations_Colon_2_to_silence_this_error, name, removedInVersion, deprecatedInVersion, /*arg3*/ undefined, value, useInstead);
}
else {
createDiagnostic(name, Diagnostics.Option_0_1_is_deprecated_and_will_stop_functioning_in_TypeScript_2_Specify_compilerOption_ignoreDeprecations_Colon_3_to_silence_this_error, name, value, removedInVersion, deprecatedInVersion, value, useInstead);
}
}
});
}
}

function verifyDeprecatedProjectReference(ref: ProjectReference, parentFile: JsonSourceFile | undefined, index: number) {
if (ref.prepend) {
const version = getVersionForDeprecationDiagnostics(/*reportInvalidIgnoreDeprecations*/ false);
if (version) {
createDeprecatedOptionForVersionDiagnostic(
version,
(message, arg0, arg1, arg2) => createDiagnosticForReference(parentFile, index, message, arg0, arg1, arg2),
"prepend",
);
function verifyDeprecatedCompilerOptions() {
function createDiagnostic(name: string, message: DiagnosticMessage, arg0: string, arg1?: string, arg2?: string, arg3?: string, value?: string, useInstead?: string) {
if (useInstead) {
const details = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Use_0_instead, useInstead);
const chain = chainDiagnosticMessages(details, message, arg0, arg1, arg2, arg3);
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, chain);
}
else {
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, message, arg0, arg1, arg2, arg3);
}
}
}

function createDeprecatedDiagnosticForOption(version: string, name: string, value?: string, useInstead?: string) {
return createDeprecatedOptionForVersionDiagnostic(
version,
(message, arg0, arg1, arg2) => {
if (useInstead) {
const details = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Use_0_instead, useInstead);
const chain = chainDiagnosticMessages(details, message, arg0, arg1, arg2);
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, chain);
}
else {
createDiagnosticForOption(/*onKey*/ !value, name, /*option2*/ undefined, message, arg0, arg1, arg2);
}
},
name,
value,
);
checkDeprecations(new Version(5, 0), new Version(5, 5), createDiagnostic, createDeprecatedDiagnostic => {
if (options.target === ScriptTarget.ES3) {
createDeprecatedDiagnostic("target", "ES3");
}
if (options.noImplicitUseStrict) {
createDeprecatedDiagnostic("noImplicitUseStrict");
}
if (options.keyofStringsOnly) {
createDeprecatedDiagnostic("keyofStringsOnly");
}
if (options.suppressExcessPropertyErrors) {
createDeprecatedDiagnostic("suppressExcessPropertyErrors");
}
if (options.suppressImplicitAnyIndexErrors) {
createDeprecatedDiagnostic("suppressImplicitAnyIndexErrors");
}
if (options.noStrictGenericChecks) {
createDeprecatedDiagnostic("noStrictGenericChecks");
}
if (options.charset) {
createDeprecatedDiagnostic("charset");
}
if (options.out) {
createDeprecatedDiagnostic("out");
jakebailey marked this conversation as resolved.
Show resolved Hide resolved
}
if (options.importsNotUsedAsValues) {
createDeprecatedDiagnostic("importsNotUsedAsValues", /*value*/ undefined, "verbatimModuleSyntax");
}
if (options.preserveValueImports) {
createDeprecatedDiagnostic("preserveValueImports", /*value*/ undefined, "verbatimModuleSyntax");
}
});
}

function createDeprecatedOptionForVersionDiagnostic(
version: string,
createDiagnostic: (message: DiagnosticMessage, arg0: string, arg1?: string, arg2?: string) => void,
name: string,
value?: string) {
if (version === DeprecationVersion.v6_0) {
createDiagnostic(Diagnostics.Flag_0_is_deprecated_Please_remove_it_from_your_configuration, value || name);
}
else {
createDiagnostic(Diagnostics.Flag_0_is_deprecated_and_will_stop_functioning_in_TypeScript_1_Specify_compilerOption_ignoreDeprecations_Colon_2_to_silence_this_error, value || name, DeprecationVersion.v5_5, DeprecationVersion.v5_0);
function verifyDeprecatedProjectReference(ref: ProjectReference, parentFile: JsonSourceFile | undefined, index: number) {
function createDiagnostic(_name: string, message: DiagnosticMessage, arg0: string, arg1?: string, arg2?: string, _value?: string, _useInstead?: string) {
createDiagnosticForReference(parentFile, index, message, arg0, arg1, arg2);
}

checkDeprecations(new Version(5, 0), new Version(5, 5), createDiagnostic, createDeprecatedDiagnostic => {
if (ref.prepend) {
createDeprecatedDiagnostic("prepend");
}
});
}

function createDiagnosticExplainingFile(file: SourceFile | undefined, fileProcessingReason: FileIncludeReason | undefined, diagnostic: DiagnosticMessage, args: (string | number | undefined)[] | undefined): Diagnostic {
Expand Down Expand Up @@ -4660,19 +4680,19 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessageChain): void;
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): void;
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number): void {
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void;
function createDiagnosticForOption(onKey: boolean, option1: string, option2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): void {
const compilerOptionsObjectLiteralSyntax = getCompilerOptionsObjectLiteralSyntax();
const needCompilerDiagnostic = !compilerOptionsObjectLiteralSyntax ||
!createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2);
!createOptionDiagnosticInObjectLiteralSyntax(compilerOptionsObjectLiteralSyntax, onKey, option1, option2, message, arg0, arg1, arg2, arg3);

if (needCompilerDiagnostic) {
// eslint-disable-next-line local/no-in-operator
if ("messageText" in message) {
programDiagnostics.add(createCompilerDiagnosticFromMessageChain(message));
}
else {
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2));
programDiagnostics.add(createCompilerDiagnostic(message, arg0, arg1, arg2, arg3));
}
}
}
Expand All @@ -4694,17 +4714,17 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
}

function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, messageChain: DiagnosticMessageChain): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number): boolean {
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): boolean;
function createOptionDiagnosticInObjectLiteralSyntax(objectLiteral: ObjectLiteralExpression, onKey: boolean, key1: string, key2: string | undefined, message: DiagnosticMessage | DiagnosticMessageChain, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): boolean {
const props = getPropertyAssignment(objectLiteral, key1, key2);
for (const prop of props) {
// eslint-disable-next-line local/no-in-operator
if ("messageText" in message) {
programDiagnostics.add(createDiagnosticForNodeFromMessageChain(options.configFile!, onKey ? prop.name : prop.initializer, message));
}
else {
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2));
programDiagnostics.add(createDiagnosticForNodeInSourceFile(options.configFile!, onKey ? prop.name : prop.initializer, message, arg0, arg1, arg2, arg3));
}
}
return !!props.length;
Expand Down
9 changes: 0 additions & 9 deletions src/compiler/types.ts
Expand Up @@ -9874,12 +9874,3 @@ export interface Queue<T> {
dequeue(): T;
isEmpty(): boolean;
}

/** @internal */
export const enum DeprecationVersion {
/* eslint-disable @typescript-eslint/naming-convention */
v5_0 = "5.0",
v5_5 = "5.5",
v6_0 = "6.0",
/* eslint-enable @typescript-eslint/naming-convention */
}