Another fix for AssertThatStringIsEmpty, added support for CharSequences, added hasSize(0) case, refactored stuff. Added missing html.

This commit is contained in:
Chris Hodges 2019-03-24 13:03:53 +01:00
parent 458542de7c
commit e2ffce753c
13 changed files with 91 additions and 85 deletions

View File

@ -36,8 +36,9 @@ Then AssertJ would tell you the contents of the collection on failure.
> from: assertThat(booleanValue).isEqualTo(true/false/Boolean.TRUE/Boolean.FALSE); > from: assertThat(booleanValue).isEqualTo(true/false/Boolean.TRUE/Boolean.FALSE);
> to: assertThat(booleanValue).isTrue()/isFalse(); > to: assertThat(booleanValue).isTrue()/isFalse();
- AssertThatStringIsEmpty - AssertThatStringIsEmpty
> from: assertThat(string).isEqualTo("") > from: assertThat(charSequence/string).isEqualTo("");
> to: assertThat(string).isEmpty(); > from: assertThat(charSequence/string).hasSize(0);
> to: assertThat(charSequence/string).isEmpty();
## TODO ## TODO
- AssertThatArrayHasLiteralSize - AssertThatArrayHasLiteralSize

View File

@ -1,19 +1,35 @@
package de.platon42.intellij.plugins.cajon.inspections package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.CommonClassNames import com.intellij.psi.CommonClassNames
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiCapturedWildcardType import com.intellij.psi.PsiCapturedWildcardType
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiTypesUtil
import com.siyeh.ig.callMatcher.CallMatcher import com.siyeh.ig.callMatcher.CallMatcher
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls
open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() { open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() {
companion object { companion object {
@NonNls
const val SIMPLIFY_MESSAGE_TEMPLATE = "%s can be simplified to %s"
@NonNls
const val REPLACE_DESCRIPTION_TEMPLATE = "Replace %s with %s"
const val ABSTRACT_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractAssert" const val ABSTRACT_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractAssert"
const val ABSTRACT_BOOLEAN_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractBooleanAssert" const val ABSTRACT_BOOLEAN_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractBooleanAssert"
const val ABSTRACT_STRING_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractStringAssert" const val ABSTRACT_STRING_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractStringAssert"
const val ABSTRACT_CHAR_SEQUENCE_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractCharSequenceAssert"
const val IS_EQUAL_TO = "isEqualTo" const val IS_EQUAL_TO = "isEqualTo"
const val IS_NOT_EQUAL_TO = "isNotEqualTo" const val IS_NOT_EQUAL_TO = "isNotEqualTo"
const val HAS_SIZE = "hasSize"
val IS_EQUAL_TO_OBJECT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_EQUAL_TO) val IS_EQUAL_TO_OBJECT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_EQUAL_TO)
.parameterTypes(CommonClassNames.JAVA_LANG_OBJECT)!! .parameterTypes(CommonClassNames.JAVA_LANG_OBJECT)!!
@ -23,17 +39,42 @@ open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() {
.parameterTypes("boolean")!! .parameterTypes("boolean")!!
val IS_NOT_EQUAL_TO_BOOLEAN = CallMatcher.instanceCall(ABSTRACT_BOOLEAN_ASSERT_CLASSNAME, IS_NOT_EQUAL_TO) val IS_NOT_EQUAL_TO_BOOLEAN = CallMatcher.instanceCall(ABSTRACT_BOOLEAN_ASSERT_CLASSNAME, IS_NOT_EQUAL_TO)
.parameterTypes("boolean")!! .parameterTypes("boolean")!!
val CHAR_SEQUENCE_HAS_SIZE = CallMatcher.instanceCall(ABSTRACT_CHAR_SEQUENCE_ASSERT_CLASSNAME, HAS_SIZE)
.parameterTypes("int")!!
} }
override fun getGroupDisplayName(): String { override fun getGroupDisplayName(): String {
return "AssertJ" return "AssertJ"
} }
protected fun checkAssertedType(expression: PsiMethodCallExpression, prefix: String): Boolean { protected fun checkAssertedType(expression: PsiMethodCallExpression, classname: String): Boolean {
var assertedType = expression.methodExpression.qualifierExpression?.type var assertedType = expression.methodExpression.qualifierExpression?.type ?: return false
if (assertedType is PsiCapturedWildcardType) { if (assertedType is PsiCapturedWildcardType) {
assertedType = assertedType.upperBound assertedType = assertedType.upperBound
} }
return assertedType?.canonicalText?.startsWith(prefix) ?: false val assertedClass = PsiTypesUtil.getPsiClass(assertedType) ?: return false
val expectedClass = JavaPsiFacade.getInstance(expression.project)
.findClass(classname, GlobalSearchScope.allScope(expression.project)) ?: return false
return assertedClass.isEquivalentTo(expectedClass) || assertedClass.isInheritor(expectedClass, true)
}
protected fun getOriginalMethodName(expression: PsiMethodCallExpression) =
expression.resolveMethod()?.name?.plus("()")
protected fun registerSimplifyMethod(
holder: ProblemsHolder,
expression: PsiMethodCallExpression,
replacementMethod: String
) {
val originalMethod = getOriginalMethodName(expression) ?: return
val description = String.format(REPLACE_DESCRIPTION_TEMPLATE, originalMethod, replacementMethod)
val message = String.format(SIMPLIFY_MESSAGE_TEMPLATE, originalMethod, replacementMethod)
holder.registerProblem(
expression,
message,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceSimpleMethodCallQuickFix(description, replacementMethod)
)
} }
} }

