Implemented AssertThatSizeInspection.
This commit is contained in:
+45
@@ -21,24 +21,48 @@ open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() {
|
||||
|
||||
const val REPLACE_DESCRIPTION_TEMPLATE = "Replace %s with %s"
|
||||
|
||||
@NonNls
|
||||
const val ASSERTIONS_CLASSNAME = "org.assertj.core.api.Assertions"
|
||||
|
||||
@NonNls
|
||||
const val ABSTRACT_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractAssert"
|
||||
@NonNls
|
||||
const val ABSTRACT_BOOLEAN_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractBooleanAssert"
|
||||
@NonNls
|
||||
const val ABSTRACT_INTEGER_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractIntegerAssert"
|
||||
@NonNls
|
||||
const val ABSTRACT_COMPARABLE_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractComparableAssert"
|
||||
@NonNls
|
||||
const val ABSTRACT_STRING_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractStringAssert"
|
||||
@NonNls
|
||||
const val ABSTRACT_CHAR_SEQUENCE_ASSERT_CLASSNAME = "org.assertj.core.api.AbstractCharSequenceAssert"
|
||||
@NonNls
|
||||
const val ABSTRACT_ENUMERABLE_ASSERT_CLASSNAME = "org.assertj.core.api.EnumerableAssert"
|
||||
|
||||
@NonNls
|
||||
const val ASSERT_THAT_METHOD = "assertThat"
|
||||
@NonNls
|
||||
const val IS_EQUAL_TO_METHOD = "isEqualTo"
|
||||
@NonNls
|
||||
const val IS_NOT_EQUAL_TO_METHOD = "isNotEqualTo"
|
||||
@NonNls
|
||||
const val IS_GREATER_THAN_METHOD = "isGreaterThan"
|
||||
@NonNls
|
||||
const val IS_GREATER_THAN_OR_EQUAL_TO_METHOD = "isGreaterThanOrEqualTo"
|
||||
@NonNls
|
||||
const val IS_LESS_THAN_METHOD = "isLessThan"
|
||||
@NonNls
|
||||
const val IS_LESS_THAN_OR_EQUAL_TO_METHOD = "isLessThanOrEqualTo"
|
||||
@NonNls
|
||||
const val IS_ZERO_METHOD = "isZero"
|
||||
@NonNls
|
||||
const val IS_NOT_ZERO_METHOD = "isNotZero"
|
||||
@NonNls
|
||||
const val HAS_SIZE_METHOD = "hasSize"
|
||||
|
||||
val ASSERT_THAT_INT = CallMatcher.staticCall(ASSERTIONS_CLASSNAME, ASSERT_THAT_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
|
||||
val IS_EQUAL_TO_OBJECT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_EQUAL_TO_METHOD)
|
||||
.parameterTypes(CommonClassNames.JAVA_LANG_OBJECT)!!
|
||||
val IS_NOT_EQUAL_TO_OBJECT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_NOT_EQUAL_TO_METHOD)
|
||||
@@ -50,6 +74,26 @@ open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() {
|
||||
.parameterTypes("boolean")!!
|
||||
val HAS_SIZE = CallMatcher.instanceCall(ABSTRACT_ENUMERABLE_ASSERT_CLASSNAME, HAS_SIZE_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
|
||||
val IS_EQUAL_TO_INT = CallMatcher.instanceCall(ABSTRACT_ASSERT_CLASSNAME, IS_EQUAL_TO_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
val IS_GREATER_THAN_INT = CallMatcher.instanceCall(ABSTRACT_COMPARABLE_ASSERT_CLASSNAME, IS_GREATER_THAN_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
val IS_GREATER_THAN_OR_EQUAL_TO_INT = CallMatcher.instanceCall(ABSTRACT_COMPARABLE_ASSERT_CLASSNAME, IS_GREATER_THAN_OR_EQUAL_TO_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
|
||||
val IS_LESS_THAN_INT = CallMatcher.instanceCall(ABSTRACT_COMPARABLE_ASSERT_CLASSNAME, IS_LESS_THAN_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
val IS_LESS_THAN_OR_EQUAL_TO_INT = CallMatcher.instanceCall(ABSTRACT_COMPARABLE_ASSERT_CLASSNAME, IS_LESS_THAN_OR_EQUAL_TO_METHOD)
|
||||
.parameterTypes("int")!!
|
||||
|
||||
val IS_ZERO = CallMatcher.instanceCall(ABSTRACT_INTEGER_ASSERT_CLASSNAME, IS_ZERO_METHOD)
|
||||
.parameterCount(0)!!
|
||||
val IS_NOT_ZERO = CallMatcher.instanceCall(ABSTRACT_INTEGER_ASSERT_CLASSNAME, IS_NOT_ZERO_METHOD)
|
||||
.parameterCount(0)!!
|
||||
|
||||
val COLLECTION_SIZE = CallMatcher.instanceCall(CommonClassNames.JAVA_UTIL_COLLECTION, "size")
|
||||
.parameterCount(0)!!
|
||||
}
|
||||
|
||||
override fun getGroupDisplayName(): String {
|
||||
@@ -88,6 +132,7 @@ open class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool() {
|
||||
}
|
||||
|
||||
protected fun calculateConstantParameterValue(expression: PsiMethodCallExpression, argIndex: Int): Any? {
|
||||
if (argIndex >= expression.argumentList.expressionCount) return null
|
||||
val valueExpression = expression.argumentList.expressions[argIndex] ?: return null
|
||||
val constantEvaluationHelper = JavaPsiFacade.getInstance(expression.project).constantEvaluationHelper
|
||||
return constantEvaluationHelper.computeConstantExpression(valueExpression)
|
||||
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package de.platon42.intellij.plugins.cajon.inspections
|
||||
|
||||
import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import de.platon42.intellij.plugins.cajon.quickfixes.ReplaceSizeMethodCallQuickFix
|
||||
|
||||
class AssertThatSizeInspection : AbstractAssertJInspection() {
|
||||
|
||||
companion object {
|
||||
private const val DISPLAY_NAME = "Asserting the size of an collection or array"
|
||||
private const val CONCISER_MESSAGE_TEMPLATE = "%s would be conciser than %s"
|
||||
}
|
||||
|
||||
override fun getDisplayName() = DISPLAY_NAME
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
return object : JavaElementVisitor() {
|
||||
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
|
||||
super.visitMethodCallExpression(expression)
|
||||
if (!ASSERT_THAT_INT.test(expression)) {
|
||||
return
|
||||
}
|
||||
val actualExpression = expression.argumentList.expressions[0] ?: return
|
||||
|
||||
if (isArrayLength(actualExpression) || isCollectionSize(actualExpression)) {
|
||||
val statement = PsiTreeUtil.getParentOfType(expression, PsiStatement::class.java) ?: return
|
||||
val expectedCallExpression = PsiTreeUtil.findChildOfType(statement, PsiMethodCallExpression::class.java) ?: return
|
||||
val constValue = calculateConstantParameterValue(expectedCallExpression, 0)
|
||||
if (IS_EQUAL_TO_INT.test(expectedCallExpression)) {
|
||||
if (constValue == 0) {
|
||||
registerSizeMethod(holder, expression, "isEmpty()", noExpectedExpression = true)
|
||||
return
|
||||
}
|
||||
val equalToExpression = expectedCallExpression.argumentList.expressions[0]
|
||||
if (isCollectionSize(equalToExpression) || isArrayLength(equalToExpression)) {
|
||||
registerSizeMethod(holder, expression, "hasSameSizeAs()", expectedIsCollection = true)
|
||||
return
|
||||
}
|
||||
registerSizeMethod(holder, expression, "hasSize()")
|
||||
} else {
|
||||
if ((IS_LESS_THAN_OR_EQUAL_TO_INT.test(expectedCallExpression) && (constValue == 0))
|
||||
|| (IS_LESS_THAN_INT.test(expectedCallExpression) && (constValue == 1))
|
||||
|| IS_ZERO.test(expectedCallExpression)
|
||||
) {
|
||||
registerSizeMethod(holder, expression, "isEmpty()", noExpectedExpression = true)
|
||||
return
|
||||
}
|
||||
if ((IS_GREATER_THAN_INT.test(expectedCallExpression) && (constValue == 0))
|
||||
|| (IS_GREATER_THAN_OR_EQUAL_TO_INT.test(expectedCallExpression) && (constValue == 1))
|
||||
|| IS_NOT_ZERO.test(expectedCallExpression)
|
||||
) {
|
||||
registerSizeMethod(holder, expression, "isNotEmpty()", noExpectedExpression = true)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isCollectionSize(expression: PsiExpression) = (expression is PsiMethodCallExpression) && COLLECTION_SIZE.test(expression)
|
||||
|
||||
private fun isArrayLength(expression: PsiExpression): Boolean {
|
||||
val psiReferenceExpression = expression as? PsiReferenceExpression ?: return false
|
||||
return ((psiReferenceExpression.qualifierExpression?.type is PsiArrayType)
|
||||
&& ((psiReferenceExpression.resolve() as? PsiField)?.name == "length"))
|
||||
}
|
||||
|
||||
private fun registerSizeMethod(
|
||||
holder: ProblemsHolder,
|
||||
expression: PsiMethodCallExpression,
|
||||
replacementMethod: String,
|
||||
noExpectedExpression: Boolean = false,
|
||||
expectedIsCollection: Boolean = false
|
||||
) {
|
||||
val originalMethod = getOriginalMethodName(expression) ?: return
|
||||
val description = REPLACE_DESCRIPTION_TEMPLATE.format(replacementMethod, originalMethod)
|
||||
val message = CONCISER_MESSAGE_TEMPLATE.format(originalMethod, replacementMethod)
|
||||
holder.registerProblem(
|
||||
expression,
|
||||
message,
|
||||
ProblemHighlightType.INFORMATION,
|
||||
null as TextRange?,
|
||||
ReplaceSizeMethodCallQuickFix(description, replacementMethod, noExpectedExpression, expectedIsCollection)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -12,9 +12,9 @@ class ReplaceSimpleMethodCallQuickFix(description: String, private val replaceme
|
||||
val factory = JavaPsiFacade.getElementFactory(element.project)
|
||||
val methodCallExpression = element as? PsiMethodCallExpression ?: return
|
||||
val oldQualifier = methodCallExpression.methodExpression.qualifierExpression ?: return
|
||||
val isEmptyExpression =
|
||||
val expectedExpression =
|
||||
factory.createExpressionFromText("a.$replacementMethod", element) as PsiMethodCallExpression
|
||||
isEmptyExpression.methodExpression.qualifierExpression!!.replace(oldQualifier)
|
||||
element.replace(isEmptyExpression)
|
||||
expectedExpression.methodExpression.qualifierExpression!!.replace(oldQualifier)
|
||||
element.replace(expectedExpression)
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package de.platon42.intellij.plugins.cajon.quickfixes
|
||||
|
||||
import com.intellij.codeInspection.ProblemDescriptor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
|
||||
class ReplaceSizeMethodCallQuickFix(
|
||||
description: String,
|
||||
private val replacementMethod: String,
|
||||
private val noExpectedExpression: Boolean,
|
||||
private val expectedIsCollection: Boolean
|
||||
) : AbstractCommonQuickFix(description) {
|
||||
|
||||
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
|
||||
val element = descriptor.startElement
|
||||
val factory = JavaPsiFacade.getElementFactory(element.project)
|
||||
val methodCallExpression = element as? PsiMethodCallExpression ?: return
|
||||
val assertExpression = methodCallExpression.argumentList.expressions[0] ?: return
|
||||
replaceCollectionSizeOrArrayLength(assertExpression)
|
||||
val statement = PsiTreeUtil.getParentOfType(element, PsiStatement::class.java) ?: return
|
||||
val oldExpectedExpression = PsiTreeUtil.findChildOfType(statement, PsiMethodCallExpression::class.java) ?: return
|
||||
val expectedExpression =
|
||||
factory.createExpressionFromText("a.${if (noExpectedExpression) replacementMethod else replacementMethod.replace("()", "(e)")}", element) as PsiMethodCallExpression
|
||||
if (!noExpectedExpression) {
|
||||
if (expectedIsCollection) {
|
||||
replaceCollectionSizeOrArrayLength(oldExpectedExpression.argumentList.expressions[0])
|
||||
}
|
||||
expectedExpression.argumentList.expressions[0].replace(oldExpectedExpression.argumentList.expressions[0])
|
||||
}
|
||||
expectedExpression.methodExpression.qualifierExpression!!.replace(oldExpectedExpression.methodExpression.qualifierExpression!!)
|
||||
oldExpectedExpression.replace(expectedExpression)
|
||||
}
|
||||
|
||||
private fun replaceCollectionSizeOrArrayLength(assertExpression: PsiExpression) {
|
||||
assertExpression.replace(
|
||||
when (assertExpression) {
|
||||
is PsiReferenceExpression -> assertExpression.qualifierExpression!!
|
||||
is PsiMethodCallExpression -> assertExpression.methodExpression.qualifierExpression!!
|
||||
else -> return
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,10 @@
|
||||
level="WARNING"
|
||||
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatEnumerableIsEmptyInspection"/>
|
||||
|
||||
<localInspection groupPath="Java" shortName="AssertThatSize" enabledByDefault="true"
|
||||
level="WARNING"
|
||||
implementationClass="de.platon42.intellij.plugins.cajon.inspections.AssertThatSizeInspection"/>
|
||||
|
||||
<localInspection groupPath="Java" shortName="JUnitAssertToAssertJInspection" enabledByDefault="true"
|
||||
level="WARNING"
|
||||
implementationClass="de.platon42.intellij.plugins.cajon.inspections.JUnitAssertToAssertJInspection"/>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
Makes assertions on sizes of arrays or collections more concise by replacing them with isEmpty(), isNotEmpty(), hasSize(), or hasSameSizeAs().
|
||||
<!-- tooltip end -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -24,6 +24,11 @@ public class Playground {
|
||||
assertThat(new Long[1]).as("etc").isEmpty();
|
||||
|
||||
assertThat(new ArrayList<>().size()).isEqualTo(1);
|
||||
assertThat(new ArrayList<String>().size()).isEqualTo(1);
|
||||
assertThat(new ArrayList<String>().size()).isGreaterThanOrEqualTo(1);
|
||||
assertThat(new ArrayList<String>().size()).isZero();
|
||||
assertThat(new ArrayList<String>()).hasSameSizeAs(new ArrayList<>());
|
||||
assertThat(new Long[1]).as("etc").hasSameSizeAs(new Long[2]);
|
||||
}
|
||||
|
||||
private void sizeOfArray() {
|
||||
@@ -178,7 +183,6 @@ public class Playground {
|
||||
assertThat(new double[1]).as("array equals").containsExactly(new double[2], offset(1.0));
|
||||
assertThat(new float[1]).containsExactly(new float[2], offset(1.0f));
|
||||
assertThat(new float[1]).as("array equals").containsExactly(new float[2], offset(1.0f));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+21
@@ -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 AssertThatSizeInspectionTest : AbstractCajonTest() {
|
||||
|
||||
@Test
|
||||
@TestDataSubPath("inspections/AssertThatSize")
|
||||
internal fun assertThat_size_of_array_or_collection_can_be_simplified(@MyFixture myFixture: JavaCodeInsightTestFixture) {
|
||||
runTest {
|
||||
myFixture.enableInspections(AssertThatSizeInspection::class.java)
|
||||
myFixture.configureByFile("AssertThatSizeBefore.java")
|
||||
executeQuickFixes(myFixture, Regex("Replace .*"), 20)
|
||||
myFixture.checkResultByFile("AssertThatSizeAfter.java")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class assertThatSize {
|
||||
|
||||
private void assertThatSize() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
ArrayList<String> otherList = new ArrayList<>();
|
||||
long[] array = new long[5];
|
||||
long[] otherArray = new long[4];
|
||||
|
||||
assertThat(list).isEmpty();
|
||||
assertThat(list).isEmpty();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(list).as("hi").isNotEmpty();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(list).isEmpty();
|
||||
assertThat(list).isEmpty();
|
||||
assertThat(list).hasSameSizeAs(otherList);
|
||||
assertThat(list).hasSameSizeAs(array);
|
||||
assertThat(list).hasSize(1);
|
||||
|
||||
assertThat(array).isEmpty();
|
||||
assertThat(array).isEmpty();
|
||||
assertThat(array).isNotEmpty();
|
||||
assertThat(array).as("hi").isNotEmpty();
|
||||
assertThat(array).isNotEmpty();
|
||||
assertThat(array).isEmpty();
|
||||
assertThat(array).isEmpty();
|
||||
assertThat(array).hasSameSizeAs(list);
|
||||
assertThat(array).hasSameSizeAs(otherArray);
|
||||
assertThat(array).hasSize(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class assertThatSize {
|
||||
|
||||
private void assertThatSize() {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
ArrayList<String> otherList = new ArrayList<>();
|
||||
long[] array = new long[5];
|
||||
long[] otherArray = new long[4];
|
||||
|
||||
assertThat(list.size()).isEqualTo(0);
|
||||
assertThat(list.size()).isZero();
|
||||
assertThat(list.size()).isNotZero();
|
||||
assertThat(list.size()).as("hi").isGreaterThan(0);
|
||||
assertThat(list.size()).isGreaterThanOrEqualTo(1);
|
||||
assertThat(list.size()).isLessThan(1);
|
||||
assertThat(list.size()).isLessThanOrEqualTo(0);
|
||||
assertThat(list.size()).isEqualTo(otherList.size());
|
||||
assertThat(list.size()).isEqualTo(array.length);
|
||||
assertThat(list.size()).isEqualTo(1);
|
||||
|
||||
assertThat(array.length).isEqualTo(0);
|
||||
assertThat(array.length).isZero();
|
||||
assertThat(array.length).isNotZero();
|
||||
assertThat(array.length).as("hi").isGreaterThan(0);
|
||||
assertThat(array.length).isGreaterThanOrEqualTo(1);
|
||||
assertThat(array.length).isLessThan(1);
|
||||
assertThat(array.length).isLessThanOrEqualTo(0);
|
||||
assertThat(array.length).isEqualTo(list.size());
|
||||
assertThat(array.length).isEqualTo(otherArray.length);
|
||||
assertThat(array.length).isEqualTo(1);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2,9 +2,9 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.data.Offset.offset;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class JUnitAssertToAssertJInspection {
|
||||
public class JUnitAssertToAssertJ {
|
||||
|
||||
private void jUnitAssertToAssertJInspection() {
|
||||
private void jUnitAssertToAssertJ() {
|
||||
String foo = "foo";
|
||||
String bar = "bar";
|
||||
assertThat(foo == "foo").isTrue();
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class JUnitAssertToAssertJInspection {
|
||||
public class JUnitAssertToAssertJ {
|
||||
|
||||
private void jUnitAssertToAssertJInspection() {
|
||||
private void jUnitAssertToAssertJ() {
|
||||
String foo = "foo";
|
||||
String bar = "bar";
|
||||
assertTrue(foo == "foo");
|
||||
|
||||
Reference in New Issue
Block a user