core/quality/eslint/no-bitwise-outside-flags.eslint.rule.ts
core/quality/eslint/no-bitwise-outside-flags.eslint.rule.ts is a file in GovLab Quality. 95 lines of code and 13 definitions.
import { asNode, identName } from "#core/selectors/syntax.selector";
import type { Rule } from "eslint";
import type { SyntaxNode } from "#types/syntax.types";
import { govlabMeta } from "#core/factories/eslint.factory";
const BITWISE_BINARY = new Set(["&", "|", "^", "<<", ">>", ">>>"]);
const BITWISE_ASSIGN = new Set(["&=", "|=", "^=", "<<=", ">>=", ">>>="]);
const FLAGS_SUFFIX = "Flags";
const enumNameIsFlags = function enumNameIsFlags(obj: SyntaxNode | undefined): boolean {
if (!obj) {
return false;
}
if (obj.type === "Identifier") {
return (obj.name ?? "").endsWith(FLAGS_SUFFIX);
}
return obj.type === "MemberExpression" && (identName(obj.property) ?? "").endsWith(FLAGS_SUFFIX);
};
const isFlagMember = function isFlagMember(node: SyntaxNode | undefined): boolean {
return node?.type === "MemberExpression" && enumNameIsFlags(node.object);
};
const isFlagExpr = function isFlagExpr(node: SyntaxNode | undefined): boolean {
if (!node) {
return false;
}
if (isFlagMember(node)) {
return true;
}
if (node.type === "BinaryExpression" && typeof node.operator === "string" && BITWISE_BINARY.has(node.operator)) {
return isFlagExpr(node.left) && isFlagExpr(node.right);
}
if (node.type === "UnaryExpression" && node.operator === "~") {
return isFlagExpr(node.argument);
}
return false;
};
const flagBitwise = function flagBitwise(context: Rule.RuleContext, raw: Rule.Node): void {
context.report({ messageId: "bitwise", node: raw });
};
const reportBinary = function reportBinary(context: Rule.RuleContext, raw: Rule.Node): void {
const node = asNode(raw);
if (node !== null && typeof node.operator === "string" && BITWISE_BINARY.has(node.operator) && !isFlagExpr(node)) {
flagBitwise(context, raw);
}
};
const reportUnary = function reportUnary(context: Rule.RuleContext, raw: Rule.Node): void {
const node = asNode(raw);
if (node?.operator === "~" && !isFlagExpr(node.argument)) {
flagBitwise(context, raw);
}
};
const reportAssign = function reportAssign(context: Rule.RuleContext, raw: Rule.Node): void {
const node = asNode(raw);
if (
node !== null &&
typeof node.operator === "string" &&
BITWISE_ASSIGN.has(node.operator) &&
!isFlagExpr(node.right)
) {
flagBitwise(context, raw);
}
};
export default {
create(context: Rule.RuleContext): Rule.RuleListener {
const handlers: [string, (node: Rule.Node) => void][] = [
[
"BinaryExpression",
(node): void => {
reportBinary(context, node);
},
],
[
"UnaryExpression",
(node): void => {
reportUnary(context, node);
},
],
[
"AssignmentExpression",
(node): void => {
reportAssign(context, node);
},
],
];
return Object.fromEntries(handlers);
},
meta: govlabMeta({
canonical: ["operator-style"],
description:
"Reserve bitwise operators for bitfield-enum flag algebra; use arithmetic and logical operators for numeric and boolean logic",
messages: {
bitwise:
"Bitwise operators are reserved for bitfield-enum flag algebra: every operand must be a flag-enum member whose enum name ends in `Flags`. Use arithmetic (`+`/`-`/`*`) or logical (`&&`/`||`/`!`) operators for numeric and boolean logic; test a single flag bit with the parity form `Math.floor(flags / mask) % 2 === 1`; combine flags with `|` across `*Flags` enum members.",
},
ruleId: "no_bitwise_outside_flags",
}),
} satisfies Rule.RuleModule;