PY-84464 Support @property decorator when matching a protocol and its implementation

(cherry picked from commit fcbeeb57323336c7d921edad373ea595ef687d6b)

IJ-MR-174248

GitOrigin-RevId: 6c44a1a7af026faaf4a5e9c1242ed29f3fb94e0e
This commit is contained in:
evgeny.bovykin
2025-08-20 13:15:39 +02:00
committed by intellij-monorepo-bot
parent 59d7739bd6
commit 1331c92868
17 changed files with 629 additions and 198 deletions

View File

@@ -8,7 +8,7 @@ import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.types.PyCallableType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypedResolveResult;
import com.jetbrains.python.psi.types.PyTypeMember;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -78,9 +78,9 @@ public interface PyTypeProvider {
@NotNull TypeEvalContext context);
@ApiStatus.Experimental
@Nullable List<@NotNull PyTypedResolveResult> getMemberTypes(@NotNull PyType type,
@NotNull String name,
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext context);
@Nullable List<@NotNull PyTypeMember> getMemberTypes(@NotNull PyType type,
@NotNull String name,
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext context);
}

View File

@@ -5,12 +5,9 @@ import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.psi.AccessDirection;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyQualifiedNameOwner;
import com.jetbrains.python.psi.PyTypedElement;
import com.jetbrains.python.psi.impl.PyTypeProvider;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import org.jetbrains.annotations.ApiStatus;
@@ -51,34 +48,19 @@ public interface PyType {
final @NotNull AccessDirection direction,
final @NotNull PyResolveContext resolveContext);
@ApiStatus.Experimental
@Nullable
default List<@NotNull PyTypedResolveResult> getMemberTypes(@NotNull String name,
final @Nullable PyExpression location,
final @NotNull AccessDirection direction,
final @NotNull PyResolveContext context) {
for (PyTypeProvider typeProvider : PyTypeProvider.EP_NAME.getExtensionList()) {
List<PyTypedResolveResult> types = typeProvider.getMemberTypes(this, name, location, direction, context);
if (types != null) {
return types;
}
}
default @NotNull List<@NotNull PyTypeMember> getAllMembers(final @NotNull PyResolveContext resolveContext) {
return List.of();
}
List<? extends RatedResolveResult> results = resolveMember(name, location, direction, context);
if (results == null) {
return null;
}
return ContainerUtil.map(results, result -> {
PsiElement element = result.getElement();
if (element instanceof PyTypedElement typedElement) {
return new PyTypedResolveResult(typedElement,
context.getTypeEvalContext().getType(typedElement));
}
else {
return new PyTypedResolveResult(element, null);
}
});
/**
* Returns a list of members with a given name
* There can be several members with the same name (for example, methods with @overload)
*/
@ApiStatus.Experimental
default @NotNull List<@NotNull PyTypeMember> findMember(@NotNull String name, final @NotNull PyResolveContext resolveContext) {
return List.of();
}
/**

View File

@@ -0,0 +1,38 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
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.resolve.RatedResolveResult
import org.jetbrains.annotations.ApiStatus
/**
* Represents a high-level member of a class
* It can be a simple attribute, a method, a property, a dataclass attribute and so on
*
* For a property, one member represents its getter, setter and deleter
*/
@ApiStatus.Experimental
class PyTypeMember @JvmOverloads constructor(
val mainElement: PsiElement?,
val type: PyType?,
val isClassVar: Boolean = false,
val getter: PsiElement? = mainElement,
val setter: PsiElement? = mainElement,
val deleter: PsiElement? = mainElement,
) : RatedResolveResult(0, mainElement) {
constructor(property: Property, type: PyType?) : this(
property.getter.value(),
type,
getter = property.getter.value(),
setter = property.setter.valueOrNull(),
deleter = property.deleter.valueOrNull(),
)
val isWritable: Boolean get() = setter != null
val isDeletable: Boolean get() = deleter != null
val name: String? get() = if (mainElement is PsiNamedElement) mainElement.name else null
}

View File

@@ -84,11 +84,11 @@ public class PyTypeProviderBase implements PyTypeProvider {
}
@Override
public @Nullable List<@NotNull PyTypedResolveResult> getMemberTypes(@NotNull PyType type,
@NotNull String name,
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext context) {
public @Nullable List<@NotNull PyTypeMember> getMemberTypes(@NotNull PyType type,
@NotNull String name,
@Nullable PyExpression location,
@NotNull AccessDirection direction,
@NotNull PyResolveContext context) {
return null;
}

View File

@@ -1,14 +0,0 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.jetbrains.python.psi.types
import com.intellij.psi.PsiElement
import com.intellij.psi.ResolveResult
class PyTypedResolveResult(private val el: PsiElement?, val type: PyType?) : ResolveResult {
override fun getElement(): PsiElement? {
return el
}
override fun isValidResult(): Boolean {
return element != null
}
}