From d6c2f86eb00a05d92542c2f01292d1461ff2142b Mon Sep 17 00:00:00 2001 From: MartinWitt Date: Fri, 21 Jan 2022 06:07:09 +0100 Subject: [PATCH] feat: LambdaToExecutableReference (#39) --- .../code_solver/TransformationEngine.java | 2 + .../self/LambdaToExecutableReference.java | 80 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/transformations/self/LambdaToExecutableReference.java diff --git a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/TransformationEngine.java b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/TransformationEngine.java index 5b0181ed6..ac238a6d5 100644 --- a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/TransformationEngine.java +++ b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/TransformationEngine.java @@ -39,6 +39,7 @@ import xyz.keksdose.spoon.code_solver.transformations.junit.simplification.TempoaryFolderAsParameter; import xyz.keksdose.spoon.code_solver.transformations.qodana.ArraysToString; import xyz.keksdose.spoon.code_solver.transformations.qodana.EmptyStringCheck; +import xyz.keksdose.spoon.code_solver.transformations.self.LambdaToExecutableReference; import xyz.keksdose.spoon.code_solver.transformations.self.StringBuilderDirectUse; import xyz.keksdose.spoon.code_solver.transformations.self.ThreadLocalWithInitial; @@ -82,6 +83,7 @@ protected void addProcessors(ProcessingManager pm, ChangeListener listener) { pm.addProcessor(new EmptyStringCheck(listener)); pm.addProcessor(new StringBuilderDirectUse(listener)); pm.addProcessor(new ThreadLocalWithInitial(listener)); + pm.addProcessor(new LambdaToExecutableReference(listener)); // pm.addProcessor(new AssertTrueEqualsCheck(listener)); // pm.addProcessor(new AssertFalseEqualsCheck(listener)); // pm.addProcessor(new AssertNullTransformation(listener)); diff --git a/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/transformations/self/LambdaToExecutableReference.java b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/transformations/self/LambdaToExecutableReference.java new file mode 100644 index 000000000..167d376eb --- /dev/null +++ b/code-transformation/src/main/java/xyz/keksdose/spoon/code_solver/transformations/self/LambdaToExecutableReference.java @@ -0,0 +1,80 @@ + +package xyz.keksdose.spoon.code_solver.transformations.self; + +import java.util.List; + +import spoon.reflect.code.CtConstructorCall; +import spoon.reflect.code.CtExecutableReferenceExpression; +import spoon.reflect.code.CtExpression; +import spoon.reflect.code.CtInvocation; +import spoon.reflect.code.CtLambda; +import spoon.reflect.code.CtTypeAccess; +import spoon.reflect.declaration.CtType; +import spoon.reflect.declaration.CtTypedElement; +import spoon.reflect.reference.CtExecutableReference; +import spoon.reflect.reference.CtTypeReference; +import xyz.keksdose.spoon.code_solver.history.Change; +import xyz.keksdose.spoon.code_solver.history.ChangeListener; +import xyz.keksdose.spoon.code_solver.history.Link; +import xyz.keksdose.spoon.code_solver.history.MarkdownString; +import xyz.keksdose.spoon.code_solver.transformations.BadSmell; +import xyz.keksdose.spoon.code_solver.transformations.TransformationProcessor; + +public class LambdaToExecutableReference extends TransformationProcessor> { + + private static final BadSmell lambdaInsteadOfExecutableReference = new BadSmell() { + @Override + public MarkdownString getName() { + return MarkdownString.fromRaw("LambdaInsteadOfExecutableReference"); + } + + @Override + public MarkdownString getDescription() { + return MarkdownString.fromRaw("Lambda is used instead of executable reference"); + } + + @Override + public List getLinks() { + return List.of(new Link("https://rules.sonarsource.com/java/RSPEC-1612")); + } + }; + public LambdaToExecutableReference(ChangeListener listener) { + super(listener); + } + + @Override + public void process(CtLambda lambda) { + if (lambda.getBody() == null) { + CtExpression expression = lambda.getExpression(); + if (expression instanceof CtInvocation) { + //TODO: handle this + } + if (expression instanceof CtConstructorCall) { + refactorConstructorCall(lambda, expression); + } + } + } + + private void refactorConstructorCall(CtLambda lambda, CtExpression expression) { + CtConstructorCall invocation = (CtConstructorCall) expression; + if (invocation.getArguments().isEmpty()) { + CtExecutableReferenceExpression> exec = getFactory().Core() + .createExecutableReferenceExpression(); + exec.setExecutable((CtExecutableReference) invocation.getExecutable()); + exec.setType(getType(invocation)); + exec.setTarget(getFactory().createTypeAccess(invocation.getType())); + lambda.replace(exec); + String raw = String.format("Replaced lambda %s with executable ref %s", lambda, exec); + String markdown = String.format("Replaced lambda `%s` with executable ref `%s`", lambda, exec); + CtType topLevelType = lambda.getParent(CtType.class).getTopLevelType(); + setChanged(topLevelType, new Change(lambdaInsteadOfExecutableReference, + MarkdownString.fromMarkdown(raw, markdown), topLevelType)); + } + } + + @SuppressWarnings("unchecked") + private CtTypeReference getType(CtTypedElement typedElement) { + return (CtTypeReference) typedElement.getType(); + } + +}