Skip to content

Commit

Permalink
⭐ feat(rules/number-literal-format): add fix
Browse files Browse the repository at this point in the history
  • Loading branch information
ColCh committed Feb 3, 2019
1 parent 694e59a commit aa548ff
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 41 deletions.
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);
const [dot = "", numbers = "", zeroes = ""] = Array.isArray(match) ? match.slice(1) : [];

if (exp !== undefined && (exp.startsWith("-0") || exp.startsWith("0"))) {
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(".")) {
// .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;
~~ [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.

0 comments on commit aa548ff

Please sign in to comment.