Skip to content

IndexOutOfBoundsException when parsing Anonymous Classes using this in a method #6345

@MrThaler

Description

@MrThaler

What version of OpenRewrite are you using?

I am using

  • rewrite-recipe-bom 3.18.0

How are you running OpenRewrite?

Writing my own recipes, reproducing occurring errors in JUnit Tests.

What is the smallest, simplest way to reproduce the problem?

 @Language("java")
  private static final String EXCEPTION_THROWN =
      """
          package some.testpackage;

          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          import java.util.Comparator;

          public class MyTestClass {
            /** LOGGER */
            private static final Logger logger = LoggerFactory.getLogger(MyTestClass.class);

            public void logSomething() {
                Comparator<String> comp = new Comparator<>() {
                      @Override
                      public int compare(String o1, String o2) {
                          // No issues
                          logger.info("Created: " + Integer.valueOf(3));
                          logger.info("Created: {}, this);
                          System.out.println("Created: " + this);
                          // Here is the issue!
                          logger.info("Created: " + this);
                          return o1.compareTo(o2);
                      }
                };
            }
          }
          """;

  @Test
  void exceptionDemonstration() {
    // This test should succeed or fail, but throws an Exception instead
    rewriteRun(
        // The specific recipe does not matter as much, it just has to be one that actually looks into the specific line
        spec -> spec.recipeFromResources("org.openrewrite.java.logging.slf4j.Slf4jBestPractices"),
        java(EXCEPTION_THROWN));
  }

What did you expect to see?

The test should fail because the recipe in question should change parts of the code.

What did you see instead?

The test threw an exception because the line logger.info("Created: " + this") triggers an IndexOutOfBoundsException

What is the full stack trace of any errors you encountered?

java.lang.AssertionError: Failed to run recipe at Cursor{MethodInvocation->JRightPadded(element=logger.info("Created: " + this), after=Space(comments=<0 comments>, whitespace=<empty>))->Block->MethodDeclaration->JRightPadded(element=MethodDeclaration{some.testpackage.MyTestClass$1{name=compare,return=int,parameters=[java.lang.String,java.lang.String]}}, after=Space(comments=<0 comments>, whitespace=<empty>))->Block->NewClass->JLeftPadded(before=Space(comments=<0 comments>, whitespace='·₁'), element=new Comparator<>() {
            @Override
            public int compare(String o1, String o2) {
                logger.info("Created: " + this);
                return o1.compareTo(o2);
            }
      })->NamedVariable->JRightPadded(element=comp = new Comparator<>() {
            @Override
            public int compare(String o1, String o2) {
                logger.info("Created: " + this);
                return o1.compareTo(o2);
            }
      }, after=Space(comments=<0 comments>, whitespace=<empty>))->VariableDeclarations->JRightPadded(element=Comparator<String> comp = new Comparator<>() {
            @Override
            public int compare(String o1, String o2) {
                logger.info("Created: " + this);
                return o1.compareTo(o2);
            }
      }, after=Space(comments=<0 comments>, whitespace=<empty>))->Block->MethodDeclaration->JRightPadded(element=MethodDeclaration{some.testpackage.MyTestClass{name=logSomething,return=void,parameters=[]}}, after=Space(comments=<0 comments>, whitespace=<empty>))->Block->ClassDeclaration->CompilationUnit->root}

(Full stack has been attached)
Full_Stacktrace.txt

Additional Information

As far as I can tell, the issue does not occur:

  • With anything other than this
  • When using a lambda expression instead of an Anonymous Class
  • When the recipe does not try to change the method call (because logger.info("Created: {}", this) does not trigger the exception)

Honestly, my use-case seems like an absolute corner case barely anyone will stumble across, but just in case this is a deeper issue with the way the Cursor handles Anonymous Classes I'll leave this here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions