mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-21 05:51:25 +07:00
[java-inspections] IDEA-14669 fixed: suggest adding specific non-null assertions from test frameworks
(cherry picked from commit 0d22d640ea4ee77a6bdfb5b4af504332e7456b68) IJ-MR-150371 GitOrigin-RevId: 04a5fe47cf874ae78c5ad73282b55123f55e84cb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
47a2170f17
commit
5569a2965d
@@ -1,10 +1,11 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.UnwrapSwitchLabelFix;
|
||||
import com.intellij.codeInsight.options.JavaInspectionButtons;
|
||||
import com.intellij.codeInsight.options.JavaInspectionControls;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.AddAssertNonNullFromTestFrameworksFix.Variant;
|
||||
import com.intellij.codeInspection.dataFlow.fix.*;
|
||||
import com.intellij.codeInspection.nullable.NullableStuffInspection;
|
||||
import com.intellij.codeInspection.options.OptPane;
|
||||
@@ -140,9 +141,14 @@ public final class DataFlowInspection extends DataFlowInspectionBase {
|
||||
if (isVolatileFieldReference(qualifier)) {
|
||||
ContainerUtil.addIfNotNull(fixes, createIntroduceVariableFix());
|
||||
}
|
||||
else if (!alwaysNull && !SideEffectChecker.mayHaveSideEffects(qualifier)) {
|
||||
else if (!alwaysNull && !SideEffectChecker.mayHaveSideEffects(qualifier)) {
|
||||
String suffix = " != null";
|
||||
if (PsiUtil.isAvailable(JavaFeature.ASSERTIONS, qualifier) && CodeBlockSurrounder.canSurround(expression)) {
|
||||
|
||||
Variant testFrameworkFixVariant = AddAssertNonNullFromTestFrameworksFix.isAvailable(expression);
|
||||
if (testFrameworkFixVariant != null) {
|
||||
fixes.add(new AddAssertNonNullFromTestFrameworksFix(qualifier, testFrameworkFixVariant));
|
||||
}
|
||||
else if (PsiUtil.isAvailable(JavaFeature.ASSERTIONS, qualifier) && CodeBlockSurrounder.canSurround(expression)) {
|
||||
String replacement = ParenthesesUtils.getText(qualifier, ParenthesesUtils.EQUALITY_PRECEDENCE) + suffix;
|
||||
fixes.add(new AddAssertStatementFix(replacement));
|
||||
}
|
||||
@@ -176,7 +182,9 @@ public final class DataFlowInspection extends DataFlowInspectionBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<@NotNull LocalQuickFix> createUnboxingNullableFixes(@NotNull PsiExpression qualifier, PsiElement anchor, boolean onTheFly) {
|
||||
protected @NotNull List<@NotNull LocalQuickFix> createUnboxingNullableFixes(@NotNull PsiExpression qualifier,
|
||||
PsiElement anchor,
|
||||
boolean onTheFly) {
|
||||
List<LocalQuickFix> result = new SmartList<>();
|
||||
if (TypeConversionUtil.isBooleanType(qualifier.getType())) {
|
||||
result.add(new ReplaceWithBooleanEqualsFix(qualifier));
|
||||
@@ -218,4 +226,4 @@ public final class DataFlowInspection extends DataFlowInspectionBase {
|
||||
JavaInspectionButtons.ButtonKind.NULLABILITY_ANNOTATIONS)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.TestFrameworks;
|
||||
import com.intellij.codeInsight.intention.HighPriorityAction;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testIntegration.TestFramework;
|
||||
import com.siyeh.ig.psiutils.CodeBlockSurrounder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
||||
public class AddAssertNonNullFromTestFrameworksFix extends PsiUpdateModCommandQuickFix implements HighPriorityAction {
|
||||
private final String myText;
|
||||
private final SmartPsiElementPointer<PsiExpression> myQualifierPointer;
|
||||
private final Variant myVariant;
|
||||
|
||||
public AddAssertNonNullFromTestFrameworksFix(@NotNull PsiExpression qualifier, Variant variant) {
|
||||
myText = qualifier.getText();
|
||||
myQualifierPointer = SmartPointerManager.getInstance(qualifier.getProject()).createSmartPsiElementPointer(qualifier);
|
||||
myVariant = variant;
|
||||
}
|
||||
|
||||
public enum Variant {
|
||||
JUNIT_3("JUnit 3", "assertNotNull"),
|
||||
JUNIT_4("JUnit 4", "Assert.assertNotNull"),
|
||||
JUNIT_5("JUnit 5", "Assertions.assertNotNull"),
|
||||
TESTNG("TestNG", "Assert.assertNotNull");
|
||||
|
||||
/// Used only for presentation purposes.
|
||||
public final String name;
|
||||
|
||||
/// Used only for presentation purposes.
|
||||
public final String replacement;
|
||||
|
||||
Variant(String name, String replacement) {
|
||||
this.name = name;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return JavaBundle.message("inspection.testframework.assert.quickfix", myVariant.name, myVariant.replacement + "(" + myText + ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getFamilyName() {
|
||||
return JavaBundle.message("inspection.quickfix.assert.family");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull ModPsiUpdater updater) {
|
||||
PsiExpression qualifier = updater.getWritable(myQualifierPointer.getElement());
|
||||
|
||||
PsiExpression expr = PsiTreeUtil.getNonStrictParentOfType(qualifier, PsiExpression.class);
|
||||
if (expr == null) return;
|
||||
CodeBlockSurrounder surrounder = CodeBlockSurrounder.forExpression(expr);
|
||||
if (surrounder == null) return;
|
||||
CodeBlockSurrounder.SurroundResult result = surrounder.surround();
|
||||
expr = result.getExpression();
|
||||
PsiElement anchorElement = result.getAnchor();
|
||||
|
||||
// If the element before our qualifier is an inspection suppression comment, then we want to
|
||||
// add assertion before this suppression comment so it's not accidentally disabled.
|
||||
PsiElement prev = PsiTreeUtil.skipWhitespacesBackward(anchorElement);
|
||||
if (prev instanceof PsiComment && JavaSuppressionUtil.getSuppressedInspectionIdsIn(prev) != null) {
|
||||
anchorElement = prev;
|
||||
}
|
||||
|
||||
String text = switch (myVariant) {
|
||||
case JUNIT_3 -> "assertNotNull(" + myText + ")";
|
||||
case JUNIT_4 -> "org.junit.Assert.assertNotNull(" + myText + ")";
|
||||
case JUNIT_5 -> "org.junit.jupiter.api.Assertions.assertNotNull(" + myText + ")";
|
||||
case TESTNG -> "org.testng.Assert.assertNotNull(" + myText + ")";
|
||||
} + ";";
|
||||
PsiStatement assertStatement = JavaPsiFacade.getElementFactory(project).createStatementFromText(text, expr);
|
||||
PsiElement added = anchorElement.getParent().addBefore(assertStatement, anchorElement);
|
||||
JavaCodeStyleManager.getInstance(project).shortenClassReferences(added);
|
||||
}
|
||||
|
||||
public static @Nullable Variant isAvailable(@NotNull PsiExpression expression) {
|
||||
PsiMethod containingMethod = PsiTreeUtil.getParentOfType(expression, PsiMethod.class);
|
||||
if (containingMethod == null) return null;
|
||||
PsiClass containingClass = containingMethod.getContainingClass();
|
||||
if (containingClass == null) return null;
|
||||
TestFramework detectedTestFramework = TestFrameworks.detectFramework(containingClass);
|
||||
if (detectedTestFramework == null) return null;
|
||||
return switch (detectedTestFramework.getName()) {
|
||||
case "JUnit3" -> Variant.JUNIT_3;
|
||||
case "JUnit4" -> Variant.JUNIT_4;
|
||||
case "JUnit5" -> Variant.JUNIT_5;
|
||||
case "TestNG" -> Variant.TESTNG;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Assert with JUnit 3 'assertNotNull(s)'" "true-preview"
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SomeJUnit3Test extends TestCase {
|
||||
@Nullable
|
||||
String getNullableString() {
|
||||
double random = Math.random();
|
||||
if (random > 0.75) return null;
|
||||
if (random > 0.50) return "";
|
||||
else return "bruh";
|
||||
}
|
||||
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
assertNotNull(s);
|
||||
assertTrue(s.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Assert with JUnit 4 'Assert.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class SomeJUnit4Test {
|
||||
@Nullable
|
||||
native String getNullableString();
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
Assert.assertNotNull(s);
|
||||
assertTrue(s.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Assert with JUnit 4 'Assert.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class SomeJUnit4Test {
|
||||
@Nullable
|
||||
native String getNullableString();
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
Assert.assertNotNull(s);
|
||||
//noinspection SimplifiableConditionalExpression
|
||||
assertTrue(s.isEmpty() ? true : false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// "Assert with JUnit 5 'Assertions.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SomeJUnit5Test {
|
||||
@Nullable
|
||||
String getNullableString() {
|
||||
double random = Math.random();
|
||||
if (random > 0.75) return null;
|
||||
if (random > 0.50) return "";
|
||||
else return "bruh";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
Assertions.assertNotNull(s);
|
||||
assertTrue(s.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// "Assert with TestNG 'Assert.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.testng.Assert;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class SomeTestNGTest {
|
||||
@Nullable
|
||||
String getNullableString() {
|
||||
double random = Math.random();
|
||||
if (random > 0.75) return null;
|
||||
if (random > 0.50) return "";
|
||||
else return "bruh";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
Assert.assertNotNull(s);
|
||||
assertTrue(s.isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Assert with JUnit 3 'assertNotNull(s)'" "true-preview"
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class SomeJUnit3Test extends TestCase {
|
||||
@Nullable
|
||||
String getNullableString() {
|
||||
double random = Math.random();
|
||||
if (random > 0.75) return null;
|
||||
if (random > 0.50) return "";
|
||||
else return "bruh";
|
||||
}
|
||||
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
assertTrue(s.isEm<caret>pty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// "Assert with JUnit 4 'Assert.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class SomeJUnit4Test {
|
||||
@Nullable
|
||||
native String getNullableString();
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
assertTrue(s.isEm<caret>pty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Assert with JUnit 4 'Assert.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class SomeJUnit4Test {
|
||||
@Nullable
|
||||
native String getNullableString();
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
//noinspection SimplifiableConditionalExpression
|
||||
assertTrue(s.isEm<caret>pty() ? true : false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// "Assert with JUnit 5 'Assertions.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SomeJUnit5Test {
|
||||
@Nullable
|
||||
String getNullableString() {
|
||||
double random = Math.random();
|
||||
if (random > 0.75) return null;
|
||||
if (random > 0.50) return "";
|
||||
else return "bruh";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
assertTrue(s.isEm<caret>pty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// "Assert with TestNG 'Assert.assertNotNull(s)'" "true-preview"
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class SomeTestNGTest {
|
||||
@Nullable
|
||||
String getNullableString() {
|
||||
double random = Math.random();
|
||||
if (random > 0.75) return null;
|
||||
if (random > 0.50) return "";
|
||||
else return "bruh";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String s = getNullableString();
|
||||
assertTrue(s.isEm<caret>pty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInsight.daemon.impl.quickfix;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.dataFlow.DataFlowInspection;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.project.IntelliJProjectConfiguration;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class AddAssertNonNullFromTestFrameworksFixTest extends LightQuickFixParameterizedTestCase {
|
||||
private static final LightProjectDescriptor ourProjectDescriptor = new DefaultLightProjectDescriptor() {
|
||||
@Override
|
||||
public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) {
|
||||
super.configureModule(module, model, contentEntry);
|
||||
|
||||
IntelliJProjectConfiguration.LibraryRoots junit3Library = IntelliJProjectConfiguration.getProjectLibrary("JUnit3");
|
||||
PsiTestUtil.addLibrary(model, "JUnit3", "", ArrayUtil.toStringArray(junit3Library.getClassesPaths()));
|
||||
|
||||
IntelliJProjectConfiguration.LibraryRoots junit4Library = IntelliJProjectConfiguration.getProjectLibrary("JUnit4");
|
||||
PsiTestUtil.addLibrary(model, "JUnit4", "", ArrayUtil.toStringArray(junit4Library.getClassesPaths()));
|
||||
|
||||
IntelliJProjectConfiguration.LibraryRoots junit5Library = IntelliJProjectConfiguration.getProjectLibrary("JUnit5");
|
||||
PsiTestUtil.addLibrary(model, "JUnit5", "", ArrayUtil.toStringArray(junit5Library.getClassesPaths()));
|
||||
|
||||
IntelliJProjectConfiguration.LibraryRoots testNGLibrary = IntelliJProjectConfiguration.getProjectLibrary("TestNG");
|
||||
PsiTestUtil.addLibrary(model, "TestNG", "", ArrayUtil.toStringArray(testNGLibrary.getClassesPaths()));
|
||||
|
||||
DefaultLightProjectDescriptor.addJetBrainsAnnotations(model);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return ourProjectDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new DataFlowInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull String getBasePath() {
|
||||
return "/codeInsight/daemonCodeAnalyzer/quickFix/addAssertNonNullFromTestFrameworks";
|
||||
}
|
||||
}
|
||||
@@ -371,6 +371,7 @@ include.accessors=&Include Accessors
|
||||
infer.nullity.progress=Post-processing results\u2026
|
||||
insert.override.annotation=Insert @&Override annotation
|
||||
inspection.assert.quickfix=Assert ''{0}''
|
||||
inspection.testframework.assert.quickfix=Assert with {0} ''{1}''
|
||||
inspection.capturing.cleaner=Runnable passed to Cleaner.register() captures ''{0}'' reference
|
||||
inspection.capturing.cleaner.description=Cleaner captures object reference
|
||||
inspection.cast.can.be.removed.narrowing.variable.type.fix.family.name=Change variable type and remove cast
|
||||
@@ -1984,4 +1985,4 @@ command.completion.inline.text=Inline
|
||||
command.completion.project.tool.text=Open Project tool window
|
||||
command.completion.recent.files.text=Open recent files popup
|
||||
|
||||
advanced.setting.java.show.irrelevant.templates.in.source.roots=Show irrelevant New File templates in Java source roots
|
||||
advanced.setting.java.show.irrelevant.templates.in.source.roots=Show irrelevant New File templates in Java source roots
|
||||
|
||||
Reference in New Issue
Block a user