Skip to content

Commit

Permalink
prefer-math-trunc: Use suggestion instead of fix for x | 0 (#1014)
Browse files Browse the repository at this point in the history
Co-authored-by: fisker Cheung <lionkay@gmail.com>
  • Loading branch information
noftaly and fisker committed Jan 13, 2021
1 parent b7cbd99 commit 74b1b2d
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 239 deletions.
31 changes: 24 additions & 7 deletions rules/prefer-math-trunc.js
Expand Up @@ -2,11 +2,13 @@
const {hasSideEffect} = require('eslint-utils');
const getDocumentationUrl = require('./utils/get-documentation-url');

const MESSAGE_ID_BITWISE = 'bitwise';
const MESSAGE_ID_BITWISE_NOT = 'bitwiseNot';
const ERROR_BITWISE = 'error-bitwise';
const ERROR_BITWISE_NOT = 'error-bitwise-not';
const SUGGESTION_BITWISE = 'suggestion-bitwise';
const messages = {
[MESSAGE_ID_BITWISE]: 'Use `Math.trunc` instead of `{{operator}} {{value}}`.',
[MESSAGE_ID_BITWISE_NOT]: 'Use `Math.trunc` instead of `~~`.'
[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) => {
Expand Down Expand Up @@ -49,30 +51,45 @@ const create = context => {

const problem = {
node,
messageId: MESSAGE_ID_BITWISE,
messageId: ERROR_BITWISE,
data: {
operator,
value: right.raw
}
};

if (!isAssignment || !hasSideEffect(left, sourceCode)) {
problem.fix = fixer => {
const fix = fixer => {
let fixed = mathTruncFunctionCall(left);
if (isAssignment) {
fixed = `${sourceCode.getText(left)} = ${fixed}`;
}

return fixer.replaceText(node, fixed);
};

if (operator === '|') {
problem.suggest = [
{
messageId: SUGGESTION_BITWISE,
data: {
operator,
value: right.raw
},
fix
}
];
} else {
problem.fix = fix;
}
}

context.report(problem);
},
[bitwiseNotUnaryExpressionSelector]: node => {
context.report({
node,
messageId: MESSAGE_ID_BITWISE_NOT,
messageId: ERROR_BITWISE_NOT,
fix: fixer => fixer.replaceText(node, mathTruncFunctionCall(node.argument.argument))
});
}
Expand Down
234 changes: 116 additions & 118 deletions test/prefer-math-trunc.js
@@ -1,7 +1,7 @@
import {outdent} from 'outdent';
import {test} from './utils/test.js';

test({
test.visualize({
valid: [
'const foo = 1 | 1;',
'const foo = 0 | 1;',
Expand All @@ -24,129 +24,127 @@ test({
foo |= 1; // comment 2 and 1.2 | 0
`
],
invalid: []
});

test.visualize([
// Basic "bitwise OR with 0" case
'const foo = 1.1 | 0;',
'const foo = 111 | 0;',
'const foo = (1 + 2 / 3.4) | 0;',
'const foo = bar((1.4 | 0) + 2);',
'const foo = (0, 1.4) | 0;',
invalid: [
// Basic "bitwise OR with 0" case
'const foo = 1.1 | 0;',
'const foo = 111 | 0;',
'const foo = (1 + 2 / 3.4) | 0;',
'const foo = bar((1.4 | 0) + 2);',
'const foo = (0, 1.4) | 0;',

// Different "types" of 0
'const foo = 1.4 | 0.;',
'const foo = 1.4 | .0;',
'const foo = 1.4 | 0.0000_0000_0000;',
'const foo = 1.4 | 0b0;',
'const foo = 1.4 | 0x0000_0000_0000;',
'const foo = 1.4 | 0o0;',
// Different "types" of 0
'const foo = 1.4 | 0.;',
'const foo = 1.4 | .0;',
'const foo = 1.4 | 0.0000_0000_0000;',
'const foo = 1.4 | 0b0;',
'const foo = 1.4 | 0x0000_0000_0000;',
'const foo = 1.4 | 0o0;',

// Multiple bitwise OR
'const foo = 1.23 | 0 | 4;',
// Multiple bitwise OR
'const foo = 1.23 | 0 | 4;',

// Basic "bitwise NOT" case
'const foo = ~~3.9;',
'const foo = ~~111;',
'const foo = ~~(1 + 2 / 3.4);',
'const foo = ~~1 + 2 / 3.4;',
'const foo = ~~(0, 1.4);',
'const foo = ~~~10.01;',
'const foo = ~~(~10.01);',
'const foo = ~(~~10.01);',
'const foo = ~~-10.01;',
'const foo = ~~~~10.01;',
// Basic "bitwise NOT" case
'const foo = ~~3.9;',
'const foo = ~~111;',
'const foo = ~~(1 + 2 / 3.4);',
'const foo = ~~1 + 2 / 3.4;',
'const foo = ~~(0, 1.4);',
'const foo = ~~~10.01;',
'const foo = ~~(~10.01);',
'const foo = ~(~~10.01);',
'const foo = ~~-10.01;',
'const foo = ~~~~10.01;',

// Other operators
'const foo = bar >> 0;',
'const foo = bar << 0;',
'const foo = bar ^ 0;',
// Other operators
'const foo = bar >> 0;',
'const foo = bar << 0;',
'const foo = bar ^ 0;',

// Case with objects (MemberExpression and ChainExpression)
outdent`
const foo = {a: {b: {c: 3}}};
const bar = a.b.c | 0;
`,
outdent`
const foo = {a: {b: {c: 3}}};
const bar = a.b?.c | 0;
`,
outdent`
const foo = {a: {b: {c: 3}}};
const bar = ~~a.b?.c;
`,
// With a variable
outdent`
const foo = 3;
const bar = foo | 0;
`,
outdent`
const foo = 3;
const bar = ~~foo;
`,
// Case with objects (MemberExpression and ChainExpression)
outdent`
const foo = {a: {b: {c: 3}}};
const bar = a.b.c | 0;
`,
outdent`
const foo = {a: {b: {c: 3}}};
const bar = a.b?.c | 0;
`,
outdent`
const foo = {a: {b: {c: 3}}};
const bar = ~~a.b?.c;
`,
// With a variable
outdent`
const foo = 3;
const bar = foo | 0;
`,
outdent`
const foo = 3;
const bar = ~~foo;
`,

// With an AssignmentExpression
outdent`
let foo = 2;
foo |= 0;
`,
outdent`
const foo = {a: {b: 3.4}};
foo.a.b |= 0;
`,
outdent`
const foo = 10.01;
const bar = ~~foo;
`,
outdent`
let foo = 10.01;
foo >>= 0;
`,
outdent`
let foo = 10.01;
foo <<= 0;
`,
outdent`
let foo = 10.01;
foo ^= 0;
`,
// With an AssignmentExpression
outdent`
let foo = 2;
foo |= 0;
`,
outdent`
const foo = {a: {b: 3.4}};
foo.a.b |= 0;
`,
outdent`
const foo = 10.01;
const bar = ~~foo;
`,
outdent`
let foo = 10.01;
foo >>= 0;
`,
outdent`
let foo = 10.01;
foo <<= 0;
`,
outdent`
let foo = 10.01;
foo ^= 0;
`,

// With comments
'const foo = /* first comment */ 3.4 | 0; // A B C',
'const foo = /* first comment */ ~~3.4; // A B C',
outdent`
const foo = {a: {b: 3.4}};
foo /* Comment 1 */ .a /* Comment 2 */ . /* Comment 3 */ b |= /* Comment 4 */ 0 /* Comment 5 */;
`,
outdent`
const foo = {a: {b: 3.4}};
const bar = /* Comment 1 */ ~~ a /* Comment 3 */ . /* Comment 4 */ b /* Comment 5 */;
`,
'const foo = /* will keep */ 3.4 /* will remove 1 */ | /* will remove 2 */ 0;',
'const foo = /* will keep */ ~ /* will remove 1 */ ~ /* will remove 2 */ 3.4;',
outdent`
const foo = 3.4; // comment 1
foo |= 0; // comment 2
`,
outdent`
const foo = 3.4; // comment 1
const bar = ~~foo; // comment 2
`,
// With comments
'const foo = /* first comment */ 3.4 | 0; // A B C',
'const foo = /* first comment */ ~~3.4; // A B C',
outdent`
const foo = {a: {b: 3.4}};
foo /* Comment 1 */ .a /* Comment 2 */ . /* Comment 3 */ b |= /* Comment 4 */ 0 /* Comment 5 */;
`,
outdent`
const foo = {a: {b: 3.4}};
const bar = /* Comment 1 */ ~~ a /* Comment 3 */ . /* Comment 4 */ b /* Comment 5 */;
`,
'const foo = /* will keep */ 3.4 /* will remove 1 */ | /* will remove 2 */ 0;',
'const foo = /* will keep */ ~ /* will remove 1 */ ~ /* will remove 2 */ 3.4;',
outdent`
const foo = 3.4; // comment 1
foo |= 0; // comment 2
`,
outdent`
const foo = 3.4; // comment 1
const bar = ~~foo; // comment 2
`,

// Multiple errors
'const foo = ~~bar | 0;',
'const foo = ~~(bar| 0);',
'const foo = bar | 0 | 0;',
'const foo = ~~~~((bar | 0 | 0) >> 0 >> 0 << 0 << 0 ^ 0 ^0);',
// Multiple errors
'const foo = ~~bar | 0;',
'const foo = ~~(bar| 0);',
'const foo = bar | 0 | 0;',
'const foo = ~~~~((bar | 0 | 0) >> 0 >> 0 << 0 << 0 ^ 0 ^0);',

// Left-hand side has side effect
outdent`
const foo = Array.from({length: 10}, (_, index) => (index + 1) + (index + 1) /100);
let i = 0;
while (i < foo.length) {
foo[i++] |= 0;
}
console.log(foo);
`
]);
// Left-hand side has side effect
outdent`
const foo = Array.from({length: 10}, (_, index) => (index + 1) + (index + 1) /100);
let i = 0;
while (i < foo.length) {
foo[i++] |= 0;
}
console.log(foo);
`
]
});

0 comments on commit 74b1b2d

Please sign in to comment.