OperatorModifierInspection: reused code from OperatorChecks

This commit is contained in:
Valentin Kipyatkov
2015-10-16 22:55:42 +03:00
parent 4e73fe0f39
commit f89d41dcda
@@ -23,97 +23,36 @@ import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
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.OverrideResolver
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.util.OperatorNameConventions
import org.jetbrains.kotlin.util.OperatorChecks
public class OperatorModifierInspection : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
return object : JetVisitorVoid() {
override fun visitNamedFunction(function: JetNamedFunction) {
val nameIdentifier = function.nameIdentifier
if (nameIdentifier != null &&
function.isMemberOrExtension() &&
function.isOperator() &&
!function.isAnnotatedAsOperator()) {
val nameIdentifier = function.nameIdentifier ?: return
val functionDescriptor = function.resolveToDescriptor() as? FunctionDescriptor ?: return
if (!functionDescriptor.isOperator && OperatorChecks.canBeOperator(functionDescriptor)) {
holder.registerProblem(nameIdentifier, "Function defines an operator but isn't annotated as such",
AddModifierLocalQuickFix())
AddModifierLocalQuickFix())
}
}
}
}
private fun JetNamedFunction.isOperator(): Boolean {
val name = nameAsName ?: return false
val arity = valueParameters.size()
if (arity == 0 &&
(name in OperatorConventions.UNARY_OPERATION_NAMES.values() ||
name == OperatorNameConventions.PLUS || name == OperatorNameConventions.MINUS || // temporary
name == OperatorNameConventions.ITERATOR ||
isComponentLike(name) ||
name == OperatorNameConventions.NEXT ||
(name == OperatorNameConventions.HAS_NEXT && isBooleanReturnType()))) {
return true
}
if (arity == 1 && (name in OperatorConventions.BINARY_OPERATION_NAMES.values() ||
name in OperatorConventions.ASSIGNMENT_OPERATIONS.values () ||
(name == OperatorNameConventions.CONTAINS && isBooleanReturnType()) ||
(name == OperatorNameConventions.COMPARE_TO && isIntReturnType()))) {
return true
}
if (name == OperatorNameConventions.INVOKE) {
return true
}
if (arity >= 1 && name == OperatorNameConventions.GET) {
return true
}
if (arity >= 2 && name == OperatorNameConventions.SET) {
return true
}
return false
}
private fun JetNamedFunction.isAnnotatedAsOperator(): Boolean {
if (hasModifier(JetTokens.OPERATOR_KEYWORD)) return true
val descriptor = descriptor as? CallableMemberDescriptor ?: return false
return OverrideResolver.getOverriddenDeclarations(descriptor).any {
(it as? FunctionDescriptor)?.isOperator == true
}
}
private fun JetNamedFunction.isMemberOrExtension(): Boolean =
receiverTypeReference != null || getStrictParentOfType<JetClassOrObject>() != null
private fun JetNamedFunction.isIntReturnType(): Boolean {
val returnType = (descriptor as? FunctionDescriptor)?.returnType ?: return false
return KotlinBuiltIns.isInt(returnType)
}
private fun JetNamedFunction.isBooleanReturnType(): Boolean {
val returnType = (descriptor as? FunctionDescriptor)?.returnType ?: return false
return KotlinBuiltIns.isBoolean(returnType)
}
}
private class AddModifierLocalQuickFix() : LocalQuickFix {