OperatorModifierInspection: reused code from OperatorChecks
This commit is contained in:
@@ -23,97 +23,36 @@ import com.intellij.codeInspection.ProblemDescriptor
|
|||||||
import com.intellij.codeInspection.ProblemsHolder
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElementVisitor
|
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.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
|
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.codeInsight.DescriptorToSourceUtilsIde
|
||||||
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
|
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.JetModifierKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.JetTokens
|
import org.jetbrains.kotlin.lexer.JetTokens
|
||||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
|
||||||
import org.jetbrains.kotlin.psi.JetModifierListOwner
|
import org.jetbrains.kotlin.psi.JetModifierListOwner
|
||||||
import org.jetbrains.kotlin.psi.JetNamedFunction
|
import org.jetbrains.kotlin.psi.JetNamedFunction
|
||||||
import org.jetbrains.kotlin.psi.JetVisitorVoid
|
import org.jetbrains.kotlin.psi.JetVisitorVoid
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.util.OperatorChecks
|
||||||
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
|
|
||||||
|
|
||||||
public class OperatorModifierInspection : AbstractKotlinInspection() {
|
public class OperatorModifierInspection : AbstractKotlinInspection() {
|
||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||||
return object : JetVisitorVoid() {
|
return object : JetVisitorVoid() {
|
||||||
override fun visitNamedFunction(function: JetNamedFunction) {
|
override fun visitNamedFunction(function: JetNamedFunction) {
|
||||||
val nameIdentifier = function.nameIdentifier
|
val nameIdentifier = function.nameIdentifier ?: return
|
||||||
if (nameIdentifier != null &&
|
val functionDescriptor = function.resolveToDescriptor() as? FunctionDescriptor ?: return
|
||||||
function.isMemberOrExtension() &&
|
if (!functionDescriptor.isOperator && OperatorChecks.canBeOperator(functionDescriptor)) {
|
||||||
function.isOperator() &&
|
|
||||||
!function.isAnnotatedAsOperator()) {
|
|
||||||
|
|
||||||
holder.registerProblem(nameIdentifier, "Function defines an operator but isn't annotated as such",
|
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 {
|
private class AddModifierLocalQuickFix() : LocalQuickFix {
|
||||||
|
|||||||
Reference in New Issue
Block a user