Skip to content

Commit

Permalink
Create rule S6959: "Array.reduce()" calls should include an initial v…
Browse files Browse the repository at this point in the history
…alue
  • Loading branch information
yassin-kammoun-sonarsource committed Mar 26, 2024
1 parent 3e0b6d3 commit 5f2dbfe
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 1 deletion.
@@ -0,0 +1,5 @@
{
"ant-design:components/anchor/Anchor.tsx": [
189
]
}
11 changes: 11 additions & 0 deletions its/ruling/src/test/expected/jsts/desktop/typescript-S6959.json
@@ -0,0 +1,11 @@
{
"desktop:app/src/lib/parse-carriage-return.ts": [
30
],
"desktop:app/src/main-process/menu/build-default-menu.ts": [
644
],
"desktop:app/src/ui/donut.tsx": [
60
]
}
13 changes: 13 additions & 0 deletions packages/jsts/src/rules/S6959/cb.fixture.ts
@@ -0,0 +1,13 @@
function noncompliant(xs: number[]) {
return xs.reduce((acc, x) => acc + x); // Noncompliant {{Add an initial value to this "reduce()" call.}}
}

function compliant(xs: number[]) {
return xs.reduce((acc, x) => acc + x, 0); // Compliant
}

function coverage(x: any) {
x.m();
x.reduce();
x.reduce(42);
}
28 changes: 28 additions & 0 deletions packages/jsts/src/rules/S6959/cb.test.ts
@@ -0,0 +1,28 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { check } from '../tools';
import { rule } from './';
import path from 'path';

const sonarId = path.basename(__dirname);

describe('Rule S6959', () => {
check(sonarId, rule, __dirname);
});
20 changes: 20 additions & 0 deletions packages/jsts/src/rules/S6959/index.ts
@@ -0,0 +1,20 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
export { rule } from './rule';
48 changes: 48 additions & 0 deletions packages/jsts/src/rules/S6959/rule.ts
@@ -0,0 +1,48 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// https://sonarsource.github.io/rspec/#/rspec/S6959/javascript

import { Rule } from 'eslint';
import { isArray, isCallingMethod, isRequiredParserServices } from '../helpers';

export const rule: Rule.RuleModule = {
meta: {
messages: {
message: 'Add an initial value to this "reduce()" call.',
},
},
create(context: Rule.RuleContext) {
const services = context.parserServices;
if (!isRequiredParserServices(services)) {
return {};
}

return {
CallExpression(node) {
if (isCallingMethod(node, 1, 'reduce') && isArray(node.callee.object, services)) {
context.report({
node: node.callee.property,
messageId: 'message',
});
}
},
};
},
};
31 changes: 31 additions & 0 deletions packages/jsts/src/rules/S6959/unit.test.ts
@@ -0,0 +1,31 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { RuleTester } from 'eslint';
import { rule } from './';

const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2018 } });
ruleTester.run('"Array.reduce()" calls should include an initial value', rule, {
valid: [
{
code: 'xs.reduce((acc, x) => acc + x);',
},
],
invalid: [],
});
2 changes: 2 additions & 0 deletions packages/jsts/src/rules/index.ts
Expand Up @@ -244,6 +244,7 @@ import { rule as S4507 } from './S4507'; // production-debug
import { rule as S2245 } from './S2245'; // pseudo-random
import { rule as S1444 } from './S1444'; // public-static-readonly
import { rule as S5443 } from './S5443'; // publicly-writable-directories
import { rule as S6959 } from './S6959'; // reduce-initial-value
import { rule as S6564 } from './S6564'; // redundant-type-aliases
import { rule as S5843 } from './S5843'; // regex-complexity
import { rule as S4784 } from './S4784'; // regular-expr
Expand Down Expand Up @@ -534,6 +535,7 @@ rules['production-debug'] = S4507;
rules['pseudo-random'] = S2245;
rules['public-static-readonly'] = S1444;
rules['publicly-writable-directories'] = S5443;
rules['reduce-initial-value'] = S6959;
rules['redundant-type-aliases'] = S6564;
rules['regex-complexity'] = S5843;
rules['regular-expr'] = S4784;
Expand Down
Expand Up @@ -394,6 +394,7 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
PubliclyWritableDirectoriesCheck.class,
ReassignedParameterCheck.class,
RedeclaredSymbolCheck.class,
ReduceInitialValueCheck.class,
RedundantAssignmentCheck.class,
RedundantTypeAliasesCheck.class,
ReferenceErrorCheck.class,
Expand Down
@@ -0,0 +1,36 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.javascript.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.javascript.api.EslintBasedCheck;
import org.sonar.plugins.javascript.api.JavaScriptRule;
import org.sonar.plugins.javascript.api.TypeScriptRule;

@JavaScriptRule
@TypeScriptRule
@Rule(key = "S6959")
public class ReduceInitialValueCheck implements EslintBasedCheck {

@Override
public String eslintKey() {
return "reduce-initial-value";
}
}
@@ -0,0 +1,32 @@
<h2>Why is this an issue?</h2>
<p>The <code>Array.prototype.reduce()</code> method in JavaScript is used to apply a function against an accumulator and each element in the array
(from left to right) to reduce it to a single output value. It is a convenient method that can simplify logic in your code.</p>
<p>However, it’s important to always provide an initial value as the second argument to <code>reduce()</code>. The initial value is used as the first
argument to the first call of the callback function. If no initial value is supplied, JavaScript will use the first element of the array as the
initial accumulator value and start iterating at the second element.</p>
<p>This can lead to runtime errors if the array is empty, as <code>reduce()</code> will throw a TypeError.</p>
<pre data-diff-id="1" data-diff-type="noncompliant">
function sum(xs) {
return xs.reduce((acc, current) =&gt; acc + current); // Noncompliant
}
console.log(sum([1, 2, 3, 4, 5])); // Prints 15
console.log(sum([])); // TypeError: Reduce of empty array with no initial value
</pre>
<p>To fix this, always provide an initial value as the second argument to <code>reduce()</code>.</p>
<pre data-diff-id="1" data-diff-type="compliant">
function sum(xs) {
return xs.reduce((acc, current) =&gt; acc + current, 0);
}
console.log(sum([1, 2, 3, 4, 5])); // Prints 15
console.log(sum([])); // Prints 0
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li> MDN web docs - <a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce">Array.prototype.reduce()</a> </li>
<li> MDN web docs - <a
href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Reduce_of_empty_array_with_no_initial_value">TypeError: Reduce of
empty array with no initial value</a> </li>
</ul>

@@ -0,0 +1,27 @@
{
"title": "\"Array.reduce()\" calls should include an initial value",
"type": "BUG",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"type-dependent"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-6959",
"sqKey": "S6959",
"scope": "All",
"quickfix": "targeted",
"code": {
"impacts": {
"RELIABILITY": "MEDIUM"
},
"attribute": "CONVENTIONAL"
},
"compatibleLanguages": [
"JAVASCRIPT",
"TYPESCRIPT"
]
}
Expand Up @@ -327,6 +327,7 @@
"S6854",
"S6855",
"S6859",
"S6861"
"S6861",
"S6959"
]
}

0 comments on commit 5f2dbfe

Please sign in to comment.