Skip to content

Commit

Permalink
Support Refaster templates which use lambdas (#111)
Browse files Browse the repository at this point in the history
This still doesn't allow a template to directly return a lambda, because the `JavaTemplate` doesn't capture the lambda's type in any way, but at least a template can still use a lambda (e.g. as a method argument).
  • Loading branch information
knutwannheden authored Sep 15, 2024
1 parent 2937038 commit e1eabde
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,20 @@ void print(Symbol sym) throws IOException {
imports.add(sym.getQualifiedName().toString());
}
}
} else if (sym instanceof Symbol.MethodSymbol || sym instanceof Symbol.VarSymbol) {
} else if (sym instanceof Symbol.VarSymbol) {
if (fullyQualified) {
if (sym.owner instanceof Symbol.ClassSymbol) {
print(sym.owner);
print('.');
}
print(sym.name);
} else {
print(sym.name);
if (!sym.packge().fullname.contentEquals("java.lang")) {
staticImports.add(sym.owner.getQualifiedName() + "." + sym.name);
}
}
} else if (sym instanceof Symbol.MethodSymbol) {
if (fullyQualified) {
print(sym.owner);
print('.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.Collections.singletonList;
Expand Down Expand Up @@ -90,6 +89,8 @@ protected List<String> computeValue(Class<?> type) {
return singletonList("J.Ternary");
} else if (JCTree.JCNewClass.class.isAssignableFrom(type)) {
return singletonList("J.NewClass");
} else if (JCTree.JCLambda.class.isAssignableFrom(type)) {
return singletonList("J.Lambda");
} else if (JCTree.JCExpression.class.isAssignableFrom(type)) {
// catch all for expressions
return singletonList("Expression");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ void skipRecipeGeneration(String recipeName) {
@ValueSource(strings = {
"Escapes",
"Generics",
"Lambdas",
"Matching",
"MultipleDereferences",
"PicnicRules",
Expand All @@ -102,7 +103,7 @@ void nestedRecipes(String recipeName) {
void stringIsEmptyPredicate() {
Compilation compilation = compileResource("refaster/StringIsEmptyPredicate.java");
assertThat(compilation).succeeded();
assertThat(compilation).hadNoteContaining("Lambdas are currently not supported");
assertThat(compilation).hadNoteContaining("Method references are currently not supported");
assertEquals(0, compilation.generatedSourceFiles().size(), "Not yet supported");
}

Expand Down
42 changes: 42 additions & 0 deletions src/test/resources/refaster/Lambdas.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package foo;

import com.google.errorprone.refaster.annotation.AfterTemplate;
import com.google.errorprone.refaster.annotation.BeforeTemplate;
import org.openrewrite.java.template.Matches;
import org.openrewrite.java.template.MethodInvocationMatcher;
import org.openrewrite.java.template.NotMatches;
import org.openrewrite.java.template.RecipeDescriptor;

import java.util.Comparator;
import java.util.List;

public class Lambdas {

public static class UsedLambda {
@BeforeTemplate
void before(List<Integer> is) {
is.sort((x, y) -> x - y);
}

@AfterTemplate
void after(List<Integer> is) {
is.sort(Comparator.comparingInt(x -> x));
}
}

}
123 changes: 123 additions & 0 deletions src/test/resources/refaster/LambdasRecipes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2024 the original author or authors.
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package foo;

import org.jspecify.annotations.NullMarked;
import org.openrewrite.ExecutionContext;
import org.openrewrite.Preconditions;
import org.openrewrite.Recipe;
import org.openrewrite.TreeVisitor;
import org.openrewrite.java.JavaParser;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.search.*;
import org.openrewrite.java.template.Primitive;
import org.openrewrite.java.template.function.*;
import org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor;
import org.openrewrite.java.tree.*;

import javax.annotation.Generated;
import java.util.*;

import static org.openrewrite.java.template.internal.AbstractRefasterJavaVisitor.EmbeddingOption.*;

/**
* OpenRewrite recipes created for Refaster template {@code foo.Lambdas}.
*/
@SuppressWarnings("all")
@Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor")
public class LambdasRecipes extends Recipe {
/**
* Instantiates a new instance.
*/
public LambdasRecipes() {}

@Override
public String getDisplayName() {
return "`Lambdas` Refaster recipes";
}

@Override
public String getDescription() {
return "Refaster template recipes for `foo.Lambdas`.";
}

@Override
public List<Recipe> getRecipeList() {
return Arrays.asList(
new UsedLambdaRecipe()
);
}

/**
* OpenRewrite recipe created for Refaster template {@code Lambdas.UsedLambda}.
*/
@SuppressWarnings("all")
@NullMarked
@Generated("org.openrewrite.java.template.processor.RefasterTemplateProcessor")
public static class UsedLambdaRecipe extends Recipe {

/**
* Instantiates a new instance.
*/
public UsedLambdaRecipe() {}

@Override
public String getDisplayName() {
return "Refaster template `Lambdas.UsedLambda`";
}

@Override
public String getDescription() {
return "Recipe created for the following Refaster template:\n```java\npublic static class UsedLambda {\n \n @BeforeTemplate()\n void before(List<Integer> is) {\n is.sort((x,y)->x - y);\n }\n \n @AfterTemplate()\n void after(List<Integer> is) {\n is.sort(Comparator.comparingInt((x)->x));\n }\n}\n```\n.";
}

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
JavaVisitor<ExecutionContext> javaVisitor = new AbstractRefasterJavaVisitor() {
final JavaTemplate before = JavaTemplate
.builder("#{is:any(java.util.List<java.lang.Integer>)}.sort((x,y)->x - y);")
.build();
final JavaTemplate after = JavaTemplate
.builder("#{is:any(java.util.List<java.lang.Integer>)}.sort(java.util.Comparator.comparingInt((x)->x));")
.build();

@Override
public J visitMethodInvocation(J.MethodInvocation elem, ExecutionContext ctx) {
JavaTemplate.Matcher matcher;
if ((matcher = before.matcher(getCursor())).find()) {
return embed(
after.apply(getCursor(), elem.getCoordinates().replace(), matcher.parameter(0)),
getCursor(),
ctx,
SHORTEN_NAMES
);
}
return super.visitMethodInvocation(elem, ctx);
}

};
return Preconditions.check(
Preconditions.and(
new UsesType<>("java.util.List", true),
new UsesMethod<>("java.util.List sort(..)", true)
),
javaVisitor
);
}
}

}

0 comments on commit e1eabde

Please sign in to comment.