PY-86249 Make functions read-only in PyTypeMember

GitOrigin-RevId: 37a8beeaad3ab47501ff85ceaf739de5af635763
This commit is contained in:
evgeny.bovykin
2025-12-14 21:38:04 +00:00
committed by intellij-monorepo-bot
parent c0e48cf676
commit 7775817184
3 changed files with 65 additions and 3 deletions
@@ -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
}
}
}
@@ -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("""
@@ -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());