mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-80627 @staticmethod, @classmethod usage with overloaded methods
GitOrigin-RevId: 3712c0f4dfb70146e3127af02c4fc04de75dcd33
This commit is contained in:
committed by
intellij-monorepo-bot
parent
58cd6bb893
commit
57c773ccb3
@@ -1049,6 +1049,8 @@ INSP.overloads.this.method.overload.signature.not.compatible.with.implementation
|
||||
INSP.overloads.this.function.overload.signature.not.compatible.with.implementation=Signature of this @overload-decorated function is not compatible with the implementation
|
||||
INSP.overloads.at.least.two.overload.decorated.methods.must.be.present=At least two @overload-decorated methods must be present
|
||||
INSP.overloads.at.least.two.overload.decorated.functions.must.be.present=At least two @overload-decorated functions must be present
|
||||
INSP.overloads.use.staticmethod.inconsistently=Overloads use @staticmethod inconsistently
|
||||
INSP.overloads.use.classmethod.inconsistently=Overloads use @classmethod inconsistently
|
||||
|
||||
# PyOverridesInspection
|
||||
INSP.NAME.invalid.usages.of.override.decorator=Invalid usages of @override decorator
|
||||
|
||||
+33
-18
@@ -6,24 +6,25 @@ import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.containers.SortedList
|
||||
import com.intellij.util.containers.sequenceOfNotNull
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.ast.PyAstFunction
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner
|
||||
import com.jetbrains.python.codeInsight.typing.isProtocol
|
||||
import com.jetbrains.python.psi.PyClass
|
||||
import com.jetbrains.python.psi.PyFile
|
||||
import com.jetbrains.python.psi.PyFunction
|
||||
import com.jetbrains.python.psi.PyKnownDecoratorUtil
|
||||
import com.jetbrains.python.psi.PyUtil
|
||||
import com.jetbrains.python.psi.*
|
||||
import com.jetbrains.python.psi.impl.PyClassImpl
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
import com.jetbrains.python.pyi.PyiFile
|
||||
import com.jetbrains.python.pyi.PyiUtil
|
||||
import java.util.EnumSet
|
||||
|
||||
class PyOverloadsInspection : PyInspection() {
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder,
|
||||
isOnTheFly: Boolean,
|
||||
session: LocalInspectionToolSession): PsiElementVisitor = Visitor(
|
||||
override fun buildVisitor(
|
||||
holder: ProblemsHolder,
|
||||
isOnTheFly: Boolean,
|
||||
session: LocalInspectionToolSession,
|
||||
): PsiElementVisitor = Visitor(
|
||||
holder,PyInspectionVisitor.getContext(session))
|
||||
|
||||
private class Visitor(holder: ProblemsHolder, context: TypeEvalContext) : PyInspectionVisitor(holder, context) {
|
||||
@@ -64,6 +65,10 @@ class PyOverloadsInspection : PyInspection() {
|
||||
})
|
||||
}
|
||||
|
||||
val implementation = implementations.lastOrNull()
|
||||
|
||||
checkClassMethodAndStaticMethodConsistency(overloads, implementation)
|
||||
|
||||
var requiresImplementation = true
|
||||
if (owner is PyClass) {
|
||||
if (isProtocol(owner, myTypeEvalContext)) {
|
||||
@@ -78,17 +83,14 @@ class PyOverloadsInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
val implementation = implementations.lastOrNull()
|
||||
if (requiresImplementation) {
|
||||
if (implementation !== functions.last()) {
|
||||
val problemElement = if (implementation == null) functions.first() else functions.last()
|
||||
registerProblem(problemElement.nameIdentifier, if (owner is PyClass) {
|
||||
PyPsiBundle.message("INSP.overloads.series.overload.decorated.methods.should.always.be.followed.by.implementation")
|
||||
}
|
||||
else {
|
||||
PyPsiBundle.message("INSP.overloads.series.overload.decorated.functions.should.always.be.followed.by.implementation")
|
||||
})
|
||||
if (requiresImplementation && implementation !== functions.last()) {
|
||||
val problemElement = if (implementation == null) functions.first() else functions.last()
|
||||
registerProblem(problemElement.nameIdentifier, if (owner is PyClass) {
|
||||
PyPsiBundle.message("INSP.overloads.series.overload.decorated.methods.should.always.be.followed.by.implementation")
|
||||
}
|
||||
else {
|
||||
PyPsiBundle.message("INSP.overloads.series.overload.decorated.functions.should.always.be.followed.by.implementation")
|
||||
})
|
||||
}
|
||||
|
||||
if (implementation != null) {
|
||||
@@ -106,6 +108,19 @@ class PyOverloadsInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkClassMethodAndStaticMethodConsistency(overloads: List<PyFunction>, implementation: PyFunction?) {
|
||||
val modifiers = overloads.mapNotNullTo(EnumSet.noneOf(PyAstFunction.Modifier::class.java)) { it.modifier }
|
||||
for (function in overloads.asSequence() + sequenceOfNotNull(implementation)) {
|
||||
val modifier = function.modifier
|
||||
if (modifiers.contains(PyAstFunction.Modifier.CLASSMETHOD) && modifier != PyAstFunction.Modifier.CLASSMETHOD) {
|
||||
registerProblem(function.nameIdentifier, PyPsiBundle.message("INSP.overloads.use.classmethod.inconsistently"))
|
||||
}
|
||||
if (modifiers.contains(PyAstFunction.Modifier.STATICMETHOD) && modifier != PyAstFunction.Modifier.STATICMETHOD) {
|
||||
registerProblem(function.nameIdentifier, PyPsiBundle.message("INSP.overloads.use.staticmethod.inconsistently"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isIncompatibleOverload(implementation: PyFunction, overload: PyFunction): Boolean {
|
||||
return implementation != overload &&
|
||||
PyiUtil.isOverload(overload, myTypeEvalContext) &&
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
from typing import overload
|
||||
|
||||
|
||||
class A:
|
||||
@overload
|
||||
def <warning descr="Overloads use @staticmethod inconsistently">foo</warning>(self, x: int, /): ...
|
||||
|
||||
@staticmethod
|
||||
@overload
|
||||
def foo(x: str, /): ...
|
||||
|
||||
def <warning descr="Overloads use @staticmethod inconsistently">foo</warning>(*args: object):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@overload
|
||||
def bar(cls, x: int, /): ...
|
||||
|
||||
@overload
|
||||
def <warning descr="Overloads use @classmethod inconsistently">bar</warning>(self, x: str, /): ...
|
||||
|
||||
def <warning descr="Overloads use @classmethod inconsistently">bar</warning>(*args: object):
|
||||
pass
|
||||
@@ -44,6 +44,10 @@ public class PyOverloadsInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testStaticOrClassMethods() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user