mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-02-05 08:06:56 +07:00
Change receiver for initialization calls and don't resolve callee to constructors
Callee is now a receiver for these cases, previously it was `null`. Callee is not replaced with constructors to have an ability to map it onto self/cls parameters and process `(cls: Type[T], ...) -> T` annotations. Stay with the previous behaviour for navigation and looking for target element. GitOrigin-RevId: c0f9894cf50fd5d7fd325f095976d096fb948e89
This commit is contained in:
committed by
intellij-monorepo-bot
parent
be15656e6d
commit
fbbb78bc4e
@@ -138,9 +138,9 @@ class super(object):
|
||||
|
||||
class int:
|
||||
@overload
|
||||
def __init__(self, x: Union[Text, bytes, SupportsInt, _SupportsIndex, _SupportsTrunc] = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[Text, bytes, SupportsInt, _SupportsIndex, _SupportsTrunc] = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, x: Union[Text, bytes, bytearray], base: int) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[Text, bytes, bytearray], base: int) -> _T: ...
|
||||
@property
|
||||
def real(self) -> int: ...
|
||||
@property
|
||||
@@ -202,7 +202,7 @@ class int:
|
||||
def __index__(self) -> int: ...
|
||||
|
||||
class float:
|
||||
def __init__(self, x: Union[SupportsFloat, _SupportsIndex, Text, bytes, bytearray] = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[SupportsFloat, _SupportsIndex, Text, bytes, bytearray] = ...) -> _T: ...
|
||||
def as_integer_ratio(self) -> Tuple[int, int]: ...
|
||||
def hex(self) -> str: ...
|
||||
def is_integer(self) -> bool: ...
|
||||
@@ -252,9 +252,9 @@ class float:
|
||||
|
||||
class complex:
|
||||
@overload
|
||||
def __init__(self, real: float = ..., imag: float = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], real: float = ..., imag: float = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, real: Union[str, SupportsComplex, _SupportsIndex]) -> None: ...
|
||||
def __new__(cls: Type[_T], real: Union[str, SupportsComplex, _SupportsIndex]) -> _T: ...
|
||||
@property
|
||||
def real(self) -> float: ...
|
||||
@property
|
||||
@@ -562,7 +562,7 @@ class memoryview(Sized, Container[str]):
|
||||
def tolist(self) -> List[int]: ...
|
||||
|
||||
class bool(int):
|
||||
def __init__(self, o: object = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], __o: object = ...) -> _T: ...
|
||||
@overload
|
||||
def __and__(self, x: bool) -> bool: ...
|
||||
@overload
|
||||
|
||||
@@ -138,9 +138,9 @@ class super(object):
|
||||
|
||||
class int:
|
||||
@overload
|
||||
def __init__(self, x: Union[Text, bytes, SupportsInt, _SupportsIndex, _SupportsTrunc] = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[Text, bytes, SupportsInt, _SupportsIndex, _SupportsTrunc] = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, x: Union[Text, bytes, bytearray], base: int) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[Text, bytes, bytearray], base: int) -> _T: ...
|
||||
@property
|
||||
def real(self) -> int: ...
|
||||
@property
|
||||
@@ -202,7 +202,7 @@ class int:
|
||||
def __index__(self) -> int: ...
|
||||
|
||||
class float:
|
||||
def __init__(self, x: Union[SupportsFloat, _SupportsIndex, Text, bytes, bytearray] = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[SupportsFloat, _SupportsIndex, Text, bytes, bytearray] = ...) -> _T: ...
|
||||
def as_integer_ratio(self) -> Tuple[int, int]: ...
|
||||
def hex(self) -> str: ...
|
||||
def is_integer(self) -> bool: ...
|
||||
@@ -252,9 +252,9 @@ class float:
|
||||
|
||||
class complex:
|
||||
@overload
|
||||
def __init__(self, real: float = ..., imag: float = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], real: float = ..., imag: float = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, real: Union[str, SupportsComplex, _SupportsIndex]) -> None: ...
|
||||
def __new__(cls: Type[_T], real: Union[str, SupportsComplex, _SupportsIndex]) -> _T: ...
|
||||
@property
|
||||
def real(self) -> float: ...
|
||||
@property
|
||||
@@ -562,7 +562,7 @@ class memoryview(Sized, Container[str]):
|
||||
def tolist(self) -> List[int]: ...
|
||||
|
||||
class bool(int):
|
||||
def __init__(self, o: object = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], __o: object = ...) -> _T: ...
|
||||
@overload
|
||||
def __and__(self, x: bool) -> bool: ...
|
||||
@overload
|
||||
|
||||
@@ -163,9 +163,9 @@ class super(object):
|
||||
|
||||
class int:
|
||||
@overload
|
||||
def __init__(self, x: Union[str, bytes, SupportsInt, _SupportsIndex, _SupportsTrunc] = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[str, bytes, SupportsInt, _SupportsIndex, _SupportsTrunc] = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, x: Union[str, bytes, bytearray], base: int) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[str, bytes, bytearray], base: int) -> _T: ...
|
||||
if sys.version_info >= (3, 8):
|
||||
def as_integer_ratio(self) -> Tuple[int, Literal[1]]: ...
|
||||
@property
|
||||
@@ -235,7 +235,7 @@ class int:
|
||||
def __index__(self) -> int: ...
|
||||
|
||||
class float:
|
||||
def __init__(self, x: Union[SupportsFloat, _SupportsIndex, str, bytes, bytearray] = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], x: Union[SupportsFloat, _SupportsIndex, str, bytes, bytearray] = ...) -> _T: ...
|
||||
def as_integer_ratio(self) -> Tuple[int, int]: ...
|
||||
def hex(self) -> str: ...
|
||||
def is_integer(self) -> bool: ...
|
||||
@@ -290,9 +290,9 @@ class float:
|
||||
|
||||
class complex:
|
||||
@overload
|
||||
def __init__(self, real: float = ..., imag: float = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], real: float = ..., imag: float = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, real: Union[str, SupportsComplex, _SupportsIndex]) -> None: ...
|
||||
def __new__(cls: Type[_T], real: Union[str, SupportsComplex, _SupportsIndex]) -> _T: ...
|
||||
@property
|
||||
def real(self) -> float: ...
|
||||
@property
|
||||
@@ -323,9 +323,9 @@ class _FormatMapMapping(Protocol):
|
||||
|
||||
class str(Sequence[str]):
|
||||
@overload
|
||||
def __init__(self, o: object = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], o: object = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, o: bytes, encoding: str = ..., errors: str = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], o: bytes, encoding: str = ..., errors: str = ...) -> _T: ...
|
||||
def capitalize(self) -> str: ...
|
||||
def casefold(self) -> str: ...
|
||||
def center(self, __width: int, __fillchar: str = ...) -> str: ...
|
||||
@@ -402,15 +402,15 @@ class str(Sequence[str]):
|
||||
|
||||
class bytes(ByteString):
|
||||
@overload
|
||||
def __init__(self, ints: Iterable[int]) -> None: ...
|
||||
def __new__(cls: Type[_T], ints: Iterable[int]) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, string: str, encoding: str, errors: str = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], string: str, encoding: str, errors: str = ...) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, length: int) -> None: ...
|
||||
def __new__(cls: Type[_T], length: int) -> _T: ...
|
||||
@overload
|
||||
def __init__(self) -> None: ...
|
||||
def __new__(cls: Type[_T]) -> _T: ...
|
||||
@overload
|
||||
def __init__(self, o: SupportsBytes) -> None: ...
|
||||
def __new__(cls: Type[_T], o: SupportsBytes) -> _T: ...
|
||||
def capitalize(self) -> bytes: ...
|
||||
def center(self, __width: int, __fillchar: bytes = ...) -> bytes: ...
|
||||
def count(self, sub: Union[bytes, int], start: Optional[int] = ..., end: Optional[int] = ...) -> int: ...
|
||||
@@ -621,7 +621,7 @@ class memoryview(Sized, Container[int]):
|
||||
def hex(self) -> str: ...
|
||||
|
||||
class bool(int):
|
||||
def __init__(self, o: object = ...) -> None: ...
|
||||
def __new__(cls: Type[_T], __o: object = ...) -> _T: ...
|
||||
@overload
|
||||
def __and__(self, x: bool) -> bool: ...
|
||||
@overload
|
||||
|
||||
@@ -69,20 +69,77 @@ public abstract class PyCommonResolveTest extends PyCommonResolveTestCase {
|
||||
assertInstanceOf(targetElement, PyFunction.class);
|
||||
}
|
||||
|
||||
public void testToConstructor() {
|
||||
PsiElement target = resolve();
|
||||
assertInstanceOf(target, PyFunction.class);
|
||||
assertEquals(PyNames.INIT, ((PyFunction)target).getName());
|
||||
public void testInitialization() {
|
||||
assertResolvesTo(
|
||||
"class Foo:\n" +
|
||||
" pass\n" +
|
||||
"Foo()\n" +
|
||||
" <ref>",
|
||||
PyClass.class,
|
||||
"Foo"
|
||||
);
|
||||
}
|
||||
|
||||
public void testInitializationWithDunderInit() {
|
||||
assertResolvesTo(
|
||||
"class Foo:\n" +
|
||||
" def __init__(self):\n" +
|
||||
" pass\n" +
|
||||
"Foo()\n" +
|
||||
" <ref>",
|
||||
PyClass.class,
|
||||
"Foo"
|
||||
);
|
||||
}
|
||||
|
||||
// PY-17877
|
||||
public void testInitializingToMetaclassDunderCall() {
|
||||
assertResolvesTo(LanguageLevel.getLatest(), PyFunction.class, PyNames.CALL);
|
||||
public void testInitializationWithMetaclassDunderCall() {
|
||||
assertResolvesTo(
|
||||
"class MyMeta(type):\n" +
|
||||
" def __call__(cls, p1, p2):\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"class MyClass(metaclass=MyMeta):\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"MyClass()\n" +
|
||||
" <ref>",
|
||||
PyClass.class,
|
||||
"MyClass"
|
||||
);
|
||||
}
|
||||
|
||||
public void testInitializationWithDunderInitAndMetaclassDunderCall() {
|
||||
assertResolvesTo(
|
||||
"class MyMeta(type):\n" +
|
||||
" def __call__(cls, p1, p2):\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"class MyClass(metaclass=MyMeta):\n" +
|
||||
" def __init__(self): pass\n" +
|
||||
"\n" +
|
||||
"MyClass()\n" +
|
||||
" <ref>",
|
||||
PyClass.class,
|
||||
"MyClass"
|
||||
);
|
||||
}
|
||||
|
||||
// PY-17877, PY-41380
|
||||
public void testInitializingNotToMetaclassSelfArgsKwargsDunderCall() {
|
||||
assertResolvesTo(LanguageLevel.getLatest(), PyClass.class, "MyClass");
|
||||
public void testInitializationWithMetaclassSelfArgsKwargsDunderCall() {
|
||||
assertResolvesTo(
|
||||
"class MyMeta(type):\n" +
|
||||
" def __call__(cls, *args, **kwargs):\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"class MyClass(metaclass=MyMeta):\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"MyClass()\n" +
|
||||
" <ref>",
|
||||
PyClass.class,
|
||||
"MyClass"
|
||||
);
|
||||
}
|
||||
|
||||
public void testInitOrNewReturnsInitWhenNewIsFirst() {
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyCallableType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -68,4 +69,23 @@ public interface PyTypeProvider {
|
||||
*/
|
||||
@NotNull
|
||||
Map<PyType, PyType> getGenericSubstitutions(@NotNull PyClass cls, @NotNull TypeEvalContext context);
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* If callee type is a class type, it is replaced in code insight for {@code call}
|
||||
* with {@code __init__}/{@code __new__} or {@code __call__} depending on whether it is a definition or an instance.
|
||||
* </p>
|
||||
* <p>
|
||||
* If the {@code type} is provided, and it is desirable to stay with the provided {@code type}, please wrap it into {@link Ref}.
|
||||
* </p>
|
||||
* <p>
|
||||
* If code insight should be suppressed for the {@code call}, return {@code null} wrapped into the {@link Ref}.
|
||||
* </p>
|
||||
* <p>
|
||||
* Return {@code null} otherwise.
|
||||
* </p>
|
||||
*/
|
||||
@Nullable Ref<@Nullable PyCallableType> prepareCalleeTypeForCall(@Nullable PyType type,
|
||||
@NotNull PyCallExpression call,
|
||||
@NotNull TypeEvalContext context);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,13 @@ public class PyTypeProviderBase implements PyTypeProvider {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Ref<@Nullable PyCallableType> prepareCalleeTypeForCall(@Nullable PyType type,
|
||||
@NotNull PyCallExpression call,
|
||||
@NotNull TypeEvalContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void registerSelfReturnType(@NotNull String classQualifiedName, @NotNull Collection<String> methods) {
|
||||
registerReturnType(classQualifiedName, methods, mySelfTypeCallback);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
<weigher key="completion" implementationClass="com.jetbrains.python.codeInsight.completion.PythonCompletionWeigher" order="first"/>
|
||||
|
||||
<referencesSearch implementation="com.jetbrains.python.psi.search.PyInitReferenceSearchExecutor"/>
|
||||
<referencesSearch implementation="com.jetbrains.python.psi.search.PyClassReferenceSearchExecutor"/>
|
||||
<referencesSearch implementation="com.jetbrains.python.psi.search.PyKeywordArgumentSearchExecutor"/>
|
||||
<referencesSearch implementation="com.jetbrains.python.psi.search.PyStringReferenceSearch"/>
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
import com.jetbrains.python.psi.PyReferenceOwner;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -44,15 +46,20 @@ public class PyTargetElementEvaluator implements TargetElementEvaluator {
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getElementByReference(@NotNull PsiReference ref, int flags) {
|
||||
if ((flags & TargetElementUtilBase.ELEMENT_NAME_ACCEPTED) == 0){
|
||||
if ((flags & TargetElementUtilBase.ELEMENT_NAME_ACCEPTED) == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final PsiElement element = ref.getElement();
|
||||
PsiElement result = ref.resolve();
|
||||
final var resolveContext = PyResolveContext
|
||||
.defaultContext()
|
||||
.withTypeEvalContext(TypeEvalContext.codeAnalysis(element.getProject(), element.getContainingFile()));
|
||||
|
||||
PsiElement result = PyResolveUtil.resolveDeclaration(ref, resolveContext);
|
||||
Set<PsiElement> visited = new HashSet<>();
|
||||
visited.add(result);
|
||||
while (result instanceof PyReferenceExpression || result instanceof PyTargetExpression) {
|
||||
PsiElement nextResult = ((PyReferenceOwner)result).getReference(PyResolveContext.defaultContext()).resolve();
|
||||
PsiElement nextResult = PyResolveUtil.resolveDeclaration(((PyReferenceOwner)result).getReference(resolveContext), resolveContext);
|
||||
if (nextResult != null && !visited.contains(nextResult) &&
|
||||
PsiTreeUtil.getParentOfType(element, ScopeOwner.class) == PsiTreeUtil.getParentOfType(result, ScopeOwner.class) &&
|
||||
(nextResult instanceof PyReferenceExpression || nextResult instanceof PyTargetExpression || nextResult instanceof PyParameter)) {
|
||||
|
||||
@@ -283,7 +283,7 @@ public class ConvertFormatOperatorToMethodIntention extends PyBaseIntentionActio
|
||||
final PyCallExpression callExpression = (PyCallExpression)rhs;
|
||||
final PyExpression callee = callExpression.getCallee();
|
||||
final PyClassType classType = PyUtil.as(rhsType, PyClassType.class);
|
||||
if (classType != null && callee != null && isDictCall(callee, classType)) {
|
||||
if (classType != null && callee != null && isDictCall(callee, classType, context)) {
|
||||
target.append(sure(sure(callExpression.getArgumentList()).getNode()).getChars());
|
||||
}
|
||||
else { // just a call, reuse
|
||||
@@ -303,12 +303,15 @@ public class ConvertFormatOperatorToMethodIntention extends PyBaseIntentionActio
|
||||
}
|
||||
|
||||
private static boolean isDictCall(@NotNull PyExpression callee,
|
||||
@NotNull PyClassType classType) {
|
||||
@NotNull PyClassType classType,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final PyClassType dictType = PyBuiltinCache.getInstance(callee.getContainingFile()).getDictType();
|
||||
final var calleeType = PyUtil.as(context.getType(callee), PyClassType.class);
|
||||
return dictType != null &&
|
||||
calleeType != null &&
|
||||
classType.getPyClass() == dictType.getPyClass() &&
|
||||
callee instanceof PyReferenceExpression &&
|
||||
PyUtil.isInitMethod(((PyReferenceExpression)callee).getReference().resolve());
|
||||
calleeType.getPyClass() == dictType.getPyClass() &&
|
||||
calleeType.isDefinition();
|
||||
}
|
||||
|
||||
private static String getSeparator(PyStringLiteralExpression leftExpression) {
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.jetbrains.python.codeInsight.stdlib
|
||||
import com.intellij.openapi.util.Ref
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ArrayUtil
|
||||
import com.intellij.util.containers.mapSmartNotNull
|
||||
import com.jetbrains.python.PyNames
|
||||
import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider
|
||||
import com.jetbrains.python.psi.*
|
||||
@@ -44,6 +45,10 @@ class PyNamedTupleTypeProvider : PyTypeProviderBase() {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun prepareCalleeTypeForCall(type: PyType?, call: PyCallExpression, context: TypeEvalContext): Ref<PyCallableType?>? {
|
||||
return if (type is PyNamedTupleType) Ref.create(type) else null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun isNamedTuple(type: PyType?, context: TypeEvalContext): Boolean {
|
||||
@@ -65,7 +70,12 @@ class PyNamedTupleTypeProvider : PyTypeProviderBase() {
|
||||
return when {
|
||||
referenceTarget is PyFunction && anchor is PyCallExpression -> getNamedTupleFunctionType(referenceTarget, context, anchor)
|
||||
referenceTarget is PyTargetExpression -> getNamedTupleTypeForTarget(referenceTarget, context)
|
||||
referenceTarget is PyClass && anchor is PyCallExpression -> getNamedTupleTypeForNTInheritorAsCallee(referenceTarget, context)
|
||||
referenceTarget is PyClass && anchor is PyCallExpression -> {
|
||||
getNamedTupleTypeForNTInheritorAsCallee(referenceTarget, context) ?:
|
||||
PyUnionType.union(
|
||||
referenceTarget.multiFindInitOrNew(false, context).mapSmartNotNull { getNamedTupleFunctionType(it, context, anchor) }
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -149,6 +159,8 @@ class PyNamedTupleTypeProvider : PyTypeProviderBase() {
|
||||
}
|
||||
|
||||
private fun getNamedTupleTypeForNTInheritorAsCallee(cls: PyClass, context: TypeEvalContext): PyType? {
|
||||
if (cls.findInitOrNew(false, context) != null) return null
|
||||
|
||||
val parameters = if (isTypingNamedTupleDirectInheritor(cls, context)) {
|
||||
val name = cls.name ?: return null
|
||||
val tupleClass = PyPsiFacade.getInstance(cls.project).createClassByQName(PyTypingTypeProvider.NAMEDTUPLE, cls) ?: return null
|
||||
|
||||
@@ -31,6 +31,10 @@ class PyTypedDictTypeProvider : PyTypeProviderBase() {
|
||||
return PyTypeUtil.notNullToRef(getTypedDictTypeForResolvedCallee(referenceTarget, context))
|
||||
}
|
||||
|
||||
override fun prepareCalleeTypeForCall(type: PyType?, call: PyCallExpression, context: TypeEvalContext): Ref<PyCallableType?>? {
|
||||
return if (type is PyTypedDictType) Ref.create(type) else null
|
||||
}
|
||||
|
||||
companion object {
|
||||
val nameIsTypedDict = { name: String? -> name == TYPED_DICT || name == TYPED_DICT_EXT }
|
||||
|
||||
|
||||
@@ -75,6 +75,13 @@ public class PyTypingNewTypeTypeProvider extends PyTypeProviderBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Ref<@Nullable PyCallableType> prepareCalleeTypeForCall(@Nullable PyType type,
|
||||
@NotNull PyCallExpression call,
|
||||
@NotNull TypeEvalContext context) {
|
||||
return type instanceof PyTypingNewType ? Ref.create((PyTypingNewType)type) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyTargetExpression getDeclaration(@NotNull PyCallExpression call) {
|
||||
final PsiElement parent = call.getParent();
|
||||
|
||||
@@ -164,10 +164,12 @@ public class PyCompatibilityInspection extends PyInspection {
|
||||
return;
|
||||
}
|
||||
|
||||
final TypeEvalContext context = TypeEvalContext.codeAnalysis(node.getProject(), node.getContainingFile());
|
||||
final PsiElement resolvedCallee = Optional
|
||||
.ofNullable(callee)
|
||||
.map(PyExpression::getReference)
|
||||
.map(PsiReference::resolve)
|
||||
.map(e -> e instanceof PyClass ? ((PyClass)e).findInitOrNew(false, context) : e)
|
||||
.orElse(null);
|
||||
|
||||
if (resolvedCallee instanceof PyFunction) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.ResolveResult;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.PyNames;
|
||||
@@ -19,12 +20,12 @@ import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.pyi.PyiUtil;
|
||||
import com.jetbrains.python.toolbox.Maybe;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -147,27 +148,29 @@ public final class PyCallExpressionHelper {
|
||||
}
|
||||
|
||||
final PyExpression callee = call.getCallee();
|
||||
if (callee != null && isImplicitlyInvokedMethod(resolvedCallee) && !Objects.equals(resolvedCallee.getName(), callee.getName())) {
|
||||
return callee;
|
||||
}
|
||||
|
||||
if (callee instanceof PyQualifiedExpression) {
|
||||
final PyQualifiedExpression qualifiedCallee = (PyQualifiedExpression)callee;
|
||||
final Predicate<String> isConstructorName = name -> PyNames.INIT.equals(name) || PyNames.NEW.equals(name);
|
||||
|
||||
if (resolvedCallee instanceof PyFunction &&
|
||||
qualifiedCallee.isQualified() &&
|
||||
isConstructorName.test(resolvedCallee.getName()) &&
|
||||
!isConstructorName.test(qualifiedCallee.getName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resolvedCallee != null && PyNames.CALL.equals(resolvedCallee.getName()) && !PyNames.CALL.equals(qualifiedCallee.getName())) {
|
||||
return qualifiedCallee;
|
||||
}
|
||||
|
||||
return qualifiedCallee.getQualifier();
|
||||
return ((PyQualifiedExpression)callee).getQualifier();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
private static boolean isImplicitlyInvokedMethod(@Nullable PyCallable resolvedCallee) {
|
||||
if (PyUtil.isInitOrNewMethod(resolvedCallee)) return true;
|
||||
|
||||
if (resolvedCallee instanceof PyFunction) {
|
||||
final PyFunction function = (PyFunction)resolvedCallee;
|
||||
return PyNames.CALL.equals(function.getName()) && function.getContainingClass() != null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* It is not the same as {@link PyCallExpressionHelper#getCalleeType} since
|
||||
* this method returns callable types that would be actually called, the mentioned method returns type of underlying callee.
|
||||
@@ -205,12 +208,39 @@ public final class PyCallExpressionHelper {
|
||||
@NotNull
|
||||
private static List<@NotNull PyCallableType> getExplicitResolveResults(@NotNull PyCallExpression call,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final List<PsiElement> dunderCallFunctions = resolveDunderCallMembers(call, resolveContext);
|
||||
final TypeEvalContext context = resolveContext.getTypeEvalContext();
|
||||
final var callee = call.getCallee();
|
||||
if (callee == null) return Collections.emptyList();
|
||||
|
||||
return !dunderCallFunctions.isEmpty()
|
||||
? selectCallableTypes(dunderCallFunctions, context)
|
||||
: selectCallableTypes(Collections.singletonList(call.getCallee()), context);
|
||||
final TypeEvalContext context = resolveContext.getTypeEvalContext();
|
||||
final var calleeType = context.getType(callee);
|
||||
|
||||
final var provided = StreamEx
|
||||
.of(PyTypeProvider.EP_NAME.getExtensionList())
|
||||
.map(e -> e.prepareCalleeTypeForCall(calleeType, call, context))
|
||||
.nonNull()
|
||||
.toList();
|
||||
if (!provided.isEmpty()) return ContainerUtil.mapNotNull(provided, Ref::deref);
|
||||
|
||||
final List<PyCallableType> result = new ArrayList<>();
|
||||
|
||||
for (PyType type : PyTypeUtil.toStream(calleeType)) {
|
||||
if (type instanceof PyClassType) {
|
||||
final var classType = (PyClassType)type;
|
||||
|
||||
final var implicitlyInvokedMethods = resolveImplicitlyInvokedMethods(classType, call, resolveContext);
|
||||
if (implicitlyInvokedMethods.isEmpty()) {
|
||||
result.add(classType);
|
||||
}
|
||||
else {
|
||||
result.addAll(changeToImplicitlyInvokedMethods(classType, implicitlyInvokedMethods, call, context));
|
||||
}
|
||||
}
|
||||
else if (type instanceof PyCallableType) {
|
||||
result.add((PyCallableType)type);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -253,24 +283,6 @@ public final class PyCallExpressionHelper {
|
||||
.toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> resolveDunderCallMembers(@NotNull PyCallExpression call,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final PyExpression callee = call.getCallee();
|
||||
if (callee == null) return Collections.emptyList();
|
||||
|
||||
final PyType calleeType = resolveContext.getTypeEvalContext().getType(callee);
|
||||
return PyTypeUtil
|
||||
.toStream(calleeType)
|
||||
.select(PyClassLikeType.class)
|
||||
.filter(type -> !type.isDefinition())
|
||||
.map(type -> type.resolveMember(PyNames.CALL, call, AccessDirection.READ, resolveContext, true))
|
||||
.nonNull()
|
||||
.flatMap(list -> StreamEx.of(list).map(RatedResolveResult::getElement))
|
||||
.nonNull()
|
||||
.toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<QualifiedRatedResolveResult> multiResolveCallee(@Nullable PyExpression callee,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
@@ -292,11 +304,7 @@ public final class PyCallExpressionHelper {
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final PsiElement resolved = resolveResult.getElement();
|
||||
|
||||
if (resolved instanceof PyClass) {
|
||||
return ContainerUtil.map(resolveConstructors((PyClass)resolved, call, resolveContext.getTypeEvalContext(), true),
|
||||
function -> new ClarifiedResolveResult(resolveResult, function, null, true));
|
||||
}
|
||||
else if (resolved instanceof PyCallExpression) { // foo = classmethod(foo)
|
||||
if (resolved instanceof PyCallExpression) { // foo = classmethod(foo)
|
||||
final PyCallExpression resolvedCall = (PyCallExpression)resolved;
|
||||
|
||||
final Pair<String, PyFunction> wrapperInfo = interpretAsModifierWrappingCall(resolvedCall);
|
||||
@@ -325,9 +333,8 @@ public final class PyCallExpressionHelper {
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isConstructor = PyUtil.isInitOrNewMethod(resolved) && call.getReceiver((PyCallable)resolved) == null;
|
||||
return resolved != null
|
||||
? Collections.singletonList(new ClarifiedResolveResult(resolveResult, resolved, null, isConstructor))
|
||||
? Collections.singletonList(new ClarifiedResolveResult(resolveResult, resolved, null, resolved instanceof PyClass))
|
||||
: Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -616,41 +623,38 @@ public final class PyCallExpressionHelper {
|
||||
private static Maybe<PyType> getSuperCallType(@NotNull PyCallExpression call, @NotNull TypeEvalContext context) {
|
||||
final PyExpression callee = call.getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PsiElement must_be_super_init = ((PyReferenceExpression)callee).getReference().resolve();
|
||||
if (must_be_super_init instanceof PyFunction) {
|
||||
PyClass must_be_super = ((PyFunction)must_be_super_init).getContainingClass();
|
||||
if (must_be_super == PyBuiltinCache.getInstance(call).getClass(PyNames.SUPER)) {
|
||||
final PyArgumentList argumentList = call.getArgumentList();
|
||||
if (argumentList != null) {
|
||||
final PyClass containingClass = PsiTreeUtil.getParentOfType(call, PyClass.class);
|
||||
PyExpression[] args = argumentList.getArguments();
|
||||
if (containingClass != null && args.length > 1) {
|
||||
PyExpression first_arg = args[0];
|
||||
if (first_arg instanceof PyReferenceExpression) {
|
||||
final PyReferenceExpression firstArgRef = (PyReferenceExpression)first_arg;
|
||||
final PyExpression qualifier = firstArgRef.getQualifier();
|
||||
if (qualifier != null && PyNames.__CLASS__.equals(firstArgRef.getReferencedName())) {
|
||||
final PsiReference qRef = qualifier.getReference();
|
||||
final PsiElement element = qRef == null ? null : qRef.resolve();
|
||||
if (element instanceof PyParameter) {
|
||||
final PyParameterList parameterList = PsiTreeUtil.getParentOfType(element, PyParameterList.class);
|
||||
if (parameterList != null && element == parameterList.getParameters()[0]) {
|
||||
return new Maybe<>(getSuperCallTypeForArguments(context, containingClass, args[1]));
|
||||
}
|
||||
PsiElement must_be_super = ((PyReferenceExpression)callee).getReference().resolve();
|
||||
if (must_be_super == PyBuiltinCache.getInstance(call).getClass(PyNames.SUPER)) {
|
||||
final PyArgumentList argumentList = call.getArgumentList();
|
||||
if (argumentList != null) {
|
||||
final PyClass containingClass = PsiTreeUtil.getParentOfType(call, PyClass.class);
|
||||
PyExpression[] args = argumentList.getArguments();
|
||||
if (containingClass != null && args.length > 1) {
|
||||
PyExpression first_arg = args[0];
|
||||
if (first_arg instanceof PyReferenceExpression) {
|
||||
final PyReferenceExpression firstArgRef = (PyReferenceExpression)first_arg;
|
||||
final PyExpression qualifier = firstArgRef.getQualifier();
|
||||
if (qualifier != null && PyNames.__CLASS__.equals(firstArgRef.getReferencedName())) {
|
||||
final PsiReference qRef = qualifier.getReference();
|
||||
final PsiElement element = qRef == null ? null : qRef.resolve();
|
||||
if (element instanceof PyParameter) {
|
||||
final PyParameterList parameterList = PsiTreeUtil.getParentOfType(element, PyParameterList.class);
|
||||
if (parameterList != null && element == parameterList.getParameters()[0]) {
|
||||
return new Maybe<>(getSuperCallTypeForArguments(context, containingClass, args[1]));
|
||||
}
|
||||
}
|
||||
PsiElement possible_class = firstArgRef.getReference().resolve();
|
||||
if (possible_class instanceof PyClass && ((PyClass)possible_class).isNewStyleClass(context)) {
|
||||
final PyClass first_class = (PyClass)possible_class;
|
||||
return new Maybe<>(getSuperCallTypeForArguments(context, first_class, args[1]));
|
||||
}
|
||||
}
|
||||
PsiElement possible_class = firstArgRef.getReference().resolve();
|
||||
if (possible_class instanceof PyClass && ((PyClass)possible_class).isNewStyleClass(context)) {
|
||||
final PyClass first_class = (PyClass)possible_class;
|
||||
return new Maybe<>(getSuperCallTypeForArguments(context, first_class, args[1]));
|
||||
}
|
||||
}
|
||||
else if ((call.getContainingFile() instanceof PyFile) &&
|
||||
((PyFile)call.getContainingFile()).getLanguageLevel().isPy3K() &&
|
||||
(containingClass != null)) {
|
||||
return new Maybe<>(getSuperClassUnionType(containingClass, context));
|
||||
}
|
||||
}
|
||||
else if ((call.getContainingFile() instanceof PyFile) &&
|
||||
((PyFile)call.getContainingFile()).getLanguageLevel().isPy3K() &&
|
||||
(containingClass != null)) {
|
||||
return new Maybe<>(getSuperClassUnionType(containingClass, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -845,15 +849,44 @@ public final class PyCallExpressionHelper {
|
||||
return ContainerUtil.find(mapping.values(), p -> p.isKeywordContainer());
|
||||
}
|
||||
|
||||
public static @NotNull List<PsiElement> resolveImplicitlyInvokedMethods(@NotNull PyClassType type,
|
||||
@Nullable PyCallSiteExpression callSite,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
return type.isDefinition() ? resolveConstructors(type, callSite, resolveContext) : resolveDunderCall(type, callSite, resolveContext);
|
||||
}
|
||||
|
||||
private static @NotNull List<@NotNull PyCallableType> changeToImplicitlyInvokedMethods(@NotNull PyClassType classType,
|
||||
@NotNull List<PsiElement> implicitlyInvokedMethods,
|
||||
@NotNull PyCallExpression call,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final var cls = classType.getPyClass();
|
||||
return StreamEx
|
||||
.of(implicitlyInvokedMethods)
|
||||
.map(
|
||||
it ->
|
||||
new ClarifiedResolveResult(
|
||||
new QualifiedRatedResolveResult(cls, Collections.emptyList(), RatedResolveResult.RATE_NORMAL, false),
|
||||
it,
|
||||
null,
|
||||
PyUtil.isInitOrNewMethod(it)
|
||||
)
|
||||
)
|
||||
.map(it -> toCallableType(call, it, context))
|
||||
.nonNull()
|
||||
.toList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<? extends PsiElement> resolveConstructors(@NotNull PyClass cls,
|
||||
@NotNull PyExpression location,
|
||||
@NotNull TypeEvalContext context,
|
||||
boolean inherited) {
|
||||
final List<PsiElement> result = new ArrayList<>();
|
||||
result.addAll(preferInitOverNew(cls.multiFindInitOrNew(inherited, context)));
|
||||
result.addAll(resolveMetaclassDunderCall(cls, location, context));
|
||||
return result;
|
||||
private static List<PsiElement> resolveConstructors(@NotNull PyClassType type,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final var metaclassDunderCall = resolveMetaclassDunderCall(type, location, resolveContext);
|
||||
if (!metaclassDunderCall.isEmpty()) {
|
||||
return metaclassDunderCall;
|
||||
}
|
||||
|
||||
final var initAndNew = type.getPyClass().multiFindInitOrNew(true, resolveContext.getTypeEvalContext());
|
||||
return new SmartList<>(preferInitOverNew(initAndNew));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -863,36 +896,32 @@ public final class PyCallExpressionHelper {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> resolveMetaclassDunderCall(@NotNull PyClass cls,
|
||||
private static List<PsiElement> resolveMetaclassDunderCall(@NotNull PyClassType type,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final PyClassLikeType metaClassType = cls.getMetaClassType(true, context);
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final var context = resolveContext.getTypeEvalContext();
|
||||
|
||||
final PyClassLikeType metaClassType = type.getMetaClassType(context, true);
|
||||
if (metaClassType == null) return Collections.emptyList();
|
||||
|
||||
final PyClassType typeType = PyBuiltinCache.getInstance(cls).getTypeType();
|
||||
final PyClassType typeType = PyBuiltinCache.getInstance(type.getPyClass()).getTypeType();
|
||||
if (metaClassType == typeType) return Collections.emptyList();
|
||||
|
||||
final PyResolveContext resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context);
|
||||
final List<? extends RatedResolveResult> results =
|
||||
PyUtil.filterTopPriorityResults(resolveDunderCall(metaClassType, location, resolveContext));
|
||||
final var results = resolveDunderCall(metaClassType, location, resolveContext);
|
||||
if (results.isEmpty()) return Collections.emptyList();
|
||||
|
||||
final Set<PsiElement> typeDunderCall =
|
||||
ContainerUtil.map2Set(resolveDunderCall(typeType, null, resolveContext), ResolveResult::getElement);
|
||||
typeType == null ? Collections.emptySet() : new HashSet<>(resolveDunderCall(typeType, null, resolveContext));
|
||||
|
||||
return StreamEx
|
||||
.of(results)
|
||||
.map(ResolveResult::getElement)
|
||||
.remove(it -> typeDunderCall.contains(it) || ParamHelper.isSelfArgsKwargsCallable(it, context))
|
||||
.toList();
|
||||
return ContainerUtil.filter(results, it -> !typeDunderCall.contains(it) && !ParamHelper.isSelfArgsKwargsCallable(it, context));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<? extends RatedResolveResult> resolveDunderCall(@Nullable PyClassLikeType type,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
if (type == null) return Collections.emptyList();
|
||||
return ObjectUtils.notNull(type.resolveMember(PyNames.CALL, location, AccessDirection.READ, resolveContext), Collections.emptyList());
|
||||
private static List<PsiElement> resolveDunderCall(@NotNull PyClassLikeType type,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull PyResolveContext resolveContext) {
|
||||
final var resolved = ContainerUtil.notNullize(type.resolveMember(PyNames.CALL, location, AccessDirection.READ, resolveContext));
|
||||
return ResolveResultList.getElements(PyUtil.filterTopPriorityResults(resolved));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.jetbrains.python.PyUserInitiatedResolvableReference;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyReferenceOwner;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.pyi.PyiFile;
|
||||
import com.jetbrains.python.pyi.PyiUtil;
|
||||
@@ -59,7 +60,7 @@ public final class PyGotoDeclarationHandler extends GotoDeclarationHandlerBase {
|
||||
referenceOwner = (PyReferenceOwner)parent; //Reference expression may be parent of IDENTIFIER
|
||||
}
|
||||
if (referenceOwner != null) {
|
||||
final PsiElement resolved = referenceOwner.getReference(context).resolve();
|
||||
final PsiElement resolved = PyResolveUtil.resolveDeclaration(referenceOwner.getReference(context), context);
|
||||
if (resolved instanceof PyiFile) {
|
||||
final PsiElement original = PyiUtil.getOriginalElement(((PyElement)resolved));
|
||||
return ObjectUtils.chooseNotNull(original, resolved);
|
||||
|
||||
@@ -108,24 +108,6 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
final List<RatedResolveResult> targets = resolveInner();
|
||||
if (targets.isEmpty()) return ResolveResult.EMPTY_ARRAY;
|
||||
|
||||
// change class results to constructor results if there are any
|
||||
if (myElement.getParent() instanceof PyCallExpression) { // we're a call
|
||||
final ListIterator<RatedResolveResult> iterator = targets.listIterator();
|
||||
while (iterator.hasNext()) {
|
||||
final RatedResolveResult rrr = iterator.next();
|
||||
final PsiElement element = rrr.getElement();
|
||||
if (element instanceof PyClass) {
|
||||
final PyClass cls = (PyClass)element;
|
||||
final TypeEvalContext context = myContext.getTypeEvalContext();
|
||||
final Collection<? extends PsiElement> constructors = PyCallExpressionHelper.resolveConstructors(cls, myElement, context, false);
|
||||
if (!constructors.isEmpty()) {
|
||||
iterator.remove();
|
||||
constructors.forEach(c -> iterator.add(rrr.replace(c)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RatedResolveResult.sorted(targets).toArray(ResolveResult.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,7 @@ package com.jetbrains.python.psi.resolve;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.ProjectScope;
|
||||
@@ -32,13 +29,12 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.impl.ResolveResultList;
|
||||
import com.jetbrains.python.psi.impl.*;
|
||||
import com.jetbrains.python.psi.search.PySearchUtilBase;
|
||||
import com.jetbrains.python.psi.stubs.PyClassAttributesIndex;
|
||||
import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
|
||||
import com.jetbrains.python.psi.types.PyClassLikeType;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.pyi.PyiFile;
|
||||
@@ -460,4 +456,30 @@ public final class PyResolveUtil {
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement resolveDeclaration(@NotNull PsiReference reference, @NotNull PyResolveContext resolveContext) {
|
||||
final PsiElement element = reference.getElement();
|
||||
|
||||
final var context = resolveContext.getTypeEvalContext();
|
||||
final var call = context.maySwitchToAST(element) ? PyCallExpressionNavigator.getPyCallExpressionByCallee(element) : null;
|
||||
if (call != null && element instanceof PyTypedElement) {
|
||||
final var type = PyUtil.as(context.getType((PyTypedElement)element), PyClassType.class);
|
||||
|
||||
if (type != null && type.isDefinition()) {
|
||||
final var cls = type.getPyClass();
|
||||
|
||||
final var constructor = ContainerUtil.find(
|
||||
PyCallExpressionHelper.resolveImplicitlyInvokedMethods(type, call, resolveContext),
|
||||
it -> it instanceof PyPossibleClassMember && ((PyPossibleClassMember)it).getContainingClass() == cls
|
||||
);
|
||||
|
||||
if (constructor != null) {
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return reference.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.psi.search;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.intellij.openapi.application.QueryExecutorBase;
|
||||
import com.intellij.openapi.application.ReadAction;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.UsageSearchContext;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.util.Processor;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class PyClassReferenceSearchExecutor extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
|
||||
@Override
|
||||
public void processQuery(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull final Processor<? super PsiReference> consumer) {
|
||||
PyClass pyClass = PyUtil.as(queryParameters.getElementToSearch(), PyClass.class);
|
||||
if (pyClass == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ReadAction.run(() -> {
|
||||
final String className = pyClass.getName();
|
||||
if (Strings.isNullOrEmpty(className)) {
|
||||
return;
|
||||
}
|
||||
final PyFunction initMethod = pyClass.findMethodByName(PyNames.INIT, false, null);
|
||||
if (initMethod == null) {
|
||||
return;
|
||||
}
|
||||
SearchScope searchScope = queryParameters.getEffectiveSearchScope();
|
||||
if (searchScope instanceof GlobalSearchScope) {
|
||||
searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE);
|
||||
}
|
||||
queryParameters.getOptimizer().searchWord(className, searchScope, UsageSearchContext.IN_CODE, true, initMethod);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,14 @@ import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.QueryExecutorBase;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.SearchScope;
|
||||
import com.intellij.psi.search.UsageSearchContext;
|
||||
import com.intellij.psi.search.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.util.Processor;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.impl.PyCallExpressionNavigator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -21,7 +20,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
*/
|
||||
public class PyInitReferenceSearchExecutor extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
|
||||
@Override
|
||||
public void processQuery(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull final Processor<? super PsiReference> consumer) {
|
||||
public void processQuery(@NotNull ReferencesSearch.SearchParameters queryParameters, @NotNull Processor<? super PsiReference> consumer) {
|
||||
PsiElement element = queryParameters.getElementToSearch();
|
||||
if (!(element instanceof PyFunction)) {
|
||||
return;
|
||||
@@ -46,7 +45,30 @@ public class PyInitReferenceSearchExecutor extends QueryExecutorBase<PsiReferenc
|
||||
}
|
||||
|
||||
|
||||
queryParameters.getOptimizer().searchWord(className, searchScope, UsageSearchContext.IN_CODE, true, function);
|
||||
final var processor = new ClassInitializationProcessor(pyClass);
|
||||
queryParameters.getOptimizer().searchWord(className, searchScope, UsageSearchContext.IN_CODE, true, pyClass, processor);
|
||||
});
|
||||
}
|
||||
|
||||
private static class ClassInitializationProcessor extends RequestResultProcessor {
|
||||
|
||||
@NotNull
|
||||
private final SingleTargetRequestResultProcessor myProcessor;
|
||||
|
||||
private ClassInitializationProcessor(@NotNull PyClass cls) {
|
||||
super(cls);
|
||||
myProcessor = new SingleTargetRequestResultProcessor(cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processTextOccurrence(@NotNull PsiElement element,
|
||||
int offsetInElement,
|
||||
@NotNull Processor<? super PsiReference> consumer) {
|
||||
if (PyCallExpressionNavigator.getPyCallExpressionByCallee(element) != null) {
|
||||
return myProcessor.processTextOccurrence(element, offsetInElement, consumer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.jetbrains.python.codeInsight.PyCustomMember;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMemberUtils;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.impl.PyCallExpressionHelper;
|
||||
import com.jetbrains.python.psi.impl.PyResolveResultRater;
|
||||
import com.jetbrains.python.psi.impl.ResolveResultList;
|
||||
import com.jetbrains.python.psi.impl.references.PyReferenceImpl;
|
||||
@@ -347,10 +348,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
@Nullable
|
||||
@Override
|
||||
public List<PyCallableParameter> getParameters(@NotNull TypeEvalContext context) {
|
||||
final List<String> methodNames = isDefinition() ? Arrays.asList(PyNames.INIT, PyNames.NEW) : Collections.singletonList(PyNames.CALL);
|
||||
final var resolveContext = PyResolveContext.defaultContext().withTypeEvalContext(context);
|
||||
|
||||
return StreamEx
|
||||
.of(methodNames)
|
||||
.of(PyCallExpressionHelper.resolveImplicitlyInvokedMethods(this, null, resolveContext))
|
||||
.map(name -> getParametersOfMethod(name, context))
|
||||
.findFirst(Objects::nonNull)
|
||||
// If resolved parameters are empty, consider them as invalid and return null
|
||||
@@ -361,18 +362,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<PyCallableParameter> getParametersOfMethod(@NotNull String name, @NotNull TypeEvalContext context) {
|
||||
final List<? extends RatedResolveResult> results =
|
||||
resolveMember(name, null, AccessDirection.READ, PyResolveContext.defaultContext().withTypeEvalContext(context), true);
|
||||
if (results != null) {
|
||||
return StreamEx.of(results)
|
||||
.map(RatedResolveResult::getElement)
|
||||
.select(PyCallable.class)
|
||||
.map(func -> func.getParameters(context))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
return null;
|
||||
private static List<PyCallableParameter> getParametersOfMethod(@NotNull PsiElement element, @NotNull TypeEvalContext context) {
|
||||
return element instanceof PyCallable ? ((PyCallable)element).getParameters(context) : null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -734,6 +734,14 @@ public final class PyTypeChecker {
|
||||
.foldLeft(PyUnionType::union)
|
||||
.orElse(actualType);
|
||||
}
|
||||
else if (PyUtil.isInitMethod(function)) {
|
||||
actualType = PyTypeUtil.toStream(actualType)
|
||||
.select(PyInstantiableType.class)
|
||||
.map(PyInstantiableType::toInstance)
|
||||
.select(PyType.class)
|
||||
.foldLeft(PyUnionType::union)
|
||||
.orElse(actualType);
|
||||
}
|
||||
}
|
||||
if (!match(expectedType, actualType, context, substitutions)) {
|
||||
return null;
|
||||
|
||||
@@ -3,3 +3,4 @@ class C:
|
||||
pass
|
||||
|
||||
c = C()
|
||||
print(C)
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
class MyMeta(type):
|
||||
def __call__(cls, *args, **kwargs):
|
||||
pass
|
||||
|
||||
class MyClass(metaclass=MyMeta):
|
||||
pass
|
||||
|
||||
MyClass()
|
||||
<ref>
|
||||
@@ -1,9 +0,0 @@
|
||||
class MyMeta(type):
|
||||
def __call__(cls, p1, p2):
|
||||
pass
|
||||
|
||||
class MyClass(metaclass=MyMeta):
|
||||
pass
|
||||
|
||||
MyClass()
|
||||
<ref>
|
||||
@@ -1,6 +0,0 @@
|
||||
# must resolve to constructor
|
||||
class Foo:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
F<ref>oo()
|
||||
@@ -8,7 +8,9 @@ import com.intellij.psi.PsiElement
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.fixtures.CodeInsightTestUtil
|
||||
import com.jetbrains.python.fixtures.PyTestCase
|
||||
import com.jetbrains.python.psi.PyClass
|
||||
import com.jetbrains.python.psi.PyFile
|
||||
import com.jetbrains.python.psi.PyFunction
|
||||
import com.jetbrains.python.psi.PyTargetExpression
|
||||
import com.jetbrains.python.psi.impl.PyGotoDeclarationHandler
|
||||
import com.jetbrains.python.pyi.PyiFile
|
||||
@@ -75,6 +77,58 @@ class PyNavigationTest : PyTestCase() {
|
||||
"print(va<caret>r)")
|
||||
}
|
||||
|
||||
fun testGotoDeclarationOnInitialization() {
|
||||
myFixture.configureByText(
|
||||
"a.py",
|
||||
"class MyClass:\n" +
|
||||
" pass\n" +
|
||||
"MyCla<caret>ss()"
|
||||
)
|
||||
val target = PyGotoDeclarationHandler().getGotoDeclarationTarget(elementAtCaret, myFixture.editor)
|
||||
assertInstanceOf(target, PyClass::class.java)
|
||||
}
|
||||
|
||||
fun testGotoDeclarationOnInitializationWithDunderInit() {
|
||||
myFixture.configureByText(
|
||||
"a.py",
|
||||
"class MyClass:\n" +
|
||||
" def __init__(self):\n" +
|
||||
" pass\n" +
|
||||
"MyCla<caret>ss()"
|
||||
)
|
||||
val target = PyGotoDeclarationHandler().getGotoDeclarationTarget(elementAtCaret, myFixture.editor)
|
||||
assertInstanceOf(target, PyFunction::class.java)
|
||||
}
|
||||
|
||||
fun testGotoDeclarationOnInitializationWithMetaclassDunderCall() {
|
||||
myFixture.configureByText(
|
||||
"a.py",
|
||||
"class MyMeta(type):\n" +
|
||||
" def __call__(self, p1, p2):\n" +
|
||||
" pass\n" +
|
||||
"class MyClass(metaclass=MyMeta):\n" +
|
||||
" pass\n" +
|
||||
"MyCla<caret>ss()"
|
||||
)
|
||||
val target = PyGotoDeclarationHandler().getGotoDeclarationTarget(elementAtCaret, myFixture.editor)
|
||||
assertInstanceOf(target, PyClass::class.java)
|
||||
}
|
||||
|
||||
fun testGotoDeclarationOnInitializationWithDunderInitAndMetaclassDunderCall() {
|
||||
myFixture.configureByText(
|
||||
"a.py",
|
||||
"class MyMeta(type):\n" +
|
||||
" def __call__(self, p1, p2):\n" +
|
||||
" pass\n" +
|
||||
"class MyClass(metaclass=MyMeta):\n" +
|
||||
" def __init__(self, p3, p4):\n" +
|
||||
" pass\n" +
|
||||
"MyCla<caret>ss()"
|
||||
)
|
||||
val target = PyGotoDeclarationHandler().getGotoDeclarationTarget(elementAtCaret, myFixture.editor)
|
||||
assertInstanceOf(target, PyClass::class.java)
|
||||
}
|
||||
|
||||
private fun doTestGotoDeclarationOrUsagesOutcome(expectedOutcome: GTDUOutcome, text: String) {
|
||||
myFixture.configureByText("a.py", text)
|
||||
val actualOutcome = GotoDeclarationOrUsageHandler2.testGTDUOutcome(myFixture.editor, myFixture.file, myFixture.caretOffset)
|
||||
@@ -97,6 +151,4 @@ class PyNavigationTest : PyTestCase() {
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor? = ourPy3Descriptor
|
||||
|
||||
|
||||
}
|
||||
@@ -428,7 +428,7 @@ public class PyArgumentListInspectionTest extends PyInspectionTestCase {
|
||||
" # This never gets called\n" +
|
||||
" print(f'SubFoo.__new__: {p1}, {p2}')\n" +
|
||||
"\n" +
|
||||
"sub = SubFoo(1<warning descr=\"Parameter(s) unfilledPossible callees:SubFoo.__new__(self: Type[SubFoo], p1, p2)MetaFoo.__call__(cls: MetaFoo, p3, p4)\">)</warning>\n" +
|
||||
"sub = SubFoo(1<warning descr=\"Parameter 'p4' unfilled\">)</warning>\n" +
|
||||
"foo = Foo(3<warning descr=\"Parameter 'p4' unfilled\">)</warning>")
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user