Skip to content

Commit cba6306

Browse files
sonar-nigel[bot]Vibe Bot
andauthored
JS-1580 Fix S1788 false positive for Redux reducer with destructured action (#6819)
Co-authored-by: Vibe Bot <vibe-bot@sonarsource.com>
1 parent cb8a1d8 commit cba6306

3 files changed

Lines changed: 53 additions & 13 deletions

File tree

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,28 @@
1-
function multiply(b, a = 1) {}
1+
const initialState = {};
22

3-
function multiply(b = 2, a = 1) {}
3+
function multiplyDefaultLast(b, a = 1) { return b * a; }
44

5-
function appReducer(state = initialState, action) {}
5+
function multiplyBothDefault(b = 2, a = 1) { return b * a; }
66

7-
function multiply(a = 1, b) {} // Noncompliant {{Default parameters should be last.}}
7+
function appReducerNamed(state = initialState, action) { return state; }
8+
9+
// Redux reducer with destructured action containing 'type' property
10+
function appReducerWithType(state = initialState, { type }) { return state; }
11+
12+
const dataReducer = (state = null, { type, payload }) => state;
13+
14+
export const isPanelOpen = (state = false, { type, isShowing }) =>
15+
type === 'SET_IS_SHOWING' ? isShowing : state;
16+
17+
function multiply(a = 1, b) { return a * b; } // Noncompliant {{Default parameters should be last.}}
818
// ^^^^^
919

10-
function appReducer(state = initialState, action, param) {} // Noncompliant {{Default parameters should be last.}}
20+
function appReducer(state = initialState, action, param) { return state; } // Noncompliant {{Default parameters should be last.}}
1121
// ^^^^^^^^^^^^^^^^^^^^
1222

13-
function appReducer(status = initialState, action) {} // Noncompliant {{Default parameters should be last.}}
14-
// ^^^^^^^^^^^^^^^^^^^^^
23+
function appReducerWrongParam(status = initialState, action) { return status; } // Noncompliant {{Default parameters should be last.}}
24+
// ^^^^^^^^^^^^^^^^^^^^^
25+
26+
// Destructured action without 'type' is not a Redux reducer
27+
function notReducer(state = initialState, { payload }) { return state; } // Noncompliant {{Default parameters should be last.}}
28+
// ^^^^^^^^^^^^^^^^^^^^

packages/analysis/src/jsts/rules/S1788/cb.test.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,32 @@
1515
* along with this program; if not, see https://sonarsource.com/license/ssal/
1616
*/
1717
import { test } from '../../../../tests/jsts/tools/testers/comment-based/checker.js';
18+
import { DefaultParserRuleTester } from '../../../../tests/jsts/tools/testers/rule-tester.js';
1819
import { rule } from './index.js';
19-
import { describe } from 'node:test';
20+
import { rules as tsEslintRules } from '../external/typescript-eslint/index.js';
21+
import { describe, it } from 'node:test';
2022
import * as meta from './generated-meta.js';
2123

24+
const upstreamRule = tsEslintRules['default-param-last'];
25+
26+
// Sentinel: verify that the upstream ESLint rule still raises on the patterns our decorator fixes.
27+
// If this test starts failing (i.e., the upstream rule no longer reports these patterns),
28+
// it signals that the decorator can be safely removed.
29+
describe('S1788 upstream sentinel', () => {
30+
it('upstream default-param-last raises on destructured action patterns that decorator suppresses', () => {
31+
const ruleTester = new DefaultParserRuleTester();
32+
ruleTester.run('default-param-last', upstreamRule, {
33+
valid: [],
34+
invalid: [
35+
// (state = x, { type }) — suppressed by decorator, raised by upstream
36+
{ code: `function appReducer(state = initialState, { type }) {}`, errors: 1 },
37+
// (state = x, { type, payload }) — suppressed by decorator, raised by upstream
38+
{ code: `const dataReducer = (state = null, { type, payload }) => {};`, errors: 1 },
39+
],
40+
});
41+
});
42+
});
43+
2244
describe('Rule S1788', () => {
2345
test(meta, rule, import.meta.dirname);
2446
});

packages/analysis/src/jsts/rules/S1788/decorator.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,15 @@ function reportExempting(
5454
function isReduxReducer(enclosingFunction: BaseFunction) {
5555
if (enclosingFunction.params.length === NUM_ARGS_REDUX_REDUCER) {
5656
const [firstParam, secondParam] = enclosingFunction.params;
57-
return (
58-
firstParam.type === 'AssignmentPattern' &&
59-
isIdentifier(firstParam.left, 'state') &&
60-
isIdentifier(secondParam, 'action')
61-
);
57+
if (firstParam.type === 'AssignmentPattern' && isIdentifier(firstParam.left, 'state')) {
58+
return (
59+
isIdentifier(secondParam, 'action') ||
60+
(secondParam.type === 'ObjectPattern' &&
61+
secondParam.properties.some(
62+
prop => prop.type === 'Property' && isIdentifier(prop.key, 'type'),
63+
))
64+
);
65+
}
6266
}
6367
return false;
6468
}

0 commit comments

Comments
 (0)