Repository: eslint-stylistic/eslint-stylistic
Pull request #738 (2025-03-23 14:43:15)

fix(no-extra-parens): do not break specific `ReturnStatement`
### Description
case:
```js
((a, b) => {
  return (
    a % b == 0
  ) || (a % b == 1)
})()
```
It is not the job of `no-extra-parens` to make this code into one line. For this case, the problem of program errors caused by parentheses should be avoided. The modified solution is to **check whether the argument of ReturnStatement wraps and the first line contains only one '('**

### Linked Issues

fixes  #699

### Additional context

Are there any other cases that I haven't considered?


9romise: I've updated the logic.

This exception handles cases where:
- Parent node is a `ReturnStatement`
- The return statement spans multiple lines
- The expression is not already wrapped in parentheses
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/pull/738?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 77.22%. Comparing base [(`85eeaa1`)](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/commit/85eeaa16f102190f04801a8bfa9675f6638b51a4?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic) to head [(`8dced76`)](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/commit/8dced767cdd29947c4dfa037148046c70a8c64a0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic).

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #738   +/-   ##
=======================================
  Coverage   77.22%   77.22%           
=======================================
  Files         122      122           
  Lines       23957    23958    +1     
  Branches     4672     4673    +1     
=======================================
+ Hits        18500    18501    +1     
  Misses       5457     5457           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/eslint-stylistic/eslint-stylistic/pull/738?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=eslint-stylistic).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- :package: [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>

Patch:
diff --git a/packages/eslint-plugin/rules/indent/indent._ts_.ts b/packages/eslint-plugin/rules/indent/indent._ts_.ts
index be71c35d..b15f2bd1 100644
--- a/packages/eslint-plugin/rules/indent/indent._ts_.ts
+++ b/packages/eslint-plugin/rules/indent/indent._ts_.ts
@@ -1562,7 +1562,7 @@ export default createRule<RuleOptions, MessageIds>({
       PropertyDefinition(node) {
         const firstToken = sourceCode.getFirstToken(node)!
         const maybeSemicolonToken = sourceCode.getLastToken(node)!
-        let keyLastToken = null
+        let keyLastToken: Tree.Token | null = null
 
         // Indent key.
         if (node.computed) {
@@ -1587,9 +1587,11 @@ export default createRule<RuleOptions, MessageIds>({
         if (node.value) {
           const eqToken = sourceCode.getTokenBefore(node.value, isEqToken)!
           const valueToken = sourceCode.getTokenAfter(eqToken)!
+          const typeToken = sourceCode.getTokenBefore(eqToken)!
 
           offsets.setDesiredOffset(eqToken, keyLastToken, 1)
-          offsets.setDesiredOffset(valueToken, eqToken, 1)
+          // value token set offset by equal token or ts type token
+          offsets.setDesiredOffset(valueToken, keyLastToken === typeToken ? eqToken : typeToken, 1)
           if (isSemicolonToken(maybeSemicolonToken))
             offsets.setDesiredOffset(maybeSemicolonToken, eqToken, 1)
         }
diff --git a/packages/eslint-plugin/rules/jsx-sort-props/README._jsx_.md b/packages/eslint-plugin/rules/jsx-sort-props/README._jsx_.md
index 007f31b7..535236ee 100644
--- a/packages/eslint-plugin/rules/jsx-sort-props/README._jsx_.md
+++ b/packages/eslint-plugin/rules/jsx-sort-props/README._jsx_.md
@@ -120,7 +120,7 @@ When `true`, alphabetical order is **not** enforced:
 
 This can be a boolean or an array option.
 
-When `reservedFirst` is defined, React reserved props (`children`, `dangerouslySetInnerHTML` - **only for DOM components**, `key`, and `ref`) must be listed before all other props, but still respecting the alphabetical order:
+When `reservedFirst` is `true`, the default reserved props are [`children`, `dangerouslySetInnerHTML` (only for DOM components), `key`, `ref`]. These props must be listed before all other props, but still respecting the alphabetical order:
 
 ```jsx
 <Hello key={0} ref={johnRef} name="John">
@@ -128,7 +128,18 @@ When `reservedFirst` is defined, React reserved props (`children`, `dangerouslyS
 </Hello>
 ```
 
-If given as an array, the array's values will override the default list of reserved props. **Note**: the values in the array may only be a **subset** of React reserved props.
+If given as an array, the array's values will override the default list of reserved props.
+These props will respect the order specified in the array:
+
+```jsx
+// 'jsx-sort-props': [1, { reservedFirst: ['v-if', 'v-for'] }]
+
+// before
+<App a v-for={i in list} v-if={list} b />
+
+// after
+<App v-if={list} v-for={i in list} a b />
+```
 
 With `reservedFirst: ["key"]`, the following will **not** warn:
 
@@ -136,6 +147,21 @@ With `reservedFirst: ["key"]`, the following will **not** warn:
 <Hello key={'uuid'} name="John" ref={johnRef} />
 ```
 
+### `reservedLast`
+
+This can be an array option. These props must be listed after all other props.
+These will respect the order specified in the array:
+
+```jsx
+// 'jsx-sort-props': [1, { reservedLast: ['v-slot'] }]
+
+// before
+<App v-slot={{ foo }} onClick={onClick} />
+
+// after
+<App onClick={onClick} v-slot={{ foo }} />
+```
+
 ### `locale`
 
 Defaults to `"auto"`, meaning, the locale of the current environment.
diff --git a/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.ts b/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.ts
index f4fc2e25..f39a9564 100644
--- a/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.ts
+++ b/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.ts
@@ -15,8 +15,9 @@ interface JsxCompareOptions {
   shorthandLast: boolean
   multiline: 'first' | 'ignore' | 'last'
   noSortAlphabetically: boolean
-  reservedFirst: boolean | unknown[]
-  reservedList: unknown[]
+  reservedFirst: boolean | string[]
+  reservedList: string[]
+  reservedLast: string[]
   locale: string
 }
 
@@ -29,9 +30,9 @@ function isMultilineProp(node: ASTNode) {
 }
 
 const messages = {
-  noUnreservedProps: 'A customized reserved first list must only contain a subset of React reserved props. Remove: {{unreservedWords}}',
   listIsEmpty: 'A customized reserved first list must not be empty',
   listReservedPropsFirst: 'Reserved props must be listed before all other props',
+  listReservedPropsLast: 'Reserved props must be listed after all other props',
   listCallbacksLast: 'Callbacks must be listed after all other props',
   listShorthandFirst: 'Shorthand props must be listed before all other props',
   listShorthandLast: 'Shorthand props must be listed after all other props',
@@ -47,8 +48,8 @@ const RESERVED_PROPS_LIST = [
   'ref',
 ]
 
-function isReservedPropName(name: unknown, list: unknown[]) {
-  return list.includes(name)
+function getReservedPropIndex(name: string, list: string[]) {
+  return list.indexOf(name.split(':')[0])
 }
 
 let attributeMap: WeakMap<Tree.JSXAttribute, { end: number, hasComment: boolean }>
@@ -62,6 +63,8 @@ function shouldSortToEnd(node: Tree.JSXAttribute) {
 function contextCompare(a: Tree.JSXAttribute, b: Tree.JSXAttribute, options: JsxCompareOptions) {
   let aProp = getPropName(a)
   let bProp = getPropName(b)
+  const aPropNamespace = aProp.split(':')[0]
+  const bPropNamespace = bProp.split(':')[0]
 
   const aSortToEnd = shouldSortToEnd(a)
   const bSortToEnd = shouldSortToEnd(b)
@@ -72,13 +75,29 @@ function contextCompare(a: Tree.JSXAttribute, b: Tree.JSXAttribute, options: Jsx
     return -1
 
   if (options.reservedFirst) {
-    const aIsReserved = isReservedPropName(aProp, options.reservedList)
-    const bIsReserved = isReservedPropName(bProp, options.reservedList)
-    if (aIsReserved && !bIsReserved)
+    const aIndex = getReservedPropIndex(aProp, options.reservedList)
+    const bIndex = getReservedPropIndex(bProp, options.reservedList)
+    if (aIndex > -1 && bIndex === -1)
       return -1
 
-    if (!aIsReserved && bIsReserved)
+    if (aIndex === -1 && bIndex > -1)
       return 1
+
+    if (aIndex > -1 && bIndex > -1 && aPropNamespace !== bPropNamespace)
+      return aIndex > bIndex ? 1 : -1
+  }
+
+  if (options.reservedLast.length > 0) {
+    const aLastIndex = getReservedPropIndex(aProp, options.reservedLast)
+    const bLastIndex = getReservedPropIndex(bProp, options.reservedLast)
+    if (aLastIndex > -1 && bLastIndex === -1)
+      return 1
+
+    if (aLastIndex === -1 && bLastIndex > -1)
+      return -1
+
+    if (aLastIndex > -1 && bLastIndex > -1 && aPropNamespace !== bPropNamespace)
+      return aLastIndex > bLastIndex ? -1 : 1
   }
 
   if (options.callbacksLast) {
@@ -214,7 +233,7 @@ function getGroupsOfSortableAttributes(attributes: (Tree.JSXAttribute | Tree.JSX
   return sortableAttributeGroups
 }
 
-function generateFixerFunction(node: Tree.JSXOpeningElement, context: Readonly<RuleContext<MessageIds, RuleOptions>>, reservedList: unknown[]) {
+function generateFixerFunction(node: Tree.JSXOpeningElement, context: Readonly<RuleContext<MessageIds, RuleOptions>>, reservedList: string[]) {
   const sourceCode = context.sourceCode
   const attributes = node.attributes.slice(0)
   const configuration = context.options[0] || {}
@@ -225,6 +244,7 @@ function generateFixerFunction(node: Tree.JSXOpeningElement, context: Readonly<R
   const multiline = configuration.multiline || 'ignore'
   const noSortAlphabetically = configuration.noSortAlphabetically || false
   const reservedFirst = configuration.reservedFirst || false
+  const reservedLast = configuration.reservedLast || []
   const locale = configuration.locale || 'auto'
 
   // Sort props according to the context. Only supports ignoreCase.
@@ -239,6 +259,7 @@ function generateFixerFunction(node: Tree.JSXOpeningElement, context: Readonly<R
     noSortAlphabetically,
     reservedFirst,
     reservedList,
+    reservedLast,
     locale,
   }
   const sortableAttributeGroups = getGroupsOfSortableAttributes(attributes, context)
@@ -286,12 +307,6 @@ function generateFixerFunction(node: Tree.JSXOpeningElement, context: Readonly<R
 function validateReservedFirstConfig(context: Readonly<RuleContext<MessageIds, RuleOptions>>, reservedFirst: unknown[] | boolean) {
   if (reservedFirst) {
     if (Array.isArray(reservedFirst)) {
-      // Only allow a subset of reserved words in customized lists
-      const nonReservedWords = reservedFirst.filter(word => !isReservedPropName(
-        word,
-        RESERVED_PROPS_LIST,
-      ))
-
       if (reservedFirst.length === 0) {
         return function Report(decl: ASTNode | Tree.Token) {
           context.report({
@@ -300,17 +315,6 @@ function validateReservedFirstConfig(context: Readonly<RuleContext<MessageIds, R
           })
         }
       }
-      if (nonReservedWords.length > 0) {
-        return function Report(decl: ASTNode | Tree.Token) {
-          context.report({
-            node: decl,
-            messageId: 'noUnreservedProps',
-            data: {
-              unreservedWords: nonReservedWords.toString(),
-            },
-          })
-        }
-      }
     }
   }
 }
@@ -326,7 +330,7 @@ const reportedNodeAttributes = new WeakMap()
  * @param context The context of the rule
  * @param reservedList The list of reserved props
  */
-function reportNodeAttribute(nodeAttribute: Tree.JSXAttribute | Tree.JSXSpreadAttribute, errorType: MessageIds, node: Tree.JSXOpeningElement, context: Readonly<RuleContext<MessageIds, RuleOptions>>, reservedList: unknown[]) {
+function reportNodeAttribute(nodeAttribute: Tree.JSXAttribute | Tree.JSXSpreadAttribute, errorType: MessageIds, node: Tree.JSXOpeningElement, context: Readonly<RuleContext<MessageIds, RuleOptions>>, reservedList: string[]) {
   const errors = reportedNodeAttributes.get(nodeAttribute) || []
 
   if (errors.includes(errorType))
@@ -388,6 +392,9 @@ export default createRule<RuleOptions, MessageIds>({
         reservedFirst: {
           type: ['array', 'boolean'],
         },
+        reservedLast: {
+          type: 'array',
+        },
         locale: {
           type: 'string',
           default: 'auto',
@@ -408,6 +415,7 @@ export default createRule<RuleOptions, MessageIds>({
     const reservedFirst = configuration.reservedFirst || false
     const reservedFirstError = validateReservedFirstConfig(context, reservedFirst)
     const reservedList = Array.isArray(reservedFirst) ? reservedFirst : RESERVED_PROPS_LIST
+    const reservedLastList = configuration.reservedLast || []
     const locale = configuration.locale || 'auto'
 
     return {
@@ -425,6 +433,8 @@ export default createRule<RuleOptions, MessageIds>({
 
           let previousPropName = getPropName(memo)
           let currentPropName = getPropName(decl)
+          const previousReservedNamespace = previousPropName.split(':')[0]
+          const currentReservedNamespace = currentPropName.split(':')[0]
           const previousValue = (<Tree.JSXAttribute>memo).value
           const currentValue = decl.value
           const previousIsCallback = isCallbackPropName(previousPropName)
@@ -441,17 +451,52 @@ export default createRule<RuleOptions, MessageIds>({
               return memo
             }
 
-            const previousIsReserved = isReservedPropName(previousPropName, nodeReservedList)
-            const currentIsReserved = isReservedPropName(currentPropName, nodeReservedList)
+            const previousReservedIndex = getReservedPropIndex(previousPropName, nodeReservedList)
+            const currentReservedIndex = getReservedPropIndex(currentPropName, nodeReservedList)
 
-            if (previousIsReserved && !currentIsReserved)
+            if (previousReservedIndex > -1 && currentReservedIndex === -1)
               return decl
 
-            if (!previousIsReserved && currentIsReserved) {
+            if (
+              reservedFirst !== true && previousReservedIndex > currentReservedIndex
+              || previousReservedIndex === -1 && currentReservedIndex > -1
+            ) {
               reportNodeAttribute(decl, 'listReservedPropsFirst', node, context, nodeReservedList)
 
               return memo
             }
+
+            if (
+              previousReservedIndex > -1
+              && currentReservedIndex > -1
+              && currentReservedIndex > previousReservedIndex
+              && previousReservedNamespace !== currentReservedNamespace
+            ) {
+              return decl
+            }
+          }
+
+          if (reservedLastList.length > 0) {
+            const previousReservedIndex = getReservedPropIndex(previousPropName, reservedLastList)
+            const currentReservedIndex = getReservedPropIndex(currentPropName, reservedLastList)
+
+            if (previousReservedIndex === -1 && currentReservedIndex > -1)
+              return decl
+
+            if (previousReservedIndex < currentReservedIndex || previousReservedIndex > -1 && currentReservedIndex === -1) {
+              reportNodeAttribute(decl, 'listReservedPropsLast', node, context, nodeReservedList)
+
+              return memo
+            }
+
+            if (
+              previousReservedIndex > -1
+              && currentReservedIndex > -1
+              && currentReservedIndex > previousReservedIndex
+              && previousReservedNamespace !== currentReservedNamespace
+            ) {
+              return decl
+            }
           }
 
           if (callbacksLast) {
diff --git a/packages/eslint-plugin/rules/jsx-sort-props/types.d.ts b/packages/eslint-plugin/rules/jsx-sort-props/types.d.ts
index 71e659b6..b1a283d9 100644
--- a/packages/eslint-plugin/rules/jsx-sort-props/types.d.ts
+++ b/packages/eslint-plugin/rules/jsx-sort-props/types.d.ts
@@ -9,7 +9,8 @@ export interface JsxSortPropsSchema0 {
   multiline?: 'ignore' | 'first' | 'last'
   ignoreCase?: boolean
   noSortAlphabetically?: boolean
-  reservedFirst?: unknown[] | boolean
+  reservedFirst?: string[] | boolean
+  reservedLast?: string[]
   locale?: string
 }
 
@@ -17,9 +18,9 @@ export type JsxSortPropsRuleOptions = [JsxSortPropsSchema0?]
 
 export type RuleOptions = JsxSortPropsRuleOptions
 export type MessageIds =
-  | 'noUnreservedProps'
   | 'listIsEmpty'
   | 'listReservedPropsFirst'
+  | 'listReservedPropsLast'
   | 'listCallbacksLast'
   | 'listShorthandFirst'
   | 'listShorthandLast'
diff --git a/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.ts b/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.ts
index 5e30f719..246e6686 100644
--- a/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.ts
+++ b/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.ts
@@ -704,6 +704,8 @@ export default createRule<RuleOptions, MessageIds>({
           if (
             !(['AwaitExpression', 'UnaryExpression'].includes(n.left.type) && isExponentiation)
             && !isMixedLogicalAndCoalesceExpressions(n.left, n)
+            // The parent is a ReturnStatement spanning multiple lines without parentheses
+            && !(n.parent.type === 'ReturnStatement' && n.parent.loc.start.line !== n.left.loc.start.line && !isParenthesised(n))
             && (leftPrecedence > prec || (leftPrecedence === prec && !isExponentiation))
             || isParenthesisedTwice(n.left)
           ) {


Test patch:
diff --git a/packages/eslint-plugin/rules/indent/indent._ts_.test.ts b/packages/eslint-plugin/rules/indent/indent._ts_.test.ts
index 4f4b75c5..a448954f 100644
--- a/packages/eslint-plugin/rules/indent/indent._ts_.test.ts
+++ b/packages/eslint-plugin/rules/indent/indent._ts_.test.ts
@@ -2,7 +2,7 @@
 
 import type { InvalidTestCase, TestCaseError, TestCasesOptions, ValidTestCase } from '#test'
 import type { MessageIds, RuleOptions } from './types'
-import { run } from '#test'
+import { $, run } from '#test'
 import { AST_NODE_TYPES } from '@typescript-eslint/utils'
 import rule from '.'
 
@@ -781,6 +781,47 @@ const map2 = Object.keys(map)
       `,
       options: [2],
     },
+    {
+      code: $`
+        class Some {
+          range: {
+            index?: number
+            length?: number
+          } = {
+            index: 0,
+            length: 0
+          }
+        }
+      `,
+      options: [2],
+    },
+    {
+      code: $`
+        const some: {
+          index?: number
+          length?: number
+        } = {
+          index: 0,
+          length: 0
+        }
+      `,
+      options: [2],
+    },
+    {
+      code: $`
+        const some: {
+          index?: number
+          length?: number
+        } = {
+          index: 0,
+          length: 0
+        } as {
+          index?: number
+          length?: number
+        }
+      `,
+      options: [2],
+    },
   ],
   invalid: [
     ...individualNodeTests.invalid!,
diff --git a/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.test.ts b/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.test.ts
index 4f2b6a50..45aac6a1 100644
--- a/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.test.ts
+++ b/packages/eslint-plugin/rules/jsx-sort-props/jsx-sort-props._jsx_.test.ts
@@ -13,6 +13,10 @@ const expectedError: TestCaseError<MessageIds> = {
   messageId: 'sortPropsByAlpha',
   type: 'JSXIdentifier',
 }
+const expectedWithNamespaceError: TestCaseError<MessageIds> = {
+  messageId: 'sortPropsByAlpha',
+  nodeType: 'JSXNamespacedName',
+}
 const expectedCallbackError: TestCaseError<MessageIds> = {
   messageId: 'listCallbacksLast',
   type: 'JSXIdentifier',
@@ -37,13 +41,17 @@ const expectedReservedFirstError: TestCaseError<MessageIds> = {
   messageId: 'listReservedPropsFirst',
   type: 'JSXIdentifier',
 }
+const expectedReservedLastError: TestCaseError<MessageIds> = {
+  messageId: 'listReservedPropsLast',
+  type: 'JSXIdentifier',
+}
+const expectedReservedFirstWithNamespaceError: TestCaseError<MessageIds> = {
+  messageId: 'listReservedPropsFirst',
+  type: 'JSXNamespacedName',
+}
 const expectedEmptyReservedFirstError: TestCaseError<MessageIds> = {
   messageId: 'listIsEmpty',
 }
-const expectedInvalidReservedFirstError: TestCaseError<MessageIds> = {
-  messageId: 'noUnreservedProps',
-  data: { unreservedWords: 'notReserved' },
-}
 const callbacksLastArgs: RuleOptions = [{ callbacksLast: true }]
 const ignoreCaseAndCallbackLastArgs: RuleOptions = [
   {
@@ -83,7 +91,6 @@ const reservedFirstWithShorthandLast: RuleOptions = [
   },
 ]
 const reservedFirstAsEmptyArrayArgs: RuleOptions = [{ reservedFirst: [] }]
-const reservedFirstAsInvalidArrayArgs: RuleOptions = [{ reservedFirst: ['notReserved'] }]
 const multilineFirstArgs: RuleOptions = [{ multiline: 'first' }]
 const multilineAndShorthandFirstArgs: RuleOptions = [
   {
@@ -510,6 +517,30 @@ run<RuleOptions, MessageIds>({
       options: reservedFirstWithShorthandLast,
       output: '<App key="key" ref="ref" veryLastAttribute="yes" isShorthand />',
     },
+    {
+      code: '<App a v-for={i in 4} v-if={true} b />',
+      errors: [expectedReservedFirstError, expectedReservedFirstError],
+      options: [{ reservedFirst: ['v-if', 'v-for'] }],
+      output: '<App v-if={true} v-for={i in 4} a b />',
+    },
+    {
+      code: '<App v-slot={{ foo }} v-slots={{}} onClick={() => {}} />',
+      errors: [expectedReservedLastError, expectedReservedLastError],
+      options: [{ reservedLast: ['v-slot', 'v-slots'] }],
+      output: '<App onClick={() => {}} v-slots={{}} v-slot={{ foo }} />',
+    },
+    {
+      code: '<App a v-model:b={foo} b v-model={foo} c v-slot:b={{ foo }} v-slot:a={{ foo }} onClick={() => {}} />',
+      errors: [expectedReservedFirstWithNamespaceError, expectedReservedFirstError, expectedWithNamespaceError, expectedReservedLastError],
+      options: [{ reservedFirst: ['v-model'], reservedLast: ['v-slot'] }],
+      output: '<App v-model={foo} v-model:b={foo} a b c onClick={() => {}} v-slot:a={{ foo }} v-slot:b={{ foo }} />',
+    },
+    {
+      code: '<App a v-model:b={foo} b v-model={foo} c v-slot:b={{ foo }} v-slot:a={{ foo }} onClick={() => {}} />',
+      errors: [expectedReservedFirstWithNamespaceError, expectedReservedFirstError, expectedReservedLastError],
+      options: [{ noSortAlphabetically: true, reservedFirst: ['v-model'], reservedLast: ['v-slot'] }],
+      output: '<App v-model:b={foo} v-model={foo} a b c onClick={() => {}} v-slot:b={{ foo }} v-slot:a={{ foo }} />',
+    },
     {
       code: '<App a z onFoo onBar />;',
       errors: [expectedError],
@@ -592,7 +623,7 @@ run<RuleOptions, MessageIds>({
     {
       code: '<App key={3} children={<App />} />',
       options: reservedFirstAsArrayArgs,
-      errors: [expectedError],
+      errors: [expectedReservedFirstError],
       output: '<App children={<App />} key={3} />',
     },
     {
@@ -606,11 +637,6 @@ run<RuleOptions, MessageIds>({
       options: reservedFirstAsEmptyArrayArgs,
       errors: [expectedEmptyReservedFirstError],
     },
-    {
-      code: '<App key={5} />',
-      options: reservedFirstAsInvalidArrayArgs,
-      errors: [expectedInvalidReservedFirstError],
-    },
     {
       code: '<App onBar z />;',
       output: '<App z onBar />;',
diff --git a/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.test.ts b/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.test.ts
index 98f7a0f4..bb97592c 100644
--- a/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.test.ts
+++ b/packages/eslint-plugin/rules/no-extra-parens/no-extra-parens._ts_.test.ts
@@ -27,6 +27,13 @@ run<RuleOptions, MessageIds>({
     'a<import(\'\')>(1);',
     'new a<import(\'\')>(1);',
     'a<A>(1);',
+    {
+      code: $`
+        return (
+          a % b == 0
+        )
+      `,
+    },
     {
       code: '(++(<A>a))(b); ((c as C)++)(d);',
       parserOptions: { ecmaFeatures: { jsx: false } },
@@ -792,6 +799,23 @@ run<RuleOptions, MessageIds>({
         },
       ],
     },
+    {
+      code: $`
+        function fn(){
+          return (a==b)
+        }
+      `,
+      output: $`
+        function fn(){
+          return a==b
+        }
+      `,
+      errors: [
+        {
+          messageId: 'unexpected',
+        },
+      ],
+    },
     {
       code: 'const x = (a as string)',
       output: 'const x = a as string',
@@ -829,5 +853,92 @@ run<RuleOptions, MessageIds>({
         },
       ],
     },
+    // https://github.com/eslint-stylistic/eslint-stylistic/issues/699
+    {
+      code: `
+        ((a, b) => {
+          return (
+            a % b == 0
+          ) || (a % b == 1)
+        })()
+      `,
+      output: `
+        ((a, b) => {
+          return (
+            a % b == 0
+          ) || a % b == 1
+        })()
+      `,
+      errors: [
+        {
+          messageId: 'unexpected',
+        },
+      ],
+    },
+    {
+      code: `
+        ((a, b) => {
+          return (
+            (a % b == 0)
+            || a % b == 1
+          )
+        })()
+      `,
+      output: `
+        ((a, b) => {
+          return (
+            a % b == 0
+            || a % b == 1
+          )
+        })()
+      `,
+      errors: [
+        {
+          messageId: 'unexpected',
+        },
+      ],
+    },
+    {
+      code: `
+        ((a, b) => {
+          return (a % b == 0)
+            || (a % b == 1)
+        })()
+      `,
+      output: `
+        ((a, b) => {
+          return a % b == 0
+            || a % b == 1
+        })()
+      `,
+      errors: [
+        {
+          messageId: 'unexpected',
+        },
+        {
+          messageId: 'unexpected',
+        },
+      ],
+    },
+    {
+      code: `
+        (a, b) => {
+          return (a % b == 0) || (a % b == 1)
+        }
+      `,
+      output: `
+        (a, b) => {
+          return a % b == 0 || a % b == 1
+        }
+      `,
+      errors: [
+        {
+          messageId: 'unexpected',
+        },
+        {
+          messageId: 'unexpected',
+        },
+      ],
+    },
   ],
 })