Skip to content

Fix IllegalArgumentException in Input.getRelativePath for relative paths#6843

Merged
shanman190 merged 7 commits intoopenrewrite:mainfrom
timtebeek:timtebeek/fix-relative-path-relativize
Mar 3, 2026
Merged

Fix IllegalArgumentException in Input.getRelativePath for relative paths#6843
shanman190 merged 7 commits intoopenrewrite:mainfrom
timtebeek:timtebeek/fix-relative-path-relativize

Conversation

@timtebeek
Copy link
Member

@timtebeek timtebeek commented Feb 27, 2026

What's Changed

When a Parser$Input has a relative path (e.g. synthetic dependsOn stubs created by KotlinParser.determinePath() via Paths.get(pkg, prefix + className + ".kt")), calling getRelativePath(relativeTo) with an absolute relativeTo path causes Path.relativize() to throw IllegalArgumentException: 'other' is different type of Path on JDK 21+.

This happens because JDK 21+ strictly enforces that both paths must be of the same type (both absolute or both relative) in Path.relativize().

The fix returns relative paths as-is since they're already relative and don't need relativization.

When a Parser$Input has a relative path (e.g. synthetic dependsOn stubs
created by KotlinParser.determinePath()), calling getRelativePath() with
an absolute relativeTo path causes Path.relativize() to throw
IllegalArgumentException on JDK 21+ because one path is absolute and the
other is relative.

Return relative paths as-is since they don't need relativization.

Fixes openrewrite/rewrite-gradle-plugin#424
@shanman190
Copy link
Contributor

shanman190 commented Feb 27, 2026

It seems like the KotlinParser doesn't follow the convention of prefixing dependsOn snippets like every other parser that I've checked.

.map(input -> Input.fromString(resolveSourcePathFromSourceText(Paths.get(""), input), input))

static Path resolveSourcePathFromSourceText(Path prefix, String sourceCode) {
Pattern packagePattern = Pattern.compile("^package\\s+([^;]+);");
Pattern classPattern = Pattern.compile("(class|interface|enum|record)\\s*(<[^>]*>)?\\s+(\\w+)");
Pattern publicClassPattern = Pattern.compile("public\\s+" + classPattern.pattern());
Function<String, @Nullable String> simpleName = sourceStr -> {
Matcher classMatcher = publicClassPattern.matcher(sourceStr);
if (classMatcher.find()) {
return classMatcher.group(3);
}
classMatcher = classPattern.matcher(sourceStr);
return classMatcher.find() ? classMatcher.group(3) : null;
};
Matcher packageMatcher = packagePattern.matcher(sourceCode);
String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : "";
String className = Optional.ofNullable(simpleName.apply(sourceCode))
.orElse(Long.toString(System.nanoTime())) + ".java";
return prefix.resolve(Paths.get(pkg + className));
}

@timtebeek timtebeek marked this pull request as draft February 27, 2026 16:07
Drop the "dependsOn-" filename prefix and instead track dependsOn
input paths in a Set for filtering, matching how JavaParser handles
dependsOn snippets.
@timtebeek timtebeek changed the title Fix IllegalArgumentException in Input.getRelativePath for relative paths Fix IllegalArgumentException in Input.getRelativePath for relative paths Feb 27, 2026
public Builder dependsOn(@Language("kotlin") String... inputsAsStrings) {
this.dependsOn = Arrays.stream(inputsAsStrings)
.map(input -> Input.fromString(determinePath("dependsOn-", input), input))
.map(input -> Input.fromString(determinePath("", input), input))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think normally the absolute path just gets pretended here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went by what I'd seen in the JavaParser:

.map(input -> Input.fromString(resolveSourcePathFromSourceText(Paths.get(""), input), input))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is doing a Paths.get("") which isn't represented here. That's the same thing that I was meaning.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm; with how that Paths.get("") is being used I couldn't mentally compute any difference, but I'll replicate it here.

static Path resolveSourcePathFromSourceText(Path prefix, String sourceCode) {
Pattern packagePattern = Pattern.compile("^package\\s+([^;]+);");
Pattern classPattern = Pattern.compile("(class|interface|enum|record)\\s*(<[^>]*>)?\\s+(\\w+)");
Pattern publicClassPattern = Pattern.compile("public\\s+" + classPattern.pattern());
Function<String, @Nullable String> simpleName = sourceStr -> {
Matcher classMatcher = publicClassPattern.matcher(sourceStr);
if (classMatcher.find()) {
return classMatcher.group(3);
}
classMatcher = classPattern.matcher(sourceStr);
return classMatcher.find() ? classMatcher.group(3) : null;
};
Matcher packageMatcher = packagePattern.matcher(sourceCode);
String pkg = packageMatcher.find() ? packageMatcher.group(1).replace('.', '/') + "/" : "";
String className = Optional.ofNullable(simpleName.apply(sourceCode))
.orElse(Long.toString(System.nanoTime())) + ".java";
return prefix.resolve(Paths.get(pkg + className));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could even remove the now always empty prefix for no real effect that I can see; am I missing anything?
image

@timtebeek timtebeek requested a review from shanman190 March 2, 2026 17:48
@timtebeek timtebeek marked this pull request as ready for review March 2, 2026 18:01
@shanman190 shanman190 merged commit 3a5a2f8 into openrewrite:main Mar 3, 2026
1 check passed
@github-project-automation github-project-automation bot moved this from Ready to Review to Done in OpenRewrite Mar 3, 2026
@timtebeek timtebeek deleted the timtebeek/fix-relative-path-relativize branch March 3, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working kotlin parser

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

java.lang.IllegalArgumentException: 'other' is different type of Path

2 participants