eslint-plugin-sonarjs
Version:
SonarJS rules for ESLint
149 lines (148 loc) • 5.93 kB
JavaScript
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2025 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 Sonar Source-Available License Version 1, as published by SonarSource SA.
*
* 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 Sonar Source-Available License for more details.
*
* You should have received a copy of the Sonar Source-Available License
* along with this program; if not, see https://sonarsource.com/license/ssal/
*/
// https://sonarsource.github.io/rspec/#/rspec/S1172/javascript
Object.defineProperty(exports, "__esModule", { value: true });
exports.rule = void 0;
exports.isParameterProperty = isParameterProperty;
const index_js_1 = require("../helpers/index.js");
const meta_js_1 = require("./meta.js");
exports.rule = {
meta: (0, index_js_1.generateMeta)(meta_js_1.meta, {
hasSuggestions: true,
messages: {
removeOrRenameParameter: 'Remove the unused function parameter "{{param}}" or rename it to "_{{param}}" to make intention explicit.',
suggestRemoveParameter: 'Remove "{{param}}" (beware of call sites)',
suggestRenameParameter: 'Rename "{{param}}" to "_{{param}}"',
},
}),
create(context) {
return {
'FunctionDeclaration, FunctionExpression'(node) {
reportUnusedArgument(node, node.id, context);
},
ArrowFunctionExpression: (node) => {
reportUnusedArgument(node, undefined, context);
},
};
},
};
function reportUnusedArgument(node, functionId, context) {
const parent = node.parent;
if (parent && parent.type === 'Property' && parent.kind === 'set') {
return;
}
if (context.sourceCode
.getScope(node)
.variables.some(v => v.name === 'arguments' && v.identifiers.length === 0 && v.references.length > 0)) {
return;
}
let parametersVariable = context.sourceCode.getDeclaredVariables(node);
if (functionId) {
parametersVariable = parametersVariable.filter(v => v.name !== functionId.name);
}
for (const param of parametersVariable) {
if (isUnusedVariable(param) &&
!isIgnoredParameter(param) &&
!isParameterProperty(param) &&
!isThisParameter(param)) {
context.report({
messageId: 'removeOrRenameParameter',
node: param.identifiers[0],
data: {
param: param.name,
},
suggest: getSuggestions(param, context),
});
}
}
}
function getSuggestions(paramVariable, context) {
const paramIdentifier = paramVariable.identifiers[0];
const suggestions = [
{
messageId: 'suggestRenameParameter',
data: {
param: paramVariable.name,
},
fix: fixer => fixer.insertTextBefore(paramIdentifier, '_'),
},
];
const func = paramVariable.defs[0].node;
if (paramIdentifier.parent === func) {
suggestions.push(getParameterRemovalSuggestion(func, paramVariable, paramIdentifier, context));
}
return suggestions;
}
function getParameterRemovalSuggestion(func, paramVariable, paramIdentifier, context) {
return {
messageId: 'suggestRemoveParameter',
data: {
param: paramVariable.name,
},
fix: fixer => {
const paramIndex = func.params.indexOf(paramIdentifier);
const param = func.params[paramIndex];
if (func.params.length === 1) {
const openingParenthesis = context.sourceCode.getTokenBefore(param);
const closingParenthesis = context.sourceCode.getTokenAfter(param, token => token.value === ')');
let [start, end] = param.range;
if (openingParenthesis && openingParenthesis.value === '(') {
start = openingParenthesis.range[0];
end = closingParenthesis.range[1];
}
return fixer.replaceTextRange([start, end], '()');
}
else if (func.params.length - 1 === paramIndex) {
const commaAfter = context.sourceCode.getTokenAfter(param, token => token.value === ',');
const commaBefore = context.sourceCode.getTokenBefore(param, token => token.value === ',');
let start = commaBefore.range[1];
let end = param.range[1];
if (commaAfter) {
end = commaAfter.range[1];
}
else {
start = commaBefore.range[0];
}
return fixer.removeRange([start, end]);
}
else {
const [start] = func.params[paramIndex].range;
const [end] = func.params[paramIndex + 1].range;
return fixer.removeRange([start, end]);
}
},
};
}
function isUnusedVariable(variable) {
const refs = variable.references;
//Parameter with default value has one reference, but should still be considered as unused.
return refs.length === 0 || (refs.length === 1 && refs[0].init);
}
function isIgnoredParameter(variable) {
return variable.name.startsWith('_');
}
function isParameterProperty(variable) {
return variable.defs.some(def => {
const parent = def.name.parent;
return (parent?.type === 'TSParameterProperty' ||
(parent?.type === 'AssignmentPattern' && parent.parent?.type === 'TSParameterProperty'));
});
}
function isThisParameter(variable) {
return variable.name === 'this';
}
;