From 8051511524b521a4c6bcd51f3dcdddcb248f4aef Mon Sep 17 00:00:00 2001 From: chrisly42 Date: Sat, 23 Mar 2019 22:44:22 +0100 Subject: [PATCH] Implemented AssertThatBooleanIsTrueOrFalseInspection. --- README.md | 6 +- .../inspections/AbstractAssertJInspection.kt | 11 ++- ...ssertThatBooleanIsTrueOrFalseInspection.kt | 96 +++++++++++++++++++ .../AssertThatObjectIsNotNullInspection.kt | 2 +- .../AssertThatObjectIsNullInspection.kt | 2 +- src/main/resources/META-INF/plugin.xml | 3 + .../AssertThatBooleanIsTrueOrFalse.html | 7 ++ .../intellij/playground/Playground.java | 33 +++++++ .../plugins/cajon/AbstractCajonTest.kt | 4 +- ...tThatBooleanIsTrueOrFalseInspectionTest.kt | 21 ++++ ...AssertThatObjectIsNotNullInspectionTest.kt | 2 +- .../AssertThatObjectIsNullInspectionTest.kt | 2 +- .../BooleanIsTrueOrFalseAfter.java | 32 +++++++ .../BooleanIsTrueOrFalseBefore.java | 32 +++++++ 14 files changed, 241 insertions(+), 12 deletions(-) create mode 100644 src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspection.kt create mode 100644 src/main/resources/inspectionDescriptions/AssertThatBooleanIsTrueOrFalse.html create mode 100644 src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspectionTest.kt create mode 100644 src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseAfter.java create mode 100644 src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseBefore.java diff --git a/README.md b/README.md index 510018c..c20e86a 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,12 @@ This makes finding bugs and fixing failed tests easier. - AssertThatObjectIsNotNull > from: assertThat(object).isNotEqualTo(null); > to: assertThat(object).isNotNull(); - -## TODO - AssertThatBooleanIsTrueOrFalse > from: assertThat(booleanValue).isEqualTo(true/false/Boolean.TRUE/Boolean.FALSE); > to: assertThat(booleanValue).isTrue()/isFalse(); -- AssertThatStringEmpty + +## TODO +- AssertThatStringIsEmpty > from: assertThat(string).isEqualTo("") > to: assertThat(string).isEmpty(); - AssertThatArrayHasLiteralSize diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AbstractAssertJInspection.kt b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AbstractAssertJInspection.kt index adc0f1b..1ecc873 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AbstractAssertJInspection.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AbstractAssertJInspection.kt @@ -7,14 +7,19 @@ import com.siyeh.ig.callMatcher.CallMatcher open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() { companion object { - private const val ABSTRACT_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractAssert" - private const val IS_EQUAL_TO = "isEqualTo" - private const val IS_NOT_EQUAL_TO = "isNotEqualTo" + const val ABSTRACT_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractAssert" + const val ABSTRACT_BOOLEAN_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractBooleanAssert" + const val IS_EQUAL_TO = "isEqualTo" + const val IS_NOT_EQUAL_TO = "isNotEqualTo" val IS_EQUAL_TO_OBJECT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_EQUAL_TO) .parameterTypes(CommonClassNames.JAVA_LANG_OBJECT)!! val IS_NOT_EQUAL_TO_OBJECT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_NOT_EQUAL_TO) .parameterTypes(CommonClassNames.JAVA_LANG_OBJECT)!! + val IS_EQUAL_TO_BOOLEAN = CallMatcher.instanceCall(ABSTRACT_BOOLEAN_ASSERT_CLASSNAME, IS_EQUAL_TO) + .parameterTypes("boolean")!! + val IS_NOT_EQUAL_TO_BOOLEAN = CallMatcher.instanceCall(ABSTRACT_BOOLEAN_ASSERT_CLASSNAME, IS_NOT_EQUAL_TO) + .parameterTypes("boolean")!! } override fun getGroupDisplayName(): String { diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspection.kt b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspection.kt new file mode 100644 index 0000000..89afeb3 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspection.kt @@ -0,0 +1,96 @@ +package de.platon42.intellij.plugins.cajon.inspections + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.psi.* +import com.intellij.psi.util.TypeConversionUtil +import org.jetbrains.annotations.NonNls + +class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() { + + companion object { + @NonNls + private val DISPLAY_NAME = "Asserting true or false" + + @NonNls + private val INSPECTION_MESSAGE = "isEqualTo(true/false) can be simplified to isTrue()/isFalse()" + + @NonNls + private val QUICKFIX_DESCRIPTION_IS_TRUE = "Replace isEqualTo(true/false) with isTrue()/isFalse()" + + @NonNls + private val QUICKFIX_DESCRIPTION_NOT_IS_TRUE = "Replace isNotEqualTo(true/false) with isFalse()/isTrue()" + } + + override fun getDisplayName() = DISPLAY_NAME + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object : JavaElementVisitor() { + override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { + super.visitMethodCallExpression(expression) + val isEqualToObject = IS_EQUAL_TO_OBJECT.test(expression) + val isEqualToBoolean = IS_EQUAL_TO_BOOLEAN.test(expression) + val isNotEqualToObject = IS_NOT_EQUAL_TO_OBJECT.test(expression) + val isNotEqualToBoolean = IS_NOT_EQUAL_TO_BOOLEAN.test(expression) + val normalBooleanTest = isEqualToObject || isEqualToBoolean + val flippedBooleanTest = isNotEqualToObject || isNotEqualToBoolean + if (!(normalBooleanTest || flippedBooleanTest)) { + return + } + var assertedType = expression.methodExpression.qualifierExpression?.type + if (assertedType is PsiCapturedWildcardType) { + assertedType = assertedType.upperBound + } + val assertedTypeIsBoolean = + assertedType?.canonicalText?.startsWith(ABSTRACT_BOOLEAN_ASSERT_CLASSNAME) ?: false + val equalToExpression = expression.argumentList.expressions[0]!! + if (!TypeConversionUtil.isBooleanType(equalToExpression.type) || !assertedTypeIsBoolean) { + return + } + val constantEvaluationHelper = JavaPsiFacade.getInstance(holder.project).constantEvaluationHelper + var result = constantEvaluationHelper.computeConstantExpression(equalToExpression) + if (result == null) { + val field = (equalToExpression as? PsiReferenceExpression)?.resolve() as? PsiField + if (field?.containingClass?.qualifiedName == CommonClassNames.JAVA_LANG_BOOLEAN) { + when { + field.name == "TRUE" -> result = true + field.name == "FALSE" -> result = false + } + } + } + val expectedResult = result as? Boolean ?: return + holder.registerProblem( + expression, + INSPECTION_MESSAGE, + ProblemHighlightType.INFORMATION, + null as TextRange?, + ReplaceWithIsNullQuickFix( + expectedResult xor flippedBooleanTest, + if (flippedBooleanTest) QUICKFIX_DESCRIPTION_NOT_IS_TRUE else QUICKFIX_DESCRIPTION_IS_TRUE + ) + ) + } + } + } + + + private class ReplaceWithIsNullQuickFix(val expectedResult: Boolean, val quickfixName: String) : LocalQuickFix { + override fun getFamilyName() = quickfixName + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val element = descriptor.startElement + val factory = JavaPsiFacade.getElementFactory(element.project) + val methodCallExpression = element as? PsiMethodCallExpression ?: return + val oldQualifier = methodCallExpression.methodExpression.qualifierExpression ?: return + val methodName = if (expectedResult) "isTrue()" else "isFalse()" + val isNullExpression = + factory.createExpressionFromText("a.$methodName", null) as PsiMethodCallExpression + isNullExpression.methodExpression.qualifierExpression!!.replace(oldQualifier) + element.replace(isNullExpression) + } + } +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspection.kt b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspection.kt index 6cc77f0..b24905d 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspection.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspection.kt @@ -13,7 +13,7 @@ class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() { companion object { @NonNls - private val DISPLAY_NAME = "Comparing to non-null" + private val DISPLAY_NAME = "Asserting non-null" @NonNls private val INSPECTION_MESSAGE = "isNotEqualTo(null) can be simplified to isNotNull()" diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspection.kt b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspection.kt index 35b5cfc..4bce1c0 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspection.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspection.kt @@ -13,7 +13,7 @@ class AssertThatObjectIsNullInspection : AbstractAssertJInspection() { companion object { @NonNls - private val DISPLAY_NAME = "Comparing to null" + private val DISPLAY_NAME = "Asserting null" @NonNls private val INSPECTION_MESSAGE = "isEqualTo(null) can be simplified to isNull()" diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index e6c41e7..cf81350 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -22,6 +22,9 @@ implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatObjectIsNullInspection"/> + diff --git a/src/main/resources/inspectionDescriptions/AssertThatBooleanIsTrueOrFalse.html b/src/main/resources/inspectionDescriptions/AssertThatBooleanIsTrueOrFalse.html new file mode 100644 index 0000000..d87d6de --- /dev/null +++ b/src/main/resources/inspectionDescriptions/AssertThatBooleanIsTrueOrFalse.html @@ -0,0 +1,7 @@ + + +Turns assertThat(booleanExpression).isEqualTo(true/false) or assertThat(booleanExpression).isNotEqualTo(true/false) +into assertThat(booleanExpression).isTrue() or assertThat(booleanExpression).isFalse(). + + + \ No newline at end of file diff --git a/src/test/java/de/platon42/intellij/playground/Playground.java b/src/test/java/de/platon42/intellij/playground/Playground.java index bd298bf..a0ada4b 100644 --- a/src/test/java/de/platon42/intellij/playground/Playground.java +++ b/src/test/java/de/platon42/intellij/playground/Playground.java @@ -14,5 +14,38 @@ public class Playground { assertThat(new String[1].length).isLessThanOrEqualTo(1); assertThat(new String[1]).hasSameSizeAs(new Object()); assertThat("").isEqualTo(null); + assertThat(true).isTrue(); + assertThat(true).isEqualTo(true); + assertThat(Boolean.TRUE).isEqualTo(Boolean.FALSE); + assertThat(Boolean.TRUE).isEqualTo(true); } + + private void booleanIsTrueOrFalse() { + boolean primitive = false; + Boolean object = java.lang.Boolean.TRUE; + + assertThat(primitive).isEqualTo(Boolean.TRUE); + assertThat(primitive).isEqualTo(Boolean.FALSE); + assertThat(object).isEqualTo(Boolean.TRUE); + assertThat(object).isEqualTo(Boolean.FALSE); + assertThat(primitive).isEqualTo(true); + assertThat(primitive).isEqualTo(false); + assertThat(object).isEqualTo(true); + assertThat(object).isEqualTo(false); + + assertThat(primitive).isNotEqualTo(Boolean.TRUE); + assertThat(primitive).isNotEqualTo(Boolean.FALSE); + assertThat(object).isNotEqualTo(Boolean.TRUE); + assertThat(object).isNotEqualTo(Boolean.FALSE); + assertThat(primitive).isNotEqualTo(true); + assertThat(primitive).isNotEqualTo(false); + assertThat(object).isNotEqualTo(true); + assertThat(object).isNotEqualTo(false); + + assertThat(primitive).as("nah").isEqualTo(true && !true); + assertThat(object).isEqualTo(Boolean.TRUE && !Boolean.TRUE); + + assertThat("").isEqualTo(Boolean.TRUE); + } + } diff --git a/src/test/java/de/platon42/intellij/plugins/cajon/AbstractCajonTest.kt b/src/test/java/de/platon42/intellij/plugins/cajon/AbstractCajonTest.kt index b877670..cb14ed9 100644 --- a/src/test/java/de/platon42/intellij/plugins/cajon/AbstractCajonTest.kt +++ b/src/test/java/de/platon42/intellij/plugins/cajon/AbstractCajonTest.kt @@ -59,8 +59,8 @@ abstract class AbstractCajonTest { } } - protected fun executeQuickFixes(myFixture: JavaCodeInsightTestFixture, expectedFixes: Int) { - val quickfixes = myFixture.getAllQuickFixes() + protected fun executeQuickFixes(myFixture: JavaCodeInsightTestFixture, regex: Regex, expectedFixes: Int) { + val quickfixes = myFixture.getAllQuickFixes().filter { it.familyName.matches(regex) } assertThat(quickfixes).hasSize(expectedFixes) quickfixes.forEach(myFixture::launchAction) } diff --git a/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspectionTest.kt b/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspectionTest.kt new file mode 100644 index 0000000..b0c0114 --- /dev/null +++ b/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatBooleanIsTrueOrFalseInspectionTest.kt @@ -0,0 +1,21 @@ +package de.platon42.intellij.plugins.cajon.inspections + +import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture +import de.platon42.intellij.jupiter.MyFixture +import de.platon42.intellij.jupiter.TestDataSubPath +import de.platon42.intellij.plugins.cajon.AbstractCajonTest +import org.junit.jupiter.api.Test + +internal class AssertThatBooleanIsTrueOrFalseInspectionTest : AbstractCajonTest() { + + @Test + @TestDataSubPath("inspections/BooleanIsTrueOrFalse") + internal fun assertThat_with_isEqualTo_true_or_false_can_use_isTrue_or_isFalse(@MyFixture myFixture: JavaCodeInsightTestFixture) { + runTest { + myFixture.enableInspections(AssertThatBooleanIsTrueOrFalseInspection::class.java) + myFixture.configureByFile("BooleanIsTrueOrFalseBefore.java") + executeQuickFixes(myFixture, Regex("Replace is.*"), 17) + myFixture.checkResultByFile("BooleanIsTrueOrFalseAfter.java") + } + } +} \ No newline at end of file diff --git a/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspectionTest.kt b/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspectionTest.kt index 421cd13..c14ddb4 100644 --- a/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspectionTest.kt +++ b/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNotNullInspectionTest.kt @@ -14,7 +14,7 @@ internal class AssertThatObjectIsNotNullInspectionTest : AbstractCajonTest() { runTest { myFixture.enableInspections(AssertThatObjectIsNotNullInspection::class.java) myFixture.configureByFile("ObjectIsNotNullBefore.java") - executeQuickFixes(myFixture, 3) + executeQuickFixes(myFixture, Regex.fromLiteral("Replace isNotEqualTo(null) with isNotNull()"), 3) myFixture.checkResultByFile("ObjectIsNotNullAfter.java") } } diff --git a/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspectionTest.kt b/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspectionTest.kt index b934fa6..a5bed5e 100644 --- a/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspectionTest.kt +++ b/src/test/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatObjectIsNullInspectionTest.kt @@ -14,7 +14,7 @@ internal class AssertThatObjectIsNullInspectionTest : AbstractCajonTest() { runTest { myFixture.enableInspections(AssertThatObjectIsNullInspection::class.java) myFixture.configureByFile("ObjectIsNullBefore.java") - executeQuickFixes(myFixture, 3) + executeQuickFixes(myFixture, Regex.fromLiteral("Replace isEqualTo(null) with isNull()"), 3) myFixture.checkResultByFile("ObjectIsNullAfter.java") } } diff --git a/src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseAfter.java b/src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseAfter.java new file mode 100644 index 0000000..afe2840 --- /dev/null +++ b/src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseAfter.java @@ -0,0 +1,32 @@ +import static org.assertj.core.api.Assertions.assertThat; + +public class BooleanIsTrueOrFalse { + + private void booleanIsTrueOrFalse() { + boolean primitive = false; + Boolean object = Boolean.TRUE; + + assertThat(primitive).isTrue(); + assertThat(primitive).isFalse(); + assertThat(object).isTrue(); + assertThat(object).isFalse(); + assertThat(primitive).isTrue(); + assertThat(primitive).isFalse(); + assertThat(object).isTrue(); + assertThat(object).isFalse(); + + assertThat(primitive).isFalse(); + assertThat(primitive).isTrue(); + assertThat(object).isFalse(); + assertThat(object).isTrue(); + assertThat(primitive).isFalse(); + assertThat(primitive).isTrue(); + assertThat(object).isFalse(); + assertThat(object).isTrue(); + + assertThat(primitive).as("nah").isFalse(); + assertThat(object).isEqualTo(Boolean.TRUE && !Boolean.TRUE); + + assertThat("").isEqualTo(Boolean.TRUE); + } +} diff --git a/src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseBefore.java b/src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseBefore.java new file mode 100644 index 0000000..6da7ac6 --- /dev/null +++ b/src/test/resources/inspections/BooleanIsTrueOrFalse/BooleanIsTrueOrFalseBefore.java @@ -0,0 +1,32 @@ +import static org.assertj.core.api.Assertions.assertThat; + +public class BooleanIsTrueOrFalse { + + private void booleanIsTrueOrFalse() { + boolean primitive = false; + Boolean object = Boolean.TRUE; + + assertThat(primitive).isEqualTo(Boolean.TRUE); + assertThat(primitive).isEqualTo(Boolean.FALSE); + assertThat(object).isEqualTo(Boolean.TRUE); + assertThat(object).isEqualTo(Boolean.FALSE); + assertThat(primitive).isEqualTo(true); + assertThat(primitive).isEqualTo(false); + assertThat(object).isEqualTo(true); + assertThat(object).isEqualTo(false); + + assertThat(primitive).isNotEqualTo(Boolean.TRUE); + assertThat(primitive).isNotEqualTo(Boolean.FALSE); + assertThat(object).isNotEqualTo(Boolean.TRUE); + assertThat(object).isNotEqualTo(Boolean.FALSE); + assertThat(primitive).isNotEqualTo(true); + assertThat(primitive).isNotEqualTo(false); + assertThat(object).isNotEqualTo(true); + assertThat(object).isNotEqualTo(false); + + assertThat(primitive).as("nah").isEqualTo(true && !true); + assertThat(object).isEqualTo(Boolean.TRUE && !Boolean.TRUE); + + assertThat("").isEqualTo(Boolean.TRUE); + } +}