Skip to content
Draft
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 @@ -31,6 +31,7 @@
import java.util.Set;

import static java.util.Collections.singleton;
import static org.openrewrite.staticanalysis.SideEffects.mayHaveSideEffects;

@EqualsAndHashCode(callSuper = false)
@Value
Expand Down Expand Up @@ -90,15 +91,17 @@ public J visitBinary(J.Binary binary, ExecutionContext ctx) {
}

private boolean isRedundantNullCheck(J.Binary nullCheck, J.InstanceOf instanceOf) {
if (nullCheck.getOperator() == J.Binary.Type.NotEqual) {
if (J.Literal.isLiteralValue(nullCheck.getLeft(), null)) {
return SemanticallyEqual.areEqual(nullCheck.getRight(), instanceOf.getExpression());
}
if (J.Literal.isLiteralValue(nullCheck.getRight(), null)) {
return SemanticallyEqual.areEqual(nullCheck.getLeft(), instanceOf.getExpression());
}
if (nullCheck.getOperator() != J.Binary.Type.NotEqual) {
return false;
}
Expression checked = J.Literal.isLiteralValue(nullCheck.getLeft(), null) ? nullCheck.getRight() :
J.Literal.isLiteralValue(nullCheck.getRight(), null) ? nullCheck.getLeft() : null;
if (checked == null || !SemanticallyEqual.areEqual(checked, instanceOf.getExpression())) {
return false;
}
return false;
// The rewrite evaluates once what was evaluated twice, so both occurrences must be side-effect
// free; they can differ, as `SemanticallyEqual` matches a static field access against its qualified form
return !mayHaveSideEffects(checked) && !mayHaveSideEffects(instanceOf.getExpression());
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,35 +87,124 @@ void foo(Object obj) {
);
}


@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/953")
@Test
void removeRedundantNullCheckWithMethodInvocation() {
void doNotChangeWhenNullCheckedExpressionIsMethodInvocation() {
rewriteRun(
//language=java
java(
"""
class A {
void foo() {
void direct() {
if (getValue() != null && getValue() instanceof String) {
System.out.println("String value");
}
if (null != getValue() && getValue() instanceof String) {
System.out.println("String value");
}
}

void chained(boolean enabled) {
if (enabled && getValue() != null && getValue() instanceof String) {
System.out.println("String value");
}
}

String getValue() {
return "test";
}
}
"""
)
);
}

@Test
void removeRedundantNullCheckInChainedCondition() {
rewriteRun(
//language=java
java(
"""
class A {
void foo(boolean enabled, Object obj) {
if (enabled && obj != null && obj instanceof String) {
System.out.println("String value");
}
}
}
""",
"""
class A {
void foo(boolean enabled, Object obj) {
if (enabled && obj instanceof String) {
System.out.println("String value");
}
}
}
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/953")
@Test
void doNotChangeWhenConstructorCall() {
rewriteRun(
//language=java
java(
"""
class A {
void foo() {
if (new StringBuilder() != null && new StringBuilder() instanceof CharSequence) {
System.out.println("CharSequence value");
}
}
}
"""
)
);
}

@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/953")
@Test
void doNotChangeWhenArrayIndexHasSideEffect() {
rewriteRun(
//language=java
java(
"""
class A {
Object[] values = new Object[2];
int i;

void foo() {
if (getValue() instanceof String) {
if (values[i++] != null && values[i++] instanceof String) {
System.out.println("String value");
}
}
}
"""
)
);
}

String getValue() {
return "test";
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/953")
@Test
void doNotChangeWhenOnlyTheNullCheckedOperandHasSideEffects() {
rewriteRun(
//language=java
java(
"""
class A {
static Integer count = 1;

A getInstance() {
return this;
}

void foo() {
if (getInstance().count != null && count instanceof Integer) {
System.out.println("Integer value");
}
}
}
"""
Expand Down
Loading