View File

@ -1,11 +1,8 @@
package de.platon42.intellij.plugins.cajon.inspections package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.util.TypeConversionUtil import com.intellij.psi.util.TypeConversionUtil
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() { class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() {
@ -13,15 +10,6 @@ class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() {
companion object { companion object {
@NonNls @NonNls
private val DISPLAY_NAME = "Asserting true or false" 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 getDisplayName() = DISPLAY_NAME
@ -59,16 +47,9 @@ class AssertThatBooleanIsTrueOrFalseInspection : AbstractAssertJInspection() {
} }
} }
val expectedResult = result as? Boolean ?: return val expectedResult = result as? Boolean ?: return
val description =
if (flippedBooleanTest) QUICKFIX_DESCRIPTION_NOT_IS_TRUE else QUICKFIX_DESCRIPTION_IS_TRUE
val replacementMethod = if (expectedResult xor flippedBooleanTest) "isTrue()" else "isFalse()" val replacementMethod = if (expectedResult xor flippedBooleanTest) "isTrue()" else "isFalse()"
holder.registerProblem( registerSimplifyMethod(holder, expression, replacementMethod)
expression,
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceSimpleMethodCallQuickFix(description, replacementMethod)
)
} }
} }
} }

View File

@ -1,13 +1,10 @@
package de.platon42.intellij.plugins.cajon.inspections package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.JavaElementVisitor import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiType import com.intellij.psi.PsiType
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() { class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() {
@ -15,12 +12,6 @@ class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() {
companion object { companion object {
@NonNls @NonNls
private val DISPLAY_NAME = "Asserting non-null" private val DISPLAY_NAME = "Asserting non-null"
@NonNls
private val INSPECTION_MESSAGE = "isNotEqualTo(null) can be simplified to isNotNull()"
@NonNls
private val QUICKFIX_DESCRIPTION = "Replace isNotEqualTo(null) with isNotNull()"
} }
override fun getDisplayName() = DISPLAY_NAME override fun getDisplayName() = DISPLAY_NAME
@ -34,13 +25,7 @@ class AssertThatObjectIsNotNullInspection : AbstractAssertJInspection() {
} }
if (expression.argumentList.expressions[0].type == PsiType.NULL) { if (expression.argumentList.expressions[0].type == PsiType.NULL) {
holder.registerProblem( registerSimplifyMethod(holder, expression, "isNotNull()")
expression,
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceSimpleMethodCallQuickFix(QUICKFIX_DESCRIPTION, "isNotNull()")
)
} }
} }
} }

View File

