Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@

import lombok.EqualsAndHashCode;
import lombok.Value;
import org.jspecify.annotations.Nullable;
import org.openrewrite.*;
import org.openrewrite.internal.ListUtils;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;
import org.openrewrite.java.tree.TypeUtils;
Expand Down Expand Up @@ -77,7 +79,13 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m
JavaType.FullyQualified originalType = multiVariable.getTypeAsFullyQualified();
J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx);

if (methodUpdated) {
// Only change the declared type when a variable's initializer is itself the matched
// method invocation. A match nested deeper (e.g. as an argument to another call, such
// as `Cell c = row.createCell(i, other.getCellType())`) must not change the variable type.
boolean initializedByMatch = mv.getVariables().stream()
.anyMatch(v -> isInitializedByMatch(v.getInitializer()));

if (methodUpdated && initializedByMatch) {
JavaType newType = JavaType.buildType(newReturnType);
JavaType.FullyQualified newFieldType = TypeUtils.asFullyQualified(newType);

Expand Down Expand Up @@ -107,6 +115,27 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m

return mv;
}

/**
* Returns true when the matched invocation is the direct initializer, is inside
* wrapping parentheses (stripped before checking), or is a branch of a ternary —
* in all of those positions the invocation determines the variable's type.
*/
private boolean isInitializedByMatch(@Nullable Expression expression) {
if (expression == null) {
return false;
}
Expression unwrapped = expression.unwrap();
if (unwrapped instanceof J.MethodInvocation) {
return methodMatcher.matches((J.MethodInvocation) unwrapped);
}
if (unwrapped instanceof J.Ternary) {
J.Ternary ternary = (J.Ternary) unwrapped;
return isInitializedByMatch(ternary.getTruePart()) ||
isInitializedByMatch(ternary.getFalsePart());
}
return false;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,129 @@ void bar() {
);
}

@Test
void shouldNotChangeVariableTypeWhenMatchIsNestedInInitializer() {
rewriteRun(
//language=java
java(
"""
class Foo {
void bar() {
int direct = Integer.parseInt("1");
// Integer.parseInt(...) is only an argument here, not the initializer of `s`
String s = String.valueOf(Integer.parseInt("2"));
}
}
""",
"""
class Foo {
void bar() {
long direct = Integer.parseInt("1");
// Integer.parseInt(...) is only an argument here, not the initializer of `s`
String s = String.valueOf(Integer.parseInt("2"));
}
}
"""
)
);
}

@Test
void replaceParenthesizedInitializer() {
rewriteRun(
//language=java
java(
"""
class Foo {
void bar() {
int one = (Integer.parseInt("1"));
}
}
""",
"""
class Foo {
void bar() {
long one = (Integer.parseInt("1"));
}
}
"""
)
);
}

@Test
void replaceTernaryInitializerWithMatchInBothBranches() {
rewriteRun(
//language=java
java(
"""
class Foo {
void bar(boolean flag) {
int one = flag ? Integer.parseInt("1") : Integer.parseInt("2");
}
}
""",
"""
class Foo {
void bar(boolean flag) {
long one = flag ? Integer.parseInt("1") : Integer.parseInt("2");
}
}
"""
)
);
}

@Test
void replaceTernaryInitializerWithMatchInOneBranch() {
rewriteRun(
//language=java
java(
"""
class Foo {
void bar(boolean flag) {
int one = flag ? (Integer.parseInt("1")) : 0;
}
}
""",
"""
class Foo {
void bar(boolean flag) {
long one = flag ? (Integer.parseInt("1")) : 0;
}
}
"""
)
);
}

@Test
void shouldNotChangeVariableTypeWhenInitializerIsTypeCast() {
rewriteRun(
//language=java
java(
"""
class Foo {
void bar() {
int direct = Integer.parseInt("1");
// the cast, not the matched invocation, determines the type of `one`
int one = (int) Integer.parseInt("2");
}
}
""",
"""
class Foo {
void bar() {
long direct = Integer.parseInt("1");
// the cast, not the matched invocation, determines the type of `one`
int one = (int) Integer.parseInt("2");
}
}
"""
)
);
}

@Test
void replaceVariableAssignmentFullyQualified() {
rewriteRun(
Expand Down
Loading