code review; add 'operator' also to next() and hasNext(); check toplevel functions

This commit is contained in:
Dmitry Jemerov
2015-09-24 20:35:21 +02:00
parent e0f8d68a5f
commit 8b0ccce4f1
5 changed files with 49 additions and 9 deletions
@@ -21,13 +21,15 @@ import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.lexer.JetModifierKeywordToken
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.psi.JetClassOrObject
import org.jetbrains.kotlin.psi.JetModifierListOwner
import org.jetbrains.kotlin.psi.JetNamedFunction
import org.jetbrains.kotlin.psi.JetVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.expressions.OperatorConventions
public class OperatorModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
@@ -35,9 +37,13 @@ public class OperatorModifierInspection : AbstractKotlinInspection(), CleanupLoc
return object : JetVisitorVoid() {
override fun visitNamedFunction(function: JetNamedFunction) {
val nameIdentifier = function.nameIdentifier
if (nameIdentifier != null && function.isOperator() && !function.hasModifier(JetTokens.OPERATOR_KEYWORD)) {
if (nameIdentifier != null &&
function.isMemberOrExtension() &&
function.isOperator() &&
!function.hasModifier(JetTokens.OPERATOR_KEYWORD)) {
holder.registerProblem(nameIdentifier, "Function defines an operator but isn't annotated as such",
AddModifierLocalQuickFix(JetTokens.OPERATOR_KEYWORD))
AddModifierLocalQuickFix())
}
}
}
@@ -49,7 +55,9 @@ public class OperatorModifierInspection : AbstractKotlinInspection(), CleanupLoc
if (arity == 0 &&
(name in OperatorConventions.UNARY_OPERATION_NAMES.values() ||
name == OperatorConventions.ITERATOR ||
isComponentLike(name))) {
isComponentLike(name) ||
name == OperatorConventions.NEXT ||
(name == OperatorConventions.HAS_NEXT && isBooleanReturnType()))) {
return true
}
if (arity == 1 && (name in OperatorConventions.BINARY_OPERATION_NAMES.values() ||
@@ -70,21 +78,24 @@ public class OperatorModifierInspection : AbstractKotlinInspection(), CleanupLoc
return false
}
private fun JetNamedFunction.isMemberOrExtension(): Boolean =
receiverTypeReference != null || getStrictParentOfType<JetClassOrObject>() != null
private fun JetNamedFunction.isIntReturnType(): Boolean {
return KotlinBuiltIns.isInt(analyze().getType(this) ?: return false)
return KotlinBuiltIns.isInt(analyze(BodyResolveMode.PARTIAL).getType(this) ?: return false)
}
private fun JetNamedFunction.isBooleanReturnType(): Boolean {
return KotlinBuiltIns.isBoolean(analyze().getType(this) ?: return false)
return KotlinBuiltIns.isBoolean(analyze(BodyResolveMode.PARTIAL).getType(this) ?: return false)
}
}
class AddModifierLocalQuickFix(val modifier: JetModifierKeywordToken) : LocalQuickFix {
override fun getName(): String = "Add '${modifier.value}' modifier"
private class AddModifierLocalQuickFix() : LocalQuickFix {
override fun getName(): String = "Add 'operator' modifier"
override fun getFamilyName(): String = getName()
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val modifierListOwner = descriptor.psiElement.getNonStrictParentOfType<JetModifierListOwner>()
modifierListOwner?.addModifier(modifier)
modifierListOwner?.addModifier(JetTokens.OPERATOR_KEYWORD)
}
}
@@ -0,0 +1,5 @@
// "Add 'operator' modifier" "true"
class A {
}
fun A.<caret>plus(other: A): A = A()
@@ -0,0 +1,5 @@
// "Add 'operator' modifier" "true"
class A {
}
operator fun A.<caret>plus(other: A): A = A()
@@ -0,0 +1,7 @@
// "Add 'operator' modifier" "false"
// ACTION: Convert to block body
// ACTION: Remove explicit type specification
class A {
}
fun <caret>plus(other: A): A = A()
@@ -4201,11 +4201,23 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("extension.kt")
public void testExtension() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/operatorModifier/extension.kt");
doTest(fileName);
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/operatorModifier/simple.kt");
doTest(fileName);
}
@TestMetadata("toplevel.kt")
public void testToplevel() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/operatorModifier/toplevel.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/migration/removeNameFromFunctionExpression")