@ -1,13 +1,10 @@
package de.platon42.intellij.plugins.cajon.inspections package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.JavaElementVisitor import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiType import com.intellij.psi.PsiType
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
class AssertThatObjectIsNullInspection : AbstractAssertJInspection() { class AssertThatObjectIsNullInspection : AbstractAssertJInspection() {
@ -15,12 +12,6 @@ class AssertThatObjectIsNullInspection : AbstractAssertJInspection() {
companion object { companion object {
@NonNls @NonNls
private val DISPLAY_NAME = "Asserting null" private val DISPLAY_NAME = "Asserting null"
@NonNls
private val INSPECTION_MESSAGE = "isEqualTo(null) can be simplified to isNull()"
@NonNls
private val QUICKFIX_DESCRIPTION = "Replace isEqualTo(null) with isNull()"
} }
override fun getDisplayName() = DISPLAY_NAME override fun getDisplayName() = DISPLAY_NAME
@ -34,13 +25,7 @@ class AssertThatObjectIsNullInspection : AbstractAssertJInspection() {
} }
if (expression.argumentList.expressions[0].type == PsiType.NULL) { if (expression.argumentList.expressions[0].type == PsiType.NULL) {
holder.registerProblem( registerSimplifyMethod(holder, expression, "isNull()")
expression,
INSPECTION_MESSAGE,
ProblemHighlightType.INFORMATION,
null as TextRange?,
ReplaceSimpleMethodCallQuickFix(QUICKFIX_DESCRIPTION, "isNull()")
)
} }
} }
} }

View File

@ -1,13 +1,10 @@
package de.platon42.intellij.plugins.cajon.inspections package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.JavaElementVisitor import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiLiteralExpression import com.intellij.psi.PsiLiteralExpression
import com.intellij.psi.PsiMethodCallExpression import com.intellij.psi.PsiMethodCallExpression
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSimpleMethodCallQuickFix
import org.jetbrains.annotations.NonNls import org.jetbrains.annotations.NonNls
class AssertThatStringIsEmptyInspection : AbstractAssertJInspection() { class AssertThatStringIsEmptyInspection : AbstractAssertJInspection() {
@ -15,12 +12,6 @@ class AssertThatStringIsEmptyInspection : AbstractAssertJInspection() {
companion object { companion object {
@NonNls @NonNls
private val DISPLAY_NAME = "Asserting an empty string" private val DISPLAY_NAME = "Asserting an empty string"
@NonNls
private val INSPECTION_MESSAGE = "isEqualTo(\"\") can be simplified to isEmpty()"
@NonNls
private val QUICKFIX_DESCRIPTION = "Replace isEqualTo(\"\") with isEmpty()"
} }
override fun getDisplayName() = DISPLAY_NAME override fun getDisplayName() = DISPLAY_NAME
@ -29,24 +20,27 @@ class AssertThatStringIsEmptyInspection : AbstractAssertJInspection() {
return object : JavaElementVisitor() { return object : JavaElementVisitor() {
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) { override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
super.visitMethodCallExpression(expression) super.visitMethodCallExpression(expression)
if (!IS_EQUAL_TO_OBJECT.test(expression)) { val isEqual = IS_EQUAL_TO_OBJECT.test(expression)
val hasSize = CHAR_SEQUENCE_HAS_SIZE.test(expression)
if (!(isEqual || hasSize)) {
return return
} }
if (!checkAssertedType(expression, ABSTRACT_STRING_ASSERT_CLASSNAME)) { if (!checkAssertedType(expression, ABSTRACT_CHAR_SEQUENCE_ASSERT_CLASSNAME)) {
return return
} }
val psiExpression = expression.argumentList.expressions[0] as? PsiLiteralExpression ?: return if (isEqual) {
val psiExpression = expression.argumentList.expressions[0] as? PsiLiteralExpression ?: return
if (psiExpression.value == "") { if (psiExpression.value == "") {
holder.registerProblem( registerSimplifyMethod(holder, expression, "isEmpty()")
expression, }
INSPECTION_MESSAGE, } else {
ProblemHighlightType.INFORMATION, val psiExpression = expression.argumentList.expressions[0] as? PsiLiteralExpression ?: return
null as TextRange?, if (psiExpression.value == 0) {
ReplaceSimpleMethodCallQuickFix(QUICKFIX_DESCRIPTION, "isEmpty()") registerSimplifyMethod(holder, expression, "isEmpty()")
) }
} }
} }
} }

View File

@ -0,0 +1,7 @@
<html>
<body>
Turns assertThat(string).isEqualTo("") or assertThat(string).hasSize(0) into assertThat(string).isEmpty().
<!-- tooltip end -->
Also works with CharSequences.
</body>
</html>

View File

@ -1,7 +1,5 @@
package de.platon42.intellij.playground; package de.platon42.intellij.playground;
import org.assertj.core.api.AbstractStringAssert;
import java.util.ArrayList; import java.util.ArrayList;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -52,8 +50,8 @@ public class Playground {
private void stringIsEmpty() { private void stringIsEmpty() {
String foo = "bar"; String foo = "bar";
AbstractStringAssert<?> abstractStringAssert = assertThat(foo); assertThat(foo).isEqualTo("");
abstractStringAssert.isEqualTo(""); assertThat(foo).hasSize(0);
} }
} }

View File

@ -14,7 +14,7 @@ internal class AssertThatObjectIsNotNullInspectionTest : AbstractCajonTest() {
runTest { runTest {
myFixture.enableInspections(AssertThatObjectIsNotNullInspection::class.java) myFixture.enableInspections(AssertThatObjectIsNotNullInspection::class.java)
myFixture.configureByFile("ObjectIsNotNullBefore.java") myFixture.configureByFile("ObjectIsNotNullBefore.java")
executeQuickFixes(myFixture, Regex.fromLiteral("Replace isNotEqualTo(null) with isNotNull()"), 3) executeQuickFixes(myFixture, Regex.fromLiteral("Replace isNotEqualTo() with isNotNull()"), 3)
myFixture.checkResultByFile("ObjectIsNotNullAfter.java") myFixture.checkResultByFile("ObjectIsNotNullAfter.java")
} }
} }

