org.sonarsource.slang.checks.UnusedFunctionParameterCheck Maven / Gradle / Ivy
/*
* SonarSource SLang
* Copyright (C) 2018-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 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/
*/
package org.sonarsource.slang.checks;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
import org.sonar.check.Rule;
import org.sonarsource.slang.api.FunctionDeclarationTree;
import org.sonarsource.slang.api.IdentifierTree;
import org.sonarsource.slang.api.ParameterTree;
import org.sonarsource.slang.api.TopLevelTree;
import org.sonarsource.slang.checks.api.CheckContext;
import org.sonarsource.slang.checks.api.InitContext;
import org.sonarsource.slang.checks.api.SecondaryLocation;
import org.sonarsource.slang.checks.api.SlangCheck;
import static org.sonarsource.slang.checks.utils.FunctionUtils.isOverrideMethod;
import static org.sonarsource.slang.checks.utils.FunctionUtils.isPrivateMethod;
import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent;
@Rule(key = "S1172")
public class UnusedFunctionParameterCheck implements SlangCheck {
// Currently we ignore all functions named "main", however this should be configurable based on the analyzed language in the future.
protected static final Pattern IGNORED_PATTERN = Pattern.compile("main", Pattern.CASE_INSENSITIVE);
@Override
public void initialize(InitContext init) {
init.register(FunctionDeclarationTree.class, (ctx, functionDeclarationTree) -> {
if (functionDeclarationTree.isConstructor() || shouldBeIgnored(ctx, functionDeclarationTree)) {
return;
}
List unusedParameters = getUnusedParameters(functionDeclarationTree);
if (unusedParameters.isEmpty()) {
return;
}
reportUnusedParameters(ctx, unusedParameters);
});
}
protected static List getUnusedParameters(FunctionDeclarationTree functionDeclarationTree) {
return functionDeclarationTree.formalParameters().stream()
.filter(ParameterTree.class::isInstance)
.map(ParameterTree.class::cast)
.filter(parameterTree -> parameterTree.modifiers().isEmpty() && functionDeclarationTree.descendants()
.noneMatch(tree -> !tree.equals(parameterTree.identifier()) && areEquivalent(tree, parameterTree.identifier())))
.toList();
}
protected void reportUnusedParameters(CheckContext ctx, List unusedParameters) {
List secondaryLocations = unusedParameters.stream()
.map(unusedParameter -> {
// Identifier can be null only in case of Scala. In that case modifiers like 'using' should be used.
// If modifiers are present, such parameters won't be checked by this rule.
IdentifierTree identifier = unusedParameter.identifier();
Objects.requireNonNull(identifier, "Identifier for an unused parameter is null");
return new SecondaryLocation(identifier, "Remove this unused method parameter " + identifier.name() + "\".");
})
.toList();
IdentifierTree firstUnused = unusedParameters.get(0).identifier();
Objects.requireNonNull(firstUnused, "Identifier for an unused parameter is null");
String msg;
if (unusedParameters.size() > 1) {
msg = "Remove these unused function parameters.";
} else {
msg = "Remove this unused function parameter \"" + firstUnused.name() + "\".";
}
ctx.reportIssue(firstUnused, msg, secondaryLocations);
}
protected boolean isValidFunctionForRule(CheckContext ctx, FunctionDeclarationTree tree) {
return ctx.parent() instanceof TopLevelTree || (isPrivateMethod(tree) && !isOverrideMethod(tree));
}
protected boolean shouldBeIgnored(CheckContext ctx, FunctionDeclarationTree tree) {
IdentifierTree name = tree.name();
boolean validFunctionForRule = isValidFunctionForRule(ctx, tree);
return !validFunctionForRule
|| tree.body() == null
|| (name != null && IGNORED_PATTERN.matcher(name.name()).matches());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy