Track minimum language version for various language features (#57670)

This commit is contained in:
Ron Buckton 2024-03-11 17:13:29 -04:00 committed by GitHub
parent 1e982d880c
commit 193d3ccef7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 151 additions and 43 deletions

View File

@ -813,6 +813,7 @@ import {
JsxTagNameExpression,
KeywordTypeNode,
LabeledStatement,
LanguageFeatureMinimumTarget,
last,
lastOrUndefined,
LateBoundBinaryExpressionDeclaration,
@ -31374,7 +31375,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function checkSpreadExpression(node: SpreadElement, checkMode?: CheckMode): Type {
if (languageVersion < ScriptTarget.ES2015) {
if (languageVersion < LanguageFeatureMinimumTarget.SpreadElements) {
checkExternalEmitHelpers(node, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArray);
}
@ -31411,7 +31412,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
for (let i = 0; i < elementCount; i++) {
const e = elements[i];
if (e.kind === SyntaxKind.SpreadElement) {
if (languageVersion < ScriptTarget.ES2015) {
if (languageVersion < LanguageFeatureMinimumTarget.SpreadElements) {
checkExternalEmitHelpers(e, compilerOptions.downlevelIteration ? ExternalEmitHelpers.SpreadIncludes : ExternalEmitHelpers.SpreadArray);
}
const spreadType = checkExpression((e as SpreadElement).expression, checkMode, forceTuple);
@ -31696,7 +31697,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
if (languageVersion < ScriptTarget.ES2015) {
if (languageVersion < LanguageFeatureMinimumTarget.ObjectAssign) {
checkExternalEmitHelpers(memberDecl, ExternalEmitHelpers.Assign);
}
if (propertiesArray.length > 0) {
@ -32983,7 +32984,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const isAnyLike = isTypeAny(apparentType) || apparentType === silentNeverType;
let prop: Symbol | undefined;
if (isPrivateIdentifier(right)) {
if (languageVersion < ScriptTarget.ESNext) {
if (
languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators ||
!useDefineForClassFields
) {
if (assignmentKind !== AssignmentKind.None) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.ClassPrivateFieldSet);
}
@ -36082,7 +36087,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type {
if (!checkGrammarTaggedTemplateChain(node)) checkGrammarTypeArguments(node, node.typeArguments);
if (languageVersion < ScriptTarget.ES2015) {
if (languageVersion < LanguageFeatureMinimumTarget.TaggedTemplates) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.MakeTemplateObject);
}
const signature = getResolvedSignature(node);
@ -38088,7 +38093,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return silentNeverType;
}
if (isPrivateIdentifier(left)) {
if (languageVersion < ScriptTarget.ESNext) {
if (
languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators ||
!useDefineForClassFields
) {
checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn);
}
// Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type
@ -38151,7 +38160,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
error(property, Diagnostics.A_rest_element_must_be_last_in_a_destructuring_pattern);
}
else {
if (languageVersion < ScriptTarget.ESNext) {
if (languageVersion < LanguageFeatureMinimumTarget.ObjectSpreadRest) {
checkExternalEmitHelpers(property, ExternalEmitHelpers.Rest);
}
const nonRestNames: PropertyName[] = [];
@ -38174,7 +38183,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkArrayLiteralAssignment(node: ArrayLiteralExpression, sourceType: Type, checkMode?: CheckMode): Type {
const elements = node.elements;
if (languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
if (languageVersion < LanguageFeatureMinimumTarget.DestructuringAssignment && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Read);
}
// This elementType will be used if the specific property corresponding to this index is not
@ -38279,6 +38288,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target);
}
if (isPrivateIdentifierPropertyAccessExpression(target)) {
// NOTE: we do not limit this to LanguageFeatureTargets.PrivateNames as some other feature downleveling still requires this.
checkExternalEmitHelpers(target.parent, ExternalEmitHelpers.ClassPrivateFieldSet);
}
return sourceType;
@ -38998,14 +39008,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const isAsync = (functionFlags & FunctionFlags.Async) !== 0;
if (node.asteriskToken) {
// Async generator functions prior to ESNext require the __await, __asyncDelegator,
// Async generator functions prior to ES2018 require the __await, __asyncDelegator,
// and __asyncValues helpers
if (isAsync && languageVersion < ScriptTarget.ESNext) {
if (isAsync && languageVersion < LanguageFeatureMinimumTarget.AsyncGenerators) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncDelegatorIncludes);
}
// Generator functions prior to ES2015 require the __values helper
if (!isAsync && languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
if (!isAsync && languageVersion < LanguageFeatureMinimumTarget.Generators && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Values);
}
}
@ -39874,19 +39884,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const functionFlags = getFunctionFlags(node as FunctionLikeDeclaration);
if (!(functionFlags & FunctionFlags.Invalid)) {
// Async generators prior to ESNext require the __await and __asyncGenerator helpers
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && languageVersion < ScriptTarget.ESNext) {
// Async generators prior to ES2018 require the __await and __asyncGenerator helpers
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.AsyncGenerator && languageVersion < LanguageFeatureMinimumTarget.AsyncGenerators) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.AsyncGeneratorIncludes);
}
// Async functions prior to ES2017 require the __awaiter helper
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async && languageVersion < ScriptTarget.ES2017) {
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async && languageVersion < LanguageFeatureMinimumTarget.AsyncFunctions) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Awaiter);
}
// Generator functions, Async functions, and Async Generator functions prior to
// ES2015 require the __generator helper
if ((functionFlags & FunctionFlags.AsyncGenerator) !== FunctionFlags.Normal && languageVersion < ScriptTarget.ES2015) {
if ((functionFlags & FunctionFlags.AsyncGenerator) !== FunctionFlags.Normal && languageVersion < LanguageFeatureMinimumTarget.Generators) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Generator);
}
}
@ -40194,19 +40204,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function setNodeLinksForPrivateIdentifierScope(node: PropertyDeclaration | PropertySignature | MethodDeclaration | MethodSignature | AccessorDeclaration) {
if (isPrivateIdentifier(node.name) && languageVersion < ScriptTarget.ESNext) {
for (let lexicalScope = getEnclosingBlockScopeContainer(node); !!lexicalScope; lexicalScope = getEnclosingBlockScopeContainer(lexicalScope)) {
getNodeLinks(lexicalScope).flags |= NodeCheckFlags.ContainsClassWithPrivateIdentifiers;
}
if (isPrivateIdentifier(node.name)) {
if (
languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators ||
!useDefineForClassFields
) {
for (let lexicalScope = getEnclosingBlockScopeContainer(node); !!lexicalScope; lexicalScope = getEnclosingBlockScopeContainer(lexicalScope)) {
getNodeLinks(lexicalScope).flags |= NodeCheckFlags.ContainsClassWithPrivateIdentifiers;
}
// If this is a private element in a class expression inside the body of a loop,
// then we must use a block-scoped binding to store the additional variables required
// to transform private elements.
if (isClassExpression(node.parent)) {
const enclosingIterationStatement = getEnclosingIterationStatement(node.parent);
if (enclosingIterationStatement) {
getNodeLinks(node.name).flags |= NodeCheckFlags.BlockScopedBindingInLoop;
getNodeLinks(enclosingIterationStatement).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
// If this is a private element in a class expression inside the body of a loop,
// then we must use a block-scoped binding to store the additional variables required
// to transform private elements.
if (isClassExpression(node.parent)) {
const enclosingIterationStatement = getEnclosingIterationStatement(node.parent);
if (enclosingIterationStatement) {
getNodeLinks(node.name).flags |= NodeCheckFlags.BlockScopedBindingInLoop;
getNodeLinks(enclosingIterationStatement).flags |= NodeCheckFlags.LoopWithCapturedBlockScopedBinding;
}
}
}
}
@ -41778,7 +41794,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.Param);
}
}
else if (languageVersion < ScriptTarget.ESNext) {
else if (languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators) {
checkExternalEmitHelpers(firstDecorator, ExternalEmitHelpers.ESDecorateAndRunInitializers);
if (isClassDeclaration(node)) {
if (!node.name) {
@ -42727,7 +42743,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return;
}
if (isObjectBindingPattern(node.parent) && node.dotDotDotToken && languageVersion < ScriptTarget.ES2018) {
if (isObjectBindingPattern(node.parent) && node.dotDotDotToken && languageVersion < LanguageFeatureMinimumTarget.ObjectSpreadRest) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Rest);
}
// check computed properties inside property names of binding elements
@ -42755,7 +42771,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// For a binding pattern, check contained binding elements
if (isBindingPattern(node.name)) {
if (node.name.kind === SyntaxKind.ArrayBindingPattern && languageVersion < ScriptTarget.ES2015 && compilerOptions.downlevelIteration) {
if (node.name.kind === SyntaxKind.ArrayBindingPattern && languageVersion < LanguageFeatureMinimumTarget.BindingPatterns && compilerOptions.downlevelIteration) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.Read);
}
@ -42925,7 +42941,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkVariableDeclarationList(node: VariableDeclarationList) {
const blockScopeKind = getCombinedNodeFlags(node) & NodeFlags.BlockScoped;
if (blockScopeKind === NodeFlags.Using || blockScopeKind === NodeFlags.AwaitUsing) {
if ((blockScopeKind === NodeFlags.Using || blockScopeKind === NodeFlags.AwaitUsing) && languageVersion < LanguageFeatureMinimumTarget.UsingAndAwaitUsing) {
checkExternalEmitHelpers(node, ExternalEmitHelpers.AddDisposableResourceAndDisposeResources);
}
@ -43145,13 +43161,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
else {
const functionFlags = getFunctionFlags(container);
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < ScriptTarget.ESNext) {
if ((functionFlags & (FunctionFlags.Invalid | FunctionFlags.Async)) === FunctionFlags.Async && languageVersion < LanguageFeatureMinimumTarget.ForAwaitOf) {
// for..await..of in an async function or async generator function prior to ESNext requires the __asyncValues helper
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes);
}
}
}
else if (compilerOptions.downlevelIteration && languageVersion < ScriptTarget.ES2015) {
else if (compilerOptions.downlevelIteration && languageVersion < LanguageFeatureMinimumTarget.ForOf) {
// for..of prior to ES2015 requires the __values helper when downlevelIteration is enabled
checkExternalEmitHelpers(node, ExternalEmitHelpers.ForOfIncludes);
}
@ -44564,9 +44580,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function getFirstTransformableStaticClassElement(node: ClassLikeDeclaration) {
const willTransformStaticElementsOfDecoratedClass = !legacyDecorators && languageVersion < ScriptTarget.ESNext &&
const willTransformStaticElementsOfDecoratedClass = !legacyDecorators && languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators &&
classOrConstructorParameterIsDecorated(/*useLegacyDecorators*/ false, node);
const willTransformPrivateElementsOrClassStaticBlocks = languageVersion <= ScriptTarget.ES2022;
const willTransformPrivateElementsOrClassStaticBlocks = languageVersion < LanguageFeatureMinimumTarget.PrivateNamesAndClassStaticBlocks ||
languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators;
const willTransformInitializers = !emitStandardClassFields;
if (willTransformStaticElementsOfDecoratedClass || willTransformPrivateElementsOrClassStaticBlocks) {
for (const member of node.members) {
@ -44596,7 +44613,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const parent = walkUpOuterExpressions(node);
if (!isNamedEvaluationSource(parent)) return;
const willTransformESDecorators = !legacyDecorators && languageVersion < ScriptTarget.ESNext;
const willTransformESDecorators = !legacyDecorators && languageVersion < LanguageFeatureMinimumTarget.ClassAndClassElementDecorators;
let location: Node | undefined;
if (willTransformESDecorators && classOrConstructorParameterIsDecorated(/*useLegacyDecorators*/ false, node)) {
location = firstOrUndefined(getDecorators(node)) ?? node;
@ -44662,7 +44679,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const baseTypeNode = getEffectiveBaseTypeNode(node);
if (baseTypeNode) {
forEach(baseTypeNode.typeArguments, checkSourceElement);
if (languageVersion < ScriptTarget.ES2015) {
if (languageVersion < LanguageFeatureMinimumTarget.Classes) {
checkExternalEmitHelpers(baseTypeNode.parent, ExternalEmitHelpers.Extends);
}
// check both @extends and extends if both are specified.

View File

@ -144,6 +144,9 @@ export const inverseJsxOptionMap = new Map(mapIterator(jsxOptionMap.entries(), (
// order in the generated program (see `getDefaultLibPriority` in program.ts). This
// order also affects overload resolution when a type declared in one lib is
// augmented in another lib.
// NOTE: We must reevaluate the target for upcoming features when each successive TC39 edition is ratified in
// June of each year. This includes changes to `LanguageFeatureMinimumTarget`, `ScriptTarget`,
// transformers/esnext.ts, commandLineParser.ts, and the contents of each lib/esnext.*.d.ts file.
const libEntries: [string, string][] = [
// JavaScript only
["es5", "lib.es5.d.ts"],

View File

@ -71,6 +71,10 @@ const enum UsingKind {
/** @internal */
export function transformESNext(context: TransformationContext): (x: SourceFile | Bundle) => SourceFile | Bundle {
// NOTE: We must reevaluate the target for upcoming features when each successive TC39 edition is ratified in
// June of each year. This includes changes to `LanguageFeatureMinimumTarget`, `ScriptTarget`,
// transformers/esnext.ts, commandLineParser.ts, and the contents of each lib/esnext.*.d.ts file.
const {
factory,
getEmitHelperFactory: emitHelpers,

View File

@ -7277,6 +7277,9 @@ export const enum ScriptKind {
Deferred = 7,
}
// NOTE: We must reevaluate the target for upcoming features when each successive TC39 edition is ratified in
// June of each year. This includes changes to `LanguageFeatureMinimumTarget`, `ScriptTarget`,
// transformers/esnext.ts, commandLineParser.ts, and the contents of each lib/esnext.*.d.ts file.
export const enum ScriptTarget {
/** @deprecated */
ES3 = 0,
@ -8035,6 +8038,65 @@ export type UniqueNameHandler = (baseName: string, checkFn?: (name: string) => b
export type EmitHelperUniqueNameCallback = (name: string) => string;
/**
* Indicates the minimum `ScriptTarget` (inclusive) after which a specific language feature is no longer transpiled.
*
* @internal
*/
export const enum LanguageFeatureMinimumTarget {
// ES2015 Features
Classes = ScriptTarget.ES2015,
ForOf = ScriptTarget.ES2015,
Generators = ScriptTarget.ES2015,
Iteration = ScriptTarget.ES2015,
SpreadElements = ScriptTarget.ES2015,
RestElements = ScriptTarget.ES2015,
TaggedTemplates = ScriptTarget.ES2015,
DestructuringAssignment = ScriptTarget.ES2015,
BindingPatterns = ScriptTarget.ES2015,
ArrowFunctions = ScriptTarget.ES2015,
BlockScopedVariables = ScriptTarget.ES2015,
ObjectAssign = ScriptTarget.ES2015,
// ES2016 Features
Exponentiation = ScriptTarget.ES2016, // `x ** y`
// ES2017 Features
AsyncFunctions = ScriptTarget.ES2017, // `async function f() {}`
// ES2018 Features
ForAwaitOf = ScriptTarget.ES2018, // `for await (const x of y)`
AsyncGenerators = ScriptTarget.ES2018, // `async function * f() { }`
AsyncIteration = ScriptTarget.ES2018, // `Symbol.asyncIterator`
ObjectSpreadRest = ScriptTarget.ES2018, // `{ ...obj }`
// ES2019 Features
BindinglessCatch = ScriptTarget.ES2019, // `try { } catch { }`
// ES2020 Features
BigInt = ScriptTarget.ES2020, // `0n`
NullishCoalesce = ScriptTarget.ES2020, // `a ?? b`
OptionalChaining = ScriptTarget.ES2020, // `a?.b`
// ES2021 Features
LogicalAssignment = ScriptTarget.ES2021, // `a ||= b`, `a &&= b`, `a ??= b`
// ES2022 Features
TopLevelAwait = ScriptTarget.ES2022,
ClassFields = ScriptTarget.ES2022,
PrivateNamesAndClassStaticBlocks = ScriptTarget.ES2022, // `class C { static {} #x = y, #m() {} }`, `#x in y`
// ES2023 Features
ShebangComments = ScriptTarget.ESNext,
// Upcoming Features
// NOTE: We must reevaluate the target for upcoming features when each successive TC39 edition is ratified in
// June of each year. This includes changes to `LanguageFeatureMinimumTarget`, `ScriptTarget`,
// transformers/esnext.ts, commandLineParser.ts, and the contents of each lib/esnext.*.d.ts file.
UsingAndAwaitUsing = ScriptTarget.ESNext, // `using x = y`, `await using x = y`
ClassAndClassElementDecorators = ScriptTarget.ESNext, // `@dec class C {}`, `class C { @dec m() {} }`
}
// dprint-ignore
/**
* Used by the checker, this enum keeps track of external emit helpers that should be type

View File

@ -8,7 +8,19 @@ async function f() {
}
//// [awaitUsingDeclarationsWithImportHelpers.js]
import { __addDisposableResource, __disposeResources } from "tslib";
async function f() {
await using a = null;
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const a = __addDisposableResource(env_1, null, true);
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
const result_1 = __disposeResources(env_1);
if (result_1)
await result_1;
}
}
export {};

View File

@ -8,7 +8,17 @@ export {};
}
//// [usingDeclarationsWithImportHelpers.js]
import { __addDisposableResource, __disposeResources } from "tslib";
{
using a = null;
const env_1 = { stack: [], error: void 0, hasError: false };
try {
const a = __addDisposableResource(env_1, null, false);
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
__disposeResources(env_1);
}
}
export {};

View File

@ -1,4 +1,4 @@
// @target: esnext
// @target: es2022
// @module: esnext
// @lib: esnext
// @importHelpers: true

View File

@ -1,4 +1,4 @@
// @target: esnext
// @target: es2022
// @module: esnext
// @lib: esnext
// @importHelpers: true