diff --git a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts index a0e11f5cb69e..5947d292f8bd 100644 --- a/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts +++ b/packages/eslint-plugin/src/rules/explicit-member-accessibility.ts @@ -7,6 +7,10 @@ import { nullThrows, NullThrowsReasons, } from '../util'; +import { + getMemberHeadLoc, + getParameterPropertyHeadLoc, +} from '../util/getMemberHeadLoc'; type AccessibilityLevel = | 'explicit' // require an accessor (including public) @@ -166,7 +170,7 @@ export default createRule({ }); } else if (check === 'explicit' && !methodDefinition.accessibility) { context.report({ - loc: getMissingAccessibilityReportLoc(methodDefinition), + loc: getMemberHeadLoc(context.sourceCode, methodDefinition), messageId: 'missingAccessibility', data: { type: nodeType, @@ -221,87 +225,6 @@ export default createRule({ return { range: keywordRange, rangeToRemove }; } - /** - * For missing accessibility modifiers, we want to report any keywords - * out in front of the key, and the key itself, but not anything afterwards, - * i.e. parens, type annotations, method bodies, or `?`. - */ - function getMissingAccessibilityReportLoc( - node: - | TSESTree.MethodDefinition - | TSESTree.TSAbstractMethodDefinition - | TSESTree.PropertyDefinition - | TSESTree.TSAbstractPropertyDefinition, - ): TSESTree.SourceLocation { - let start: TSESTree.Position; - - if (node.decorators.length === 0) { - start = node.loc.start; - } else { - const lastDecorator = node.decorators[node.decorators.length - 1]; - const nextToken = nullThrows( - context.sourceCode.getTokenAfter(lastDecorator), - NullThrowsReasons.MissingToken('token', 'last decorator'), - ); - start = nextToken.loc.start; - } - - let end: TSESTree.Position; - - if (!node.computed) { - end = node.key.loc.end; - } else { - const closingBracket = nullThrows( - context.sourceCode.getTokenAfter( - node.key, - token => token.value === ']', - ), - NullThrowsReasons.MissingToken(']', node.type), - ); - end = closingBracket.loc.end; - } - - return { - start: structuredClone(start), - end: structuredClone(end), - }; - } - - /** - * For missing accessibility modifiers, we want to report any keywords - * out in front of the key, and the key itself, but not anything afterwards, - * i.e. parens, type annotations, method bodies, or `?`. - */ - function getMissingAccessibilityReportLocForParameterProperty( - node: TSESTree.TSParameterProperty, - nodeName: string, - ): TSESTree.SourceLocation { - // Parameter properties have a weirdly different AST structure - // than other class members. - - let start: TSESTree.Position; - - if (node.decorators.length === 0) { - start = structuredClone(node.loc.start); - } else { - const lastDecorator = node.decorators[node.decorators.length - 1]; - const nextToken = nullThrows( - context.sourceCode.getTokenAfter(lastDecorator), - NullThrowsReasons.MissingToken('token', 'last decorator'), - ); - start = structuredClone(nextToken.loc.start); - } - - const end = context.sourceCode.getLocFromIndex( - node.parameter.range[0] + nodeName.length, - ); - - return { - start, - end, - }; - } - /** * Creates a fixer that adds an accessibility modifier keyword */ @@ -385,7 +308,7 @@ export default createRule({ !propertyDefinition.accessibility ) { context.report({ - loc: getMissingAccessibilityReportLoc(propertyDefinition), + loc: getMemberHeadLoc(context.sourceCode, propertyDefinition), messageId: 'missingAccessibility', data: { type: nodeType, @@ -422,7 +345,8 @@ export default createRule({ case 'explicit': { if (!node.accessibility) { context.report({ - loc: getMissingAccessibilityReportLocForParameterProperty( + loc: getParameterPropertyHeadLoc( + context.sourceCode, node, nodeName, ), diff --git a/packages/eslint-plugin/src/rules/prefer-readonly.ts b/packages/eslint-plugin/src/rules/prefer-readonly.ts index 80f398394965..f163b1aaab34 100644 --- a/packages/eslint-plugin/src/rules/prefer-readonly.ts +++ b/packages/eslint-plugin/src/rules/prefer-readonly.ts @@ -9,6 +9,10 @@ import { nullThrows, typeIsOrHasBaseType, } from '../util'; +import { + getMemberHeadLoc, + getParameterPropertyHeadLoc, +} from '../util/getMemberHeadLoc'; type MessageIds = 'preferReadonly'; type Options = [ @@ -160,15 +164,6 @@ export default createRule({ function getEsNodesFromViolatingNode( violatingNode: ParameterOrPropertyDeclaration, ): { esNode: TSESTree.Node; nameNode: TSESTree.Node } { - if ( - ts.isParameterPropertyDeclaration(violatingNode, violatingNode.parent) - ) { - return { - esNode: services.tsNodeToESTreeNodeMap.get(violatingNode.name), - nameNode: services.tsNodeToESTreeNodeMap.get(violatingNode.name), - }; - } - return { esNode: services.tsNodeToESTreeNodeMap.get(violatingNode), nameNode: services.tsNodeToESTreeNodeMap.get(violatingNode.name), @@ -196,13 +191,35 @@ export default createRule({ for (const violatingNode of finalizedClassScope.finalizeUnmodifiedPrivateNonReadonlys()) { const { esNode, nameNode } = getEsNodesFromViolatingNode(violatingNode); + + const reportNodeOrLoc: + | { node: TSESTree.Node } + | { loc: TSESTree.SourceLocation } = (() => { + switch (esNode.type) { + case AST_NODE_TYPES.MethodDefinition: + case AST_NODE_TYPES.PropertyDefinition: + case AST_NODE_TYPES.TSAbstractMethodDefinition: + return { loc: getMemberHeadLoc(context.sourceCode, esNode) }; + case AST_NODE_TYPES.TSParameterProperty: + return { + loc: getParameterPropertyHeadLoc( + context.sourceCode, + esNode, + (nameNode as TSESTree.Identifier).name, + ), + }; + default: + return { node: esNode }; + } + })(); + context.report({ + ...reportNodeOrLoc, data: { name: context.sourceCode.getText(nameNode), }, fix: fixer => fixer.insertTextBefore(nameNode, 'readonly '), messageId: 'preferReadonly', - node: esNode, }); } }, diff --git a/packages/eslint-plugin/src/util/getMemberHeadLoc.ts b/packages/eslint-plugin/src/util/getMemberHeadLoc.ts new file mode 100644 index 000000000000..19401230e235 --- /dev/null +++ b/packages/eslint-plugin/src/util/getMemberHeadLoc.ts @@ -0,0 +1,108 @@ +import type { TSESLint, TSESTree } from '@typescript-eslint/utils'; +import { + nullThrows, + NullThrowsReasons, +} from '@typescript-eslint/utils/eslint-utils'; + +/** + * Generates report loc suitable for reporting on how a class member is + * declared, rather than how it's implemented. + * + * ```ts + * class A { + * abstract method(): void; + * ~~~~~~~~~~~~~~~ + * + * concreteMethod(): void { + * ~~~~~~~~~~~~~~ + * // code + * } + * + * abstract private property?: string; + * ~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @decorator override concreteProperty = 'value'; + * ~~~~~~~~~~~~~~~~~~~~~~~~~ + * } + * ``` + */ +export function getMemberHeadLoc( + sourceCode: Readonly, + node: + | TSESTree.MethodDefinition + | TSESTree.TSAbstractMethodDefinition + | TSESTree.PropertyDefinition + | TSESTree.TSAbstractPropertyDefinition, +): TSESTree.SourceLocation { + let start: TSESTree.Position; + + if (node.decorators.length === 0) { + start = node.loc.start; + } else { + const lastDecorator = node.decorators[node.decorators.length - 1]; + const nextToken = nullThrows( + sourceCode.getTokenAfter(lastDecorator), + NullThrowsReasons.MissingToken('token', 'last decorator'), + ); + start = nextToken.loc.start; + } + + let end: TSESTree.Position; + + if (!node.computed) { + end = node.key.loc.end; + } else { + const closingBracket = nullThrows( + sourceCode.getTokenAfter(node.key, token => token.value === ']'), + NullThrowsReasons.MissingToken(']', node.type), + ); + end = closingBracket.loc.end; + } + + return { + start: structuredClone(start), + end: structuredClone(end), + }; +} + +/** + * Generates report loc suitable for reporting on how a parameter property is + * declared. + * + * ```ts + * class A { + * constructor(private property: string = 'value') { + * ~~~~~~~~~~~~~~~~ + * } + * ``` + */ +export function getParameterPropertyHeadLoc( + sourceCode: Readonly, + node: TSESTree.TSParameterProperty, + nodeName: string, +): TSESTree.SourceLocation { + // Parameter properties have a weirdly different AST structure + // than other class members. + + let start: TSESTree.Position; + + if (node.decorators.length === 0) { + start = structuredClone(node.loc.start); + } else { + const lastDecorator = node.decorators[node.decorators.length - 1]; + const nextToken = nullThrows( + sourceCode.getTokenAfter(lastDecorator), + NullThrowsReasons.MissingToken('token', 'last decorator'), + ); + start = structuredClone(nextToken.loc.start); + } + + const end = sourceCode.getLocFromIndex( + node.parameter.range[0] + nodeName.length, + ); + + return { + start, + end, + }; +} diff --git a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-readonly.shot b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-readonly.shot index 375126979792..e45cf78a327c 100644 --- a/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-readonly.shot +++ b/packages/eslint-plugin/tests/docs-eslint-output-snapshots/prefer-readonly.shot @@ -6,17 +6,17 @@ exports[`Validating rule docs prefer-readonly.mdx code examples ESLint output 1` class Container { // These member variables could be marked as readonly private neverModifiedMember = true; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'neverModifiedMember' is never reassigned; mark it as \`readonly\`. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'neverModifiedMember' is never reassigned; mark it as \`readonly\`. private onlyModifiedInConstructor: number; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'onlyModifiedInConstructor' is never reassigned; mark it as \`readonly\`. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'onlyModifiedInConstructor' is never reassigned; mark it as \`readonly\`. #neverModifiedPrivateField = 3; - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member '#neverModifiedPrivateField' is never reassigned; mark it as \`readonly\`. + ~~~~~~~~~~~~~~~~~~~~~~~~~~ Member '#neverModifiedPrivateField' is never reassigned; mark it as \`readonly\`. public constructor( onlyModifiedInConstructor: number, // Private parameter properties can also be marked as readonly private neverModifiedParameter: string, - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'neverModifiedParameter: string' is never reassigned; mark it as \`readonly\`. + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'neverModifiedParameter: string' is never reassigned; mark it as \`readonly\`. ) { this.onlyModifiedInConstructor = onlyModifiedInConstructor; } @@ -57,11 +57,9 @@ Options: { "onlyInlineLambdas": true } class Container { private onClick = () => { - ~~~~~~~~~~~~~~~~~~~~~~~~~ Member 'onClick' is never reassigned; mark it as \`readonly\`. + ~~~~~~~~~~~~~~~ Member 'onClick' is never reassigned; mark it as \`readonly\`. /* ... */ -~~~~~~~~~~~~~ }; -~~~~ } " `; diff --git a/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts b/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts index 3b1c25e75f11..31e134788601 100644 --- a/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-readonly.test.ts @@ -473,6 +473,11 @@ ruleTester.run('prefer-readonly', rule, { } } `, + ` + class TestComputedParameter { + private ['computed-ignored-by-rule'] = 1; + } + `, { code: ` class Foo { @@ -747,6 +752,10 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 53, data: { name: 'incorrectlyModifiableStatic', }, @@ -767,6 +776,10 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 46, data: { name: '#incorrectlyModifiableStatic', }, @@ -787,6 +800,10 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 58, data: { name: 'incorrectlyModifiableStaticArrow', }, @@ -807,6 +824,10 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 51, data: { name: '#incorrectlyModifiableStaticArrow', }, @@ -833,17 +854,23 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 46, data: { name: 'incorrectlyModifiableInline', }, - line: 3, messageId: 'preferReadonly', }, { + line: 7, + column: 15, + endLine: 7, + endColumn: 50, data: { name: 'incorrectlyModifiableInline', }, - line: 7, messageId: 'preferReadonly', }, ], @@ -873,17 +900,23 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 39, data: { name: '#incorrectlyModifiableInline', }, - line: 3, messageId: 'preferReadonly', }, { + line: 7, + column: 15, + endLine: 7, + endColumn: 43, data: { name: '#incorrectlyModifiableInline', }, - line: 7, messageId: 'preferReadonly', }, ], @@ -911,6 +944,10 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 47, data: { name: 'incorrectlyModifiableDelayed', }, @@ -939,6 +976,10 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 40, data: { name: '#incorrectlyModifiableDelayed', }, @@ -973,10 +1014,13 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 49, data: { name: 'childClassExpressionModifiable', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1014,10 +1058,13 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 42, data: { name: '#childClassExpressionModifiable', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1049,10 +1096,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 49, + data: { name: 'incorrectlyModifiablePostMinus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1078,10 +1129,13 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 42, data: { name: '#incorrectlyModifiablePostMinus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1107,10 +1161,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 48, + data: { name: 'incorrectlyModifiablePostPlus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1136,10 +1194,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 41, + data: { name: '#incorrectlyModifiablePostPlus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1165,10 +1227,13 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 48, data: { name: 'incorrectlyModifiablePreMinus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1194,10 +1259,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 41, + data: { name: '#incorrectlyModifiablePreMinus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1223,10 +1292,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 47, + data: { name: 'incorrectlyModifiablePrePlus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1252,10 +1325,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 40, + data: { name: '#incorrectlyModifiablePrePlus', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1285,10 +1362,14 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 43, + data: { name: 'overlappingClassVariable', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1314,10 +1395,13 @@ class Foo { `, errors: [ { + line: 3, + column: 30, + endLine: 3, + endColumn: 68, data: { name: 'incorrectlyModifiableParameter', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1338,10 +1422,14 @@ class Foo { `, errors: [ { + line: 5, + column: 13, + endLine: 5, + endColumn: 51, + data: { name: 'incorrectlyModifiableParameter', }, - line: 5, messageId: 'preferReadonly', }, ], @@ -1362,10 +1450,13 @@ class Foo { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 42, data: { name: 'incorrectlyInlineLambda', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1397,10 +1488,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 4, + column: 5, + endLine: 4, + endColumn: 18, data: { name: '_name', }, - line: 4, messageId: 'preferReadonly', }, ], @@ -1422,10 +1516,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 4, + column: 5, + endLine: 4, + endColumn: 10, data: { name: '#name', }, - line: 4, messageId: 'preferReadonly', }, ], @@ -1455,10 +1552,14 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, + data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1488,10 +1589,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1525,10 +1629,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 7, + column: 11, + endLine: 7, + endColumn: 26, data: { name: 'testObj', }, - line: 7, messageId: 'preferReadonly', }, ], @@ -1562,10 +1669,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 7, + column: 11, + endLine: 7, + endColumn: 19, data: { name: '#testObj', }, - line: 7, messageId: 'preferReadonly', }, ], @@ -1593,10 +1703,14 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, + data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1624,10 +1738,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1651,10 +1768,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1678,10 +1798,14 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, + data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1705,10 +1829,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1732,10 +1859,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1759,10 +1889,14 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, + data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1786,10 +1920,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1813,10 +1950,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1840,10 +1980,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1867,10 +2010,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1894,10 +2040,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1921,10 +2070,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1948,10 +2100,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -1975,10 +2130,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -2002,10 +2160,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -2029,10 +2190,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 26, data: { name: 'testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -2056,10 +2220,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 19, data: { name: '#testObj', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -2087,10 +2254,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 23, data: { name: 'prop', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -2118,10 +2288,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 23, data: { name: 'prop', }, - line: 3, messageId: 'preferReadonly', }, ], @@ -2149,10 +2322,13 @@ function ClassWithName {}>(Base: TBase) { `, errors: [ { + line: 3, + column: 11, + endLine: 3, + endColumn: 23, data: { name: 'prop', }, - line: 3, messageId: 'preferReadonly', }, ],