From 2b2f2d7f4702a7e518d51bef686a716895d4c44d Mon Sep 17 00:00:00 2001 From: Alexander T Date: Tue, 5 Nov 2019 18:19:58 +0200 Subject: [PATCH 1/7] fix(eslint-plugin): crash fixing readonly arrays to generic (#1172) --- .../eslint-plugin/src/rules/array-type.ts | 45 +++++++++++++------ .../tests/rules/array-type.test.ts | 30 +++++++++++++ 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/packages/eslint-plugin/src/rules/array-type.ts b/packages/eslint-plugin/src/rules/array-type.ts index b9977618c40c..0ed4266a240d 100644 --- a/packages/eslint-plugin/src/rules/array-type.ts +++ b/packages/eslint-plugin/src/rules/array-type.ts @@ -146,7 +146,8 @@ export default util.createRule({ return false; } - if (node.range[0] - prevToken.range[1] > 0) { + const nextToken = sourceCode.getTokenAfter(prevToken); + if (nextToken && sourceCode.isSpaceBetweenTokens(prevToken, nextToken)) { return false; } @@ -168,6 +169,21 @@ export default util.createRule({ return 'T'; } + /** + * @param node the node to be evaluated + */ + function getTypeOpNodeRange( + node: TSESTree.Node | null, + ): [number, number] | undefined { + if (!node) { + return undefined; + } + + const firstToken = sourceCode.getFirstToken(node)!; + const nextToken = sourceCode.getTokenAfter(firstToken)!; + return [firstToken.range[0], nextToken.range[0]]; + } + return { TSArrayType(node): void { if ( @@ -208,24 +224,27 @@ export default util.createRule({ type: getMessageType(node.elementType), }, fix(fixer) { - const startText = requireWhitespaceBefore(node); const toFix = [ fixer.replaceTextRange([node.range[1] - 2, node.range[1]], '>'), - fixer.insertTextBefore( - node, - `${startText ? ' ' : ''}${isReadonly ? 'Readonly' : ''}Array<`, - ), ]; - if (typeOpNode) { - // remove the readonly operator if it exists - toFix.unshift( - fixer.removeRange([ - typeOpNode.range[0], - typeOpNode.range[0] + 'readonly '.length, - ]), + const startText = requireWhitespaceBefore(node); + const typeOpNodeRange = getTypeOpNodeRange(typeOpNode); + + if (typeOpNodeRange) { + toFix.unshift(fixer.removeRange(typeOpNodeRange)); + } else { + toFix.push( + fixer.insertTextBefore(node, `${startText ? ' ' : ''}`), ); } + toFix.push( + fixer.insertTextBefore( + node, + `${isReadonly ? 'Readonly' : ''}Array<`, + ), + ); + if (node.elementType.type === AST_NODE_TYPES.TSParenthesizedType) { const first = sourceCode.getFirstToken(node.elementType); const last = sourceCode.getLastToken(node.elementType); diff --git a/packages/eslint-plugin/tests/rules/array-type.test.ts b/packages/eslint-plugin/tests/rules/array-type.test.ts index 32de7e32c892..86cb1940826b 100644 --- a/packages/eslint-plugin/tests/rules/array-type.test.ts +++ b/packages/eslint-plugin/tests/rules/array-type.test.ts @@ -1071,5 +1071,35 @@ class Foo extends Bar implements Baz { `let a: readonly Array[] = []`, 'array', ); + testOutput( + 'generic', + `type T = readonly(string)[]`, + `type T = ReadonlyArray`, + 'generic', + ); + testOutput( + 'generic', + `let a: readonly(readonly string[])[] = []`, + `let a: ReadonlyArray> = []`, + 'generic', + ); + testOutput( + 'generic', + `type T = readonly(readonly string[])[]`, + `type T = ReadonlyArray>`, + 'generic', + ); + testOutput( + 'generic', + `type T = readonly (readonly string[])[]`, + `type T = ReadonlyArray>`, + 'generic', + ); + testOutput( + 'generic', + `type T = readonly (readonly string[])[]`, + `type T = ReadonlyArray>`, + 'generic', + ); }); }); From f63c9c2c31c524e32df27c5895df7a48f40deb6e Mon Sep 17 00:00:00 2001 From: Daniel Ruf <827205+DanielRuf@users.noreply.github.com> Date: Tue, 5 Nov 2019 17:21:38 +0100 Subject: [PATCH 2/7] docs(eslint-plugin): nit make package name more visible (#1173) --- packages/eslint-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index baec8f2426e6..6d874a9c2b1c 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -10,7 +10,7 @@ ## Installation -Make sure you have TypeScript and @typescript-eslint/parser installed, then install the plugin: +Make sure you have TypeScript and `@typescript-eslint/parser` installed, then install the plugin: ```sh npm i @typescript-eslint/eslint-plugin --save-dev From c8fe51575d743ad317a09c18658c79d45059412b Mon Sep 17 00:00:00 2001 From: Georgii Dolzhykov Date: Wed, 6 Nov 2019 02:46:09 +0200 Subject: [PATCH 3/7] fix(typescript-estree): reduce bundle footprint of tsutils (#1177) --- packages/typescript-estree/src/convert-comments.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert-comments.ts b/packages/typescript-estree/src/convert-comments.ts index 9a7de3e112a7..a82d9941dbd3 100644 --- a/packages/typescript-estree/src/convert-comments.ts +++ b/packages/typescript-estree/src/convert-comments.ts @@ -1,5 +1,5 @@ import * as ts from 'typescript'; // leave this as * as ts so people using util package don't need syntheticDefaultImports -import { forEachComment } from 'tsutils'; +import { forEachComment } from 'tsutils/util/util'; import { getLocFor } from './node-utils'; import { TSESTree } from './ts-estree'; From 96d1cc3039e2c6402d60ed5443cd392232c23e0b Mon Sep 17 00:00:00 2001 From: IU Date: Sun, 10 Nov 2019 00:47:02 +0800 Subject: [PATCH 4/7] fix(typescript-estree): hash code to reduce update frequency (#1179) --- .../src/create-program/createWatchProgram.ts | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/typescript-estree/src/create-program/createWatchProgram.ts b/packages/typescript-estree/src/create-program/createWatchProgram.ts index beabcdd68886..9223107986af 100644 --- a/packages/typescript-estree/src/create-program/createWatchProgram.ts +++ b/packages/typescript-estree/src/create-program/createWatchProgram.ts @@ -45,7 +45,7 @@ const programFileListCache = new Map>(); */ const tsconfigLastModifiedTimestampCache = new Map(); -const parsedFilesSeen = new Set(); +const parsedFilesSeenHash = new Map(); /** * Clear all of the parser caches. @@ -55,7 +55,7 @@ function clearCaches(): void { knownWatchProgramMap.clear(); fileWatchCallbackTrackingMap.clear(); folderWatchCallbackTrackingMap.clear(); - parsedFilesSeen.clear(); + parsedFilesSeenHash.clear(); programFileListCache.clear(); tsconfigLastModifiedTimestampCache.clear(); } @@ -104,6 +104,19 @@ function diagnosticReporter(diagnostic: ts.Diagnostic): void { ); } +/** + * Hash content for compare content. + * @param content hashed contend + * @returns hashed result + */ +function createHash(content: string): string { + // No ts.sys in browser environments. + if (ts.sys && ts.sys.createHash) { + return ts.sys.createHash(content); + } + return content; +} + /** * Calculate project environments using options provided by consumer and paths from config * @param code The code being linted @@ -125,10 +138,10 @@ function getProgramsForProjects( currentLintOperationState.filePath = filePath; // Update file version if necessary - // TODO: only update when necessary, currently marks as changed on every lint const fileWatchCallbacks = fileWatchCallbackTrackingMap.get(filePath); + const codeHash = createHash(code); if ( - parsedFilesSeen.has(filePath) && + parsedFilesSeenHash.get(filePath) !== codeHash && fileWatchCallbacks && fileWatchCallbacks.size > 0 ) { @@ -232,11 +245,15 @@ function createWatchProgram( const oldReadFile = watchCompilerHost.readFile; watchCompilerHost.readFile = (filePathIn, encoding): string | undefined => { const filePath = getCanonicalFileName(filePathIn); - parsedFilesSeen.add(filePath); - return path.normalize(filePath) === + const fileContent = + path.normalize(filePath) === path.normalize(currentLintOperationState.filePath) - ? currentLintOperationState.code - : oldReadFile(filePath, encoding); + ? currentLintOperationState.code + : oldReadFile(filePath, encoding); + if (fileContent) { + parsedFilesSeenHash.set(filePath, createHash(fileContent)); + } + return fileContent; }; // ensure process reports error on failure instead of exiting process immediately From 026ceb991d12a7bf0f37cefee009bf2f408153ec Mon Sep 17 00:00:00 2001 From: Peter Potapov Date: Mon, 11 Nov 2019 20:07:53 +0300 Subject: [PATCH 5/7] feat(parser): handle optional chaining in scope analysis (#1169) Co-authored-by: nizarius Co-authored-by: Brad Zacher --- .../no-restricted-globals.test.ts | 67 +- .../tests/eslint-rules/no-undef.test.ts | 53 +- .../eslint-rules/no-use-before-define.test.ts | 85 ++ packages/parser/src/analyze-scope.ts | 23 + .../lib/__snapshots__/typescript.ts.snap | 1134 +++-------------- 5 files changed, 384 insertions(+), 978 deletions(-) create mode 100644 packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts diff --git a/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts index 40f3398fb7df..0cb6ec5d2919 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-restricted-globals.test.ts @@ -30,6 +30,71 @@ type Handler = (event: string) => any `, options: ['event'], }, + { + code: ` + const a = foo?.bar?.name + `, + }, + { + code: ` + const a = foo?.bar?.name ?? "foobar" + `, + }, + { + code: ` + const a = foo()?.bar; + `, + }, + { + code: ` + const a = foo()?.bar ?? true; + `, + }, + ], + invalid: [ + { + code: ` +function onClick() { + console.log(event); +} + +fdescribe("foo", function() { +}); + `, + options: ['event'], + errors: [ + { + message: "Unexpected use of 'event'.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, + { + code: ` +confirm("TEST"); + `, + options: ['confirm'], + errors: [ + { + message: "Unexpected use of 'confirm'.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, + { + code: ` +var a = confirm("TEST")?.a; + `, + options: ['confirm'], + errors: [ + { + message: "Unexpected use of 'confirm'.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, ], - invalid: [], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts index 84626c38201f..38d58b482fd3 100644 --- a/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts +++ b/packages/eslint-plugin/tests/eslint-rules/no-undef.test.ts @@ -68,6 +68,57 @@ function eachr(subject: Object | Array): typeof subject { ` function eachr(subject: Map): typeof subject; `, + ` + var a = { b: 3 }; + var c = a?.b; + `, + ` + var a = { b: { c: 3 } }; + var d = a?.["b"]?.c; + `, + ` + var a = { b: 3 }; + var c = { }; + var d = (a || c)?.b; + `, + ` + var a = { b: () => {} }; + a?.b(); + `, + ], + invalid: [ + { + code: 'a = 5;', + errors: [ + { + messageId: 'undef', + data: { + name: 'a', + }, + }, + ], + }, + { + code: 'a?.b = 5;', + errors: [ + { + messageId: 'undef', + data: { + name: 'a', + }, + }, + ], + }, + { + code: 'a()?.b = 5;', + errors: [ + { + messageId: 'undef', + data: { + name: 'a', + }, + }, + ], + }, ], - invalid: [], }); diff --git a/packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts b/packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts new file mode 100644 index 000000000000..4571cd04afbb --- /dev/null +++ b/packages/eslint-plugin/tests/eslint-rules/no-use-before-define.test.ts @@ -0,0 +1,85 @@ +import rule from 'eslint/lib/rules/no-use-before-define'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + ecmaFeatures: {}, + }, + parser: '@typescript-eslint/parser', +}); + +ruleTester.run('no-use-before-define', rule, { + valid: [ + ` +const updatedAt = data?.updatedAt; + `, + ` +function f() { + return function t() {}; +} +f()?.(); + `, + ` +var a = { b: 5 }; +alert(a?.b); + `, + ], + invalid: [ + { + code: ` +f(); +function f() {} + `, + errors: [ + { + message: "'f' was used before it was defined.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, + { + code: ` +alert(a); +var a = 10; + `, + errors: [ + { + message: "'a' was used before it was defined.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, + { + code: ` +f()?.(); +function f() { + return function t() {}; +} + `, + errors: [ + { + message: "'f' was used before it was defined.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, + { + code: ` +alert(a?.b); +var a = { b: 5 }; + `, + errors: [ + { + message: "'a' was used before it was defined.", + // the base rule doesn't use messageId + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any, + ], + }, + ], +}); diff --git a/packages/parser/src/analyze-scope.ts b/packages/parser/src/analyze-scope.ts index 1b98c064db09..27e90a46565d 100644 --- a/packages/parser/src/analyze-scope.ts +++ b/packages/parser/src/analyze-scope.ts @@ -344,6 +344,29 @@ class Referencer extends TSESLintScope.Referencer { node.arguments.forEach(this.visit, this); } + /** + * Visit optional member expression. + * @param node The OptionalMemberExpression node to visit. + */ + OptionalMemberExpression(node: TSESTree.OptionalMemberExpression): void { + this.visit(node.object); + if (node.computed) { + this.visit(node.property); + } + } + + /** + * Visit optional call expression. + * @param node The OptionalMemberExpression node to visit. + */ + OptionalCallExpression(node: TSESTree.OptionalCallExpression): void { + this.visitTypeParameters(node); + + this.visit(node.callee); + + node.arguments.forEach(this.visit, this); + } + /** * Define the variable of this function declaration only once. * Because to avoid confusion of `no-redeclare` rule by overloading. diff --git a/packages/parser/tests/lib/__snapshots__/typescript.ts.snap b/packages/parser/tests/lib/__snapshots__/typescript.ts.snap index 3d94d81e424b..39f3da70df9c 100644 --- a/packages/parser/tests/lib/__snapshots__/typescript.ts.snap +++ b/packages/parser/tests/lib/__snapshots__/typescript.ts.snap @@ -22070,7 +22070,7 @@ Object { exports[`typescript fixtures/basics/optional-chain.src 1`] = ` Object { - "$id": 18, + "$id": 10, "block": Object { "range": Array [ 0, @@ -22080,7 +22080,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 17, + "$id": 9, "block": Object { "range": Array [ 0, @@ -22090,7 +22090,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 16, + "$id": 8, "block": Object { "range": Array [ 0, @@ -22105,7 +22105,7 @@ Object { Object { "$id": 3, "from": Object { - "$ref": 16, + "$ref": 8, }, "identifier": Object { "name": "one", @@ -22124,24 +22124,7 @@ Object { Object { "$id": 4, "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "two", - "range": Array [ - 45, - 48, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 5, - "from": Object { - "$ref": 16, + "$ref": 8, }, "identifier": Object { "name": "one", @@ -22158,43 +22141,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 6, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "two", - "range": Array [ - 57, - 60, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 7, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 61, - 66, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 8, + "$id": 5, "from": Object { - "$ref": 16, + "$ref": 8, }, "identifier": Object { "name": "one", @@ -22211,26 +22160,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 9, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 79, - 84, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 10, + "$id": 6, "from": Object { - "$ref": 16, + "$ref": 8, }, "identifier": Object { "name": "one", @@ -22247,43 +22179,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 11, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 97, - 102, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 12, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "four", - "range": Array [ - 103, - 107, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 13, + "$id": 7, "from": Object { - "$ref": 16, + "$ref": 8, }, "identifier": Object { "name": "one", @@ -22299,70 +22197,11 @@ Object { }, "writeExpr": undefined, }, - Object { - "$id": 14, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 120, - 125, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 15, - "from": Object { - "$ref": 16, - }, - "identifier": Object { - "name": "four", - "range": Array [ - 127, - 131, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - ], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 7, - }, - Object { - "$ref": 9, - }, - Object { - "$ref": 11, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 14, - }, - Object { - "$ref": 15, - }, ], + "throughReferences": Array [], "type": "function", "upperScope": Object { - "$ref": 17, + "$ref": 9, }, "variableMap": Object { "arguments": Object { @@ -22373,7 +22212,7 @@ Object { }, }, "variableScope": Object { - "$ref": 16, + "$ref": 8, }, "variables": Array [ Object { @@ -22384,7 +22223,7 @@ Object { "name": "arguments", "references": Array [], "scope": Object { - "$ref": 16, + "$ref": 8, }, }, Object { @@ -22427,20 +22266,20 @@ Object { "$ref": 3, }, Object { - "$ref": 5, + "$ref": 4, }, Object { - "$ref": 8, + "$ref": 5, }, Object { - "$ref": 10, + "$ref": 6, }, Object { - "$ref": 13, + "$ref": 7, }, ], "scope": Object { - "$ref": 16, + "$ref": 8, }, }, ], @@ -22449,35 +22288,10 @@ Object { "functionExpressionScope": false, "isStrict": true, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 7, - }, - Object { - "$ref": 9, - }, - Object { - "$ref": 11, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 14, - }, - Object { - "$ref": 15, - }, - ], + "throughReferences": Array [], "type": "module", "upperScope": Object { - "$ref": 18, + "$ref": 10, }, "variableMap": Object { "processOptional": Object { @@ -22485,7 +22299,7 @@ Object { }, }, "variableScope": Object { - "$ref": 17, + "$ref": 9, }, "variables": Array [ Object { @@ -22525,7 +22339,7 @@ Object { "name": "processOptional", "references": Array [], "scope": Object { - "$ref": 17, + "$ref": 9, }, }, ], @@ -22534,37 +22348,12 @@ Object { "functionExpressionScope": false, "isStrict": false, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 7, - }, - Object { - "$ref": 9, - }, - Object { - "$ref": 11, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 14, - }, - Object { - "$ref": 15, - }, - ], + "throughReferences": Array [], "type": "global", "upperScope": null, "variableMap": Object {}, "variableScope": Object { - "$ref": 18, + "$ref": 10, }, "variables": Array [], } @@ -22572,7 +22361,7 @@ Object { exports[`typescript fixtures/basics/optional-chain-call.src 1`] = ` Object { - "$id": 23, + "$id": 14, "block": Object { "range": Array [ 0, @@ -22582,7 +22371,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 22, + "$id": 13, "block": Object { "range": Array [ 0, @@ -22592,7 +22381,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 21, + "$id": 12, "block": Object { "range": Array [ 0, @@ -22607,7 +22396,7 @@ Object { Object { "$id": 3, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -22626,30 +22415,32 @@ Object { Object { "$id": 4, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { - "name": "fn", + "name": "one", "range": Array [ - 49, - 51, + 57, + 60, ], "type": "Identifier", }, "kind": "r", - "resolved": null, + "resolved": Object { + "$ref": 2, + }, "writeExpr": undefined, }, Object { "$id": 5, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { "name": "one", "range": Array [ - 57, - 60, + 74, + 77, ], "type": "Identifier", }, @@ -22662,133 +22453,29 @@ Object { Object { "$id": 6, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { - "name": "two", + "name": "one", "range": Array [ - 62, - 65, + 91, + 94, ], "type": "Identifier", }, "kind": "r", - "resolved": null, + "resolved": Object { + "$ref": 2, + }, "writeExpr": undefined, }, Object { "$id": 7, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { - "name": "fn", - "range": Array [ - 66, - 68, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 8, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "one", - "range": Array [ - 74, - 77, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": Object { - "$ref": 2, - }, - "writeExpr": undefined, - }, - Object { - "$id": 9, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "fn", - "range": Array [ - 83, - 85, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 10, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "one", - "range": Array [ - 91, - 94, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": Object { - "$ref": 2, - }, - "writeExpr": undefined, - }, - Object { - "$id": 11, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 100, - 105, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 12, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "fn", - "range": Array [ - 106, - 108, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 13, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "one", + "name": "one", "range": Array [ 114, 117, @@ -22802,43 +22489,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 14, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 123, - 128, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 15, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "fn", - "range": Array [ - 130, - 132, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 16, + "$id": 8, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -22855,9 +22508,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 17, + "$id": 9, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -22874,9 +22527,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 18, + "$id": 10, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -22893,9 +22546,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 19, + "$id": 11, "from": Object { - "$ref": 21, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -22911,56 +22564,11 @@ Object { }, "writeExpr": undefined, }, - Object { - "$id": 20, - "from": Object { - "$ref": 21, - }, - "identifier": Object { - "name": "two", - "range": Array [ - 187, - 190, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - ], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 7, - }, - Object { - "$ref": 9, - }, - Object { - "$ref": 11, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 14, - }, - Object { - "$ref": 15, - }, - Object { - "$ref": 20, - }, ], + "throughReferences": Array [], "type": "function", "upperScope": Object { - "$ref": 22, + "$ref": 13, }, "variableMap": Object { "arguments": Object { @@ -22971,7 +22579,7 @@ Object { }, }, "variableScope": Object { - "$ref": 21, + "$ref": 12, }, "variables": Array [ Object { @@ -22982,7 +22590,7 @@ Object { "name": "arguments", "references": Array [], "scope": Object { - "$ref": 21, + "$ref": 12, }, }, Object { @@ -23025,32 +22633,32 @@ Object { "$ref": 3, }, Object { - "$ref": 5, + "$ref": 4, }, Object { - "$ref": 8, + "$ref": 5, }, Object { - "$ref": 10, + "$ref": 6, }, Object { - "$ref": 13, + "$ref": 7, }, Object { - "$ref": 16, + "$ref": 8, }, Object { - "$ref": 17, + "$ref": 9, }, Object { - "$ref": 18, + "$ref": 10, }, Object { - "$ref": 19, + "$ref": 11, }, ], "scope": Object { - "$ref": 21, + "$ref": 12, }, }, ], @@ -23059,38 +22667,10 @@ Object { "functionExpressionScope": false, "isStrict": true, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 7, - }, - Object { - "$ref": 9, - }, - Object { - "$ref": 11, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 14, - }, - Object { - "$ref": 15, - }, - Object { - "$ref": 20, - }, - ], + "throughReferences": Array [], "type": "module", "upperScope": Object { - "$ref": 23, + "$ref": 14, }, "variableMap": Object { "processOptionalCall": Object { @@ -23098,7 +22678,7 @@ Object { }, }, "variableScope": Object { - "$ref": 22, + "$ref": 13, }, "variables": Array [ Object { @@ -23138,7 +22718,7 @@ Object { "name": "processOptionalCall", "references": Array [], "scope": Object { - "$ref": 22, + "$ref": 13, }, }, ], @@ -23147,40 +22727,12 @@ Object { "functionExpressionScope": false, "isStrict": false, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 7, - }, - Object { - "$ref": 9, - }, - Object { - "$ref": 11, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 14, - }, - Object { - "$ref": 15, - }, - Object { - "$ref": 20, - }, - ], + "throughReferences": Array [], "type": "global", "upperScope": null, "variableMap": Object {}, "variableScope": Object { - "$ref": 23, + "$ref": 14, }, "variables": Array [], } @@ -23188,7 +22740,7 @@ Object { exports[`typescript fixtures/basics/optional-chain-call-with-parens.src 1`] = ` Object { - "$id": 20, + "$id": 14, "block": Object { "range": Array [ 0, @@ -23198,7 +22750,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 19, + "$id": 13, "block": Object { "range": Array [ 0, @@ -23208,7 +22760,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 18, + "$id": 12, "block": Object { "range": Array [ 0, @@ -23223,7 +22775,7 @@ Object { Object { "$id": 3, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23242,24 +22794,7 @@ Object { Object { "$id": 4, "from": Object { - "$ref": 18, - }, - "identifier": Object { - "name": "fn", - "range": Array [ - 56, - 58, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 5, - "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23276,26 +22811,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 6, - "from": Object { - "$ref": 18, - }, - "identifier": Object { - "name": "two", - "range": Array [ - 71, - 74, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 7, + "$id": 5, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23312,26 +22830,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 8, - "from": Object { - "$ref": 18, - }, - "identifier": Object { - "name": "fn", - "range": Array [ - 94, - 96, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 9, + "$id": 6, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23348,26 +22849,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 10, - "from": Object { - "$ref": 18, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 113, - 118, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 11, + "$id": 7, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23384,43 +22868,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 12, - "from": Object { - "$ref": 18, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 138, - 143, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 13, - "from": Object { - "$ref": 18, - }, - "identifier": Object { - "name": "fn", - "range": Array [ - 145, - 147, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 14, + "$id": 8, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23437,9 +22887,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 15, + "$id": 9, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23456,9 +22906,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 16, + "$id": 10, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23475,9 +22925,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 17, + "$id": 11, "from": Object { - "$ref": 18, + "$ref": 12, }, "identifier": Object { "name": "one", @@ -23494,29 +22944,10 @@ Object { "writeExpr": undefined, }, ], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 8, - }, - Object { - "$ref": 10, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 13, - }, - ], + "throughReferences": Array [], "type": "function", "upperScope": Object { - "$ref": 19, + "$ref": 13, }, "variableMap": Object { "arguments": Object { @@ -23527,7 +22958,7 @@ Object { }, }, "variableScope": Object { - "$ref": 18, + "$ref": 12, }, "variables": Array [ Object { @@ -23538,7 +22969,7 @@ Object { "name": "arguments", "references": Array [], "scope": Object { - "$ref": 18, + "$ref": 12, }, }, Object { @@ -23581,32 +23012,32 @@ Object { "$ref": 3, }, Object { - "$ref": 5, + "$ref": 4, }, Object { - "$ref": 7, + "$ref": 5, }, Object { - "$ref": 9, + "$ref": 6, }, Object { - "$ref": 11, + "$ref": 7, }, Object { - "$ref": 14, + "$ref": 8, }, Object { - "$ref": 15, + "$ref": 9, }, Object { - "$ref": 16, + "$ref": 10, }, Object { - "$ref": 17, + "$ref": 11, }, ], "scope": Object { - "$ref": 18, + "$ref": 12, }, }, ], @@ -23615,29 +23046,10 @@ Object { "functionExpressionScope": false, "isStrict": true, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 8, - }, - Object { - "$ref": 10, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 13, - }, - ], + "throughReferences": Array [], "type": "module", "upperScope": Object { - "$ref": 20, + "$ref": 14, }, "variableMap": Object { "processOptionalCallParens": Object { @@ -23645,7 +23057,7 @@ Object { }, }, "variableScope": Object { - "$ref": 19, + "$ref": 13, }, "variables": Array [ Object { @@ -23676,49 +23088,30 @@ Object { Object { "name": "processOptionalCallParens", "range": Array [ - 9, - 34, - ], - "type": "Identifier", - }, - ], - "name": "processOptionalCallParens", - "references": Array [], - "scope": Object { - "$ref": 19, - }, - }, - ], - }, - ], - "functionExpressionScope": false, - "isStrict": false, - "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 8, - }, - Object { - "$ref": 10, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 13, + 9, + 34, + ], + "type": "Identifier", + }, + ], + "name": "processOptionalCallParens", + "references": Array [], + "scope": Object { + "$ref": 13, + }, + }, + ], }, ], + "functionExpressionScope": false, + "isStrict": false, + "references": Array [], + "throughReferences": Array [], "type": "global", "upperScope": null, "variableMap": Object {}, "variableScope": Object { - "$ref": 20, + "$ref": 14, }, "variables": Array [], } @@ -24352,7 +23745,7 @@ Object { exports[`typescript fixtures/basics/optional-chain-with-parens.src 1`] = ` Object { - "$id": 19, + "$id": 11, "block": Object { "range": Array [ 0, @@ -24362,7 +23755,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 18, + "$id": 10, "block": Object { "range": Array [ 0, @@ -24372,7 +23765,7 @@ Object { }, "childScopes": Array [ Object { - "$id": 17, + "$id": 9, "block": Object { "range": Array [ 0, @@ -24387,7 +23780,7 @@ Object { Object { "$id": 3, "from": Object { - "$ref": 17, + "$ref": 9, }, "identifier": Object { "name": "one", @@ -24406,24 +23799,7 @@ Object { Object { "$id": 4, "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "two", - "range": Array [ - 52, - 55, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 5, - "from": Object { - "$ref": 17, + "$ref": 9, }, "identifier": Object { "name": "one", @@ -24440,26 +23816,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 6, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "two", - "range": Array [ - 66, - 69, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 7, + "$id": 5, "from": Object { - "$ref": 17, + "$ref": 9, }, "identifier": Object { "name": "one", @@ -24476,26 +23835,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 8, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 90, - 95, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 9, + "$id": 6, "from": Object { - "$ref": 17, + "$ref": 9, }, "identifier": Object { "name": "one", @@ -24512,26 +23854,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 10, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 110, - 115, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 11, + "$id": 7, "from": Object { - "$ref": 17, + "$ref": 9, }, "identifier": Object { "name": "one", @@ -24548,43 +23873,9 @@ Object { "writeExpr": undefined, }, Object { - "$id": 12, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 135, - 140, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 13, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "four", - "range": Array [ - 142, - 146, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 14, + "$id": 8, "from": Object { - "$ref": 17, + "$ref": 9, }, "identifier": Object { "name": "one", @@ -24600,70 +23891,11 @@ Object { }, "writeExpr": undefined, }, - Object { - "$id": 15, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "three", - "range": Array [ - 161, - 166, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - Object { - "$id": 16, - "from": Object { - "$ref": 17, - }, - "identifier": Object { - "name": "four", - "range": Array [ - 168, - 172, - ], - "type": "Identifier", - }, - "kind": "r", - "resolved": null, - "writeExpr": undefined, - }, - ], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 8, - }, - Object { - "$ref": 10, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 13, - }, - Object { - "$ref": 15, - }, - Object { - "$ref": 16, - }, ], + "throughReferences": Array [], "type": "function", "upperScope": Object { - "$ref": 18, + "$ref": 10, }, "variableMap": Object { "arguments": Object { @@ -24674,7 +23906,7 @@ Object { }, }, "variableScope": Object { - "$ref": 17, + "$ref": 9, }, "variables": Array [ Object { @@ -24685,7 +23917,7 @@ Object { "name": "arguments", "references": Array [], "scope": Object { - "$ref": 17, + "$ref": 9, }, }, Object { @@ -24728,23 +23960,23 @@ Object { "$ref": 3, }, Object { - "$ref": 5, + "$ref": 4, }, Object { - "$ref": 7, + "$ref": 5, }, Object { - "$ref": 9, + "$ref": 6, }, Object { - "$ref": 11, + "$ref": 7, }, Object { - "$ref": 14, + "$ref": 8, }, ], "scope": Object { - "$ref": 17, + "$ref": 9, }, }, ], @@ -24753,35 +23985,10 @@ Object { "functionExpressionScope": false, "isStrict": true, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 8, - }, - Object { - "$ref": 10, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 13, - }, - Object { - "$ref": 15, - }, - Object { - "$ref": 16, - }, - ], + "throughReferences": Array [], "type": "module", "upperScope": Object { - "$ref": 19, + "$ref": 11, }, "variableMap": Object { "processOptionalParens": Object { @@ -24789,7 +23996,7 @@ Object { }, }, "variableScope": Object { - "$ref": 18, + "$ref": 10, }, "variables": Array [ Object { @@ -24829,7 +24036,7 @@ Object { "name": "processOptionalParens", "references": Array [], "scope": Object { - "$ref": 18, + "$ref": 10, }, }, ], @@ -24838,37 +24045,12 @@ Object { "functionExpressionScope": false, "isStrict": false, "references": Array [], - "throughReferences": Array [ - Object { - "$ref": 4, - }, - Object { - "$ref": 6, - }, - Object { - "$ref": 8, - }, - Object { - "$ref": 10, - }, - Object { - "$ref": 12, - }, - Object { - "$ref": 13, - }, - Object { - "$ref": 15, - }, - Object { - "$ref": 16, - }, - ], + "throughReferences": Array [], "type": "global", "upperScope": null, "variableMap": Object {}, "variableScope": Object { - "$ref": 19, + "$ref": 11, }, "variables": Array [], } From 57d63b7488f6b21f0f2d38aa27e14146ea6d2ed0 Mon Sep 17 00:00:00 2001 From: Peter Potapov Date: Mon, 11 Nov 2019 20:36:52 +0300 Subject: [PATCH 6/7] feat(eslint-plugin): [no-unused-expressions] extend for optional chaining (#1175) --- packages/eslint-plugin/README.md | 5 +- .../docs/rules/no-unused-expressions.md | 25 +++ packages/eslint-plugin/src/configs/all.json | 1 + packages/eslint-plugin/src/rules/index.ts | 2 + .../src/rules/no-unused-expressions.ts | 33 ++++ .../tests/rules/no-unused-expressions.test.ts | 180 ++++++++++++++++++ .../eslint-plugin/typings/eslint-rules.d.ts | 20 ++ 7 files changed, 264 insertions(+), 2 deletions(-) create mode 100644 packages/eslint-plugin/docs/rules/no-unused-expressions.md create mode 100644 packages/eslint-plugin/src/rules/no-unused-expressions.ts create mode 100644 packages/eslint-plugin/tests/rules/no-unused-expressions.test.ts diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 6d874a9c2b1c..b616245454e6 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -145,7 +145,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallows awaiting a value that is not a Thenable | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/ban-ts-ignore`](./docs/rules/ban-ts-ignore.md) | Bans “// @ts-ignore” comments from being used | :heavy_check_mark: | | | | [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Bans specific types from being used | :heavy_check_mark: | :wrench: | | -| [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | +| [`@typescript-eslint/brace-style`](./docs/rules/brace-style.md) | Enforce consistent brace style for blocks | | :wrench: | | | [`@typescript-eslint/camelcase`](./docs/rules/camelcase.md) | Enforce camelCase naming convention | :heavy_check_mark: | | | | [`@typescript-eslint/class-name-casing`](./docs/rules/class-name-casing.md) | Require PascalCased class and interface names | :heavy_check_mark: | | | | [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions. | :heavy_check_mark: | | | @@ -181,6 +181,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/no-unnecessary-qualifier`](./docs/rules/no-unnecessary-qualifier.md) | Warns when a namespace qualifier is unnecessary | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-arguments`](./docs/rules/no-unnecessary-type-arguments.md) | Warns if an explicitly specified type argument is the default for that type parameter | | :wrench: | :thought_balloon: | | [`@typescript-eslint/no-unnecessary-type-assertion`](./docs/rules/no-unnecessary-type-assertion.md) | Warns if a type assertion does not change the type of an expression | :heavy_check_mark: | :wrench: | :thought_balloon: | +| [`@typescript-eslint/no-unused-expressions`](./docs/rules/no-unused-expressions.md) | Disallow unused expressions | | | | | [`@typescript-eslint/no-unused-vars`](./docs/rules/no-unused-vars.md) | Disallow unused variables | :heavy_check_mark: | | | | [`@typescript-eslint/no-use-before-define`](./docs/rules/no-use-before-define.md) | Disallow the use of variables before they are defined | :heavy_check_mark: | | | | [`@typescript-eslint/no-useless-constructor`](./docs/rules/no-useless-constructor.md) | Disallow unnecessary constructors | | | | @@ -193,7 +194,7 @@ Then you should add `airbnb` (or `airbnb-base`) to your `extends` section of `.e | [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Prefer RegExp#exec() over String#match() if no global flag is provided | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | :heavy_check_mark: | :wrench: | :thought_balloon: | | [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: | -| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | +| [`@typescript-eslint/quotes`](./docs/rules/quotes.md) | Enforce the consistent use of either backticks, double, or single quotes | | :wrench: | | | [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Enforce giving `compare` argument to `Array#sort` | | | :thought_balloon: | | [`@typescript-eslint/require-await`](./docs/rules/require-await.md) | Disallow async functions which have no `await` expression | :heavy_check_mark: | | :thought_balloon: | | [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | | | :thought_balloon: | diff --git a/packages/eslint-plugin/docs/rules/no-unused-expressions.md b/packages/eslint-plugin/docs/rules/no-unused-expressions.md new file mode 100644 index 000000000000..7da998ab2c6c --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-unused-expressions.md @@ -0,0 +1,25 @@ +# require or disallow semicolons instead of ASI (semi) + +This rule aims to eliminate unused expressions which have no effect on the state of the program. + +## Rule Details + +This rule extends the base [eslint/no-unused-expressions](https://eslint.org/docs/rules/no-unused-expressions) rule. +It supports all options and features of the base rule. +This version adds support for numerous typescript features. + +## How to use + +```cjson +{ + // note you must disable the base rule as it can report incorrect errors + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": ["error"] +} +``` + +## Options + +See [eslint/no-unused-expressions options](https://eslint.org/docs/rules/no-unused-expressions#options). + +Taken with ❤️ [from ESLint core](https://github.com/eslint/eslint/blob/master/docs/rules/no-unused-expressions.md) diff --git a/packages/eslint-plugin/src/configs/all.json b/packages/eslint-plugin/src/configs/all.json index 377f4b58f0e4..395c1af592ed 100644 --- a/packages/eslint-plugin/src/configs/all.json +++ b/packages/eslint-plugin/src/configs/all.json @@ -50,6 +50,7 @@ "@typescript-eslint/no-unnecessary-qualifier": "error", "@typescript-eslint/no-unnecessary-type-arguments": "error", "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-unused-expressions": "error", "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": "error", "no-use-before-define": "off", diff --git a/packages/eslint-plugin/src/rules/index.ts b/packages/eslint-plugin/src/rules/index.ts index 5302abd05de0..4aa8beea1f63 100644 --- a/packages/eslint-plugin/src/rules/index.ts +++ b/packages/eslint-plugin/src/rules/index.ts @@ -39,6 +39,7 @@ import noUnnecessaryCondition from './no-unnecessary-condition'; import noUnnecessaryQualifier from './no-unnecessary-qualifier'; import noUnnecessaryTypeAssertion from './no-unnecessary-type-assertion'; import noUnusedVars from './no-unused-vars'; +import noUnusedExpressions from './no-unused-expressions'; import noUseBeforeDefine from './no-use-before-define'; import noUselessConstructor from './no-useless-constructor'; import noVarRequires from './no-var-requires'; @@ -106,6 +107,7 @@ export default { 'no-unnecessary-type-arguments': useDefaultTypeParameter, 'no-unnecessary-type-assertion': noUnnecessaryTypeAssertion, 'no-unused-vars': noUnusedVars, + 'no-unused-expressions': noUnusedExpressions, 'no-use-before-define': noUseBeforeDefine, 'no-useless-constructor': noUselessConstructor, 'no-var-requires': noVarRequires, diff --git a/packages/eslint-plugin/src/rules/no-unused-expressions.ts b/packages/eslint-plugin/src/rules/no-unused-expressions.ts new file mode 100644 index 000000000000..f7d86f50c1c7 --- /dev/null +++ b/packages/eslint-plugin/src/rules/no-unused-expressions.ts @@ -0,0 +1,33 @@ +import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import baseRule from 'eslint/lib/rules/no-unused-expressions'; +import * as util from '../util'; + +export default util.createRule({ + name: 'no-unused-expressions', + meta: { + type: 'suggestion', + docs: { + description: 'Disallow unused expressions', + category: 'Best Practices', + recommended: false, + }, + schema: baseRule.meta.schema, + messages: { + expected: + 'Expected an assignment or function call and instead saw an expression.', + }, + }, + defaultOptions: [], + create(context) { + const rules = baseRule.create(context); + + return { + ExpressionStatement(node): void { + if (node.expression.type === AST_NODE_TYPES.OptionalCallExpression) { + return; + } + rules.ExpressionStatement(node); + }, + }; + }, +}); diff --git a/packages/eslint-plugin/tests/rules/no-unused-expressions.test.ts b/packages/eslint-plugin/tests/rules/no-unused-expressions.test.ts new file mode 100644 index 000000000000..46653b30c663 --- /dev/null +++ b/packages/eslint-plugin/tests/rules/no-unused-expressions.test.ts @@ -0,0 +1,180 @@ +import rule from '../../src/rules/no-unused-expressions'; +import { RuleTester } from '../RuleTester'; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 6, + sourceType: 'module', + ecmaFeatures: {}, + }, + parser: '@typescript-eslint/parser', +}); + +// the base rule doesn't have messageIds +function error( + messages: { line: number; column: number }[], + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any[] { + return messages.map(message => ({ + ...message, + message: + 'Expected an assignment or function call and instead saw an expression.', + })); +} + +ruleTester.run('no-unused-expressions', rule, { + valid: [ + ` + test.age?.toLocaleString(); + `, + ` + let a = (a?.b).c; + `, + ` + let b = a?.['b']; + `, + ` + let c = one[2]?.[3][4]; + `, + ` + one[2]?.[3][4]?.(); + `, + ` + a?.['b']?.c(); + `, + ], + invalid: [ + { + code: ` +if(0) 0 + `, + errors: error([ + { + line: 2, + column: 7, + }, + ]), + }, + { + code: ` +f(0), {} + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +a, b() + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +a() && function namedFunctionInExpressionContext () {f();} + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +a?.b + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +(a?.b).c + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +a?.['b'] + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +(a?.['b']).c + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +a?.b()?.c + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +(a?.b()).c + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +one[2]?.[3][4]; + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + { + code: ` +one.two?.three.four; + `, + errors: error([ + { + line: 2, + column: 1, + }, + ]), + }, + ], +}); diff --git a/packages/eslint-plugin/typings/eslint-rules.d.ts b/packages/eslint-plugin/typings/eslint-rules.d.ts index 120b11433cb9..c21b235be448 100644 --- a/packages/eslint-plugin/typings/eslint-rules.d.ts +++ b/packages/eslint-plugin/typings/eslint-rules.d.ts @@ -304,6 +304,26 @@ declare module 'eslint/lib/rules/no-unused-vars' { export = rule; } +declare module 'eslint/lib/rules/no-unused-expressions' { + import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; + + const rule: TSESLint.RuleModule< + 'expected', + ( + | 'all' + | 'local' + | { + allowShortCircuit?: boolean; + allowTernary?: boolean; + allowTaggedTemplates?: boolean; + })[], + { + ExpressionStatement(node: TSESTree.ExpressionStatement): void; + } + >; + export = rule; +} + declare module 'eslint/lib/rules/no-use-before-define' { import { TSESLint, TSESTree } from '@typescript-eslint/experimental-utils'; From 62b5a942f40472135d1b246f960ff1aed77f7307 Mon Sep 17 00:00:00 2001 From: James Henry Date: Mon, 11 Nov 2019 18:01:31 +0000 Subject: [PATCH 7/7] chore: publish v2.7.0 --- CHANGELOG.md | 19 +++++++++++++++++++ lerna.json | 2 +- packages/eslint-plugin-tslint/CHANGELOG.md | 8 ++++++++ packages/eslint-plugin-tslint/package.json | 6 +++--- packages/eslint-plugin/CHANGELOG.md | 17 +++++++++++++++++ packages/eslint-plugin/package.json | 4 ++-- packages/experimental-utils/CHANGELOG.md | 8 ++++++++ packages/experimental-utils/package.json | 4 ++-- packages/parser/CHANGELOG.md | 11 +++++++++++ packages/parser/package.json | 8 ++++---- packages/shared-fixtures/CHANGELOG.md | 8 ++++++++ packages/shared-fixtures/package.json | 2 +- packages/typescript-estree/CHANGELOG.md | 12 ++++++++++++ packages/typescript-estree/package.json | 4 ++-- 14 files changed, 98 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e89159a5e78..f800e619f2dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,25 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + + +### Bug Fixes + +* **eslint-plugin:** crash fixing readonly arrays to generic ([#1172](https://github.com/typescript-eslint/typescript-eslint/issues/1172)) ([2b2f2d7](https://github.com/typescript-eslint/typescript-eslint/commit/2b2f2d7)) +* **typescript-estree:** hash code to reduce update frequency ([#1179](https://github.com/typescript-eslint/typescript-eslint/issues/1179)) ([96d1cc3](https://github.com/typescript-eslint/typescript-eslint/commit/96d1cc3)) +* **typescript-estree:** reduce bundle footprint of tsutils ([#1177](https://github.com/typescript-eslint/typescript-eslint/issues/1177)) ([c8fe515](https://github.com/typescript-eslint/typescript-eslint/commit/c8fe515)) + + +### Features + +* **eslint-plugin:** [no-unused-expressions] extend for optional chaining ([#1175](https://github.com/typescript-eslint/typescript-eslint/issues/1175)) ([57d63b7](https://github.com/typescript-eslint/typescript-eslint/commit/57d63b7)) +* **parser:** handle optional chaining in scope analysis ([#1169](https://github.com/typescript-eslint/typescript-eslint/issues/1169)) ([026ceb9](https://github.com/typescript-eslint/typescript-eslint/commit/026ceb9)) + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) diff --git a/lerna.json b/lerna.json index f467fc228a1e..991899f51336 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.6.1", + "version": "2.7.0", "npmClient": "yarn", "useWorkspaces": true, "stream": true diff --git a/packages/eslint-plugin-tslint/CHANGELOG.md b/packages/eslint-plugin-tslint/CHANGELOG.md index bfbc5e387413..9613287c0606 100644 --- a/packages/eslint-plugin-tslint/CHANGELOG.md +++ b/packages/eslint-plugin-tslint/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + +**Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) **Note:** Version bump only for package @typescript-eslint/eslint-plugin-tslint diff --git a/packages/eslint-plugin-tslint/package.json b/packages/eslint-plugin-tslint/package.json index c0432f50ed1b..b23071880ad8 100644 --- a/packages/eslint-plugin-tslint/package.json +++ b/packages/eslint-plugin-tslint/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin-tslint", - "version": "2.6.1", + "version": "2.7.0", "main": "dist/index.js", "typings": "src/index.ts", "description": "TSLint wrapper plugin for ESLint", @@ -31,7 +31,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.6.1", + "@typescript-eslint/experimental-utils": "2.7.0", "lodash.memoize": "^4.1.2" }, "peerDependencies": { @@ -41,6 +41,6 @@ }, "devDependencies": { "@types/lodash.memoize": "^4.1.4", - "@typescript-eslint/parser": "2.6.1" + "@typescript-eslint/parser": "2.7.0" } } diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index bf30ed088fc7..1759b88a3c13 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -3,6 +3,23 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + + +### Bug Fixes + +* **eslint-plugin:** crash fixing readonly arrays to generic ([#1172](https://github.com/typescript-eslint/typescript-eslint/issues/1172)) ([2b2f2d7](https://github.com/typescript-eslint/typescript-eslint/commit/2b2f2d7)) + + +### Features + +* **eslint-plugin:** [no-unused-expressions] extend for optional chaining ([#1175](https://github.com/typescript-eslint/typescript-eslint/issues/1175)) ([57d63b7](https://github.com/typescript-eslint/typescript-eslint/commit/57d63b7)) +* **parser:** handle optional chaining in scope analysis ([#1169](https://github.com/typescript-eslint/typescript-eslint/issues/1169)) ([026ceb9](https://github.com/typescript-eslint/typescript-eslint/commit/026ceb9)) + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) **Note:** Version bump only for package @typescript-eslint/eslint-plugin diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 75b4a0d903f6..b315da580591 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/eslint-plugin", - "version": "2.6.1", + "version": "2.7.0", "description": "TypeScript plugin for ESLint", "keywords": [ "eslint", @@ -40,7 +40,7 @@ "typecheck": "tsc -p tsconfig.json --noEmit" }, "dependencies": { - "@typescript-eslint/experimental-utils": "2.6.1", + "@typescript-eslint/experimental-utils": "2.7.0", "eslint-utils": "^1.4.2", "functional-red-black-tree": "^1.0.1", "regexpp": "^2.0.1", diff --git a/packages/experimental-utils/CHANGELOG.md b/packages/experimental-utils/CHANGELOG.md index cabf42dd1a9b..fa483c6bd8f4 100644 --- a/packages/experimental-utils/CHANGELOG.md +++ b/packages/experimental-utils/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + +**Note:** Version bump only for package @typescript-eslint/experimental-utils + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) **Note:** Version bump only for package @typescript-eslint/experimental-utils diff --git a/packages/experimental-utils/package.json b/packages/experimental-utils/package.json index d05bf07280fb..2bbb6ddc9ec3 100644 --- a/packages/experimental-utils/package.json +++ b/packages/experimental-utils/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/experimental-utils", - "version": "2.6.1", + "version": "2.7.0", "description": "(Experimental) Utilities for working with TypeScript + ESLint together", "keywords": [ "eslint", @@ -37,7 +37,7 @@ }, "dependencies": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.6.1", + "@typescript-eslint/typescript-estree": "2.7.0", "eslint-scope": "^5.0.0" }, "peerDependencies": { diff --git a/packages/parser/CHANGELOG.md b/packages/parser/CHANGELOG.md index b6b7fc716a62..45ba8b728f68 100644 --- a/packages/parser/CHANGELOG.md +++ b/packages/parser/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + + +### Features + +* **parser:** handle optional chaining in scope analysis ([#1169](https://github.com/typescript-eslint/typescript-eslint/issues/1169)) ([026ceb9](https://github.com/typescript-eslint/typescript-eslint/commit/026ceb9)) + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) diff --git a/packages/parser/package.json b/packages/parser/package.json index 8283ffd33030..549dda9e82fc 100644 --- a/packages/parser/package.json +++ b/packages/parser/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/parser", - "version": "2.6.1", + "version": "2.7.0", "description": "An ESLint custom parser which leverages TypeScript ESTree", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -43,13 +43,13 @@ }, "dependencies": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "2.6.1", - "@typescript-eslint/typescript-estree": "2.6.1", + "@typescript-eslint/experimental-utils": "2.7.0", + "@typescript-eslint/typescript-estree": "2.7.0", "eslint-visitor-keys": "^1.1.0" }, "devDependencies": { "@types/glob": "^7.1.1", - "@typescript-eslint/shared-fixtures": "2.6.1", + "@typescript-eslint/shared-fixtures": "2.7.0", "glob": "^7.1.4" } } diff --git a/packages/shared-fixtures/CHANGELOG.md b/packages/shared-fixtures/CHANGELOG.md index 320f1ba9ed0c..aa253e3d46ee 100644 --- a/packages/shared-fixtures/CHANGELOG.md +++ b/packages/shared-fixtures/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + +**Note:** Version bump only for package @typescript-eslint/shared-fixtures + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) **Note:** Version bump only for package @typescript-eslint/shared-fixtures diff --git a/packages/shared-fixtures/package.json b/packages/shared-fixtures/package.json index 9d94966b4e18..c457e83d4dd3 100644 --- a/packages/shared-fixtures/package.json +++ b/packages/shared-fixtures/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/shared-fixtures", - "version": "2.6.1", + "version": "2.7.0", "private": true, "scripts": { "build": "tsc -b tsconfig.build.json", diff --git a/packages/typescript-estree/CHANGELOG.md b/packages/typescript-estree/CHANGELOG.md index c90a056ec276..28d91bddc6b1 100644 --- a/packages/typescript-estree/CHANGELOG.md +++ b/packages/typescript-estree/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +# [2.7.0](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.1...v2.7.0) (2019-11-11) + + +### Bug Fixes + +* **typescript-estree:** hash code to reduce update frequency ([#1179](https://github.com/typescript-eslint/typescript-eslint/issues/1179)) ([96d1cc3](https://github.com/typescript-eslint/typescript-eslint/commit/96d1cc3)) +* **typescript-estree:** reduce bundle footprint of tsutils ([#1177](https://github.com/typescript-eslint/typescript-eslint/issues/1177)) ([c8fe515](https://github.com/typescript-eslint/typescript-eslint/commit/c8fe515)) + + + + + ## [2.6.1](https://github.com/typescript-eslint/typescript-eslint/compare/v2.6.0...v2.6.1) (2019-11-04) diff --git a/packages/typescript-estree/package.json b/packages/typescript-estree/package.json index 1104de8adbd7..85a3a2b92b10 100644 --- a/packages/typescript-estree/package.json +++ b/packages/typescript-estree/package.json @@ -1,6 +1,6 @@ { "name": "@typescript-eslint/typescript-estree", - "version": "2.6.1", + "version": "2.7.0", "description": "A parser that converts TypeScript source code into an ESTree compatible form", "main": "dist/parser.js", "types": "dist/parser.d.ts", @@ -58,7 +58,7 @@ "@types/lodash.unescape": "^4.0.4", "@types/semver": "^6.0.1", "@types/tmp": "^0.1.0", - "@typescript-eslint/shared-fixtures": "2.6.1", + "@typescript-eslint/shared-fixtures": "2.7.0", "babel-code-frame": "^6.26.0", "glob": "^7.1.4", "lodash.isplainobject": "4.0.6",