mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-14216 Code completion for Model.objects does not work with Django 1.7.x
PY-14714 Unresolved attribute reference 'objects' for class 'Foo'
This commit is contained in:
@@ -45,6 +45,11 @@ public class PyCustomMember {
|
||||
|
||||
boolean myFunction = false;
|
||||
|
||||
/**
|
||||
* Force resolving to {@link MyInstanceElement} even if element is function
|
||||
*/
|
||||
private boolean myAlwaysResolveToCustomElement;
|
||||
|
||||
public PyCustomMember(@NotNull final String name, @Nullable final String type, final boolean resolveToInstance) {
|
||||
myName = name;
|
||||
myResolveToInstance = resolveToInstance;
|
||||
@@ -82,6 +87,7 @@ public class PyCustomMember {
|
||||
myTypeName = typeName;
|
||||
myTypeCallback = null;
|
||||
}
|
||||
|
||||
public PyCustomMember(@NotNull final String name, @Nullable final PsiElement target) {
|
||||
this(name, target, null);
|
||||
}
|
||||
@@ -96,6 +102,15 @@ public class PyCustomMember {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force resolving to {@link MyInstanceElement} even if element is function
|
||||
*/
|
||||
@NotNull
|
||||
public final PyCustomMember alwaysResolveToCustomElement() {
|
||||
myAlwaysResolveToCustomElement = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PyCustomMember toClass(String name) {
|
||||
myPsiPath = new PyPsiPath.ToClass(myPsiPath, name);
|
||||
return this;
|
||||
@@ -156,7 +171,7 @@ public class PyCustomMember {
|
||||
PyClass targetClass =
|
||||
myTypeName != null && myTypeName.indexOf('.') > 0 ? PyPsiFacade.getInstance(context.getProject()).findClass(myTypeName) : null;
|
||||
final PsiElement resolveTarget = findResolveTarget(context);
|
||||
if (resolveTarget instanceof PyFunction) {
|
||||
if (resolveTarget instanceof PyFunction && !myAlwaysResolveToCustomElement) {
|
||||
return resolveTarget;
|
||||
}
|
||||
if (resolveTarget != null || targetClass != null) {
|
||||
@@ -211,4 +226,4 @@ public class PyCustomMember {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ public interface PyClassMembersProvider {
|
||||
ExtensionPointName<PyClassMembersProvider> EP_NAME = ExtensionPointName.create("Pythonid.pyClassMembersProvider");
|
||||
|
||||
@NotNull
|
||||
Collection<PyCustomMember> getMembers(PyClassType clazz, @Nullable PsiElement location);
|
||||
Collection<PyCustomMember> getMembers(final PyClassType clazz, PsiElement location);
|
||||
|
||||
@Nullable
|
||||
PsiElement resolveMember(PyClassType clazz, String name, @Nullable PsiElement location);
|
||||
|
||||
@@ -102,6 +102,7 @@ public class PyTypeProviderBase implements PyTypeProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getCallType(@NotNull PyFunction function, @Nullable PyCallSiteExpression callSite, @NotNull TypeEvalContext context) {
|
||||
ReturnTypeDescriptor descriptor;
|
||||
|
||||
@@ -191,7 +191,7 @@ public class TypeEvalContext {
|
||||
}
|
||||
}
|
||||
final PyType type = element.getType(this, Key.INSTANCE);
|
||||
assertValid(type, element);
|
||||
assertValid(type, element);
|
||||
synchronized (myEvaluated) {
|
||||
myEvaluated.put(element, type);
|
||||
}
|
||||
|
||||
@@ -850,5 +850,4 @@ remote.interpreter.configure.path.label=Python interpreter path:
|
||||
remote.interpreter.configure.temp.files.path.label=PyCharm helpers path:
|
||||
|
||||
# Message we display for inspection if user uses custom class type members that do not exist
|
||||
custom.type.name=Dynamic class
|
||||
custom.type.mimic.name=Dynamic class based on {0}
|
||||
@@ -1,204 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMember;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMemberUtils;
|
||||
import com.jetbrains.python.psi.AccessDirection;
|
||||
import com.jetbrains.python.psi.PyCallSiteExpression;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Custom (aka dynamic) type that has some members you pass to it. To be used for cases like "type()"
|
||||
* This class can also mimic any other class (optionally). When mimics, it has all methods from this class and its own.
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class PyCustomMembersType implements PyClassLikeType {
|
||||
@NotNull
|
||||
private final Map<String, PyCustomMember> myMembers;
|
||||
@Nullable
|
||||
private final PyClassType myTypeToMimic;
|
||||
|
||||
/**
|
||||
* @param typeToMimic this type may mimic some other class-based type. Pass it to have all members from this class + custom.
|
||||
* Check class manual for more info.
|
||||
* @param members custom members
|
||||
*/
|
||||
public PyCustomMembersType(@Nullable final PyClassType typeToMimic, @NotNull final PyCustomMember... members) {
|
||||
myTypeToMimic = typeToMimic;
|
||||
|
||||
myMembers = new HashMap<String, PyCustomMember>(members.length);
|
||||
for (final PyCustomMember member : members) {
|
||||
myMembers.put(member.getName(), member);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class we mimic (if any). Check class manual for more info.
|
||||
*/
|
||||
@Nullable
|
||||
public PyClassType getTypeToMimic() {
|
||||
return myTypeToMimic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefinition() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyClassLikeType toInstance() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getClassQName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<PyClassLikeType> getSuperClassTypes(@NotNull TypeEvalContext context) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
|
||||
@Nullable final PyExpression location,
|
||||
@NotNull final AccessDirection direction,
|
||||
@NotNull final PyResolveContext resolveContext,
|
||||
final boolean inherited) {
|
||||
if (myMembers.containsKey(name)) {
|
||||
PsiElement context = null;
|
||||
if (location != null) {
|
||||
context = location;
|
||||
}
|
||||
if (context == null) {
|
||||
context = resolveContext.getTypeEvalContext().getOrigin();
|
||||
}
|
||||
if (context != null) {
|
||||
final PsiElement resolveResult = myMembers.get(name).resolve(context);
|
||||
if (resolveResult != null) {
|
||||
return Collections.singletonList(new RatedResolveResult(0, resolveResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (myTypeToMimic != null) {
|
||||
return myTypeToMimic.resolveMember(name, location, direction, resolveContext, inherited);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyClassLikeType getMetaClassType(@NotNull TypeEvalContext context, boolean inherited) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCallable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getReturnType(@NotNull TypeEvalContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getCallType(@NotNull TypeEvalContext context, @NotNull PyCallSiteExpression callSite) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<PyCallableParameter> getParameters(@NotNull TypeEvalContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
|
||||
@Nullable final PyExpression location,
|
||||
@NotNull final AccessDirection direction,
|
||||
@NotNull final PyResolveContext resolveContext) {
|
||||
return resolveMember(name, location, direction, resolveContext, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getCompletionVariants(final String completionPrefix, final PsiElement location, final ProcessingContext context) {
|
||||
final Collection<LookupElement> lookupElements = new ArrayList<LookupElement>(myMembers.size());
|
||||
for (final PyCustomMember member : myMembers.values()) {
|
||||
lookupElements.add(PyCustomMemberUtils.toLookUpElement(member, member.getShortType()));
|
||||
}
|
||||
return ArrayUtil.mergeArrays(ArrayUtil.toObjectArray(lookupElements),
|
||||
((myTypeToMimic != null)
|
||||
? myTypeToMimic.getCompletionVariants(completionPrefix, location, context)
|
||||
: PsiElement.EMPTY_ARRAY));
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getName() {
|
||||
String mimicName = null;
|
||||
if (myTypeToMimic != null) {
|
||||
mimicName = myTypeToMimic.getName();
|
||||
}
|
||||
if (mimicName != null) {
|
||||
return PyBundle.message("custom.type.mimic.name", mimicName);
|
||||
}
|
||||
return PyBundle.message("custom.type.name");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assertValid(final String message) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name name to check
|
||||
* @return True if this class (not the one it mimics!) has member with passed name
|
||||
*/
|
||||
public boolean hasMember(@NotNull final String name) {
|
||||
return myMembers.containsKey(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Custom (aka dynamic) type that delegates calls to some classes you pass to it.
|
||||
* We say this this class <strong>mimics</strong> such classes.
|
||||
* To be used for cases like "type()".
|
||||
* It optionally filters methods using {@link ParentsMemberFilter}
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class PyCustomType implements PyClassLikeType, Predicate<RatedResolveResult> {
|
||||
|
||||
@NotNull
|
||||
private final List<PyClassLikeType> myTypesToMimic = new ArrayList<PyClassLikeType>();
|
||||
|
||||
@Nullable
|
||||
private final ParentsMemberFilter myFilter;
|
||||
|
||||
private final boolean myInstanceType;
|
||||
|
||||
|
||||
/**
|
||||
* @param filter filter to filter methods from classes (may be null to do no filtering)
|
||||
* @param instanceType if true, then this class implements instance (it reports it is not definition and returns "this
|
||||
* for {@link #toInstance()} call).
|
||||
* @param typesToMimic types to "mimic": delegate calls to (must be one at least!)
|
||||
*/
|
||||
public PyCustomType(@Nullable final ParentsMemberFilter filter,
|
||||
final boolean instanceType,
|
||||
@NotNull final PyClassLikeType... typesToMimic) {
|
||||
Preconditions.checkArgument(typesToMimic.length > 0, "Provide at least one class");
|
||||
myFilter = filter;
|
||||
myTypesToMimic.addAll(Arrays.asList(typesToMimic));
|
||||
myInstanceType = instanceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class we mimic (if any). Check class manual for more info.
|
||||
*/
|
||||
@NotNull
|
||||
public final List<PyClassLikeType> getTypesToMimic() {
|
||||
return Collections.unmodifiableList(myTypesToMimic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDefinition() {
|
||||
return !myInstanceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final PyClassLikeType toInstance() {
|
||||
return myInstanceType ? this : new PyCustomType(myFilter, true, myTypesToMimic.toArray(new PyClassLikeType[myTypesToMimic.size()]));
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final String getClassQName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public final List<PyClassLikeType> getSuperClassTypes(@NotNull final TypeEvalContext context) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
|
||||
@Nullable final PyExpression location,
|
||||
@NotNull final AccessDirection direction,
|
||||
@NotNull final PyResolveContext resolveContext,
|
||||
final boolean inherited) {
|
||||
final List<RatedResolveResult> globalResult = new ArrayList<RatedResolveResult>();
|
||||
|
||||
// Delegate calls to classes, we mimic but filter if filter is set.
|
||||
for (final PyClassLikeType parentType : myTypesToMimic) {
|
||||
final List<? extends RatedResolveResult> results = parentType.resolveMember(name, location, direction, resolveContext, inherited);
|
||||
if (results != null) {
|
||||
globalResult.addAll(Collections2.filter(results, this));
|
||||
}
|
||||
}
|
||||
return globalResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final PyClassLikeType getMetaClassType(@NotNull final TypeEvalContext context, final boolean inherited) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isCallable() {
|
||||
return true; // We do not know, actually
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final PyType getReturnType(@NotNull final TypeEvalContext context) {
|
||||
return (myInstanceType ? null : toInstance());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final PyType getCallType(@NotNull final TypeEvalContext context, @NotNull final PyCallSiteExpression callSite) {
|
||||
return getReturnType(context);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final List<PyCallableParameter> getParameters(@NotNull final TypeEvalContext context) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final List<? extends RatedResolveResult> resolveMember(@NotNull final String name,
|
||||
@Nullable final PyExpression location,
|
||||
@NotNull final AccessDirection direction,
|
||||
@NotNull final PyResolveContext resolveContext) {
|
||||
return resolveMember(name, location, direction, resolveContext, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object[] getCompletionVariants(final String completionPrefix, final PsiElement location, final ProcessingContext context) {
|
||||
final Collection<Object> lookupElements = new ArrayList<Object>();
|
||||
|
||||
for (final PyClassLikeType parentType : myTypesToMimic) {
|
||||
lookupElements.addAll(Collections2.filter(Arrays.asList(parentType.getCompletionVariants(completionPrefix, location, context)),
|
||||
new CompletionFilter()));
|
||||
}
|
||||
return lookupElements.toArray(new Object[lookupElements.size()]);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public final String getName() {
|
||||
final Collection<String> classNames = new ArrayList<String>(myTypesToMimic.size());
|
||||
for (final PyClassLikeType type : myTypesToMimic) {
|
||||
String name = type.getName();
|
||||
if (name == null && (type instanceof PyClassType)) {
|
||||
name = ((PyClassType)type).getPyClass().getName();
|
||||
}
|
||||
if (name != null) {
|
||||
classNames.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return PyBundle.message("custom.type.mimic.name", StringUtil.join(classNames, ","));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isBuiltin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void assertValid(final String message) {
|
||||
for (final PyClassLikeType type : myTypesToMimic) {
|
||||
type.assertValid(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final boolean apply(@Nullable final RatedResolveResult input) {
|
||||
if (input == null) {
|
||||
return false;
|
||||
}
|
||||
if (myFilter == null) {
|
||||
return true; // No need to check
|
||||
}
|
||||
final PyElement pyElement = PyUtil.as(input.getElement(), PyElement.class);
|
||||
if (pyElement == null) {
|
||||
return false;
|
||||
}
|
||||
return myFilter.acceptMember(pyElement);
|
||||
}
|
||||
|
||||
public interface ParentsMemberFilter {
|
||||
boolean acceptMember(@NotNull PyElement element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Predicate that filters completion using {@link #myFilter}
|
||||
*/
|
||||
private class CompletionFilter implements Predicate<Object> {
|
||||
@Override
|
||||
public final boolean apply(@Nullable final Object input) {
|
||||
if (input == null) {
|
||||
return false;
|
||||
}
|
||||
if (myFilter == null) {
|
||||
return true; // No need to check
|
||||
}
|
||||
if (!(input instanceof LookupElement)) {
|
||||
return true; // Do not know how to check
|
||||
}
|
||||
final PyElement pyElement = PyUtil.as(((LookupElement)input).getPsiElement(), PyElement.class);
|
||||
if (pyElement == null) {
|
||||
return false;
|
||||
}
|
||||
return myFilter.acceptMember(pyElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-7
@@ -38,7 +38,7 @@ import com.intellij.util.PlatformUtils;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PyCustomMembersType;
|
||||
import com.jetbrains.python.PyCustomType;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMember;
|
||||
@@ -607,13 +607,15 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if type is custom-member based and has custom member with certain name
|
||||
* Checks if type is custom type and has custom member with certain name
|
||||
* @param refName name to check
|
||||
* @param type type
|
||||
* @return true if has one
|
||||
*/
|
||||
private static boolean isHasCustomMember(@NotNull final String refName, @NotNull final PyType type) {
|
||||
return (type instanceof PyCustomMembersType) && ((PyCustomMembersType)type).hasMember(refName);
|
||||
// TODO: check
|
||||
return false;
|
||||
/*return (type instanceof PyCustomType) && ((PyCustomType)type).hasMember(refName);*/
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,11 +715,15 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (type instanceof PyCustomMembersType) {
|
||||
if (type instanceof PyCustomType) {
|
||||
// Skip custom member types that mimics another class with fuzzy parents
|
||||
PyClassType mimic = ((PyCustomMembersType)type).getTypeToMimic();
|
||||
if (mimic != null && PyUtil.hasUnresolvedAncestors(mimic.getPyClass(), myTypeEvalContext)) {
|
||||
return true;
|
||||
for (final PyClassLikeType mimic : ((PyCustomType)type).getTypesToMimic()) {
|
||||
if (!(mimic instanceof PyClassType)) {
|
||||
continue;
|
||||
}
|
||||
if (PyUtil.hasUnresolvedAncestors(((PyClassType)mimic).getPyClass(), myTypeEvalContext)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type instanceof PyClassTypeImpl) {
|
||||
|
||||
@@ -360,6 +360,9 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (qtype instanceof PyClassLikeType) {
|
||||
return ((PyClassLikeType)qtype).isDefinition(); //Any definition means callable is classmethod
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -522,6 +525,10 @@ public class PyCallExpressionHelper {
|
||||
final Callable callable = (Callable)target;
|
||||
return Ref.create(callable.getCallType(context, call));
|
||||
}
|
||||
/*PyCallExpression.PyMarkedCallee markedCallee = call.resolveCallee(PyResolveContext.defaultContext().withTypeEvalContext(context));
|
||||
if (markedCallee != null) {
|
||||
return Ref.create(markedCallee.getCallable().getCallType(context, call));
|
||||
}*/
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1219,6 +1219,7 @@ public class PyClassImpl extends PyBaseElementImpl<PyClassStub> implements PyCla
|
||||
}
|
||||
else {
|
||||
for (PyExpression expression : getSuperClassExpressions()) {
|
||||
context.getType(expression);
|
||||
expression = unfoldClass(expression);
|
||||
if (expression instanceof PyKeywordArgument) {
|
||||
continue;
|
||||
|
||||
@@ -420,7 +420,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
@Nullable PsiElement anchor) {
|
||||
for (PyTypeProvider provider : Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
|
||||
final PyType result = provider.getReferenceType(target, context, anchor);
|
||||
if (result != null) return result;
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.HashMap;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMember;
|
||||
import com.jetbrains.python.codeInsight.PyCustomMemberUtils;
|
||||
@@ -138,7 +139,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
resolving.add(key);
|
||||
try {
|
||||
return doResolveMember(name, location, direction, resolveContext, inherited);
|
||||
return doResolveMember(name, location, direction, resolveContext, inherited, null);
|
||||
}
|
||||
finally {
|
||||
resolving.remove(key);
|
||||
@@ -146,14 +147,16 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull AccessDirection direction,
|
||||
@NotNull PyResolveContext resolveContext,
|
||||
boolean inherited) {
|
||||
public List<? extends RatedResolveResult> doResolveMember(@NotNull String name,
|
||||
@Nullable PyExpression location,
|
||||
@NotNull AccessDirection direction,
|
||||
@NotNull PyResolveContext resolveContext,
|
||||
boolean inherited,
|
||||
@Nullable PyClassMembersProvider providerToSkip) {
|
||||
final TypeEvalContext context = resolveContext.getTypeEvalContext();
|
||||
PsiElement classMember =
|
||||
resolveByOverridingMembersProviders(this, name, location); //overriding members provers have priority to normal resolve
|
||||
resolveByOverridingMembersProviders(this, name, location,
|
||||
providerToSkip); //overriding members provers have priority to normal resolve
|
||||
if (classMember != null) {
|
||||
return ResolveResultList.to(classMember);
|
||||
}
|
||||
@@ -188,7 +191,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return ResolveResultList.to(classMember);
|
||||
}
|
||||
|
||||
classMember = resolveByOverridingAncestorsMembersProviders(this, name, location);
|
||||
classMember = resolveByOverridingAncestorsMembersProviders(this, name, location, providerToSkip);
|
||||
if (classMember != null) {
|
||||
return ResolveResultList.to(classMember);
|
||||
}
|
||||
@@ -225,7 +228,8 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
|
||||
if (inherited) {
|
||||
classMember =
|
||||
resolveByMembersProviders(this, name, location); //ask providers after real class introspection as providers have less priority
|
||||
resolveByMembersProviders(this, name, location,
|
||||
providerToSkip); //ask providers after real class introspection as providers have less priority
|
||||
}
|
||||
|
||||
if (classMember != null) {
|
||||
@@ -236,7 +240,7 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
for (PyClassLikeType type : myClass.getAncestorTypes(context)) {
|
||||
if (type instanceof PyClassType) {
|
||||
final PyClass pyClass = ((PyClassType)type).getPyClass();
|
||||
PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location);
|
||||
PsiElement superMember = resolveByMembersProviders(new PyClassTypeImpl(pyClass, isDefinition()), name, location, providerToSkip);
|
||||
|
||||
if (superMember != null) {
|
||||
return ResolveResultList.to(superMember);
|
||||
@@ -345,8 +349,13 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveByMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) {
|
||||
private static PsiElement resolveByMembersProviders(PyClassType aClass, String name,
|
||||
@Nullable PsiElement location,
|
||||
@Nullable PyClassMembersProvider providerToSkip) {
|
||||
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
|
||||
if (provider == providerToSkip) {
|
||||
continue;
|
||||
}
|
||||
final PsiElement resolveResult = provider.resolveMember(aClass, name, location);
|
||||
if (resolveResult != null) return resolveResult;
|
||||
}
|
||||
@@ -355,9 +364,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location) {
|
||||
private static PsiElement resolveByOverridingMembersProviders(PyClassType aClass, String name, @Nullable PsiElement location,
|
||||
@Nullable PyClassMembersProvider providerToSkip) {
|
||||
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
|
||||
if (provider instanceof PyOverridingClassMembersProvider) {
|
||||
if (provider instanceof PyOverridingClassMembersProvider && provider != providerToSkip) {
|
||||
final PsiElement resolveResult = provider.resolveMember(aClass, name, location);
|
||||
if (resolveResult != null) return resolveResult;
|
||||
}
|
||||
@@ -367,9 +377,10 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location) {
|
||||
private static PsiElement resolveByOverridingAncestorsMembersProviders(PyClassType type, String name, @Nullable PyExpression location,
|
||||
@Nullable PyClassMembersProvider providerToSkip) {
|
||||
for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
|
||||
if (provider instanceof PyOverridingAncestorsClassMembersProvider) {
|
||||
if (provider instanceof PyOverridingAncestorsClassMembersProvider && !(provider == providerToSkip)) {
|
||||
final PsiElement resolveResult = provider.resolveMember(type, name, location);
|
||||
if (resolveResult != null) return resolveResult;
|
||||
}
|
||||
@@ -409,28 +420,50 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
if (namesAlready == null) {
|
||||
namesAlready = new HashSet<String>();
|
||||
}
|
||||
List<Object> ret = new ArrayList<Object>();
|
||||
|
||||
// TODO: Doc
|
||||
final Map<String, Pair<ElementType, Object>> usedNames = new HashMap<String, Pair<ElementType, Object>>();
|
||||
|
||||
|
||||
boolean suppressParentheses = context.get(CTX_SUPPRESS_PARENTHESES) != null;
|
||||
addOwnClassMembers(location, namesAlready, suppressParentheses, ret);
|
||||
addOwnClassMembers(location, namesAlready, suppressParentheses, usedNames);
|
||||
namesAlready.addAll(usedNames.keySet());
|
||||
|
||||
PsiFile origin = (location != null) ?
|
||||
CompletionUtil.getOriginalOrSelf(location)
|
||||
.getContainingFile() :
|
||||
null;
|
||||
final TypeEvalContext typeEvalContext = TypeEvalContext.codeCompletion(myClass.getProject(), origin);
|
||||
addInheritedMembers(prefix, location, namesAlready, context, ret, typeEvalContext);
|
||||
final List<Object> inheritedMembers = addInheritedMembers(prefix, location, namesAlready, context, usedNames, typeEvalContext);
|
||||
namesAlready.addAll(usedNames.keySet());
|
||||
|
||||
|
||||
// Move to func
|
||||
// TODO: Override only if Overriding
|
||||
// from providers
|
||||
for (final PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) {
|
||||
for (final PyCustomMember member : provider.getMembers(this, location)) {
|
||||
final String name = member.getName();
|
||||
if (!namesAlready.contains(name)) {
|
||||
ret.add(PyCustomMemberUtils.toLookUpElement(member, getName()));
|
||||
final LookupElementBuilder element = PyCustomMemberUtils.toLookUpElement(member, getName());
|
||||
|
||||
final Pair<ElementType, Object> usedNameInfo = usedNames.get(name);
|
||||
// TODO: Doc
|
||||
if (usedNameInfo == null ||
|
||||
(usedNameInfo.first == ElementType.INHERITED && provider instanceof PyOverridingAncestorsClassMembersProvider) ||
|
||||
(usedNameInfo.first == ElementType.OWN && provider instanceof PyOverridingClassMembersProvider)) {
|
||||
usedNames.put(name, Pair.<ElementType, Object>create(ElementType.BY_PROVIDER, element));
|
||||
} else {
|
||||
int i = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final List<Object> ret = new ArrayList<Object>(getElementsFromUsedNames(usedNames));
|
||||
namesAlready.addAll(usedNames.keySet());
|
||||
ret.addAll(inheritedMembers);
|
||||
|
||||
|
||||
if (!myClass.isNewStyleClass()) {
|
||||
final PyBuiltinCache cache = PyBuiltinCache.getInstance(myClass);
|
||||
final PyClassType classobjType = cache.getOldstyleClassobjType();
|
||||
@@ -449,7 +482,11 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return ret.toArray();
|
||||
}
|
||||
|
||||
private void addOwnClassMembers(PsiElement expressionHook, Set<String> namesAlready, boolean suppressParentheses, List<Object> ret) {
|
||||
|
||||
private void addOwnClassMembers(PsiElement expressionHook,
|
||||
Set<String> namesAlready,
|
||||
boolean suppressParentheses,
|
||||
@NotNull final Map<String, Pair<ElementType, Object>> usedNames) {
|
||||
PyClass containingClass = PsiTreeUtil.getParentOfType(expressionHook, PyClass.class);
|
||||
if (containingClass != null) {
|
||||
containingClass = CompletionUtil.getOriginalElement(containingClass);
|
||||
@@ -475,12 +512,12 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
if (namesAlready.contains(name)) continue;
|
||||
if (!withinOurClass && isClassPrivate(name)) continue;
|
||||
namesAlready.add(name);
|
||||
ret.add(le);
|
||||
usedNames.put(name, Pair.<ElementType, Object>create(ElementType.OWN, le));
|
||||
}
|
||||
if (slots != null) {
|
||||
for (String name : slots) {
|
||||
if (!namesAlready.contains(name)) {
|
||||
ret.add(LookupElementBuilder.create(name));
|
||||
usedNames.put(name, Pair.<ElementType, Object>create(ElementType.OWN, LookupElementBuilder.create(name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -494,12 +531,14 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addInheritedMembers(String name,
|
||||
@NotNull
|
||||
private List<Object> addInheritedMembers(String name,
|
||||
PsiElement expressionHook,
|
||||
Set<String> namesAlready,
|
||||
ProcessingContext context,
|
||||
List<Object> ret,
|
||||
@NotNull final Map<String, Pair<ElementType, Object>> usedNames,
|
||||
@NotNull TypeEvalContext typeEvalContext) {
|
||||
final List<Object> ret = new ArrayList<Object>();
|
||||
for (PyExpression expression : myClass.getSuperClassExpressions()) {
|
||||
final PsiReference reference = expression.getReference();
|
||||
PsiElement element = null;
|
||||
@@ -521,13 +560,14 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
for (Object ob : ancestry) {
|
||||
String inheritedName = ob.toString();
|
||||
if (!namesAlready.contains(inheritedName) && !isClassPrivate(inheritedName)) {
|
||||
ret.add(ob);
|
||||
usedNames.put(inheritedName, Pair.create(ElementType.INHERITED, ob));
|
||||
namesAlready.add(inheritedName);
|
||||
}
|
||||
}
|
||||
ContainerUtil.addAll(ret, ancestry);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean isClassPrivate(String lookup_string) {
|
||||
@@ -607,4 +647,20 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType {
|
||||
}
|
||||
return new PyClassTypeImpl(pyClass, isDefinition);
|
||||
}
|
||||
|
||||
// TOOD: Doc
|
||||
private static List<Object> getElementsFromUsedNames(@NotNull final Map<String, Pair<ElementType, Object>> usedNames) {
|
||||
final List<Object> ret = new ArrayList<Object>();
|
||||
for (final Pair<ElementType, Object> objectPair : usedNames.values()) {
|
||||
ret.add(objectPair.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: Doc
|
||||
private enum ElementType {
|
||||
OWN,
|
||||
INHERITED,
|
||||
BY_PROVIDER
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user