Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

⭐ feat(rules/number-literal-format): add fix #4496

Merged
merged 5 commits into from
Feb 5, 2019
Merged
Show file tree
Hide file tree
Changes from 2 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
67 changes: 50 additions & 17 deletions src/rules/numberLiteralFormatRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class Rule extends Lint.Rules.AbstractRule {
/* tslint:disable:object-literal-sort-keys */
public static metadata: Lint.IRuleMetadata = {
ruleName: "number-literal-format",
hasFix: true,
description:
"Checks that decimal literals should begin with '0.' instead of just '.', and should not end with a trailing '0'.",
optionsDescription: "Not configurable.",
Expand Down Expand Up @@ -63,6 +64,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
function check(node: ts.NumericLiteral): void {
// Apparently the number literal '0.0' has a '.text' of '0', so use '.getText()' instead.
const text = node.getText(sourceFile);
const start = node.getStart();

if (text.length <= 1) {
return;
Expand All @@ -72,8 +74,14 @@ function walk(ctx: Lint.WalkContext<void>): void {
// Hex/octal/binary number can't have decimal point or exponent, so no other errors possible.
switch (text[1]) {
case "x":
if (!isUpperCase(text.slice(2))) {
ctx.addFailureAtNode(node, Rule.FAILURE_STRING_NOT_UPPERCASE);
// strip "0x"
const hexNumber = text.slice(2);
if (!isUpperCase(hexNumber)) {
ctx.addFailureAtNode(
node,
Rule.FAILURE_STRING_NOT_UPPERCASE,
Lint.Replacement.replaceNode(node, `0x${hexNumber.toUpperCase()}`),
);
}
return;
case "o":
Expand All @@ -82,35 +90,60 @@ function walk(ctx: Lint.WalkContext<void>): void {
case ".":
break;
default:
ctx.addFailureAtNode(node, Rule.FAILURE_STRING_LEADING_0);
ctx.addFailureAtNode(
node,
Rule.FAILURE_STRING_LEADING_0,
Lint.Replacement.deleteFromTo(start, start + /^0+/.exec(text)![0].length),
);
return;
}
}

const [num, exp] = text.split(/e/i);
const [num, exp = ""] = text.split(/e/i);
const [integer, float = ""] = num.split(".");
const match = /(\.)([1-9]*)(0+)/.exec(num);
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
const [dot = "", numbers = "", zeroes = ""] = Array.isArray(match) ? match.slice(1) : [];

if (exp !== undefined && (exp.startsWith("-0") || exp.startsWith("0"))) {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
ctx.addFailureAt(node.getEnd() - exp.length, exp.length, Rule.FAILURE_STRING_LEADING_0);
const expStart = start + num.length + 1; // position of exp part
const expNumberStart = /\D/.test(exp.charAt(0)) ? expStart + 1 : expStart; // do not remove "-" or "+"
ctx.addFailureAt(
node.getEnd() - exp.length,
exp.length,
Rule.FAILURE_STRING_LEADING_0,
Lint.Replacement.deleteFromTo(
expNumberStart,
expNumberStart + /0+/.exec(exp)![0].length,
),
);
}

if (!num.includes(".")) {
return;
}

if (num.startsWith(".")) {
fail(Rule.FAILURE_STRING_LEADING_DECIMAL);
}

if (num.endsWith(".")) {
fail(Rule.FAILURE_STRING_TRAILING_DECIMAL);
} else if (num.startsWith(".")) {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
// .1 -> 0.1
fail(Rule.FAILURE_STRING_LEADING_DECIMAL, Lint.Replacement.appendText(start, "0"));
} else if (num.endsWith(".")) {
// 1. -> 1
fail(
Rule.FAILURE_STRING_TRAILING_DECIMAL,
Lint.Replacement.deleteText(start + num.length - 1, 1),
);
}

// Allow '10', but not '1.0'
if (num.endsWith("0")) {
fail(Rule.FAILURE_STRING_TRAILING_0);
if (float.endsWith("0")) {
// 1.0 -> 1
const offset = numbers.length > 0 ? dot.length + numbers.length : 0;
const length = (numbers.length > 0 ? 0 : dot.length) + zeroes.length;
fail(
Rule.FAILURE_STRING_TRAILING_0,
Lint.Replacement.deleteText(start + integer.length + offset, length),
);
}

function fail(message: string): void {
ctx.addFailureAt(node.getStart(sourceFile), num.length, message);
function fail(message: string, fix?: Lint.Replacement | Lint.Replacement[]): void {
ctx.addFailureAt(node.getStart(sourceFile), num.length, message, fix);
}
}
}
26 changes: 26 additions & 0 deletions test/rules/number-literal-format/test.ts.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
0;
0.5;
10;
1.1e10;

-6000;

-777;

-0;

-0.9;

-0.2;

-0.5;

-123e3;

-145E4;

-1467e-8;

-189e10;

-0xDEADBEEF;
51 changes: 27 additions & 24 deletions test/rules/number-literal-format/test.ts.lint
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,41 @@
10;
1.1e10;

01;
~~ [leading-0]
-0000006000;
~~~~~~~~~~ [leading-0]

1.
~~ [trailing-decimal]
-777.;
~~~~ [trailing-decimal]

0.0;
~~~ [trailing-0]
0.50;
~~~~ [trailing-0]
-0.000;
~~~~~ [trailing-0]

.5;
~~ [leading-decimal]
-0.90000;
~~~~~~~ [trailing-0]

.50;
~~~ [trailing-0]
~~~ [leading-decimal]
-.2;
~~ [leading-decimal]

1e01;
~~ [leading-0]
1E01;
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
~~ [leading-0]
1e-01;
~~~ [leading-0]
1.0e10;
~~~ [trailing-0]
-.50000;
~~~~~~ [trailing-0]
~~~~~~ [leading-decimal]

0xDEAdBEEF;
~~~~~~~~~~ [uppercase]
-123e0003;
~~~~ [leading-0]

-145E0004;
~~~~ [leading-0]

-1467e-0008;
~~~~~ [leading-0]

-189.000e10;
~~~~~~~ [trailing-0]

-0xDEAdBEEF;
~~~~~~~~~~ [uppercase]
[leading-0]: Number literal should not have a leading '0'.
[trailing-0]: Number literal should not have a trailing '0'.
[trailing-decimal]: Number literal should not end in '.'.
[leading-decimal]: Number literal should begin with '0.' and not just '.'.
[uppercase]: Hexadecimal number literal should be uppercase.
[uppercase]: Hexadecimal number literal should be uppercase.