diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java index 68e697c2a865..4e1142fbd21c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/CFGBuilder.java @@ -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 + *
+ * Stack before: ... value + *
+ * Stack after: ... not(value) + * + * @return this builder + */ + public CFGBuilder not() { + add(new NotInstruction(null)); + return this; + } + /** * Generate instructions to perform an Class.isInstance operation *
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java
index bff25635cf48..15dd93a4335f 100644
--- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/ControlFlowAnalyzer.java
@@ -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()
};
}
\ No newline at end of file
diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/AllNotNullInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/AllNotNullInliner.java
new file mode 100644
index 000000000000..dd4448198a09
--- /dev/null
+++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/AllNotNullInliner.java
@@ -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;
+ }
+}
diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ObjectUtilsNullMethods.java b/java/java-tests/testData/inspection/dataFlow/fixture/ObjectUtilsNullMethods.java
new file mode 100644
index 000000000000..b8ff83e0e276
--- /dev/null
+++ b/java/java-tests/testData/inspection/dataFlow/fixture/ObjectUtilsNullMethods.java
@@ -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 (