mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-20 05:21:29 +07:00
PY-80627 @override usage with overloaded methods
GitOrigin-RevId: d864bf51ce48a662d4aed696802c321561d27877
This commit is contained in:
committed by
intellij-monorepo-bot
parent
57c773ccb3
commit
f8ed1ff69b
@@ -1051,6 +1051,8 @@ INSP.overloads.at.least.two.overload.decorated.methods.must.be.present=At least
|
||||
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
|
||||
INSP.overloads.override.should.be.placed.on.the.implementation='@override' should be placed on the implementation
|
||||
INSP.overloads.override.should.be.placed.only.on.the.first.overload='@override' should be placed only on the first overload
|
||||
|
||||
# PyOverridesInspection
|
||||
INSP.NAME.invalid.usages.of.override.decorator=Invalid usages of @override decorator
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.psi.PsiElementVisitor
|
||||
import com.intellij.util.Processor
|
||||
import com.intellij.util.containers.SortedList
|
||||
import com.intellij.util.containers.sequenceOfNotNull
|
||||
import com.intellij.util.containers.tail
|
||||
import com.jetbrains.python.PyPsiBundle
|
||||
import com.jetbrains.python.ast.PyAstFunction
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner
|
||||
@@ -69,6 +70,8 @@ class PyOverloadsInspection : PyInspection() {
|
||||
|
||||
checkClassMethodAndStaticMethodConsistency(overloads, implementation)
|
||||
|
||||
checkOverride(overloads, implementation)
|
||||
|
||||
var requiresImplementation = true
|
||||
if (owner is PyClass) {
|
||||
if (isProtocol(owner, myTypeEvalContext)) {
|
||||
@@ -121,6 +124,34 @@ class PyOverloadsInspection : PyInspection() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkOverride(overloads: List<PyFunction>, implementation: PyFunction?) {
|
||||
if (implementation == null) {
|
||||
for (overload in overloads.tail()) {
|
||||
if (isOverride(overload, myTypeEvalContext)) {
|
||||
registerProblem(overload.nameIdentifier,
|
||||
PyPsiBundle.message("INSP.overloads.override.should.be.placed.only.on.the.first.overload"))
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (overload in overloads) {
|
||||
if (isOverride(overload, myTypeEvalContext)) {
|
||||
registerProblem(overload.nameIdentifier,
|
||||
PyPsiBundle.message("INSP.overloads.override.should.be.placed.on.the.implementation"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isOverride(function: PyFunction, context: TypeEvalContext): Boolean {
|
||||
val decoratorList = function.decoratorList ?: return false
|
||||
return decoratorList.decorators.any { decorator ->
|
||||
PyKnownDecoratorUtil.asKnownDecorators(decorator, myTypeEvalContext).any {
|
||||
it == PyKnownDecorator.TYPING_OVERRIDE || it == PyKnownDecorator.TYPING_EXTENSIONS_OVERRIDE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isIncompatibleOverload(implementation: PyFunction, overload: PyFunction): Boolean {
|
||||
return implementation != overload &&
|
||||
PyiUtil.isOverload(overload, myTypeEvalContext) &&
|
||||
|
||||
@@ -23,20 +23,15 @@ class PyOverridesInspection : PyInspection() {
|
||||
override fun visitPyFunction(node: PyFunction) {
|
||||
super.visitPyFunction(node)
|
||||
|
||||
if (!PyKnownDecoratorUtil.getKnownDecorators(node, myTypeEvalContext)
|
||||
.any { it == PyKnownDecorator.TYPING_OVERRIDE ||
|
||||
it == PyKnownDecorator.TYPING_EXTENSIONS_OVERRIDE }) {
|
||||
return
|
||||
}
|
||||
|
||||
val overrideDecorator = node.decoratorList?.decorators?.firstOrNull {
|
||||
"override" == it.qualifiedName?.lastComponent
|
||||
val overrideDecorator = node.decoratorList?.decorators?.firstOrNull { decorator ->
|
||||
PyKnownDecoratorUtil.asKnownDecorators(decorator, myTypeEvalContext).any {
|
||||
it == PyKnownDecorator.TYPING_OVERRIDE || it == PyKnownDecorator.TYPING_EXTENSIONS_OVERRIDE
|
||||
}
|
||||
} ?: return
|
||||
|
||||
val superMethods = PySuperMethodsSearch.search(node, myTypeEvalContext).findAll()
|
||||
if (superMethods.isEmpty()) {
|
||||
registerProblem(overrideDecorator, PyPsiBundle.message("INSP.override.missing.super.method"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
from typing import overload, override
|
||||
|
||||
|
||||
class A:
|
||||
@overload
|
||||
def foo(self, x: int) -> None: ...
|
||||
|
||||
@overload
|
||||
def foo(self, x: str) -> None: ...
|
||||
|
||||
@override
|
||||
def foo(self, x: int | str) -> None:
|
||||
pass
|
||||
|
||||
@overload
|
||||
def bar(self, x: int) -> None: ...
|
||||
|
||||
@override
|
||||
@overload
|
||||
def <warning descr="'@override' should be placed on the implementation">bar</warning>(self, x: str) -> None: ...
|
||||
|
||||
@override
|
||||
def bar(self, x: int | str) -> None:
|
||||
pass
|
||||
@@ -51,7 +51,6 @@ namedtuples_type_compat.py
|
||||
namedtuples_usage.py
|
||||
narrowing_typeis.py
|
||||
overloads_consistency.py
|
||||
overloads_definitions.py
|
||||
overloads_definitions_stub.pyi
|
||||
overloads_evaluation.py
|
||||
protocols_class_objects.py
|
||||
|
||||
@@ -48,6 +48,10 @@ public class PyOverloadsInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testOverridenMethods() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
@@ -35,6 +35,8 @@ private val inspections
|
||||
//PyInitNewSignatureInspection(), // False negative constructors_consistency.py
|
||||
PyNewStyleGenericSyntaxInspection(),
|
||||
PyNewTypeInspection(),
|
||||
PyOverloadsInspection(),
|
||||
PyOverridesInspection(),
|
||||
PyProtocolInspection(),
|
||||
PyTypedDictInspection(),
|
||||
PyTypeCheckerInspection(),
|
||||
|
||||
Reference in New Issue
Block a user