[java-dfa] Native support of ObjectUtils.{all,any}[not]Null methods

Fixes IDEA-373254 Recognized ObjectUtils.allNotNull method in data flow inspection
(at least, my understanding of it)

GitOrigin-RevId: 5bf63072a84b22a3a5de58d53c8521f1296a0fd1
This commit is contained in:
Tagir Valeev
2025-06-10 11:20:40 +00:00
committed by intellij-monorepo-bot
parent 43effd21ae
commit ee1c23f88b
5 changed files with 137 additions and 1 deletions
@@ -298,6 +298,20 @@ public class CFGBuilder {
return add(new ResultOfInstruction(new JavaExpressionAnchor(expression)));
}
/**
* Generate instructions to apply a boolean 'not' operation to top stack value
* <p>
* Stack before: ... value
* <p>
* Stack after: ... not(value)
*
* @return this builder
*/
public CFGBuilder not() {
add(new NotInstruction(null));
return this;
}
/**
* Generate instructions to perform an Class.isInstance operation
* <p>
@@ -2752,7 +2752,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
private static final CallInliner[] INLINERS = {
new AssertJInliner(), new OptionalChainInliner(), new LambdaInliner(), new CollectionUpdateInliner(),
new StreamChainInliner(), new MapUpdateInliner(), new AssumeInliner(), new ClassMethodsInliner(),
new AssertAllInliner(), new BoxingInliner(), new SimpleMethodInliner(), new AccessorInliner(),
new AssertAllInliner(), new AllNotNullInliner(), new BoxingInliner(), new SimpleMethodInliner(), new AccessorInliner(),
new TransformInliner(), new EnumCompareInliner(), new IndexOfInliner(), new AssertInstanceOfInliner()
};
}
@@ -0,0 +1,53 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.dataFlow.java.inliner;
import com.intellij.codeInspection.dataFlow.java.CFGBuilder;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiType;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.MethodCallUtils;
import org.jetbrains.annotations.NotNull;
import static com.siyeh.ig.callMatcher.CallMatcher.anyOf;
import static com.siyeh.ig.callMatcher.CallMatcher.staticCall;
/**
* JUnit5 Assertions.assertAll
*/
public class AllNotNullInliner implements CallInliner {
private static final CallMatcher NULL_TESTS =
anyOf(
staticCall("org.apache.commons.lang3.ObjectUtils", "allNull").parameterTypes("java.lang.Object..."),
staticCall("org.apache.commons.lang3.ObjectUtils", "allNotNull").parameterTypes("java.lang.Object..."),
staticCall("org.apache.commons.lang3.ObjectUtils", "anyNull").parameterTypes("java.lang.Object..."),
staticCall("org.apache.commons.lang3.ObjectUtils", "anyNotNull").parameterTypes("java.lang.Object...")
);
@Override
public boolean tryInlineCall(@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call) {
if (!NULL_TESTS.matches(call) || !MethodCallUtils.isVarArgCall(call)) return false;
PsiExpression[] args = call.getArgumentList().getExpressions();
String methodName = call.getMethodExpression().getReferenceName();
PsiClassType objectType = PsiType.getJavaLangObject(call.getManager(), call.getResolveScope());
boolean allMatchResult = "allNotNull".equals(methodName) || "allNull".equals(methodName);
builder.push(DfTypes.booleanValue(!allMatchResult));
for (PsiExpression arg : args) {
builder.pushExpression(arg)
.boxUnbox(arg, objectType);
if ("allNotNull".equals(methodName) || "anyNull".equals(methodName)) {
builder.ifNotNull();
} else {
builder.ifNull();
}
}
builder.not();
for (int i = 0; i < args.length; i++) {
builder.end();
}
builder.resultOf(call);
return true;
}
}
@@ -0,0 +1,65 @@
package org.apache.commons.lang3;
class ObjectUtils {
public static native boolean allNotNull(final Object... values);
public static native boolean allNull(final Object... values);
public static native boolean anyNotNull(final Object... values);
public static native boolean anyNull(final Object... values);
}
class Test {
void use(Object o1, Object o2, Object o3, Object o4) {
if (ObjectUtils.allNull(o1, o2, o3, o4)) {
if (<warning descr="Condition 'o1 == null' is always 'true'">o1 == null</warning>) {}
if (<warning descr="Condition 'o2 == null' is always 'true'">o2 == null</warning>) {}
if (<warning descr="Condition 'o3 == null' is always 'true'">o3 == null</warning>) {}
if (<warning descr="Condition 'o4 == null' is always 'true'">o4 == null</warning>) {}
if (<warning descr="Condition 'ObjectUtils.allNull(o1, o2, o3, o4)' is always 'true'">ObjectUtils.allNull(o1, o2, o3, o4)</warning>) {}
if (<warning descr="Condition 'ObjectUtils.anyNull(o1, o2, o3, o4)' is always 'true'">ObjectUtils.anyNull(o1, o2, o3, o4)</warning>) {}
if (<warning descr="Condition 'ObjectUtils.anyNotNull(o1, o2, o3, o4)' is always 'false'">ObjectUtils.anyNotNull(o1, o2, o3, o4)</warning>) {}
if (<warning descr="Condition 'ObjectUtils.allNotNull(o1, o2, o3, o4)' is always 'false'">ObjectUtils.allNotNull(o1, o2, o3, o4)</warning>) {}
}
if (ObjectUtils.anyNull(o1, o2, o3, o4)) {
if (o1 != null) {
if (o2 != null) {
if (o3 != null) {
if (<warning descr="Condition 'o4 != null' is always 'false'">o4 != null</warning>) {}
}
}
}
if (ObjectUtils.allNull(o1, o2, o3, o4)) {}
if (<warning descr="Condition 'ObjectUtils.anyNull(o1, o2, o3, o4)' is always 'true'">ObjectUtils.anyNull(o1, o2, o3, o4)</warning>) {}
if (ObjectUtils.anyNotNull(o1, o2, o3, o4)) {}
if (<warning descr="Condition 'ObjectUtils.allNotNull(o1, o2, o3, o4)' is always 'false'">ObjectUtils.allNotNull(o1, o2, o3, o4)</warning>) {}
}
if (ObjectUtils.anyNotNull(o1, o2, o3, o4)) {
if (o1 == null) {
if (o2 == null) {
if (o3 == null) {
if (<warning descr="Condition 'o4 == null' is always 'false'">o4 == null</warning>) {}
}
}
}
if (<warning descr="Condition 'ObjectUtils.allNull(o1, o2, o3, o4)' is always 'false'">ObjectUtils.allNull(o1, o2, o3, o4)</warning>) {}
if (ObjectUtils.anyNull(o1, o2, o3, o4)) {}
if (<warning descr="Condition 'ObjectUtils.anyNotNull(o1, o2, o3, o4)' is always 'true'">ObjectUtils.anyNotNull(o1, o2, o3, o4)</warning>) {}
if (ObjectUtils.allNotNull(o1, o2, o3, o4)) {}
}
if (ObjectUtils.allNotNull(o1, o2, o3, o4)) {
if (<warning descr="Condition 'o1 != null' is always 'true'">o1 != null</warning>) {}
if (<warning descr="Condition 'o2 != null' is always 'true'">o2 != null</warning>) {}
if (<warning descr="Condition 'o3 != null' is always 'true'">o3 != null</warning>) {}
if (<warning descr="Condition 'o4 != null' is always 'true'">o4 != null</warning>) {}
if (<warning descr="Condition 'ObjectUtils.allNull(o1, o2, o3, o4)' is always 'false'">ObjectUtils.allNull(o1, o2, o3, o4)</warning>) {}
if (<warning descr="Condition 'ObjectUtils.anyNull(o1, o2, o3, o4)' is always 'false'">ObjectUtils.anyNull(o1, o2, o3, o4)</warning>) {}
if (<warning descr="Condition 'ObjectUtils.anyNotNull(o1, o2, o3, o4)' is always 'true'">ObjectUtils.anyNotNull(o1, o2, o3, o4)</warning>) {}
if (<warning descr="Condition 'ObjectUtils.allNotNull(o1, o2, o3, o4)' is always 'true'">ObjectUtils.allNotNull(o1, o2, o3, o4)</warning>) {}
}
}
void box(int i1, int i2, int i3, int i4) {
if (<warning descr="Condition 'ObjectUtils.allNotNull(i1, i2, i3, i4)' is always 'true'">ObjectUtils.allNotNull(i1, i2, i3, i4)</warning>) {
}
}
}
@@ -188,4 +188,8 @@ public class DataFlowInspection21Test extends DataFlowInspectionTestCase {
public void testSwitchNoUnreachableBranchesDueToUnresolvedType() {
doTest();
}
public void testObjectUtilsNullMethods() {
doTest();
}
}