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:
Semyon Proshev
2020-11-11 13:40:09 +03:00
committed by intellij-monorepo-bot
parent be15656e6d
commit fbbb78bc4e
27 changed files with 419 additions and 261 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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() {

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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"/>

View File

@@ -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)) {

View File

@@ -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) {

View File

@@ -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

View File

@@ -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 }

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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);
});
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -3,3 +3,4 @@ class C:
pass
c = C()
print(C)

View File

@@ -1,9 +0,0 @@
class MyMeta(type):
def __call__(cls, *args, **kwargs):
pass
class MyClass(metaclass=MyMeta):
pass
MyClass()
<ref>

View File

@@ -1,9 +0,0 @@
class MyMeta(type):
def __call__(cls, p1, p2):
pass
class MyClass(metaclass=MyMeta):
pass
MyClass()
<ref>

View File

@@ -1,6 +0,0 @@
# must resolve to constructor
class Foo:
def __init__(self):
pass
F<ref>oo()

View File

@@ -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
}

View File

@@ -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>")
);
}