diff --git a/README.md b/README.md index 1abbe5f..6053a4b 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,31 @@ You can toggle the various inspections in the Settings/Editor/Inspections in the ``` Analogously with ```isFalse()```. +- AssertThatCollectionOrMapExpression + + Moves collection and map operations inside assertThat() out. + + ``` + from: assertThat(collection.isEmpty()).isTrue(); + to: assertThat(collection).isEmpty(); + + from: assertThat(collection.contains("foobar")).isTrue(); + to: assertThat(collection).contains("foobar"); + + from: assertThat(collection.containsAll(otherCollection)).isTrue(); + to: assertThat(collection).containsAll(otherCollection); + + from: assertThat(map.isEmpty()).isTrue(); + to: assertThat(map).isEmpty(); + + from: assertThat(map.containsKey(key)).isTrue(); + to: assertThat(map).containsKey(key); + + from: assertThat(map.containsValue(value)).isTrue(); + to: assertThat(map).containsValue(value); + ``` + Analogously with ```isFalse()``` (except for containsAll()). + - AssertThatEnumerableIsEmpty Uses ```isEmpty()``` for ```hasSize(0)``` iterable assertions instead. @@ -428,10 +453,14 @@ Feel free to use the code (in package de.platon42.intellij.jupiter) for your pro to: assertThat(object).extracting(type::getPropOne, it -> it.propNoGetter, it -> it.getPropTwo().getInnerProp())... ``` -- Kotlin support (right now, however, with less than 100 downloads after a month, this is unlikely to happen) - ## Changelog +#### V1.0 (06-May-19) +- First release to be considered stable enough for production use. +- Fixed a NPE in AssumeThatInsteadOfReturn inspection quickfix for empty else branches. +- Fixed missing description for AssumeThatInsteadOfReturn inspection. +- Added new AssertThatCollectionOrMapExpression inspection that tries to pull out methods such as ```isEmpty()``` or ```contains()``` out of an actual assertThat() expression. + #### V0.8 (05-May-19) - Fixed missing description for JoinAssertThatStatements and detection of equivalent expressions (sorry, released it too hastily). - Fixed ```isEmpty()``` for enumerables and strings and ```isNull()``` for object conversions to be applied only if it is the terminal method call as ```isEmpty()``` and ```isNull()``` return void. diff --git a/build.gradle b/build.gradle index 54f59ec..e22698c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { } group 'de.platon42' -version '0.8' +version '1.0' repositories { mavenCentral() @@ -42,6 +42,13 @@ intellij { patchPluginXml { changeNotes """ +
Full changelog available at Github project site.
""" } diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/AssertJClassNames.kt b/src/main/java/de/platon42/intellij/plugins/cajon/AssertJClassNames.kt index 3d7b125..b234966 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/AssertJClassNames.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/AssertJClassNames.kt @@ -14,6 +14,8 @@ class AssertJClassNames { const val DESCRIPTABLE_INTERFACE = "org.assertj.core.api.Descriptable" @NonNls const val EXTENSION_POINTS_INTERFACE = "org.assertj.core.api.ExtensionPoints" + @NonNls + const val OBJECT_ENUMERABLE_ASSERT_INTERFACE = "org.assertj.core.api.ObjectEnumerableAssert" @NonNls const val ASSERT_INTERFACE = "org.assertj.core.api.Assert" diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/CommonMatchers.kt b/src/main/java/de/platon42/intellij/plugins/cajon/CommonMatchers.kt index e1b5edc..4a60485 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/CommonMatchers.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/CommonMatchers.kt @@ -23,7 +23,18 @@ val DESCRIBED_AS = CallMatcher.instanceCall(AssertJClassNames.DESCRIPTABLE_INTER val WITH_REPRESENTATION_AND_SUCH = CallMatcher.instanceCall(AssertJClassNames.ASSERT_INTERFACE, "withRepresentation", "withThreadDumpOnError")!! val USING_COMPARATOR = CallMatcher.instanceCall(AssertJClassNames.ASSERT_INTERFACE, "usingComparator", "usingDefaultComparator")!! val IN_HEXADECIMAL_OR_BINARY = CallMatcher.instanceCall(AssertJClassNames.ABSTRACT_ASSERT_CLASSNAME, MethodNames.IN_HEXADECIMAL, MethodNames.IN_BINARY)!! -val EXTENSION_POINTS = CallMatcher.instanceCall(AssertJClassNames.EXTENSION_POINTS_INTERFACE, "is", "isNot", "has", "doesNotHave", "satisfies")!! +val EXTENSION_POINTS = CallMatcher.instanceCall( + AssertJClassNames.EXTENSION_POINTS_INTERFACE, + "is", "isNot", "has", "doesNotHave", + "satisfies" +)!! +val MORE_EXTENSION_POINTS = CallMatcher.instanceCall( + AssertJClassNames.OBJECT_ENUMERABLE_ASSERT_INTERFACE, + "are", "areNot", "have", "doNotHave", + "areAtLeast", "areAtLeastOne", "areAtMost", "areExactly", + "haveAtLeastOne", "haveAtLeast", "haveAtMost", "haveExactly", + "hasOnlyOneElementSatisfying", "anyMatch", "noneMatch", "anySatisfy", "noneSatisfy" +)!! val NOT_ACTUAL_ASSERTIONS = CallMatcher.anyOf( ALL_ASSERT_THAT_MATCHERS, diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/Extensions.kt b/src/main/java/de/platon42/intellij/plugins/cajon/Extensions.kt index 20f7b01..7e13207 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/Extensions.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/Extensions.kt @@ -133,6 +133,7 @@ fun PsiExpression.getAllTheSameExpectedBooleanConstants(): Boolean? { } else { val isNotConstant = CallMatcher.anyOf( EXTENSION_POINTS, + MORE_EXTENSION_POINTS, AbstractAssertJInspection.IS_EQUAL_TO_BOOLEAN, AbstractAssertJInspection.IS_EQUAL_TO_OBJECT, AbstractAssertJInspection.IS_NOT_EQUAL_TO_BOOLEAN, diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/MethodNames.kt b/src/main/java/de/platon42/intellij/plugins/cajon/MethodNames.kt index 37c9269..b78c346 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/MethodNames.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/MethodNames.kt @@ -84,6 +84,16 @@ class MethodNames { @NonNls const val CONTAINS_EXACTLY = "containsExactly" @NonNls + const val CONTAINS_ALL = "containsAll" + @NonNls + const val CONTAINS_KEY = "containsKey" + @NonNls + const val DOES_NOT_CONTAIN_KEY = "doesNotContainKey" + @NonNls + const val CONTAINS_VALUE = "containsValue" + @NonNls + const val DOES_NOT_CONTAIN_VALUE = "doesNotContainValue" + @NonNls const val IS_EQUAL_TO_IC = "isEqualToIgnoringCase" @NonNls const val IS_NOT_EQUAL_TO_IC = "isNotEqualToIgnoringCase" diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatCollectionOrMapExpressionInspection.kt b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatCollectionOrMapExpressionInspection.kt new file mode 100644 index 0000000..a1d0af5 --- /dev/null +++ b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatCollectionOrMapExpressionInspection.kt @@ -0,0 +1,71 @@ +package de.platon42.intellij.plugins.cajon.inspections + +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.* +import com.siyeh.ig.callMatcher.CallMatcher +import de.platon42.intellij.plugins.cajon.* +import de.platon42.intellij.plugins.cajon.quickfixes.MoveOutMethodCallExpressionQuickFix + +class AssertThatCollectionOrMapExpressionInspection : AbstractAssertJInspection() { + + companion object { + private const val DISPLAY_NAME = "Asserting a collection or map specific expression" + + private val MAPPINGS = listOf( + Mapping( + CallMatcher.anyOf( + CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "isEmpty").parameterCount(0), + CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "isEmpty").parameterCount(0) + ), + MethodNames.IS_EMPTY, MethodNames.IS_NOT_EMPTY + ), + Mapping( + CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "contains").parameterCount(1), + MethodNames.CONTAINS, MethodNames.DOES_NOT_CONTAIN + ), + Mapping( + CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "containsAll").parameterCount(1), + MethodNames.CONTAINS_ALL, null + ), + Mapping( + CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "containsKey").parameterCount(1), + MethodNames.CONTAINS_KEY, MethodNames.DOES_NOT_CONTAIN_KEY + ), + Mapping( + CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_MAP, "containsValue").parameterCount(1), + MethodNames.CONTAINS_VALUE, MethodNames.DOES_NOT_CONTAIN_VALUE + ) + ) + } + + override fun getDisplayName() = DISPLAY_NAME + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object : JavaElementVisitor() { + override fun visitExpressionStatement(statement: PsiExpressionStatement) { + super.visitExpressionStatement(statement) + if (!statement.hasAssertThat()) { + return + } + val staticMethodCall = statement.findStaticMethodCall() ?: return + if (!ASSERT_THAT_BOOLEAN.test(staticMethodCall)) { + return + } + val assertThatArgument = staticMethodCall.firstArg as? PsiMethodCallExpression ?: return + val mapping = MAPPINGS.firstOrNull { it.callMatcher.test(assertThatArgument) } ?: return + + val expectedCallExpression = statement.findOutmostMethodCall() ?: return + val expectedResult = expectedCallExpression.getAllTheSameExpectedBooleanConstants() ?: return + + val replacementMethod = if (expectedResult) mapping.replacementForTrue else mapping.replacementForFalse ?: return + registerMoveOutMethod(holder, expectedCallExpression, assertThatArgument, replacementMethod, ::MoveOutMethodCallExpressionQuickFix) + } + } + } + + private class Mapping( + val callMatcher: CallMatcher, + val replacementForTrue: String, + val replacementForFalse: String? + ) +} \ No newline at end of file diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatStringExpressionInspection.kt b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatStringExpressionInspection.kt index 03dba76..2bf65ef 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatStringExpressionInspection.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/inspections/AssertThatStringExpressionInspection.kt @@ -13,30 +13,30 @@ class AssertThatStringExpressionInspection : AbstractAssertJInspection() { private val MAPPINGS = listOf( Mapping( - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "isEmpty").parameterCount(0)!!, + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "isEmpty").parameterCount(0), MethodNames.IS_EMPTY, MethodNames.IS_NOT_EMPTY ), Mapping( CallMatcher.anyOf( - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "equals").parameterCount(1)!!, - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "contentEquals").parameterCount(1)!! + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "equals").parameterCount(1), + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "contentEquals").parameterCount(1) ), MethodNames.IS_EQUAL_TO, MethodNames.IS_NOT_EQUAL_TO ), Mapping( - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "equalsIgnoreCase").parameterTypes(CommonClassNames.JAVA_LANG_STRING)!!, + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "equalsIgnoreCase").parameterTypes(CommonClassNames.JAVA_LANG_STRING), MethodNames.IS_EQUAL_TO_IC, MethodNames.IS_NOT_EQUAL_TO_IC ), Mapping( - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "contains").parameterCount(1)!!, + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "contains").parameterCount(1), MethodNames.CONTAINS, MethodNames.DOES_NOT_CONTAIN ), Mapping( - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "startsWith").parameterTypes(CommonClassNames.JAVA_LANG_STRING)!!, + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "startsWith").parameterTypes(CommonClassNames.JAVA_LANG_STRING), MethodNames.STARTS_WITH, MethodNames.DOES_NOT_START_WITH ), Mapping( - CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "endsWith").parameterTypes(CommonClassNames.JAVA_LANG_STRING)!!, + CallMatcher.instanceCall(CommonClassNames.JAVA_LANG_STRING, "endsWith").parameterTypes(CommonClassNames.JAVA_LANG_STRING), MethodNames.ENDS_WITH, MethodNames.DOES_NOT_END_WITH ) ) diff --git a/src/main/java/de/platon42/intellij/plugins/cajon/quickfixes/JoinStatementsQuickFix.kt b/src/main/java/de/platon42/intellij/plugins/cajon/quickfixes/JoinStatementsQuickFix.kt index 8e8c87f..e1a2891 100644 --- a/src/main/java/de/platon42/intellij/plugins/cajon/quickfixes/JoinStatementsQuickFix.kt +++ b/src/main/java/de/platon42/intellij/plugins/cajon/quickfixes/JoinStatementsQuickFix.kt @@ -19,20 +19,20 @@ class JoinStatementsQuickFix : AbstractCommonQuickFix(JOIN_STATEMENTS_MESSAGE) { do { val commentsToKeep = ArrayList