openrewrite / rewrite

Automated mass refactoring of source code.
https://docs.openrewrite.org
Apache License 2.0
2.19k stars 330 forks source link

Index out of bound in extensionless file #3737

Open JStronkhorst opened 10 months ago

JStronkhorst commented 10 months ago

What version of OpenRewrite are you using?

I am using Maven plugin v5.14.0

How are you running OpenRewrite?

I am using the Maven plugin, and my project is a single module project.

 mvn -U -X org.openrewrite.maven:rewrite-maven-plugin:run -D rewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-migrate-java:RELEASE -D rewrite.activeRecipes=
org.openrewrite.java.migrate.UpgradeToJava21

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

Error while visiting scripts\jenkins-promote\dev-tst\Jenkinsfile_promoteToTST: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
  java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:100)
  java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:106)
  java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:302)
  java.base/java.util.Objects.checkIndex(Objects.java:385)
  java.base/java.util.ArrayList.get(ArrayList.java:427)
  org.openrewrite.java.JavaTemplate$Matcher.parameter(JavaTemplate.java:103)
  org.openrewrite.java.migrate.net.URLConstructorsToURIRecipes$URLSingleArgumentConstructorRecipe$1.visitExpression(URLConstructorsToURIRecipes.java:68)
  org.openrewrite.java.migrate.net.URLConstructorsToURIRecipes$URLSingleArgumentConstructorRecipe$1.visitExpression(URLConstructorsToURIRecipes.java:57)
timtebeek commented 10 months ago

Thanks for your report @JStronkhorst ! it seems there's an issue with parsing Jenkinsfile_promoteToTST in a recipe we generate out of URLConstructorsToURI. Can you share any lines in Jenkinsfile_promoteToTST that relate to calling new URL(String)? That would help create an reproducer test for our groovy parser.

timtebeek commented 10 months ago

Meanwhile you can also use exclusions to skip parsing that particular file, such that you can at least apply the other changes while work on a fix for the Groovy parser or recipe.

Or use this modified recipe that does not contain the recipe that fails:

type: specs.openrewrite.org/v1beta/recipe
name: org.openrewrite.java.migrate.Java21
displayName: Migrate to Java 21
description: Excluding the URL constructor recipes.
recipeList:
  - org.openrewrite.java.migrate.UpgradeToJava17
  - org.openrewrite.java.migrate.JavaVersion21
  - org.openrewrite.java.migrate.lang.ThreadStopUnsupported
  - org.openrewrite.java.migrate.util.SequencedCollection
  - org.openrewrite.java.migrate.util.UseLocaleOf
  - org.openrewrite.staticanalysis.ReplaceDeprecatedRuntimeExecMethods
  - org.openrewrite.github.SetupJavaUpgradeJavaVersion
JStronkhorst commented 10 months ago

Hi @timtebeek, thanks for your time. As a work around I temporarly removed that file and ran the recipe and it worked. The problem with the new URL() is in a method from a groovy script in a different repository.

@Library('jenkins-pipeline-library@main') _

import org.svb.PipelineBuilder
import org.svb.GlobalVariables

// Create instance of PipelineBuild to help out with general tasks
def helper = new PipelineBuilder(this)
withCredentials([string(credentialsId: credentialId, variable: 'apiToken')]) {
    response = helper.RestRequestGet("http://${GlobalVariables.ArtifactoryApiBaseUrl_P}/api/docker/${sourceRepository}/v2/${applicationName}/tags/list", apiToken)
    sourceTags = response.get("tags")
}

That calls this method:

def RestRequestGet(String url, String apiToken){
        def responseText = ""
        def conn
        try {
            echo "Creating new connection"
            conn = new URL(url).openConnection()
            conn.setRequestMethod("GET")
            conn.setRequestProperty("Authorization", "Bearer ${apiToken}")
            def postRC = conn.getResponseCode()
            responseText = conn.getInputStream().getText()
            steps.println "Got:" + postRC + "\n" + responseText
        } finally {
            conn.disconnect()
        }
        return new groovy.json.JsonSlurperClassic().parseText(responseText)
    }
timtebeek commented 10 months ago

I've tried to replicate this with the following in GroovyParserTest, but no luck yet; this just works and is similar to what we generate for the URLConstructorsToURI, with the exception of the Semantics.expression used there.

@Test
@Issue("https://github.com/openrewrite/rewrite/issues/3737")
void groovyFirstConstructorArgument() {
    rewriteRun(
      spec -> spec.recipe(toRecipe(() -> (TreeVisitor<?, ExecutionContext>)
        new GroovyIsoVisitor<ExecutionContext>() {
            final JavaTemplate urlConstructor = JavaTemplate
              .builder("new java.net.URL(#{spec:any(java.lang.String)})")
              .build();

            @Override
            public Expression visitExpression(Expression expression, ExecutionContext executionContext) {
                Expression e = super.visitExpression(expression, executionContext);
                JavaTemplate.Matcher matcher;
                if ((matcher = urlConstructor.matcher(getCursor())).find()) {
                    System.out.println("e = " + e);
                    System.out.println("s = " + matcher.parameter(0));
                }
                return e;
            }
        })),
      groovy(
        """
          new URL("https://docs.openrewrite.org")
          """
      )
    );
}
timtebeek commented 10 months ago

When I run the following test in rewrite-migrate-java/...URLConstructorsToURITest

@Test
@Issue("https://github.com/openrewrite/rewrite/issues/3737")
void groovyFirstConstructorArgument() {
    rewriteRun(
      groovy(
        """
          new URL("https://docs.openrewrite.org")
          """,
        """
          import java.net.URI

          URI.create("https://docs.openrewrite.org").toURL()
          """
      )
    );
}

Then I get a different error, but still not what you're seeing

diff --git a/file.groovy b/file.groovy
index 9c3fd2a..e0c4163 100644
--- a/file.groovy
+++ b/file.groovy
@@ -1,3 +1 @@ 
-import java.net.URI
-
-URI.create("https://docs.openrewrite.org").toURL()
\ No newline at end of file
+import java.net.URIURI.create("https://docs.openrewrite.org").toURL()
\ No newline at end of file