View File

@ -14,7 +14,7 @@ internal class AssertThatObjectIsNullInspectionTest : AbstractCajonTest() {
runTest { runTest {
myFixture.enableInspections(AssertThatObjectIsNullInspection::class.java) myFixture.enableInspections(AssertThatObjectIsNullInspection::class.java)
myFixture.configureByFile("ObjectIsNullBefore.java") myFixture.configureByFile("ObjectIsNullBefore.java")
executeQuickFixes(myFixture, Regex.fromLiteral("Replace isEqualTo(null) with isNull()"), 3) executeQuickFixes(myFixture, Regex.fromLiteral("Replace isEqualTo() with isNull()"), 3)
myFixture.checkResultByFile("ObjectIsNullAfter.java") myFixture.checkResultByFile("ObjectIsNullAfter.java")
} }
} }

View File

@ -14,7 +14,7 @@ internal class AssertThatStringIsEmptyInspectionTest : AbstractCajonTest() {
runTest { runTest {
myFixture.enableInspections(AssertThatStringIsEmptyInspection::class.java) myFixture.enableInspections(AssertThatStringIsEmptyInspection::class.java)
myFixture.configureByFile("StringIsEmptyBefore.java") myFixture.configureByFile("StringIsEmptyBefore.java")
executeQuickFixes(myFixture, Regex("Replace is.*"), 1) executeQuickFixes(myFixture, Regex("Replace .*"), 4)
myFixture.checkResultByFile("StringIsEmptyAfter.java") myFixture.checkResultByFile("StringIsEmptyAfter.java")
} }
} }

View File

@ -4,9 +4,16 @@ public class StringIsEmpty {
private void stringIsEmpty() { private void stringIsEmpty() {
String string = "string"; String string = "string";
StringBuilder stringBuilder = new StringBuilder();
assertThat(string).isEqualTo("foo"); assertThat(string).isEqualTo("foo");
assertThat(string).as("foo").isEmpty(); assertThat(string).as("foo").isEmpty();
assertThat(string).as("bar").isEmpty();
assertThat(stringBuilder).isEqualTo("foo");
assertThat(stringBuilder).as("foo").isEmpty();
assertThat(stringBuilder).as("bar").isEmpty();
assertThat(new Object()).isEqualTo(""); assertThat(new Object()).isEqualTo("");
} }
} }

View File

@ -4,9 +4,16 @@ public class StringIsEmpty {
private void stringIsEmpty() { private void stringIsEmpty() {
String string = "string"; String string = "string";
StringBuilder stringBuilder = new StringBuilder();
assertThat(string).isEqualTo("foo"); assertThat(string).isEqualTo("foo");
assertThat(string).as("foo").isEqualTo(""); assertThat(string).as("foo").isEqualTo("");
assertThat(string).as("bar").hasSize(0);
assertThat(stringBuilder).isEqualTo("foo");
assertThat(stringBuilder).as("foo").isEqualTo("");
assertThat(stringBuilder).as("bar").hasSize(0);
assertThat(new Object()).isEqualTo(""); assertThat(new Object()).isEqualTo("");
} }
} }