123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- 'use strict';
- const {hasSideEffect} = require('@eslint-community/eslint-utils');
- const {fixSpaceAroundKeyword} = require('./fix/index.js');
- const ERROR_BITWISE = 'error-bitwise';
- const ERROR_BITWISE_NOT = 'error-bitwise-not';
- const SUGGESTION_BITWISE = 'suggestion-bitwise';
- const messages = {
- [ERROR_BITWISE]: 'Use `Math.trunc` instead of `{{operator}} {{value}}`.',
- [ERROR_BITWISE_NOT]: 'Use `Math.trunc` instead of `~~`.',
- [SUGGESTION_BITWISE]: 'Replace `{{operator}} {{value}}` with `Math.trunc`.',
- };
- const createBitwiseNotSelector = (level, isNegative) => {
- const prefix = 'argument.'.repeat(level);
- const selector = [
- `[${prefix}type="UnaryExpression"]`,
- `[${prefix}operator="~"]`,
- ].join('');
- return isNegative ? `:not(${selector})` : selector;
- };
- // Bitwise operators
- const bitwiseOperators = new Set(['|', '>>', '<<', '^']);
- // Unary Expression Selector: Inner-most 2 bitwise NOT
- const bitwiseNotUnaryExpressionSelector = [
- createBitwiseNotSelector(0),
- createBitwiseNotSelector(1),
- createBitwiseNotSelector(2, true),
- ].join('');
- /** @param {import('eslint').Rule.RuleContext} context */
- const create = context => {
- const sourceCode = context.getSourceCode();
- const mathTruncFunctionCall = node => {
- const text = sourceCode.getText(node);
- const parenthesized = node.type === 'SequenceExpression' ? `(${text})` : text;
- return `Math.trunc(${parenthesized})`;
- };
- return {
- ':matches(BinaryExpression, AssignmentExpression)[right.type="Literal"]'(node) {
- const {type, operator, right, left} = node;
- const isAssignment = type === 'AssignmentExpression';
- if (
- right.value !== 0
- || !bitwiseOperators.has(isAssignment ? operator.slice(0, -1) : operator)
- ) {
- return;
- }
- const problem = {
- node,
- messageId: ERROR_BITWISE,
- data: {
- operator,
- value: right.raw,
- },
- };
- if (!isAssignment || !hasSideEffect(left, sourceCode)) {
- const fix = function * (fixer) {
- const fixed = mathTruncFunctionCall(left);
- if (isAssignment) {
- const operatorToken = sourceCode.getTokenAfter(left, token => token.type === 'Punctuator' && token.value === operator);
- yield fixer.replaceText(operatorToken, '=');
- yield fixer.replaceText(right, fixed);
- } else {
- yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
- yield fixer.replaceText(node, fixed);
- }
- };
- if (operator === '|') {
- problem.suggest = [
- {
- messageId: SUGGESTION_BITWISE,
- fix,
- },
- ];
- } else {
- problem.fix = fix;
- }
- }
- return problem;
- },
- [bitwiseNotUnaryExpressionSelector]: node => ({
- node,
- messageId: ERROR_BITWISE_NOT,
- * fix(fixer) {
- yield fixer.replaceText(node, mathTruncFunctionCall(node.argument.argument));
- yield * fixSpaceAroundKeyword(fixer, node, sourceCode);
- },
- }),
- };
- };
- /** @type {import('eslint').Rule.RuleModule} */
- module.exports = {
- create,
- meta: {
- type: 'suggestion',
- docs: {
- description: 'Enforce the use of `Math.trunc` instead of bitwise operators.',
- },
- fixable: 'code',
- hasSuggestions: true,
- messages,
- },
- };
|