Skip to content

Commit a332a50

Browse files
baevmfisker
andauthored
prefer-bigint-literals: Support signed numbers and strings (#2784)
Co-authored-by: fisker <[email protected]>
1 parent ab4b779 commit a332a50

4 files changed

Lines changed: 782 additions & 8 deletions

File tree

rules/prefer-bigint-literals.js

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import {
22
isCallExpression,
33
isStringLiteral,
44
} from './ast/index.js';
5+
import {
6+
needsSemicolon,
7+
isParenthesized,
8+
} from './utils/index.js';
59

610
const MESSAGE_ID_ERROR = 'prefer-bigint-literals/error';
711
const MESSAGE_ID_SUGGESTION = 'prefer-bigint-literals/suggestion';
@@ -39,13 +43,33 @@ const canUseNumericLiteralRaw = numericLiteral => {
3943
function getReplacement(valueNode) {
4044
if (isStringLiteral(valueNode)) {
4145
const raw = valueNode.raw.slice(1, -1);
46+
let bigint;
4247
try {
43-
BigInt(raw);
48+
bigint = BigInt(raw);
4449
} catch {
4550
return;
4651
}
4752

48-
return {shouldUseSuggestion: false, text: `${raw.trimEnd()}n`};
53+
let text = bigint === 0n ? '0' : raw.trim();
54+
if (text.startsWith('+')) {
55+
text = text.slice(1).trim();
56+
}
57+
58+
return {shouldUseSuggestion: raw.includes('+'), text: `${text}n`, bigint};
59+
}
60+
61+
let shouldUseSuggestion = false;
62+
let isNegated = false;
63+
while (valueNode.type === 'UnaryExpression' && valueNode.prefix) {
64+
if (valueNode.operator === '+') {
65+
shouldUseSuggestion = true;
66+
valueNode = valueNode.argument;
67+
} else if (valueNode.operator === '-') {
68+
isNegated = !isNegated;
69+
valueNode = valueNode.argument;
70+
} else {
71+
return;
72+
}
4973
}
5074

5175
const {value, raw} = valueNode;
@@ -61,9 +85,20 @@ function getReplacement(valueNode) {
6185
return;
6286
}
6387

64-
const shouldUseSuggestion = !canUseNumericLiteralRaw(valueNode);
65-
const text = shouldUseSuggestion ? `${bigint}n` : `${raw}n`;
66-
return {shouldUseSuggestion, text};
88+
let text;
89+
if (canUseNumericLiteralRaw(valueNode)) {
90+
text = `${raw}n`;
91+
} else {
92+
text = `${bigint}n`;
93+
shouldUseSuggestion = true;
94+
}
95+
96+
if (isNegated && bigint !== 0n) {
97+
text = `-${text}`;
98+
bigint = -bigint;
99+
}
100+
101+
return {shouldUseSuggestion, text, bigint};
67102
}
68103

69104
/** @param {import('eslint').Rule.RuleContext} context */
@@ -88,10 +123,23 @@ const create = context => {
88123
messageId: MESSAGE_ID_ERROR,
89124
};
90125

91-
const {shouldUseSuggestion, text} = replacement;
126+
const {shouldUseSuggestion, text, bigint} = replacement;
92127

93128
/** @param {import('eslint').Rule.RuleFixer} fixer */
94-
const fix = fixer => fixer.replaceText(callExpression, text);
129+
const fix = fixer => {
130+
let replacementText = text;
131+
if (!isParenthesized(callExpression, context) && bigint < 0n) {
132+
replacementText = `(${replacementText})`;
133+
134+
const tokenBefore = context.sourceCode.getTokenBefore(callExpression);
135+
136+
if (needsSemicolon(tokenBefore, context, replacementText)) {
137+
replacementText = `;${replacementText}`;
138+
}
139+
}
140+
141+
return fixer.replaceText(callExpression, replacementText);
142+
};
95143

96144
if (shouldUseSuggestion || context.sourceCode.getCommentsInside(callExpression).length > 0) {
97145
problem.suggest = [

test/prefer-bigint-literals.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@ test.snapshot({
2525
'BigInt("1_2")',
2626
'BigInt("1\\\n2")',
2727
String.raw`BigInt("\u{31}")`,
28+
'BigInt(!1)',
29+
'BigInt(~1)',
30+
'BigInt("++1")',
31+
'BigInt("+ 1")',
32+
'BigInt(void 0)',
33+
'BigInt(NaN)',
34+
'BigInt(Infinity)',
35+
'BigInt(-Infinity)',
36+
'BigInt(BigInt)',
37+
'BigInt(globalThis.x)',
38+
'BigInt(x)',
39+
'BigInt?.("1")',
2840
],
2941
invalid: [
3042
'BigInt("0")',
@@ -52,5 +64,42 @@ test.snapshot({
5264
'BigInt(1e2)',
5365
'BigInt(/* comment */1)',
5466
`BigInt(${'9'.repeat(100)})`,
67+
'BigInt("-1")',
68+
'BigInt("+1")',
69+
'BigInt(-1)',
70+
'BigInt(+1)',
71+
'-BigInt(-1)',
72+
'-BigInt("-1")',
73+
'-BigInt(1)',
74+
'-BigInt("1")',
75+
'BigInt(" +1 ")',
76+
'BigInt(" -1 ")',
77+
`
78+
foo
79+
BigInt("-1")
80+
`,
81+
'-BigInt("+1")',
82+
'-BigInt(/* comment */-1)',
83+
'-(BigInt(-1))',
84+
'2n - BigInt("-1")',
85+
'2n -BigInt("-1")',
86+
'BigInt(-1).toString()',
87+
'functionCall(-BigInt(1))',
88+
'obj[BigInt(1)]',
89+
'arr[BigInt(0)]',
90+
'void BigInt(1)',
91+
'typeof BigInt(1)',
92+
'BigInt(1) + BigInt(2)',
93+
'BigInt(1) ** 2n',
94+
'condition ? BigInt(1) : BigInt(2)',
95+
'+BigInt(1)',
96+
'+BigInt(-1)',
97+
'+BigInt("1")',
98+
'BigInt(+0)',
99+
'BigInt(-0)',
100+
'BigInt("+0")',
101+
'-BigInt("-0")',
102+
'1n - (( BigInt("-1") ))',
103+
'BigInt(-(+(-10)))',
55104
],
56105
});

0 commit comments

Comments
 (0)