mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 22:09:38 +07:00
Fixes PY-49, PY-33, partially fixes PY-32.
Provides test cases for PY-33 and (rudimentary) for PY-49 and PY-32.
This commit is contained in:
@@ -2,35 +2,45 @@ package com.jetbrains.python;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.lang.parameterInfo.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import static com.jetbrains.python.psi.PyCallExpression.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentList, PyFunction> {
|
||||
public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentList, PyCallExpression.PyMarkedFunction> {
|
||||
|
||||
public boolean couldShowInLookup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object[] getParametersForLookup(final LookupElement item, final ParameterInfoContext context) {
|
||||
return new Object[0]; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public Object[] getParametersForDocumentation(final PyFunction p, final ParameterInfoContext context) {
|
||||
public Object[] getParametersForDocumentation(final PyCallExpression.PyMarkedFunction p, final ParameterInfoContext context) {
|
||||
return new Object[0]; //To change body of implemented methods use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
public PyArgumentList findElementForParameterInfo(final CreateParameterInfoContext context) {
|
||||
PyArgumentList result = findArgumentList(context);
|
||||
if (result != null) {
|
||||
PyCallExpression callExpression = PsiTreeUtil.getParentOfType(result, PyCallExpression.class);
|
||||
PyCallExpression callExpression = result.getCallExpression(); /*PsiTreeUtil.getParentOfType(result, PyCallExpression.class);*/
|
||||
if (callExpression == null) return null;
|
||||
/*
|
||||
boolean is_inst = callExpression.isByInstance();
|
||||
PyElement callee = callExpression.resolveCallee();
|
||||
if (callee instanceof PyClass) { // constructor call
|
||||
final PyClass cls = (PyClass)callee;
|
||||
callee = cls.findMethodByName("__init__");
|
||||
is_inst |= true;
|
||||
}
|
||||
if (!(callee instanceof PyFunction)) return null;
|
||||
PyFunction function = (PyFunction) callee;
|
||||
context.setItemsToShow(new Object[] { function });
|
||||
|
||||
context.setItemsToShow(new Object[] { new PyCallExpression.PyMarkedFunction(function, is_inst) });
|
||||
*/
|
||||
context.setItemsToShow(new Object[] { callExpression.resolveCallee() });
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -64,15 +74,18 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
return true;
|
||||
}
|
||||
|
||||
public void updateUI(final PyFunction p, final ParameterInfoUIContext context) {
|
||||
final PyParameterList parameterList = p.getParameterList();
|
||||
public void updateUI(final PyMarkedFunction p, final ParameterInfoUIContext context) {
|
||||
final PyFunction py_function = p.getFunction();
|
||||
if (py_function == null) return; // resolution failed
|
||||
final PyParameterList parameterList = py_function.getParameterList();
|
||||
final PyParameter[] params = parameterList.getParameters();
|
||||
final int currentParameterIndex = context.getCurrentParameterIndex() >= 0 ? context.getCurrentParameterIndex():params.length;
|
||||
int currentParameterIndex = context.getCurrentParameterIndex() >= 0 ? context.getCurrentParameterIndex():params.length;
|
||||
|
||||
int highlightStartOffset = -1;
|
||||
int highlightEndOffset = -1;
|
||||
|
||||
StringBuilder signatureBuilder = new StringBuilder();
|
||||
|
||||
for(int i=0; i<params.length; i++) {
|
||||
if (i == currentParameterIndex) {
|
||||
highlightStartOffset = signatureBuilder.length();
|
||||
@@ -80,6 +93,10 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
if (params [i].isPositionalContainer()) {
|
||||
signatureBuilder.append("*");
|
||||
}
|
||||
else if (i == 0 && p.getFlags().contains(Flag.IMPLICIT_FIRST_ARG)) {
|
||||
currentParameterIndex += 1;
|
||||
continue;
|
||||
}
|
||||
else if (params [i].isKeywordContainer()) {
|
||||
signatureBuilder.append("**");
|
||||
}
|
||||
|
||||
@@ -27,11 +27,16 @@ import org.jetbrains.annotations.Nullable;
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public interface PyArgumentList extends PyElement {
|
||||
@NotNull PyExpression[] getArguments();
|
||||
|
||||
@Nullable PyKeywordArgument getKeywordArgument(String name);
|
||||
@NotNull PyExpression[] getArguments();
|
||||
|
||||
@Nullable PyKeywordArgument getKeywordArgument(String name);
|
||||
|
||||
void addArgument(PyExpression arg);
|
||||
void addArgumentFirst(PyExpression arg);
|
||||
void addArgumentAfter(PyExpression argument, PyExpression afterThis);
|
||||
|
||||
@Nullable
|
||||
PyCallExpression getCallExpression();
|
||||
|
||||
void addArgument(PyExpression arg);
|
||||
void addArgumentFirst(PyExpression arg);
|
||||
void addArgumentAfter(PyExpression argument, PyExpression afterThis);
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@
|
||||
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: yole
|
||||
@@ -31,6 +34,62 @@ public interface PyCallExpression extends PyExpression {
|
||||
|
||||
void addArgument(PyExpression expression);
|
||||
|
||||
/**
|
||||
* Resolves callee down to particular function (standalone, method, or constructor).
|
||||
* Return's function part contains a function, never null.
|
||||
* Return's flag part is true if the function call is qualified by an instance, or is a constructor call.
|
||||
* Return is null if callee cannot be resolved.
|
||||
*/
|
||||
@Nullable
|
||||
PyElement resolveCallee();
|
||||
PyMarkedFunction resolveCallee();
|
||||
|
||||
/**
|
||||
* @return true if the call is a method call qualified by class instance. <b>Note:</b> Constructor calls return false.
|
||||
*/
|
||||
//boolean isByInstance();
|
||||
|
||||
enum Flag {
|
||||
/**
|
||||
* First arg of the call is implicit, drop first parameter.
|
||||
*/
|
||||
IMPLICIT_FIRST_ARG,
|
||||
/**
|
||||
* Called function is decorated with @classmethod, first param is the class.
|
||||
*/
|
||||
CLASSMETHOD,
|
||||
/**
|
||||
* Called function is decorated with @staticmethod, first param is as in a regular function.
|
||||
*/
|
||||
STATICMETHOD
|
||||
}
|
||||
|
||||
/**
|
||||
* Couples function with a flag showing if it is being called as an instance method.
|
||||
*/
|
||||
class PyMarkedFunction {
|
||||
PyFunction myFunction;
|
||||
boolean myIsInstanceCall;
|
||||
EnumSet<Flag> myFlags;
|
||||
|
||||
public PyMarkedFunction(@NotNull PyFunction function, EnumSet<Flag> flags) {
|
||||
myFunction = function;
|
||||
//myIsInstanceCall = instance;
|
||||
myFlags = flags;
|
||||
}
|
||||
|
||||
public PyFunction getFunction() {
|
||||
return myFunction;
|
||||
}
|
||||
|
||||
public EnumSet<Flag> getFlags() {
|
||||
return myFlags;
|
||||
}
|
||||
|
||||
/*
|
||||
public boolean isInstanceCall() {
|
||||
return myIsInstanceCall;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.jetbrains.python.psi.stubs.PyClassStub;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -40,14 +41,14 @@ public interface PyClass extends PsiNamedElement, PyElement, NameDefiner, PyDocS
|
||||
@Nullable
|
||||
PsiElement[] getSuperClassElements();
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
PyClass[] getSuperClasses();
|
||||
|
||||
@NotNull
|
||||
PyFunction[] getMethods();
|
||||
|
||||
@Nullable
|
||||
PyFunction findMethodByName(@NotNull final String name);
|
||||
PyFunction findMethodByName(@NotNull @NonNls final String name);
|
||||
|
||||
PyTargetExpression[] getClassAttributes();
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.impl.PyScopeProcessor;
|
||||
import com.jetbrains.python.psi.impl.ResolveImportUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -37,8 +38,11 @@ import java.util.*;
|
||||
*/
|
||||
public class PyResolveUtil {
|
||||
|
||||
private PyResolveUtil() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@NotNull @NonNls
|
||||
public static String getReadableRepr(PsiElement elt) {
|
||||
if (elt == null) return "null!";
|
||||
ASTNode node = elt.getNode();
|
||||
@@ -47,13 +51,10 @@ public class PyResolveUtil {
|
||||
String s = node.getText();
|
||||
int cut_pos = s.indexOf('\n');
|
||||
if (cut_pos < 0) cut_pos = s.length();
|
||||
return s.substring(0, java.lang.Math.min(cut_pos, s.length()));
|
||||
return s.substring(0, Math.min(cut_pos, s.length()));
|
||||
}
|
||||
}
|
||||
|
||||
private PyResolveUtil() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tries to find nearest parent that conceals nams definded inside it. Such elements are 'class' and 'def':
|
||||
@@ -63,8 +64,7 @@ public class PyResolveUtil {
|
||||
*/
|
||||
@Nullable
|
||||
public static PsiElement getConcealingParent(PsiElement elt) {
|
||||
PsiElement top = PsiTreeUtil.getParentOfType(elt, PyClass.class, PyFunction.class);
|
||||
return top;
|
||||
return PsiTreeUtil.getParentOfType(elt, PyClass.class, PyFunction.class);
|
||||
}
|
||||
|
||||
protected static PsiElement getInnermostChildOf(PsiElement elt) {
|
||||
@@ -129,7 +129,7 @@ public class PyResolveUtil {
|
||||
) {
|
||||
seeker = getPrevNodeOf(seeker, NameDefiner.class);
|
||||
}
|
||||
// maybe we're under cap
|
||||
// maybe we're under a cap
|
||||
while (true) {
|
||||
PsiElement local_cap = getConcealingParent(seeker);
|
||||
if (
|
||||
@@ -402,6 +402,10 @@ public class PyResolveUtil {
|
||||
return variants.toArray(new LookupElement[variants.size()]);
|
||||
}
|
||||
|
||||
public List<LookupElement> getResultList() {
|
||||
return new ArrayList<LookupElement>(myVariants.values());
|
||||
}
|
||||
|
||||
public boolean execute(PsiElement element, ResolveState substitutor) {
|
||||
// TODO: refactor to look saner; much code duplication
|
||||
if (element instanceof PsiNamedElement) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
@@ -224,6 +225,11 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyCallExpression getCallExpression() {
|
||||
return PsiTreeUtil.getParentOfType(this, PyCallExpression.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"SuspiciousMethodCalls"})
|
||||
protected void deletePyChild(PyBaseElementImpl element) throws IncorrectOperationException {
|
||||
if (Arrays.asList(getArguments()).contains(element)) {
|
||||
|
||||
@@ -8,16 +8,21 @@ import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyBuiltinCache {
|
||||
|
||||
public static final @NonNls String BUILTIN_FILE = "__builtin__.py";
|
||||
|
||||
public static PyBuiltinCache getInstance(Project project) {
|
||||
return ServiceManager.getService(project, PyBuiltinCache.class);
|
||||
}
|
||||
@@ -31,7 +36,7 @@ public class PyBuiltinCache {
|
||||
|
||||
public PyFile getBuiltinsFile() {
|
||||
if (myBuiltinsFile == null) {
|
||||
final PsiFile[] builtinFiles = FilenameIndex.getFilesByName(myProject, "__builtin__.py", GlobalSearchScope.allScope(myProject));
|
||||
final PsiFile[] builtinFiles = FilenameIndex.getFilesByName(myProject, BUILTIN_FILE, GlobalSearchScope.allScope(myProject));
|
||||
if (builtinFiles.length == 1) {
|
||||
myBuiltinsFile = (PyFile) builtinFiles [0];
|
||||
}
|
||||
@@ -40,11 +45,11 @@ public class PyBuiltinCache {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PyClass getClass(String name) {
|
||||
public PyClass getClass(@NonNls String name) {
|
||||
PyFile builtinsFile = getBuiltinsFile();
|
||||
if (builtinsFile != null) {
|
||||
for(PsiElement element: builtinsFile.getChildren()) {
|
||||
if (element instanceof PyClass && ((PyClass) element).getName().equals(name)) {
|
||||
if (element instanceof PyClass && (name != null) && name.equals(((PyClass) element).getName())) {
|
||||
return (PyClass) element;
|
||||
}
|
||||
}
|
||||
@@ -52,17 +57,17 @@ public class PyBuiltinCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Map<String,PyClassType> myTypeCache = new HashMap<String,PyClassType>();
|
||||
protected Map<String,PyClassType> myTypeCache = new HashMap<String, PyClassType>();
|
||||
|
||||
/**
|
||||
@return
|
||||
*/
|
||||
@NotNull
|
||||
protected PyClassType _getObjectType(String name) {
|
||||
protected PyClassType _getObjectType(@NonNls String name) {
|
||||
PyClassType val = myTypeCache.get(name);
|
||||
if (val == null) {
|
||||
PyClass cls = getClass(name);
|
||||
val = new PyObjectType(cls);
|
||||
val = new PyClassType(cls, false);
|
||||
myTypeCache.put(name, val);
|
||||
}
|
||||
return val;
|
||||
|
||||
@@ -18,11 +18,16 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
@@ -32,6 +37,7 @@ import com.jetbrains.python.psi.types.PyType;
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpression {
|
||||
|
||||
public PyCallExpressionImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
@@ -42,6 +48,7 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
}
|
||||
|
||||
@PsiCached
|
||||
@Nullable
|
||||
public PyExpression getCallee() {
|
||||
//return PsiTreeUtil.getChildOfType(this, PyReferenceExpression.class); what we call can be whatever expr, not always a ref
|
||||
return (PyExpression)getFirstChild();
|
||||
@@ -49,7 +56,8 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
|
||||
@PsiCached
|
||||
public PyArgumentList getArgumentList() {
|
||||
return PsiTreeUtil.getChildOfType(this, PyArgumentList.class);
|
||||
PyArgumentList arglist = PsiTreeUtil.getChildOfType(this, PyArgumentList.class);
|
||||
return arglist;
|
||||
}
|
||||
|
||||
public void addArgument(PyExpression expression) {
|
||||
@@ -63,11 +71,72 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
}
|
||||
}
|
||||
|
||||
public PyElement resolveCallee() {
|
||||
public PyMarkedFunction resolveCallee() {
|
||||
PyExpression calleeReference = getCallee();
|
||||
if (calleeReference != null) {
|
||||
PsiReference cref = calleeReference.getReference();
|
||||
if (cref != null) {
|
||||
PyElement resolved = (PyElement) cref.resolve();
|
||||
if (resolved != null) {
|
||||
EnumSet<Flag> flags = EnumSet.noneOf(Flag.class);
|
||||
//boolean is_inst = isByInstance();
|
||||
if (isByInstance()) flags.add(Flag.IMPLICIT_FIRST_ARG);
|
||||
if (resolved instanceof PyClass) { // constructor call
|
||||
final PyClass cls = (PyClass)resolved;
|
||||
resolved = cls.findMethodByName("__init__");
|
||||
//is_inst |= true;
|
||||
flags.add(Flag.IMPLICIT_FIRST_ARG);
|
||||
}
|
||||
if (resolved != null) {
|
||||
// look for closest decorator
|
||||
// TODO: look for all decorators
|
||||
PsiElement parent = resolved.getParent();
|
||||
if (parent instanceof PyDecoratedFunction) {
|
||||
final PyDecoratedFunction decorated = (PyDecoratedFunction)parent;
|
||||
PsiElement decorator = PsiTreeUtil.getChildOfType(decorated, PyReferenceExpression.class);
|
||||
if (decorator != null) { // just in case
|
||||
String deco_name = decorator.getText();
|
||||
@NonNls final String STATICMETHOD = "staticmethod";
|
||||
@NonNls final String CLASSMETHOD = "classmethod";
|
||||
if (STATICMETHOD.equals(deco_name)) {
|
||||
flags.add(Flag.STATICMETHOD);
|
||||
flags.remove(Flag.IMPLICIT_FIRST_ARG);
|
||||
}
|
||||
else if (CLASSMETHOD.equals(deco_name)) {
|
||||
flags.add(Flag.CLASSMETHOD);
|
||||
}
|
||||
// else could be custom decorator processing
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(resolved instanceof PyFunction)) return null; // omg, bogus __init__
|
||||
return new PyMarkedFunction((PyFunction) resolved, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public PyElement resolveCallee2() {
|
||||
PyExpression calleeReference = getCallee();
|
||||
return (PyElement) calleeReference.getReference().resolve();
|
||||
}
|
||||
|
||||
protected boolean isByInstance() {
|
||||
PyExpression callee = getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PyExpression qualifier = ((PyReferenceExpression)callee).getQualifier();
|
||||
if (qualifier != null) {
|
||||
PyType type = qualifier.getType();
|
||||
if ((type instanceof PyClassType) && (!((PyClassType)type).isDefinition())) {
|
||||
// we're calling an instance method of qualifier
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PyCallExpression: " + PyResolveUtil.getReadableRepr(getCallee()); //getCalledFunctionReference().getReferencedName();
|
||||
@@ -78,8 +147,9 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PsiElement target = ((PyReferenceExpression)callee).resolve();
|
||||
if (target instanceof PyClass) {
|
||||
return new PyClassType((PyClass) target);
|
||||
return new PyClassType((PyClass) target, false); // we call a class name, that is, the constructor, we get an instance.
|
||||
}
|
||||
// TODO: look at well-known functions and their return types
|
||||
return PyReferenceExpressionImpl.getReferenceTypeFromProviders(target);
|
||||
}
|
||||
return callee.getType();
|
||||
|
||||
@@ -45,6 +45,9 @@ import java.util.List;
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implements PyClass {
|
||||
|
||||
public static final PyClass[] EMPTY_ARRAY = new PyClassImpl[0];
|
||||
|
||||
public PyClassImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
@@ -110,7 +113,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
final PyExpression[] superExpressions = getSuperClassExpressions();
|
||||
List<PsiElement> superClasses = new ArrayList<PsiElement>();
|
||||
for(PyExpression expr: superExpressions) {
|
||||
if (expr instanceof PyReferenceExpression && !PyNames.OBJECT.equals(expr.getText())) {
|
||||
if (expr instanceof PyReferenceExpression) {
|
||||
PyReferenceExpression ref = (PyReferenceExpression) expr;
|
||||
final PsiElement result = ref.resolve();
|
||||
if (result != null) {
|
||||
@@ -121,6 +124,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
return superClasses.toArray(new PsiElement[superClasses.size()]);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PyClass[] getSuperClasses() {
|
||||
PsiElement[] superClassElements = getSuperClassElements();
|
||||
if (superClassElements != null) {
|
||||
@@ -132,7 +136,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
}
|
||||
return result.toArray(new PyClass[result.size()]);
|
||||
}
|
||||
return null;
|
||||
return EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -221,8 +221,8 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
|
||||
public PyType getType() {
|
||||
if (myType == null) {
|
||||
PyClass the_class = PyBuiltinCache.getInstance(getProject()).getClass("object"); // really 'module', but it's nowhere to import from
|
||||
if (the_class != null) myType = new PyClassType(the_class);
|
||||
PyClass the_class = PyBuiltinCache.getInstance(getProject()).getClass("object"); // TODO: generate a skel for module
|
||||
if (the_class != null) myType = new PyClassType(the_class, false); // 'module' is an instance of 'object'
|
||||
}
|
||||
return myType;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Icons;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -112,10 +112,10 @@ public class PyParameterImpl extends PyPresentableElementImpl<PyParameterStub> i
|
||||
final PyParameter[] params = parameterList.getParameters();
|
||||
if (parameterList.getParent() instanceof PyFunction) {
|
||||
PyFunction func = (PyFunction) parameterList.getParent();
|
||||
if (params [0] == this) {
|
||||
if (params [0] == this) { // must be 'self'
|
||||
final PyClass containingClass = func.getContainingClass();
|
||||
if (containingClass != null) {
|
||||
return new PyClassType(containingClass);
|
||||
return new PyClassType(containingClass, false); // TODO: check for @classmethod or @staticmethod node above us
|
||||
}
|
||||
}
|
||||
for(PyTypeProvider provider: Extensions.getExtensions(PyTypeProvider.EP_NAME)) {
|
||||
|
||||
@@ -338,7 +338,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
return ((PyExpression) target).getType();
|
||||
}
|
||||
if (target instanceof PyClass) {
|
||||
return new PyClassType((PyClass) target);
|
||||
return new PyClassType((PyClass) target, true);
|
||||
}
|
||||
if (target instanceof PsiDirectory) {
|
||||
PsiFile file = ((PsiDirectory)target).findFile(ResolveImportUtil.INIT_PY);
|
||||
|
||||
@@ -6,9 +6,8 @@ import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
import com.jetbrains.python.psi.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -18,15 +17,36 @@ import java.util.*;
|
||||
public class PyClassType implements PyType {
|
||||
|
||||
protected PyClass myClass;
|
||||
|
||||
public PyClassType(final PyClass source) {
|
||||
protected boolean myIsDefinition;
|
||||
|
||||
/**
|
||||
* Describes a class-based type. Since everyting in Python is an instance of some class, this type pretty much completes
|
||||
* the type system :)
|
||||
* Note that classes' and instances' member list can change during execution, so it is important to construct an instance of PyClassType
|
||||
* right in the place of reference, so that such changes could possibly be accounted for.
|
||||
* @param source PyClass which defines this type. For builtin or external classes, skeleton files contain the definitions.
|
||||
* @param is_definition whether this type describes an instance or a definition of the class.
|
||||
*/
|
||||
public PyClassType(final @Nullable PyClass source, boolean is_definition) {
|
||||
myClass = source;
|
||||
myIsDefinition = is_definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a PyClass which defined this type.
|
||||
*/
|
||||
@NotNull
|
||||
public PyClass getPyClass() {
|
||||
return myClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether this type refers to an instance or a definition of the class.
|
||||
*/
|
||||
public boolean isDefinition() {
|
||||
return myIsDefinition;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement resolveMember(final String name) {
|
||||
if (myClass == null) return null;
|
||||
@@ -55,17 +75,24 @@ public class PyClassType implements PyType {
|
||||
public Object[] getCompletionVariants(final PyReferenceExpression referenceExpression) {
|
||||
final PyResolveUtil.VariantsProcessor processor = new PyResolveUtil.VariantsProcessor();
|
||||
myClass.processDeclarations(processor, ResolveState.initial(), null, referenceExpression);
|
||||
return processor.getResult();
|
||||
List<Object> ret = new ArrayList<Object>();
|
||||
ret.addAll(processor.getResultList());
|
||||
for (PyClass ancestor : myClass.getSuperClasses()) {
|
||||
ret.addAll(Arrays.asList((new PyClassType(ancestor, true)).getCompletionVariants(referenceExpression)));
|
||||
}
|
||||
return ret.toArray();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<String> getPossibleInstanceMembers() {
|
||||
Set<String> ret = new HashSet<String>();
|
||||
/*
|
||||
if (myClass != null) {
|
||||
PyClassType otype = PyBuiltinCache.getInstance(myClass.getProject()).getObjectType();
|
||||
ret.addAll(otype.getPossibleInstanceMembers());
|
||||
}
|
||||
// TODO: add our own ideas here, e.g. from methods other than constructor
|
||||
*/
|
||||
// TODO: add our own ideas here, e.g. from methods other than constructor
|
||||
return Collections.unmodifiableSet(ret);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import java.util.Set;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyModuleType implements PyType {
|
||||
public class PyModuleType implements PyType { // TODO: make it a PyClassType referring to builtins.___module
|
||||
private PsiFile myModule;
|
||||
|
||||
protected static Set<String> ourPossibleFields;
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyNoneType implements PyType {
|
||||
public class PyNoneType implements PyType { // TODO must extend ClassType. It's an honest instance.
|
||||
public static final PyNoneType INSTANCE = new PyNoneType();
|
||||
|
||||
private PyNoneType() {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
// TODO: eliminate this class
|
||||
|
||||
/**
|
||||
* Represents Python 'object' type.
|
||||
@@ -29,13 +28,9 @@ public class PyObjectType extends PyClassType implements PyType {
|
||||
}
|
||||
|
||||
public PyObjectType(PyClass source) {
|
||||
super(source);
|
||||
super(source, false);
|
||||
}
|
||||
|
||||
public Object[] getCompletionVariants(final PyReferenceExpression referenceExpression) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY; // TODO: implement
|
||||
}
|
||||
|
||||
/**
|
||||
* Sometimes we can make a reasonable guess that a name is an instance member while we cannot directly resolve it.
|
||||
* All such guesses are listed by this method.
|
||||
|
||||
@@ -3,9 +3,6 @@ package com.jetbrains.python.psi.types;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.PyReferenceExpression;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
|
||||
5
python/testData/resolve/callee/ClassCall.py
Normal file
5
python/testData/resolve/callee/ClassCall.py
Normal file
@@ -0,0 +1,5 @@
|
||||
class A:
|
||||
def foo(self):
|
||||
|
||||
a = A()
|
||||
A.f<ref>oo(a)
|
||||
5
python/testData/resolve/callee/InstanceCall.py
Normal file
5
python/testData/resolve/callee/InstanceCall.py
Normal file
@@ -0,0 +1,5 @@
|
||||
class A:
|
||||
def foo(self):
|
||||
|
||||
a = A()
|
||||
a.f<ref>oo()
|
||||
40
python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java
Normal file
40
python/testSrc/com/jetbrains/python/PyResolveCalleeTest.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.ResolveTestCase;
|
||||
import com.jetbrains.python.psi.PyCallExpression;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Tests callee resolution in PyCallExpressionImpl.
|
||||
* User: dcheryasov
|
||||
* Date: Aug 21, 2008
|
||||
*/
|
||||
public class PyResolveCalleeTest extends ResolveTestCase {
|
||||
|
||||
private PyCallExpression.PyMarkedFunction resolveCallee() throws Exception {
|
||||
PsiReference ref = configureByFile(getTestName(false) + ".py");
|
||||
PyCallExpression call = PsiTreeUtil.getParentOfType(ref.getElement(), PyCallExpression.class);
|
||||
return call.resolveCallee();
|
||||
}
|
||||
|
||||
public void testInstanceCall() throws Exception {
|
||||
PyCallExpression.PyMarkedFunction resolved = resolveCallee();
|
||||
assertNotNull(resolved.getFunction());
|
||||
assertTrue(resolved.getFlags().equals(EnumSet.of(PyCallExpression.Flag.IMPLICIT_FIRST_ARG)));
|
||||
}
|
||||
|
||||
public void testClassCall() throws Exception {
|
||||
PyCallExpression.PyMarkedFunction resolved = resolveCallee();
|
||||
assertNotNull(resolved.getFunction());
|
||||
assertTrue(resolved.getFlags().equals(EnumSet.noneOf(PyCallExpression.Flag.class)));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return PathManager.getHomePath() + "/plugins/python/testData/resolve/callee/";
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ public class PythonHighlightingTest extends DaemonAnalyzerTestCase {
|
||||
return PathManager.getHomePath() + "/plugins/python/testData/highlighting/";
|
||||
}
|
||||
|
||||
public void testCallArgs() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testImportInTry() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user