Skip to content
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

Rewrite tests #283

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
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
41 changes: 36 additions & 5 deletions docs/rules/no-zero-fractions.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,58 @@ There is no difference in JavaScript between, for example, `1`, `1.0` and `1.`,

This rule is fixable.


## Fail

```js
const foo = 1.0;
```

```js
const foo = -1.0;
const foo = 123123123.0;
```

```js
const foo = 123_456.000_000;
```

```js
const foo = 1.;
```

```js
const foo = 123.111000000;
const foo = 123.00e20;
```

```js
const foo = 123.00e20;
```

## Pass

```js
const foo = 1;
```

```js
const foo = -1;
const foo = 123123123;
```

```js
const foo = 123456;
```

```js
const foo = 1.1;
```

```js
const foo = -1.1;
const foo = 123123123.4;
```

```js
const foo = 123.456;
```

```js
const foo = 1e3;
```
56 changes: 34 additions & 22 deletions rules/no-zero-fractions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
'use strict';
const {isParenthesized} = require('eslint-utils');
const getDocumentationUrl = require('./utils/get-documentation-url');
const needsSemicolon = require('./utils/needs-semicolon');
const {isNumber, isDecimalInteger} = require('./utils/numeric');

const MESSAGE_ZERO_FRACTION = 'zero-fraction';
const MESSAGE_DANGLING_DOT = 'dangling-dot';
Expand All @@ -8,45 +11,54 @@ const messages = {
[MESSAGE_DANGLING_DOT]: 'Don\'t use a dangling dot in the number.'
};

// Groups:
// 1. Integer part.
// 2. Dangling dot or dot with zeroes.
// 3. Dot with digits except last zeroes.
// 4. Scientific notation.
const RE_DANGLINGDOT_OR_ZERO_FRACTIONS = /^(?<integerPart>[+-]?\d*)(?:(?<dotAndZeroes>\.0*)|(?<dotAndDigits>\.\d*[1-9])0+)(?<scientificNotationSuffix>e[+-]?\d+)?$/;

const create = context => {
return {
Literal: node => {
if (typeof node.value !== 'number') {
if (!isNumber(node)) {
return;
}

const match = RE_DANGLINGDOT_OR_ZERO_FRACTIONS.exec(node.raw);
if (match === null) {
// Legacy octal number `0777` and prefixed number `0o1234` cannot have a dot.
const {raw} = node;
const match = raw.match(/^(?<before>[\d_]*)(?<dotAndFractions>\.[\d_]*)(?<after>.*)$/);
if (!match) {
return;
}

const {
integerPart,
dotAndZeroes,
dotAndDigits,
scientificNotationSuffix
} = match.groups;
const {before, dotAndFractions, after} = match.groups;
const formatted = before + dotAndFractions.replace(/[.0_]+$/g, '') + after;

const isDanglingDot = dotAndZeroes === '.';
if (formatted === raw) {
return;
}

const isDanglingDot = dotAndFractions === '.';
// End of fractions
const end = node.range[0] + before.length + dotAndFractions.length;
const start = end - (raw.length - formatted.length);
const sourceCode = context.getSourceCode();
context.report({
node,
loc: {
start: sourceCode.getLocFromIndex(start),
end: sourceCode.getLocFromIndex(end)
},
messageId: isDanglingDot ? MESSAGE_DANGLING_DOT : MESSAGE_ZERO_FRACTION,
fix: fixer => {
let wantedString = dotAndZeroes === undefined ? integerPart + dotAndDigits : integerPart;
let fixed = formatted;
if (
node.parent.type === 'MemberExpression' &&
node.parent.object === node &&
isDecimalInteger(formatted) &&
!isParenthesized(node, sourceCode)
) {
fixed = `(${fixed})`;

if (scientificNotationSuffix !== undefined) {
wantedString += scientificNotationSuffix;
if (needsSemicolon(sourceCode.getTokenBefore(node), sourceCode, fixed)) {
fixed = `;${fixed}`;
}
}

return fixer.replaceText(node, wantedString);
return fixer.replaceText(node, fixed);
}
});
}
Expand Down
4 changes: 2 additions & 2 deletions rules/numeric-separators-style.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function addSeparatorFromLeft(value, options) {
}

function formatNumber(value, options) {
const {integer, dot, fractional} = numeric.parseFloat(value);
const {integer, dot, fractional} = numeric.parseFloatNumber(value);
return addSeparator(integer, options) + dot + addSeparatorFromLeft(fractional, options);
}

Expand Down Expand Up @@ -99,7 +99,7 @@ const create = context => {

return {
Literal: node => {
if (!numeric.isNumberic(node) || numeric.isLegacyOctal(node)) {
if (!numeric.isNumeric(node) || numeric.isLegacyOctal(node)) {
return;
}

Expand Down
21 changes: 16 additions & 5 deletions rules/utils/numeric.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
// Determine whether this node is a decimal integer literal.
// Copied from https://github.com/eslint/eslint/blob/cc4871369645c3409dc56ded7a555af8a9f63d51/lib/rules/utils/ast-utils.js#L1237
const DECIMAL_INTEGER_PATTERN = /^(?:0|0[0-7]*[89]\d*|[1-9](?:_?\d)*)$/u;
const isDecimalInteger = node => isNumber(node) && DECIMAL_INTEGER_PATTERN.test(node.raw);
const isDecimalInteger = text => DECIMAL_INTEGER_PATTERN.test(text);
const isDecimalIntegerNode = node => isNumber(node) && isDecimalInteger(node.raw);

const isNumber = node => typeof node.value === 'number';
const isBigInt = node => Boolean(node.bigint);
const isNumberic = node => isNumber(node) || isBigInt(node);
const isNumeric = node => isNumber(node) || isBigInt(node);
const isLegacyOctal = node => isNumber(node) && /^0\d+$/.test(node.raw);

function getPrefix(text) {
Expand All @@ -28,17 +29,27 @@ function parseNumber(text) {
mark = '',
sign = '',
power = ''
} = text.match(/^(?<number>.*?)(?:(?<mark>e)(?<sign>[+-])?(?<power>\d+))?$/i).groups;
} = text.match(/^(?<number>[\d._]*?)(?:(?<mark>[Ee])(?<sign>[+-])?(?<power>[\d_]+))?$/).groups;

return {number, mark, sign, power};
}

function parseFloat(text) {
function parseFloatNumber(text) {
const parts = text.split('.');
const [integer, fractional = ''] = parts;
const dot = parts.length === 2 ? '.' : '';

return {integer, dot, fractional};
}

module.exports = {isNumber, isBigInt, isNumberic, isLegacyOctal, getPrefix, parseNumber, parseFloat, isDecimalInteger};
module.exports = {
isDecimalIntegerNode,
isDecimalInteger,
isNumber,
isBigInt,
isNumeric,
isLegacyOctal,
getPrefix,
parseNumber,
parseFloatNumber
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

const isNewExpressionWithParentheses = require('./is-new-expression-with-parentheses');
const {isDecimalInteger} = require('./numeric');
const {isDecimalIntegerNode} = require('./numeric');

/**
Check if parentheses should to be added to a `node` when it's used as an `object` of `MemberExpression`.
Expand All @@ -24,7 +24,7 @@ function shouldAddParenthesesToMemberExpressionObject(node, sourceCode) {
return !isNewExpressionWithParentheses(node, sourceCode);
case 'Literal': {
/* istanbul ignore next */
if (isDecimalInteger(node)) {
if (isDecimalIntegerNode(node)) {
return true;
}

Expand Down
Loading