Added inversion of boolean conditions inside isEqualTo() and isNotEqualTo() for InvertedBooleanCondition inspection.

This commit is contained in:
2020-10-02 22:44:08 +02:00
parent d48c71d4ea
commit ce0b5ce872
11 changed files with 121 additions and 21 deletions
@@ -72,6 +72,8 @@ abstract class AbstractAssertJInspection : AbstractBaseJavaLocalInspectionTool()
val ASSERT_THAT_BOOLEAN = CallMatcher.staticCall(ASSERTIONS_CLASSNAME, MethodNames.ASSERT_THAT)
.parameterTypes("boolean")!!
val ASSERT_THAT_BOOLEAN_OBJ = CallMatcher.staticCall(ASSERTIONS_CLASSNAME, MethodNames.ASSERT_THAT)
.parameterTypes(CommonClassNames.JAVA_LANG_BOOLEAN)!!
val ASSERT_THAT_ANY = CallMatcher.staticCall(ASSERTIONS_CLASSNAME, MethodNames.ASSERT_THAT)
.parameterCount(1)!!
@@ -21,18 +21,19 @@ class AssertThatBooleanConditionInspection : AbstractAssertJInspection() {
override fun visitMethodCallExpression(expression: PsiMethodCallExpression) {
super.visitMethodCallExpression(expression)
if (!expression.hasAssertThat()) return
val matchingCalls = listOf(
IS_EQUAL_TO_OBJECT, IS_EQUAL_TO_BOOLEAN,
IS_NOT_EQUAL_TO_OBJECT, IS_NOT_EQUAL_TO_BOOLEAN
).map { it.test(expression) }
if (matchingCalls.none { it }) return
val isEqualToObject = IS_EQUAL_TO_OBJECT.test(expression)
val isEqualToPrimitive = IS_EQUAL_TO_BOOLEAN.test(expression)
val isNotEqualToObject = IS_NOT_EQUAL_TO_OBJECT.test(expression)
val isNotEqualToPrimitive = IS_NOT_EQUAL_TO_BOOLEAN.test(expression)
if (!(isEqualToObject || isEqualToPrimitive || isNotEqualToObject || isNotEqualToPrimitive)) return
if (!checkAssertedType(expression, ABSTRACT_BOOLEAN_ASSERT_CLASSNAME)) return
val expectedExpression = expression.firstArg
if (!TypeConversionUtil.isBooleanType(expectedExpression.type)) return
val expectedResult = expression.calculateConstantParameterValue(0) as? Boolean ?: return
val flippedBooleanTest = matchingCalls.drop(2).any { it }
val flippedBooleanTest = isNotEqualToObject || isNotEqualToPrimitive
val replacementMethod = (expectedResult xor flippedBooleanTest).map(MethodNames.IS_TRUE, MethodNames.IS_FALSE)
registerSimplifyMethod(holder, expression, replacementMethod)
@@ -1,8 +1,13 @@
package de.platon42.intellij.plugins.cajon.inspections
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.util.TextRange
import com.intellij.psi.*
import com.intellij.psi.util.PsiUtil
import com.intellij.psi.util.TypeConversionUtil
import com.siyeh.ig.callMatcher.CallMatcher
import de.platon42.intellij.plugins.cajon.*
import de.platon42.intellij.plugins.cajon.quickfixes.InvertUnaryExpressionQuickFix
import de.platon42.intellij.plugins.cajon.quickfixes.InvertUnaryStatementQuickFix
class AssertThatInvertedBooleanConditionInspection : AbstractAssertJInspection() {
@@ -10,6 +15,8 @@ class AssertThatInvertedBooleanConditionInspection : AbstractAssertJInspection()
companion object {
private const val DISPLAY_NAME = "Asserting an inverted boolean condition"
private const val INVERT_CONDITION_MESSAGE = "Condition inside assertThat() could be inverted"
private const val REMOVE_INSTANCEOF_DESCRIPTION_TEMPLATE = "Invert condition in %s() and use %s() instead"
private const val INVERT_OUTSIDE_CONDITION_MESSAGE = "Condition outside assertThat() could be inverted"
}
override fun getDisplayName() = DISPLAY_NAME
@@ -20,13 +27,27 @@ class AssertThatInvertedBooleanConditionInspection : AbstractAssertJInspection()
super.visitMethodCallExpression(expression)
if (!expression.hasAssertThat()) return
val staticMethodCall = expression.findStaticMethodCall() ?: return
if (!ASSERT_THAT_BOOLEAN.test(staticMethodCall)) return
expression.getExpectedBooleanResult() ?: return
if (expression.getExpectedBooleanResult() != null) {
if (!ASSERT_THAT_BOOLEAN.test(staticMethodCall)) return
val prefixExpression = staticMethodCall.firstArg as? PsiPrefixExpression ?: return
if (prefixExpression.operationTokenType == JavaTokenType.EXCL) {
val outmostMethodCall = expression.findOutmostMethodCall() ?: return
holder.registerProblem(outmostMethodCall, INVERT_CONDITION_MESSAGE, InvertUnaryStatementQuickFix())
}
} else {
if (!CallMatcher.anyOf(ASSERT_THAT_BOOLEAN, ASSERT_THAT_BOOLEAN_OBJ).test(staticMethodCall)) return
val isEqualTo = CallMatcher.anyOf(IS_EQUAL_TO_BOOLEAN, IS_EQUAL_TO_OBJECT).test(expression)
val isNotEqualTo = CallMatcher.anyOf(IS_NOT_EQUAL_TO_BOOLEAN, IS_NOT_EQUAL_TO_OBJECT).test(expression)
if (!(isEqualTo || isNotEqualTo)) return
val prefixExpression = expression.firstArg as? PsiPrefixExpression ?: return
val operand = PsiUtil.skipParenthesizedExprDown(prefixExpression.operand) ?: return
if (!TypeConversionUtil.isPrimitiveAndNotNull(operand.type)) return
val originalMethod = getOriginalMethodName(expression) ?: return
val prefixExpression = staticMethodCall.firstArg as? PsiPrefixExpression ?: return
if (prefixExpression.operationTokenType == JavaTokenType.EXCL) {
val outmostMethodCall = expression.findOutmostMethodCall() ?: return
holder.registerProblem(outmostMethodCall, INVERT_CONDITION_MESSAGE, InvertUnaryStatementQuickFix())
val replacementMethod = isEqualTo.map(MethodNames.IS_NOT_EQUAL_TO, MethodNames.IS_EQUAL_TO)
val description = REMOVE_INSTANCEOF_DESCRIPTION_TEMPLATE.format(originalMethod, replacementMethod)
val textRange = TextRange(staticMethodCall.textLength, expression.textLength)
holder.registerProblem(expression, textRange, INVERT_OUTSIDE_CONDITION_MESSAGE, InvertUnaryExpressionQuickFix(description, replacementMethod))
}
}
}
@@ -0,0 +1,31 @@
package de.platon42.intellij.plugins.cajon.quickfixes
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.PsiUnaryExpression
import com.intellij.psi.util.PsiUtil
import de.platon42.intellij.plugins.cajon.createExpectedMethodCall
import de.platon42.intellij.plugins.cajon.firstArg
import de.platon42.intellij.plugins.cajon.replaceQualifierFromMethodCall
class InvertUnaryExpressionQuickFix(description: String, private val replacementMethod: String) : AbstractCommonQuickFix(description) {
companion object {
private const val INVERT_CONDITION_DESCRIPTION = "Invert condition in isEqualTo()/isNotEqualTo() expressions"
}
override fun getFamilyName(): String {
return INVERT_CONDITION_DESCRIPTION
}
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val methodCall = descriptor.startElement as? PsiMethodCallExpression ?: return
val assertExpression = methodCall.firstArg as? PsiUnaryExpression ?: return
val operand = PsiUtil.skipParenthesizedExprDown(assertExpression.operand) ?: return
val expectedExpression = createExpectedMethodCall(assertExpression, replacementMethod, operand)
expectedExpression.replaceQualifierFromMethodCall(methodCall)
methodCall.replace(expectedExpression)
}
}