From d7160411f2811d6cf3f6fd3a6203e8f83616e76d Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 27 Sep 2023 17:17:46 +0200 Subject: [PATCH] [java-dfa] IDEA-333294 Support Unit 5.8 assertInstanceOf in dataflow GitOrigin-RevId: 5a772af8968128e8a95465d2ab5a42cb09006d3a --- .../dataFlow/java/CFGBuilder.java | 4 +- .../dataFlow/java/ControlFlowAnalyzer.java | 2 +- .../java/inliner/AssertInstanceOfInliner.java | 41 +++++++++++++++++++ .../dataFlow/fixture/AssertInstanceOf.java | 18 ++++++++ .../HardcodedContractsTest.java | 13 ++++++ 5 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/AssertInstanceOfInliner.java create mode 100644 java/java-tests/testData/inspection/dataFlow/fixture/AssertInstanceOf.java 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 4f7392c68b13..ce29c75274ee 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 @@ -294,8 +294,8 @@ public class CFGBuilder { * @param anchor element to bind this instruction to * @return this builder */ - public CFGBuilder isInstance(PsiMethodCallExpression anchor) { - return add(new InstanceofInstruction(new JavaExpressionAnchor(anchor), true)); + public CFGBuilder isInstance(@Nullable PsiMethodCallExpression anchor) { + return add(new InstanceofInstruction(anchor == null ? null : new JavaExpressionAnchor(anchor), true)); } /** 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 e6c755c6e679..89dcd7a5d4b0 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 @@ -2560,6 +2560,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { new AssertJInliner(), new OptionalChainInliner(), new LambdaInliner(), new CollectionUpdateInliner(), new StreamChainInliner(), new MapUpdateInliner(), new AssumeInliner(), new ClassMethodsInliner(), new AssertAllInliner(), new BoxingInliner(), new SimpleMethodInliner(), - new TransformInliner(), new EnumCompareInliner(), new IndexOfInliner() + 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/AssertInstanceOfInliner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/AssertInstanceOfInliner.java new file mode 100644 index 000000000000..371651f44005 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/java/inliner/AssertInstanceOfInliner.java @@ -0,0 +1,41 @@ +// 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.jvm.problems.ContractFailureProblem; +import com.intellij.codeInspection.dataFlow.types.DfTypes; +import com.intellij.codeInspection.dataFlow.value.RelationType; +import com.intellij.psi.*; +import com.siyeh.ig.callMatcher.CallMatcher; +import com.siyeh.ig.psiutils.MethodCallUtils; +import org.jetbrains.annotations.NotNull; + +public final class AssertInstanceOfInliner implements CallInliner { + private static final CallMatcher ASSERT_INSTANCE_OF = CallMatcher.staticCall("org.junit.jupiter.api.Assertions", "assertInstanceOf"); + + @Override + public boolean tryInlineCall(@NotNull CFGBuilder builder, @NotNull PsiMethodCallExpression call) { + if (!ASSERT_INSTANCE_OF.matches(call)) return false; + PsiExpression[] expressions = call.getArgumentList().getExpressions(); + if (expressions.length < 2 || expressions.length > 3) return false; + PsiExpression wantedClass = expressions[0]; + PsiExpression objectToTest = expressions[1]; + builder.pushExpression(wantedClass) + .pushExpression(objectToTest) + .boxUnbox(objectToTest, PsiType.getJavaLangObject(call.getManager(), call.getResolveScope())) + .splice(2, 0, 0, 1); + if (expressions.length == 3) { + builder.pushExpression(expressions[2]).pop(); + } + builder.isInstance(null) + .ensure(RelationType.EQ, DfTypes.TRUE, new ContractFailureProblem(call), CommonClassNames.JAVA_LANG_ASSERTION_ERROR) + .pop(); + return true; + } + + @Override + public boolean mayInferPreciseType(@NotNull PsiExpression expression) { + PsiParameter argument = MethodCallUtils.getParameterForArgument(expression); + return argument != null && ASSERT_INSTANCE_OF.methodMatches((PsiMethod)argument.getDeclarationScope()); + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/AssertInstanceOf.java b/java/java-tests/testData/inspection/dataFlow/fixture/AssertInstanceOf.java new file mode 100644 index 000000000000..452598fd347f --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/AssertInstanceOf.java @@ -0,0 +1,18 @@ +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +public class AssertInstanceOf { + void test(Object str) { + assertInstanceOf(String.class, str); + if(str instanceof String) { + } + assertInstanceOf(Number.class, str); + } + + void test2(Object obj) { + int x; + assertInstanceOf(String.class, obj, "hello " + (x = (obj instanceof Number ? 1 : 2))); + if (x == 2) { + + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java b/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java index abad2c609fd6..2e80ee3380f1 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInspection/HardcodedContractsTest.java @@ -209,4 +209,17 @@ public class HardcodedContractsTest extends DataFlowInspectionTestCase { }"""); checkHighlighting(); } + + public void testAssertInstanceOf() { + myFixture.addClass(""" + package org.junit.jupiter.api; + import java.util.function.Supplier; + public final class Assertions { + public static native T assertInstanceOf(Class expectedType, Object actualValue); + public static T assertInstanceOf(Class expectedType, Object actualValue, String message); + public static T assertInstanceOf(Class expectedType, Object actualValue, Supplier messageSupplier); + } + """); + checkHighlighting(); + } }