From eba19f072fadea89fc93bf0396154e02d5acf63f Mon Sep 17 00:00:00 2001 From: JounQin Date: Thu, 18 Mar 2021 22:22:12 +0800 Subject: [PATCH 1/4] feat(eslint-plugin): improve report message for `this` call and member-access close #3197 --- .../eslint-plugin/src/rules/no-unsafe-call.ts | 19 +++++++++-- .../src/rules/no-unsafe-member-access.ts | 7 +++- .../fixtures/tsconfig.noImplicitThis.json | 6 ++++ .../tests/rules/no-unsafe-call.test.ts | 22 ++++++++++++- .../rules/no-unsafe-member-access.test.ts | 32 ++++++++++++++++++- 5 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 packages/eslint-plugin/tests/fixtures/tsconfig.noImplicitThis.json diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 0535bfeab316..f5fa69ed3976 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -1,7 +1,14 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; import * as util from '../util'; -type MessageIds = 'unsafeCall' | 'unsafeNew' | 'unsafeTemplateTag'; +type MessageIds = + | 'unsafeCall' + | 'unsafeCallThis' + | 'unsafeNew' + | 'unsafeTemplateTag'; export default util.createRule<[], MessageIds>({ name: 'no-unsafe-call', @@ -15,6 +22,8 @@ export default util.createRule<[], MessageIds>({ }, messages: { unsafeCall: 'Unsafe call of an any typed value.', + unsafeCallThis: + 'Unsafe call of `this`, you can try to enable the `noImplicitThis` option.', unsafeNew: 'Unsafe construction of an any type value.', unsafeTemplateTag: 'Unsafe any typed template tag.', }, @@ -34,6 +43,12 @@ export default util.createRule<[], MessageIds>({ const type = util.getConstrainedTypeAtLocation(checker, tsNode); if (util.isTypeAnyType(type)) { + if ( + node.type === AST_NODE_TYPES.MemberExpression && + node.object.type === AST_NODE_TYPES.ThisExpression + ) { + messageId = 'unsafeCallThis'; + } context.report({ node: reportingNode, messageId: messageId, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts index b326c754136b..1057cc8da1ea 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts @@ -22,6 +22,8 @@ export default util.createRule({ messages: { unsafeMemberExpression: 'Unsafe member access {{property}} on an any value.', + unsafeThisMemberExpression: + 'Unsafe member access {{property}} on `this`, you can try to enable the `noImplicitThis` option.', unsafeComputedMemberAccess: 'Computed name {{property}} resolves to an any value.', }, @@ -60,7 +62,10 @@ export default util.createRule({ const propertyName = sourceCode.getText(node.property); context.report({ node, - messageId: 'unsafeMemberExpression', + messageId: + node.object.type === AST_NODE_TYPES.ThisExpression + ? 'unsafeThisMemberExpression' + : 'unsafeMemberExpression', data: { property: node.computed ? `[${propertyName}]` : `.${propertyName}`, }, diff --git a/packages/eslint-plugin/tests/fixtures/tsconfig.noImplicitThis.json b/packages/eslint-plugin/tests/fixtures/tsconfig.noImplicitThis.json new file mode 100644 index 000000000000..c017e51c6e4c --- /dev/null +++ b/packages/eslint-plugin/tests/fixtures/tsconfig.noImplicitThis.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noImplicitThis": false + } +} diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index 981abb0eadde..31b081c24696 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -9,7 +9,7 @@ import { const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - project: './tsconfig.json', + project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, }); @@ -148,5 +148,25 @@ function foo(x: { tag: any }) { x.tag\`foo\` } }, ], }), + { + code: noFormat` +const methods = { + methodA() { + return this.methodB() + }, + methodB() { + return true + }, +}; + `, + errors: [ + { + messageId: 'unsafeCallThis', + line: 4, + column: 12, + endColumn: 24, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts index a9c21f8712b9..d02432080db4 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts @@ -9,7 +9,7 @@ import { const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - project: './tsconfig.json', + project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, }); @@ -202,5 +202,35 @@ function foo(x: string[], y: any) { x[y] } }, ], }), + { + code: noFormat` +const methods = { + methodA() { + return this.methodB() + }, + methodB() { + const getProperty = () => Math.random() > 0.5 ? 'methodB' : 'methodC' + return this[getProperty()]() + }, + methodC() { + return true + }, +}; + `, + errors: [ + { + messageId: 'unsafeThisMemberExpression', + line: 4, + column: 12, + endColumn: 24, + }, + { + messageId: 'unsafeThisMemberExpression', + line: 8, + column: 12, + endColumn: 31, + }, + ], + }, ], }); From 99ced2810ea9cb3e7543715b83dac4c0dfd88030 Mon Sep 17 00:00:00 2001 From: JounQin Date: Sat, 20 Mar 2021 10:11:00 +0800 Subject: [PATCH 2/4] refactor: check noImplicitThis of compilerOptions --- packages/eslint-plugin/src/rules/no-unsafe-call.ts | 7 +++++++ .../eslint-plugin/src/rules/no-unsafe-member-access.ts | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index f5fa69ed3976..4b2d7e7a6cec 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -2,6 +2,7 @@ import { AST_NODE_TYPES, TSESTree, } from '@typescript-eslint/experimental-utils'; +import * as tsutils from 'tsutils'; import * as util from '../util'; type MessageIds = @@ -33,6 +34,11 @@ export default util.createRule<[], MessageIds>({ create(context) { const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); const checker = program.getTypeChecker(); + const compilerOptions = program.getCompilerOptions(); + const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled( + compilerOptions, + 'noImplicitThis', + ); function checkCall( node: TSESTree.Node, @@ -44,6 +50,7 @@ export default util.createRule<[], MessageIds>({ if (util.isTypeAnyType(type)) { if ( + !isNoImplicitThis && node.type === AST_NODE_TYPES.MemberExpression && node.object.type === AST_NODE_TYPES.ThisExpression ) { diff --git a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts index 1057cc8da1ea..f39bdb0282d4 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts @@ -2,6 +2,7 @@ import { TSESTree, AST_NODE_TYPES, } from '@typescript-eslint/experimental-utils'; +import * as tsutils from 'tsutils'; import * as util from '../util'; const enum State { @@ -33,6 +34,11 @@ export default util.createRule({ create(context) { const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); const checker = program.getTypeChecker(); + const compilerOptions = program.getCompilerOptions(); + const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled( + compilerOptions, + 'noImplicitThis', + ); const sourceCode = context.getSourceCode(); const stateCache = new Map(); @@ -63,6 +69,7 @@ export default util.createRule({ context.report({ node, messageId: + !isNoImplicitThis && node.object.type === AST_NODE_TYPES.ThisExpression ? 'unsafeThisMemberExpression' : 'unsafeMemberExpression', From be72c43e1e203ca9d7685372abc270afa8405f00 Mon Sep 17 00:00:00 2001 From: JounQin Date: Mon, 22 Mar 2021 13:08:00 +0800 Subject: [PATCH 3/4] feat: check `any` type of this expression only check no-unsafe-assignment and no-unsafe-return too --- .../src/rules/no-unsafe-assignment.ts | 39 ++++++++++++++++--- .../eslint-plugin/src/rules/no-unsafe-call.ts | 34 +++++++++------- .../src/rules/no-unsafe-member-access.ts | 36 +++++++++++++---- .../src/rules/no-unsafe-return.ts | 38 +++++++++++++++--- .../src/util/getThisExpression.ts | 26 +++++++++++++ packages/eslint-plugin/src/util/index.ts | 1 + .../tests/rules/no-unsafe-assignment.test.ts | 17 +++++++- .../tests/rules/no-unsafe-call.test.ts | 9 +++++ .../tests/rules/no-unsafe-return.test.ts | 27 ++++++++++++- 9 files changed, 194 insertions(+), 33 deletions(-) create mode 100644 packages/eslint-plugin/src/util/getThisExpression.ts diff --git a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts index 4a8f2a9ea237..ac8152085449 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-assignment.ts @@ -2,8 +2,10 @@ import { TSESTree, AST_NODE_TYPES, } from '@typescript-eslint/experimental-utils'; +import * as tsutils from 'tsutils'; import * as ts from 'typescript'; import * as util from '../util'; +import { getThisExpression } from '../util'; const enum ComparisonType { /** Do no assignment comparison */ @@ -25,13 +27,17 @@ export default util.createRule({ requiresTypeChecking: true, }, messages: { - anyAssignment: 'Unsafe assignment of an any value.', - unsafeArrayPattern: 'Unsafe array destructuring of an any array value.', + anyAssignment: 'Unsafe assignment of an `any` value.', + anyAssignmentThis: [ + 'Unsafe assignment of an `any` value. `this` is typed as `any`.', + 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', + ].join('\n'), + unsafeArrayPattern: 'Unsafe array destructuring of an `any` array value.', unsafeArrayPatternFromTuple: - 'Unsafe array destructuring of a tuple element with an any value.', + 'Unsafe array destructuring of a tuple element with an `any` value.', unsafeAssignment: 'Unsafe assignment of type {{sender}} to a variable of type {{receiver}}.', - unsafeArraySpread: 'Unsafe spread of an any value in an array.', + unsafeArraySpread: 'Unsafe spread of an `any` value in an array.', }, schema: [], }, @@ -39,6 +45,11 @@ export default util.createRule({ create(context) { const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); const checker = program.getTypeChecker(); + const compilerOptions = program.getCompilerOptions(); + const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled( + compilerOptions, + 'noImplicitThis', + ); // returns true if the assignment reported function checkArrayDestructureHelper( @@ -243,9 +254,27 @@ export default util.createRule({ return false; } + let messageId: 'anyAssignment' | 'anyAssignmentThis' = 'anyAssignment'; + + if (!isNoImplicitThis) { + // `var foo = this` + const thisExpression = getThisExpression(senderNode); + if ( + thisExpression && + util.isTypeAnyType( + util.getConstrainedTypeAtLocation( + checker, + esTreeNodeToTSNodeMap.get(thisExpression), + ), + ) + ) { + messageId = 'anyAssignmentThis'; + } + } + context.report({ node: reportingNode, - messageId: 'anyAssignment', + messageId, }); return true; } diff --git a/packages/eslint-plugin/src/rules/no-unsafe-call.ts b/packages/eslint-plugin/src/rules/no-unsafe-call.ts index 4b2d7e7a6cec..b08214d36c27 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-call.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-call.ts @@ -1,9 +1,7 @@ -import { - AST_NODE_TYPES, - TSESTree, -} from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; import * as tsutils from 'tsutils'; import * as util from '../util'; +import { getThisExpression } from '../util'; type MessageIds = | 'unsafeCall' @@ -22,9 +20,11 @@ export default util.createRule<[], MessageIds>({ requiresTypeChecking: true, }, messages: { - unsafeCall: 'Unsafe call of an any typed value.', - unsafeCallThis: - 'Unsafe call of `this`, you can try to enable the `noImplicitThis` option.', + unsafeCall: 'Unsafe call of an `any` typed value.', + unsafeCallThis: [ + 'Unsafe call of an `any` typed value. `this` is typed as `any`.', + 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', + ].join('\n'), unsafeNew: 'Unsafe construction of an any type value.', unsafeTemplateTag: 'Unsafe any typed template tag.', }, @@ -49,12 +49,20 @@ export default util.createRule<[], MessageIds>({ const type = util.getConstrainedTypeAtLocation(checker, tsNode); if (util.isTypeAnyType(type)) { - if ( - !isNoImplicitThis && - node.type === AST_NODE_TYPES.MemberExpression && - node.object.type === AST_NODE_TYPES.ThisExpression - ) { - messageId = 'unsafeCallThis'; + if (!isNoImplicitThis) { + // `this()` or `this.foo()` or `this.foo[bar]()` + const thisExpression = getThisExpression(node); + if ( + thisExpression && + util.isTypeAnyType( + util.getConstrainedTypeAtLocation( + checker, + esTreeNodeToTSNodeMap.get(thisExpression), + ), + ) + ) { + messageId = 'unsafeCallThis'; + } } context.report({ node: reportingNode, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts index f39bdb0282d4..13fd7bf0821b 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-member-access.ts @@ -4,6 +4,7 @@ import { } from '@typescript-eslint/experimental-utils'; import * as tsutils from 'tsutils'; import * as util from '../util'; +import { getThisExpression } from '../util'; const enum State { Unsafe = 1, @@ -22,9 +23,11 @@ export default util.createRule({ }, messages: { unsafeMemberExpression: - 'Unsafe member access {{property}} on an any value.', - unsafeThisMemberExpression: - 'Unsafe member access {{property}} on `this`, you can try to enable the `noImplicitThis` option.', + 'Unsafe member access {{property}} on an `any` value.', + unsafeThisMemberExpression: [ + 'Unsafe member access {{property}} on an `any` value. `this` is typed as `any`.', + 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', + ].join('\n'), unsafeComputedMemberAccess: 'Computed name {{property}} resolves to an any value.', }, @@ -66,13 +69,30 @@ export default util.createRule({ if (state === State.Unsafe) { const propertyName = sourceCode.getText(node.property); + + let messageId: 'unsafeMemberExpression' | 'unsafeThisMemberExpression' = + 'unsafeMemberExpression'; + + if (!isNoImplicitThis) { + // `this.foo` or `this.foo[bar]` + const thisExpression = getThisExpression(node); + + if ( + thisExpression && + util.isTypeAnyType( + util.getConstrainedTypeAtLocation( + checker, + esTreeNodeToTSNodeMap.get(thisExpression), + ), + ) + ) { + messageId = 'unsafeThisMemberExpression'; + } + } + context.report({ node, - messageId: - !isNoImplicitThis && - node.object.type === AST_NODE_TYPES.ThisExpression - ? 'unsafeThisMemberExpression' - : 'unsafeMemberExpression', + messageId, data: { property: node.computed ? `[${propertyName}]` : `.${propertyName}`, }, diff --git a/packages/eslint-plugin/src/rules/no-unsafe-return.ts b/packages/eslint-plugin/src/rules/no-unsafe-return.ts index c2366bc96f2e..a818be4ef4ee 100644 --- a/packages/eslint-plugin/src/rules/no-unsafe-return.ts +++ b/packages/eslint-plugin/src/rules/no-unsafe-return.ts @@ -2,8 +2,9 @@ import { TSESTree, AST_NODE_TYPES, } from '@typescript-eslint/experimental-utils'; -import { isExpression } from 'tsutils'; +import * as tsutils from 'tsutils'; import * as util from '../util'; +import { getThisExpression } from '../util'; export default util.createRule({ name: 'no-unsafe-return', @@ -16,9 +17,13 @@ export default util.createRule({ requiresTypeChecking: true, }, messages: { - unsafeReturn: 'Unsafe return of an {{type}} typed value', + unsafeReturn: 'Unsafe return of an `{{type}}` typed value.', + unsafeReturnThis: [ + 'Unsafe return of an `{{type}}` typed value. `this` is typed as `any`.', + 'You can try to fix this by turning on the `noImplicitThis` compiler option, or adding a `this` parameter to the function.', + ].join('\n'), unsafeReturnAssignment: - 'Unsafe return of type {{sender}} from function with return type {{receiver}}.', + 'Unsafe return of type `{{sender}}` from function with return type `{{receiver}}`.', }, schema: [], }, @@ -26,6 +31,11 @@ export default util.createRule({ create(context) { const { program, esTreeNodeToTSNodeMap } = util.getParserServices(context); const checker = program.getTypeChecker(); + const compilerOptions = program.getCompilerOptions(); + const isNoImplicitThis = tsutils.isStrictCompilerOptionEnabled( + compilerOptions, + 'noImplicitThis', + ); function getParentFunctionNode( node: TSESTree.Node, @@ -74,7 +84,7 @@ export default util.createRule({ // so we have to use the contextual typing in these cases, i.e. // const foo1: () => Set = () => new Set(); // the return type of the arrow function is Set even though the variable is typed as Set - let functionType = isExpression(functionTSNode) + let functionType = tsutils.isExpression(functionTSNode) ? util.getContextualType(checker, functionTSNode) : checker.getTypeAtLocation(functionTSNode); if (!functionType) { @@ -100,10 +110,28 @@ export default util.createRule({ } } + let messageId: 'unsafeReturn' | 'unsafeReturnThis' = 'unsafeReturn'; + + if (!isNoImplicitThis) { + // `return this` + const thisExpression = getThisExpression(returnNode); + if ( + thisExpression && + util.isTypeAnyType( + util.getConstrainedTypeAtLocation( + checker, + esTreeNodeToTSNodeMap.get(thisExpression), + ), + ) + ) { + messageId = 'unsafeReturnThis'; + } + } + // If the function return type was not unknown/unknown[], mark usage as unsafeReturn. return context.report({ node: reportingNode, - messageId: 'unsafeReturn', + messageId, data: { type: anyType === util.AnyType.Any ? 'any' : 'any[]', }, diff --git a/packages/eslint-plugin/src/util/getThisExpression.ts b/packages/eslint-plugin/src/util/getThisExpression.ts new file mode 100644 index 000000000000..1d3dd9c065e5 --- /dev/null +++ b/packages/eslint-plugin/src/util/getThisExpression.ts @@ -0,0 +1,26 @@ +import { + AST_NODE_TYPES, + TSESTree, +} from '@typescript-eslint/experimental-utils'; + +export function getThisExpression( + node: TSESTree.Node, +): TSESTree.ThisExpression | undefined { + while (node) { + if (node.type === AST_NODE_TYPES.CallExpression) { + node = node.callee; + } + + if (node.type === AST_NODE_TYPES.ThisExpression) { + return node; + } + + if ('object' in node) { + node = node.object; + } else { + break; + } + } + + return; +} diff --git a/packages/eslint-plugin/src/util/index.ts b/packages/eslint-plugin/src/util/index.ts index e7bb53547fc8..79e142b15fd4 100644 --- a/packages/eslint-plugin/src/util/index.ts +++ b/packages/eslint-plugin/src/util/index.ts @@ -4,6 +4,7 @@ export * from './astUtils'; export * from './collectUnusedVariables'; export * from './createRule'; export * from './getFunctionHeadLoc'; +export * from './getThisExpression'; export * from './getWrappingFixer'; export * from './isTypeReadonly'; export * from './misc'; diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts index 6f3baa3fa1a1..b0f8ec612333 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts @@ -68,7 +68,7 @@ function assignmentTest( const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - project: './tsconfig.json', + project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, }); @@ -347,5 +347,20 @@ declare function Foo(props: Props): never; }, ], }, + { + code: ` +function foo() { + const bar = this; +} + `, + errors: [ + { + messageId: 'anyAssignmentThis', + line: 3, + column: 9, + endColumn: 19, + }, + ], + }, ], }); diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts index 31b081c24696..1f70c30e248c 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-call.test.ts @@ -157,6 +157,9 @@ const methods = { methodB() { return true }, + methodC() { + return this() + } }; `, errors: [ @@ -166,6 +169,12 @@ const methods = { column: 12, endColumn: 24, }, + { + messageId: 'unsafeCallThis', + line: 10, + column: 12, + endColumn: 16, + }, ], }, ], diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts index 7d7777e46632..5cfd965ab8e1 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-return.test.ts @@ -9,7 +9,7 @@ import { const ruleTester = new RuleTester({ parser: '@typescript-eslint/parser', parserOptions: { - project: './tsconfig.json', + project: './tsconfig.noImplicitThis.json', tsconfigRootDir: getFixturesRootDir(), }, }); @@ -293,5 +293,30 @@ receiver(function test() { }, ], }, + { + code: ` +function foo() { + return this; +} + +function bar() { + return () => this; +} + `, + errors: [ + { + messageId: 'unsafeReturnThis', + line: 3, + column: 3, + endColumn: 15, + }, + { + messageId: 'unsafeReturnThis', + line: 7, + column: 16, + endColumn: 20, + }, + ], + }, ], }); From cee1fb1c373d3f9e3a59a0dbb30ca7b15eea03e6 Mon Sep 17 00:00:00 2001 From: JounQin Date: Mon, 29 Mar 2021 10:04:09 +0800 Subject: [PATCH 4/4] refactor: only ever handle one node per iteration --- packages/eslint-plugin/src/util/getThisExpression.ts | 10 ++++------ .../tests/rules/no-unsafe-member-access.test.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/packages/eslint-plugin/src/util/getThisExpression.ts b/packages/eslint-plugin/src/util/getThisExpression.ts index 1d3dd9c065e5..5e2772aebaec 100644 --- a/packages/eslint-plugin/src/util/getThisExpression.ts +++ b/packages/eslint-plugin/src/util/getThisExpression.ts @@ -9,14 +9,12 @@ export function getThisExpression( while (node) { if (node.type === AST_NODE_TYPES.CallExpression) { node = node.callee; - } - - if (node.type === AST_NODE_TYPES.ThisExpression) { + } else if (node.type === AST_NODE_TYPES.ThisExpression) { return node; - } - - if ('object' in node) { + } else if (node.type === AST_NODE_TYPES.MemberExpression) { node = node.object; + } else if (node.type === AST_NODE_TYPES.ChainExpression) { + node = node.expression; } else { break; } diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts index d02432080db4..491d5e97d9f9 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-member-access.test.ts @@ -215,6 +215,9 @@ const methods = { methodC() { return true }, + methodD() { + return (this?.methodA)?.() + } }; `, errors: [ @@ -230,6 +233,12 @@ const methods = { column: 12, endColumn: 31, }, + { + messageId: 'unsafeThisMemberExpression', + line: 14, + column: 13, + endColumn: 26, + }, ], }, ], pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy