Skip to content

Add 'bitflags' modifier for enum. #42533

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
307 changes: 208 additions & 99 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,12 @@ namespace ts {
category: Diagnostics.Advanced_Options,
description: Diagnostics.Include_modules_imported_with_json_extension
},

{
name: "bitEnum",
type: "boolean",
category: Diagnostics.Advanced_Options,
description: Diagnostics.Enable_strict_bit_checking_for_enum
},
{
name: "out",
type: "string",
Expand Down
26 changes: 24 additions & 2 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,10 @@
"category": "Error",
"code": 1264
},

"'bitflags' modifier can only be used with enum declaration": {
"category": "Error",
"code": 1265
},
"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
Expand Down Expand Up @@ -2625,6 +2628,10 @@
"category": "Error",
"code": 2622
},
"Bit operation is only allowed for enum with 'bitflags' modifier": {
"category": "Error",
"code": 2623
},

"Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": {
"category": "Error",
Expand Down Expand Up @@ -4692,7 +4699,10 @@
"category": "Error",
"code": 6238
},

"Enable strict bit checking for enum.": {
"category": "Message",
"code": 6239
},
"Projects to reference": {
"category": "Message",
"code": 6300
Expand Down Expand Up @@ -6279,5 +6289,17 @@
"Invalid value for 'jsxFragmentFactory'. '{0}' is not a valid identifier or qualified-name.": {
"category": "Error",
"code": 18035
},
"An enum member can only be number in bitflags enum.": {
"category": "Error",
"code": 18036
},
"An enum member can only set one bit witout operator in bitflags enum.": {
"category": "Error",
"code": 18037
},
"An enum member initializer can only use bitwise or shift operator in bitflags enum.": {
"category": "Error",
"code": 18038
}
}
1 change: 1 addition & 0 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,7 @@ namespace ts {
if (flags & ModifierFlags.Export) { result.push(createModifier(SyntaxKind.ExportKeyword)); }
if (flags & ModifierFlags.Ambient) { result.push(createModifier(SyntaxKind.DeclareKeyword)); }
if (flags & ModifierFlags.Default) { result.push(createModifier(SyntaxKind.DefaultKeyword)); }
if (flags & ModifierFlags.BitFlags) { result.push(createModifier(SyntaxKind.BitFlagskeyword)); }
if (flags & ModifierFlags.Const) { result.push(createModifier(SyntaxKind.ConstKeyword)); }
if (flags & ModifierFlags.Public) { result.push(createModifier(SyntaxKind.PublicKeyword)); }
if (flags & ModifierFlags.Private) { result.push(createModifier(SyntaxKind.PrivateKeyword)); }
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5879,6 +5879,7 @@ namespace ts {
return nextTokenIsIdentifierOrStringLiteralOnSameLine();
case SyntaxKind.AbstractKeyword:
case SyntaxKind.AsyncKeyword:
case SyntaxKind.BitFlagskeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.PrivateKeyword:
case SyntaxKind.ProtectedKeyword:
Expand Down Expand Up @@ -5956,6 +5957,7 @@ namespace ts {
case SyntaxKind.ImportKeyword:
return isStartOfDeclaration() || lookAhead(nextTokenIsOpenParenOrLessThanOrDot);

case SyntaxKind.BitFlagskeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.ExportKeyword:
return isStartOfDeclaration();
Expand Down Expand Up @@ -6048,6 +6050,7 @@ namespace ts {
case SyntaxKind.ModuleKeyword:
case SyntaxKind.NamespaceKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.BitFlagskeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.EnumKeyword:
case SyntaxKind.ExportKeyword:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace ts {
as: SyntaxKind.AsKeyword,
asserts: SyntaxKind.AssertsKeyword,
bigint: SyntaxKind.BigIntKeyword,
bitflags: SyntaxKind.BitFlagskeyword,
boolean: SyntaxKind.BooleanKeyword,
break: SyntaxKind.BreakKeyword,
case: SyntaxKind.CaseKeyword,
Expand Down
63 changes: 36 additions & 27 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ namespace ts {
Identifier,
PrivateIdentifier,
// Reserved words
BitFlagskeyword,
BreakKeyword,
CaseKeyword,
CatchKeyword,
Expand Down Expand Up @@ -539,6 +540,7 @@ namespace ts {
| SyntaxKind.AsyncKeyword
| SyntaxKind.AwaitKeyword
| SyntaxKind.BigIntKeyword
| SyntaxKind.BitFlagskeyword
| SyntaxKind.BooleanKeyword
| SyntaxKind.BreakKeyword
| SyntaxKind.CaseKeyword
Expand Down Expand Up @@ -614,6 +616,7 @@ namespace ts {
export type ModifierSyntaxKind =
| SyntaxKind.AbstractKeyword
| SyntaxKind.AsyncKeyword
| SyntaxKind.BitFlagskeyword
| SyntaxKind.ConstKeyword
| SyntaxKind.DeclareKeyword
| SyntaxKind.DefaultKeyword
Expand Down Expand Up @@ -794,6 +797,7 @@ namespace ts {
Abstract = 1 << 7, // Class/Method/ConstructSignature
Async = 1 << 8, // Property/Method/Function
Default = 1 << 9, // Function/Class (export default declaration)
BitFlags = 1 << 10, // BitFlags enum
Const = 1 << 11, // Const enum
HasComputedJSDocModifiers = 1 << 12, // Indicates the computed modifier flags include modifiers from JSDoc.

Expand All @@ -805,9 +809,9 @@ namespace ts {
ParameterPropertyModifier = AccessibilityModifier | Readonly,
NonPublicAccessibilityModifier = Private | Protected,

TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const,
TypeScriptModifier = Ambient | Public | Private | Protected | Readonly | Abstract | Const | BitFlags,
ExportDefault = Export | Default,
All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Async | Default | Const | Deprecated
All = Export | Ambient | Public | Private | Protected | Static | Readonly | Abstract | Async | Default | BitFlags | Const | Deprecated
}

export const enum JsxFlags {
Expand Down Expand Up @@ -1019,6 +1023,7 @@ namespace ts {

export type AbstractKeyword = ModifierToken<SyntaxKind.AbstractKeyword>;
export type AsyncKeyword = ModifierToken<SyntaxKind.AsyncKeyword>;
export type BitFlagskeyword = ModifierToken<SyntaxKind.BitFlagskeyword>;
export type ConstKeyword = ModifierToken<SyntaxKind.ConstKeyword>;
export type DeclareKeyword = ModifierToken<SyntaxKind.DeclareKeyword>;
export type DefaultKeyword = ModifierToken<SyntaxKind.DefaultKeyword>;
Expand All @@ -1035,6 +1040,7 @@ namespace ts {
export type Modifier =
| AbstractKeyword
| AsyncKeyword
| BitFlagskeyword
| ConstKeyword
| DeclareKeyword
| DefaultKeyword
Expand Down Expand Up @@ -4629,32 +4635,34 @@ namespace ts {
Function = 1 << 4, // Function
Class = 1 << 5, // Class
Interface = 1 << 6, // Interface
ConstEnum = 1 << 7, // Const enum
RegularEnum = 1 << 8, // Enum
ValueModule = 1 << 9, // Instantiated module
NamespaceModule = 1 << 10, // Uninstantiated module
TypeLiteral = 1 << 11, // Type Literal or mapped type
ObjectLiteral = 1 << 12, // Object Literal
Method = 1 << 13, // Method
Constructor = 1 << 14, // Constructor
GetAccessor = 1 << 15, // Get accessor
SetAccessor = 1 << 16, // Set accessor
Signature = 1 << 17, // Call, construct, or index signature
TypeParameter = 1 << 18, // Type parameter
TypeAlias = 1 << 19, // Type alias
ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder)
Alias = 1 << 21, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Prototype = 1 << 22, // Prototype property (no source representation)
ExportStar = 1 << 23, // Export * declaration
Optional = 1 << 24, // Optional property
Transient = 1 << 25, // Transient symbol (created during type check)
Assignment = 1 << 26, // Assignment treated as declaration (eg `this.prop = 1`)
ModuleExports = 1 << 27, // Symbol for CommonJS `module` of `module.exports`
BitFlagsEnum = 1 << 7, // Bitflags enum
ConstEnum = 1 << 8, // Const enum
RegularEnum = 1 << 9, // Enum
ValueModule = 1 << 10, // Instantiated module
NamespaceModule = 1 << 11, // Uninstantiated module
TypeLiteral = 1 << 12, // Type Literal or mapped type
ObjectLiteral = 1 << 13, // Object Literal
Method = 1 << 14, // Method
Constructor = 1 << 15, // Constructor
GetAccessor = 1 << 16, // Get accessor
SetAccessor = 1 << 17, // Set accessor
Signature = 1 << 18, // Call, construct, or index signature
TypeParameter = 1 << 19, // Type parameter
TypeAlias = 1 << 20, // Type alias
ExportValue = 1 << 21, // Exported value marker (see comment in declareModuleMember in binder)
Alias = 1 << 22, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Prototype = 1 << 23, // Prototype property (no source representation)
ExportStar = 1 << 24, // Export * declaration
Optional = 1 << 25, // Optional property
Transient = 1 << 26, // Transient symbol (created during type check)
Assignment = 1 << 27, // Assignment treated as declaration (eg `this.prop = 1`)
ModuleExports = 1 << 28, // Symbol for CommonJS `module` of `module.exports`

/* @internal */
All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral
All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | BitFlagsEnum | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral
| ObjectLiteral | Method | Constructor | GetAccessor | SetAccessor | Signature | TypeParameter | TypeAlias | ExportValue | Alias | Prototype | ExportStar | Optional | Transient,

Enum = RegularEnum | ConstEnum,
Enum = RegularEnum | ConstEnum | BitFlagsEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
Value = Variable | Property | EnumMember | ObjectLiteral | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor,
Type = Class | Interface | Enum | EnumMember | TypeLiteral | TypeParameter | TypeAlias,
Expand Down Expand Up @@ -4778,8 +4786,8 @@ namespace ts {

/* @internal */
export const enum EnumKind {
Numeric, // Numeric enum (each member has a TypeFlags.Enum type)
Literal // Literal enum (each member has a TypeFlags.EnumLiteral type)
Numeric, // Numeric enum (each member has a TypeFlags.Enum type)
StringLiteral // Literal enum (each member has a TypeFlags.EnumLiteral type)
}

/* @internal */
Expand Down Expand Up @@ -5814,6 +5822,7 @@ namespace ts {
allowUnusedLabels?: boolean;
alwaysStrict?: boolean; // Always combine with strict property
baseUrl?: string;
bitEnum?: boolean;
/** An error if set - this should only go through the -b pipeline and not actually be observed */
/*@internal*/
build?: boolean;
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,10 @@ namespace ts {
return !!(getCombinedModifierFlags(node) & ModifierFlags.Const);
}

export function isEnumBitFlags(node: EnumDeclaration): boolean {
return !!(getCombinedModifierFlags(node) & ModifierFlags.BitFlags);
}

export function isDeclarationReadonly(declaration: Declaration): boolean {
return !!(getCombinedModifierFlags(declaration) & ModifierFlags.Readonly && !isParameterPropertyDeclaration(declaration, declaration.parent));
}
Expand Down Expand Up @@ -4767,6 +4771,7 @@ namespace ts {
case SyntaxKind.AbstractKeyword: return ModifierFlags.Abstract;
case SyntaxKind.ExportKeyword: return ModifierFlags.Export;
case SyntaxKind.DeclareKeyword: return ModifierFlags.Ambient;
case SyntaxKind.BitFlagskeyword: return ModifierFlags.BitFlags;
case SyntaxKind.ConstKeyword: return ModifierFlags.Const;
case SyntaxKind.DefaultKeyword: return ModifierFlags.Default;
case SyntaxKind.AsyncKeyword: return ModifierFlags.Async;
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1139,10 +1139,11 @@ namespace ts {
// Keywords

/* @internal */
export function isModifierKind(token: SyntaxKind): token is Modifier["kind"] {
export function isModifierKind(token: SyntaxKind): token is ModifierSyntaxKind {
switch (token) {
case SyntaxKind.AbstractKeyword:
case SyntaxKind.AsyncKeyword:
case SyntaxKind.BitFlagskeyword:
case SyntaxKind.ConstKeyword:
case SyntaxKind.DeclareKeyword:
case SyntaxKind.DefaultKeyword:
Expand Down
1 change: 1 addition & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3277,6 +3277,7 @@ namespace ts.server.protocol {
allowUnusedLabels?: boolean;
alwaysStrict?: boolean;
baseUrl?: string;
bitEnum?: boolean;
charset?: string;
checkJs?: boolean;
declaration?: boolean;
Expand Down
4 changes: 4 additions & 0 deletions src/testRunner/unittests/services/transpile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ var x = 0;`, {
options: { compilerOptions: { baseUrl: "./folder/baseUrl" }, fileName: "input.js", reportDiagnostics: true }
});

transpilesCorrectly("Supports setting 'bitEnum'", "x;", {
options: { compilerOptions: { bitEnum: true }, fileName: "input.js", reportDiagnostics: true }
});

transpilesCorrectly("Supports setting 'charset'", "x;", {
options: { compilerOptions: { charset: "en-us" }, fileName: "input.js", reportDiagnostics: true }
});
Expand Down
70 changes: 70 additions & 0 deletions tests/baselines/reference/bitflagsEnum.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
tests/cases/compiler/bitflagsEnum.ts(14,5): error TS18037: An enum member can only set one bit witout operator in bitflags enum.
tests/cases/compiler/bitflagsEnum.ts(18,5): error TS18036: An enum member can only be number in bitflags enum.
tests/cases/compiler/bitflagsEnum.ts(22,5): error TS18038: An enum member initializer can only use bitwise or shift operator in bitflags enum.
tests/cases/compiler/bitflagsEnum.ts(23,5): error TS18038: An enum member initializer can only use bitwise or shift operator in bitflags enum.
tests/cases/compiler/bitflagsEnum.ts(24,5): error TS18038: An enum member initializer can only use bitwise or shift operator in bitflags enum.
tests/cases/compiler/bitflagsEnum.ts(33,9): error TS18033: Only numeric enums can have computed members, but this expression has type 'string'. If you do not need exhaustiveness checks, consider using an object literal instead.
tests/cases/compiler/bitflagsEnum.ts(38,10): error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
tests/cases/compiler/bitflagsEnum.ts(38,10): error TS2623: Bit operation is only allowed for enum with 'bitflags' modifier
tests/cases/compiler/bitflagsEnum.ts(39,1): error TS2623: Bit operation is only allowed for enum with 'bitflags' modifier


==== tests/cases/compiler/bitflagsEnum.ts (9 errors) ====
bitflags enum BitEnum {
TS = 1,
TSX = 2,
All = TS | TSX,
}

var foo1 = BitEnum.TS;
var foo2 = BitEnum.TSX;
var foo3 = foo1 | foo2; // foo3 is possiable to be BitEnum.All?
declare var foo4: BitEnum;
var foo5 = foo4 & foo1; // foo5 is possiable to be BitEnum.TS?

bitflags enum BitEnum1 {
FOO = 3
~~~
!!! error TS18037: An enum member can only set one bit witout operator in bitflags enum.
}

bitflags enum BitEnum2 {
FOO = "foo"
~~~
!!! error TS18036: An enum member can only be number in bitflags enum.
}

bitflags enum BitEnum3 {
FOO = 1 + 2,
~~~
!!! error TS18038: An enum member initializer can only use bitwise or shift operator in bitflags enum.
Foo2 = 1 + 2 | 7,
~~~~
!!! error TS18038: An enum member initializer can only use bitwise or shift operator in bitflags enum.
Foo3 = 7 | 1 + 2
~~~~
!!! error TS18038: An enum member initializer can only use bitwise or shift operator in bitflags enum.
}

enum NormalEnum {
Black,
White
}

enum Q{
q = "2"+1
~~~~~
!!! error TS18033: Only numeric enums can have computed members, but this expression has type 'string'. If you do not need exhaustiveness checks, consider using an object literal instead.
}

var q1 = NormalEnum.Black;
var w1 = NormalEnum.White;
var e1 = "123" | w1;
~~~~~
!!! error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type.
~~~~~~~~~~
!!! error TS2623: Bit operation is only allowed for enum with 'bitflags' modifier
q1 |= 2;
~~~~~~~
!!! error TS2623: Bit operation is only allowed for enum with 'bitflags' modifier

Loading