openrewrite / rewrite-analysis

OpenRewrite recipes for data flow analysis.
Apache License 2.0
8 stars 8 forks source link

`VarAccess` raises `TraitErrorsException` for accesses in lambda expressions #31

Closed aaronist closed 9 months ago

aaronist commented 12 months ago

At the moment, there is no support in OpenRewrite for handling variable accesses within lambda expressions, causing the following test cases to fail:

@Test
void lambdaException() {
    rewriteRun(
      java(
        """
          import java.util.concurrent.FutureTask;

          class Test {
              void foobar() {
                  FutureTask<Exception> task = new FutureTask<>(() -> {
                      try {
                          return null;
                      } catch (Exception e) {
                          return e;
                      }
                  });
              }
          }
          """
      )
    );
}

with the exception:

Caused by: org.openrewrite.analysis.trait.util.TraitErrorsException: TraitErrors: 
    - Method must be created from class org.openrewrite.java.tree.J$MethodDeclaration but was class org.openrewrite.java.tree.J$ControlParentheses
    - InstanceInitializer must be created from class org.openrewrite.java.tree.J$Block but was class org.openrewrite.java.tree.J$Lambda
    - Field must be declared in a class, interface, or anonymous class
    at org.openrewrite.analysis.trait.util.TraitErrors.doThrow(TraitErrors.java:43)
    at fj.data.Either$RightProjection.on(Either.java:531)
    at fj.data.Validation.on(Validation.java:146)
    at org.openrewrite.analysis.trait.expr.VarAccessBase$1.visitVariable(VarAccess.java:190)
    at org.openrewrite.analysis.trait.expr.VarAccessBase$1.visitVariable(VarAccess.java:178)
    at org.openrewrite.java.tree.J$VariableDeclarations$NamedVariable.acceptJava(J.java:5627)
    at org.openrewrite.java.tree.J.accept(J.java:59)
    at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:278)
    ...

and the test:

@Test
void lambdaNewInstance() {
    rewriteRun(
      java(
        """
          class A {
              static A newInstance() {
                  return new A();
              }
          }

          class Test {
              ThreadLocal<A> foobar =
                  ThreadLocal.withInitial(() -> {
                      A a = A.newInstance();
                      return a;
                  });
          }
          """
      )
    );
}

with the exception:

Caused by: org.openrewrite.analysis.trait.util.TraitErrorsException: TraitErrors: 
    - Method must be created from class org.openrewrite.java.tree.J$MethodDeclaration but was class org.openrewrite.java.tree.J$Block
    - No parent Method found
    - Field must be declared in a class, interface, or anonymous class
    at org.openrewrite.analysis.trait.util.TraitErrors.doThrow(TraitErrors.java:43)
    at fj.data.Either$RightProjection.on(Either.java:531)
    at fj.data.Validation.on(Validation.java:146)
    at org.openrewrite.analysis.trait.expr.VarAccessBase$1.visitVariable(VarAccess.java:190)
    at org.openrewrite.analysis.trait.expr.VarAccessBase$1.visitVariable(VarAccess.java:178)
    at org.openrewrite.java.tree.J$VariableDeclarations$NamedVariable.acceptJava(J.java:5627)
    at org.openrewrite.java.tree.J.accept(J.java:59)
    at org.openrewrite.TreeVisitor.visit(TreeVisitor.java:278)
    ...

In the future, this may involve adding a LambdaExpr trait to the trait API in rewrite-analysis to distinguish lambda expressions.

knutwannheden commented 9 months ago

As noted in the linked PR the issue was mostly linked to the handling of anonymous subclasses.