From aa548ff3bd606d36ec5dc77e90cb6cd1d85bc741 Mon Sep 17 00:00:00 2001 From: Max Sysoev Date: Sun, 3 Feb 2019 12:16:32 +0300 Subject: [PATCH] =?UTF-8?q?=E2=AD=90=20feat(rules/number-literal-format):?= =?UTF-8?q?=20add=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/rules/numberLiteralFormatRule.ts | 67 ++++++++++++++----- test/rules/number-literal-format/test.ts.fix | 26 +++++++ test/rules/number-literal-format/test.ts.lint | 51 +++++++------- 3 files changed, 103 insertions(+), 41 deletions(-) create mode 100644 test/rules/number-literal-format/test.ts.fix diff --git a/src/rules/numberLiteralFormatRule.ts b/src/rules/numberLiteralFormatRule.ts index 5e48e394923..497fe8cfbe4 100644 --- a/src/rules/numberLiteralFormatRule.ts +++ b/src/rules/numberLiteralFormatRule.ts @@ -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.", @@ -63,6 +64,7 @@ function walk(ctx: Lint.WalkContext): 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; @@ -72,8 +74,14 @@ function walk(ctx: Lint.WalkContext): 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": @@ -82,35 +90,60 @@ function walk(ctx: Lint.WalkContext): 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); } } } diff --git a/test/rules/number-literal-format/test.ts.fix b/test/rules/number-literal-format/test.ts.fix new file mode 100644 index 00000000000..3049049e8d0 --- /dev/null +++ b/test/rules/number-literal-format/test.ts.fix @@ -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; \ No newline at end of file diff --git a/test/rules/number-literal-format/test.ts.lint b/test/rules/number-literal-format/test.ts.lint index 0b6b363e78d..08403e89824 100644 --- a/test/rules/number-literal-format/test.ts.lint +++ b/test/rules/number-literal-format/test.ts.lint @@ -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. \ No newline at end of file