Skip to content

Commit

Permalink
feat: add method-may-be-static analyzer (#49)
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinWitt committed Feb 1, 2022
1 parent 296fda8 commit c1ce0b9
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import spoon.reflect.declaration.CtType;
import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.AbstractRefactoring;
import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.MethodMayBeStatic;
import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.NonProtectedConstructorInAbstractClass;
import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.ParameterNameDiffersFromOverriddenParameter;
import xyz.keksdose.spoon.code_solver.analyzer.qodana.rules.UnnecessaryInterfaceModifier;
Expand Down Expand Up @@ -41,6 +42,7 @@ public QodanaRefactor(ChangeListener listener) {
ruleParser.put("NonProtectedConstructorInAbstractClass", NonProtectedConstructorInAbstractClass::new);
ruleParser.put("UnnecessaryInterfaceModifier", UnnecessaryInterfaceModifier::new);
ruleParser.put("ParameterNameDiffersFromOverriddenParameter", ParameterNameDiffersFromOverriddenParameter::new);
ruleParser.put("MethodMayBeStatic", MethodMayBeStatic::new);
}

/**
Expand All @@ -63,5 +65,4 @@ public void process(CtType<?> type) {
refactoring.refactor(listener, type);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@ private CreateContainerResponse createQodanaContainer(DockerClient dockerClient,
.withAttachStderr(true)
.withAttachStdout(true)
.withCmd("-d", "./src/main/java")
.withUser(System.getProperty("user.name"))
.exec();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@

package xyz.keksdose.spoon.code_solver.analyzer.qodana.rules;

import java.nio.file.Path;
import java.util.LinkedHashSet;
import java.util.List;

import com.contrastsecurity.sarif.Result;
import com.google.common.flogger.FluentLogger;

import org.apache.commons.lang3.reflect.FieldUtils;

import spoon.reflect.code.CtExecutableReferenceExpression;
import spoon.reflect.code.CtTypeAccess;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.ModifierKind;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.reflect.CtExtendedModifier;
import xyz.keksdose.spoon.code_solver.history.Change;
import xyz.keksdose.spoon.code_solver.history.ChangeListener;
import xyz.keksdose.spoon.code_solver.history.MarkdownString;
import xyz.keksdose.spoon.code_solver.transformations.BadSmell;

public class MethodMayBeStatic extends AbstractRefactoring {

private static final FluentLogger logger = FluentLogger.forEnclosingClass();
private static final BadSmell METHOD_MAY_BE_STATIC = new BadSmell() {
@Override
public MarkdownString getName() {
return MarkdownString.fromRaw("Method-may-be-static");
}

@Override
public MarkdownString getDescription() {
return MarkdownString.fromRaw("Method can be static. This increases the performance of the application.");
}
};
public MethodMayBeStatic(Result result) {
super(result);
}

@Override
public void refactor(ChangeListener listener, CtType<?> type) {
if (type.isAnonymous() || !isSameType(type,
Path.of(result.getLocations().get(0).getPhysicalLocation().getArtifactLocation().getUri()))) {
return;
}
for (CtMethod<?> method : type.getMethods()) {
if (getSourceStart(method) == getSourceStartOfResult()) {
if (method.isStatic() && method.isPrivate()) {
continue;
}
List<CtExecutableReferenceExpression<?, CtTypeAccess<?>>> executableRefs = type
.getElements(new TypeFilter<>(CtExecutableReferenceExpression.class));
refactorExecutableReferences(method, executableRefs);
LinkedHashSet<CtExtendedModifier> modifiers = new LinkedHashSet<>(method.getExtendedModifiers());
modifiers.add(CtExtendedModifier.explicit(ModifierKind.STATIC));
method.setExtendedModifiers(modifiers);
listener.setChanged(type.getTopLevelType(),
new Change(METHOD_MAY_BE_STATIC,
MarkdownString.fromMarkdown(result.getMessage().getText(), result.getMessage().getMarkdown()),
type.getTopLevelType()));

}
}
}

private void refactorExecutableReferences(CtMethod<?> method,
List<CtExecutableReferenceExpression<?, CtTypeAccess<?>>> executableRefs) {
for (CtExecutableReferenceExpression<?, CtTypeAccess<?>> executableRef : executableRefs) {
if (executableRef.getExecutable() != null) {
CtExecutable<?> exec = executableRef.getExecutable().getExecutableDeclaration();
if (exec instanceof CtMethod) {
CtMethod<?> methodRef = (CtMethod<?>) exec;
if (methodRef.equals(method)) {
CtTypeReference<Object> typeReference = method.getFactory()
.createSimplyQualifiedReference(method.getDeclaringType().getQualifiedName());
executableRef.setTarget(method.getFactory().createTypeAccess(typeReference));
}
}
}
}
}

private Integer getSourceStartOfResult() {
return result.getLocations().get(0).getPhysicalLocation().getRegion().getCharOffset();
}

private int getSourceStart(CtMethod<?> method) {
try {
return (int) FieldUtils.readField(method.getPosition(), "sourceStart", true);
}
catch (Throwable e) {
logger.atSevere().withCause(e).log("Error while getting source start");
return -1;
}
}

@Override
public List<BadSmell> getHandledBadSmells() {
return List.of(METHOD_MAY_BE_STATIC);
}

}

0 comments on commit c1ce0b9

Please sign in to comment.