diff --git a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeMember.kt b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeMember.kt index 9804fba4a614..ac9f302fa8b8 100644 --- a/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeMember.kt +++ b/python/python-psi-api/src/com/jetbrains/python/psi/types/PyTypeMember.kt @@ -4,6 +4,7 @@ package com.jetbrains.python.psi.types import com.intellij.psi.PsiElement import com.intellij.psi.PsiNamedElement import com.jetbrains.python.psi.Property +import com.jetbrains.python.psi.PyFunction import com.jetbrains.python.psi.resolve.RatedResolveResult import org.jetbrains.annotations.ApiStatus @@ -14,13 +15,13 @@ import org.jetbrains.annotations.ApiStatus * For a property, one member represents its getter, setter and deleter */ @ApiStatus.Experimental -class PyTypeMember @JvmOverloads constructor( +class PyTypeMember @JvmOverloads @ApiStatus.Internal constructor( val mainElement: PsiElement?, val type: PyType?, val isClassVar: Boolean = false, val getter: PsiElement? = mainElement, - val setter: PsiElement? = mainElement, - val deleter: PsiElement? = mainElement, + val setter: PsiElement? = getSetterFromMainElement(mainElement), + val deleter: PsiElement? = getSetterFromMainElement(mainElement), ) : RatedResolveResult(0, mainElement) { constructor(property: Property, type: PyType?) : this( @@ -35,4 +36,13 @@ class PyTypeMember @JvmOverloads constructor( val isDeletable: Boolean get() = deleter != null val name: String? get() = if (mainElement is PsiNamedElement) mainElement.name else null + + private companion object { + fun getSetterFromMainElement(element: PsiElement?): PsiElement? { + if (element is PyFunction) { + return null + } + return element + } + } } \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java index e73cb51b910d..a2f80307b7a2 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3TypeCheckerInspectionTest.java @@ -3379,6 +3379,35 @@ public class Py3TypeCheckerInspectionTest extends PyInspectionTestCase { """); } + // PY-86249 + public void testProtocolAndFrozenDataclassWithMethod() { + doTestByText(""" + import abc + import dataclasses + from typing import Protocol + + + class Proto(Protocol): + @abc.abstractmethod + def to_kwargs(self) -> dict: + pass + + + @dataclasses.dataclass(frozen=True) + class Impl: + name: str + + def to_kwargs(self) -> dict: + return {"name": self.name} + + + def do(arg: Proto) -> None: ... + + + do(Impl(name="vrf1")) + """); + } + // PY-85771 public void testFlagName() { doTestByText(""" diff --git a/python/testSrc/com/jetbrains/python/inspections/PyProtocolInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyProtocolInspectionTest.java index c133a967b038..e9e110a8aabf 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyProtocolInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyProtocolInspectionTest.java @@ -183,6 +183,29 @@ public class PyProtocolInspectionTest extends PyInspectionTestCase { """); } + // PY-86249 + public void testProtocolAndFrozenDataclassWithMethod() { + doTestByText(""" + import abc + import dataclasses + from typing import Protocol + + + class Proto(Protocol): + @abc.abstractmethod + def to_kwargs(self) -> dict: + pass + + + @dataclasses.dataclass(frozen=True) + class Impl(Proto): + name: str + + def to_kwargs(self) -> dict: + return {"name": self.name} + """); + } + @Override protected void doTest() { runWithLanguageLevel(LanguageLevel.PYTHON37, () -> super.doTest());