Skip to content

Commit

Permalink
Merge pull request #32 from aaronist/lambda_expr
Browse files Browse the repository at this point in the history
  • Loading branch information
JLLeitschuh authored Sep 1, 2023
2 parents 51ba273 + 7f46515 commit ad7522c
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2023 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 org.openrewrite.analysis.trait.expr;

import fj.data.Validation;
import org.openrewrite.Cursor;
import org.openrewrite.analysis.trait.TraitFactory;
import org.openrewrite.analysis.trait.util.TraitErrors;

/**
* A functional expression is either a lambda expression or a member reference expression.
*/
public interface FunctionalExpr extends ClassInstanceExpr {
enum Factory implements TraitFactory<FunctionalExpr> {
F;

@Override
public Validation<TraitErrors, FunctionalExpr> viewOf(Cursor cursor) {
return TraitFactory.findFirstViewOf(
cursor,
LambdaExpr.Factory.F
);
}
}

static Validation<TraitErrors, FunctionalExpr> viewOf(Cursor cursor) {
return FunctionalExpr.Factory.F.viewOf(cursor);
}
}
80 changes: 80 additions & 0 deletions src/main/java/org/openrewrite/analysis/trait/expr/LambdaExpr.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2023 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 org.openrewrite.analysis.trait.expr;

import fj.data.Option;
import fj.data.Validation;
import lombok.AllArgsConstructor;
import org.openrewrite.Cursor;
import org.openrewrite.analysis.InvocationMatcher;
import org.openrewrite.analysis.trait.Top;
import org.openrewrite.analysis.trait.TraitFactory;
import org.openrewrite.analysis.trait.util.TraitErrors;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;

import java.util.UUID;

/**
* Lambda expressions are represented by their implicit class instance creation expressions,
* which instantiate an anonymous class that overrides the unique method designated by their
* functional interface type. The parameters of the lambda expression correspond to the
* parameters of the overriding method, and the lambda body corresponds to the body of the
* overriding method (enclosed by a return statement and a block in the case of lambda
* expressions whose body is an expression).
*/
public interface LambdaExpr extends FunctionalExpr {
enum Factory implements TraitFactory<LambdaExpr> {
F;

@Override
public Validation<TraitErrors, LambdaExpr> viewOf(Cursor cursor) {
if (cursor.getValue() instanceof J.Lambda) {
return LambdaExprBase.viewOf(cursor).map(m -> m);
}
return TraitErrors.invalidTraitCreationType(LambdaExpr.class, cursor, J.Lambda.class);
}
}

static Validation<TraitErrors, LambdaExpr> viewOf(Cursor cursor) {
return LambdaExpr.Factory.F.viewOf(cursor);
}
}

@AllArgsConstructor
class LambdaExprBase extends Top.Base implements LambdaExpr {
private final Cursor cursor;
private final J.Lambda lambda;

@Override
public boolean matches(InvocationMatcher callMatcher) {
return callMatcher.matches(lambda);
}

@Override
public Option<JavaType.Method> getMethodType() {
return Option.none();
}

static Validation<TraitErrors, LambdaExprBase> viewOf(Cursor cursor) {
return Validation.success(new LambdaExprBase(cursor, cursor.getValue()));
}

@Override
public UUID getId() {
return lambda.getId();
}
}
169 changes: 169 additions & 0 deletions src/test/java/org/openrewrite/analysis/trait/expr/LambdaExprTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright 2023 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 org.openrewrite.analysis.trait.expr;

import org.junit.jupiter.api.Test;
import org.openrewrite.ExecutionContext;
import org.openrewrite.java.JavaIsoVisitor;
import org.openrewrite.java.tree.J;
import org.openrewrite.marker.SearchResult;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;
import static org.openrewrite.test.RewriteTest.toRecipe;

public class LambdaExprTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec.recipe(toRecipe(() -> new JavaIsoVisitor<>() {
@Override
public J preVisit(J tree, ExecutionContext executionContext) {
return LambdaExpr.viewOf(getCursor())
.map(__ -> SearchResult.found(tree))
.orSuccess(tree);
}
})).cycles(1).expectedCyclesThatMakeChanges(1);
}

@Test
void correctlyLabelsLambda() {
rewriteRun(
java(
"""
interface Fun {
String message();
}
class Test {
void test() {
Fun foobar = () -> "foobar";
}
}
""",
"""
interface Fun {
String message();
}
class Test {
void test() {
Fun foobar = /*~~>*/() -> "foobar";
}
}
"""
)
);
}

@Test
void correctlyLabelsLambdaInReturn() {
rewriteRun(
java(
"""
import java.util.function.Function;
class Test {
Function<String, String> foo() {
return str -> {
String upperStr = str.toUpperCase();
return "Hello, " + upperStr;
};
}
}
""",
"""
import java.util.function.Function;
class Test {
Function<String, String> foo() {
return /*~~>*/str -> {
String upperStr = str.toUpperCase();
return "Hello, " + upperStr;
};
}
}
"""
)
);
}

@Test
void correctlyLabelsLambdaInConstructor() {
rewriteRun(
java(
"""
import java.util.concurrent.FutureTask;
class Test {
void foobar() {
FutureTask<Exception> task = new FutureTask<>(() -> {
try {
return null;
} catch (Exception e) {
return e;
}
});
}
}
""",
"""
import java.util.concurrent.FutureTask;
class Test {
void foobar() {
FutureTask<Exception> task = new FutureTask<>(/*~~>*/() -> {
try {
return null;
} catch (Exception e) {
return e;
}
});
}
}
"""
)
);
}

@Test
void correctlyLabelsNestedLambda() {
rewriteRun(
java(
"""
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
class Test {
void test() {
IntFunction<IntPredicate> foo = a -> b -> a % b == 0;
}
}
""",
"""
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
class Test {
void test() {
IntFunction<IntPredicate> foo = /*~~>*/a -> /*~~>*/b -> a % b == 0;
}
}
"""
)
);
}
}

0 comments on commit ad7522c

Please sign in to comment.