mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Adds multi-resolve to imported names; fixes PY-120.
This commit is contained in:
@@ -9,6 +9,7 @@ import com.intellij.lang.annotation.HighlightSeverity;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -76,7 +77,15 @@ public class PyUnresolvedReferencesInspection extends LocalInspectionTool {
|
||||
severity = ((PsiReferenceEx) reference).getUnresolvedHighlightSeverity();
|
||||
if (severity == null) continue;
|
||||
}
|
||||
if (reference.resolve() == null) {
|
||||
boolean unresolved;
|
||||
if (reference instanceof PsiPolyVariantReference) {
|
||||
final PsiPolyVariantReference poly = (PsiPolyVariantReference)reference;
|
||||
unresolved = (poly.multiResolve(false).length == 0);
|
||||
}
|
||||
else {
|
||||
unresolved = (reference.resolve() == null);
|
||||
}
|
||||
if (unresolved) {
|
||||
StringBuffer description_buf = new StringBuffer("");
|
||||
String text = reference.getElement().getText();
|
||||
String ref_text = reference.getRangeInElement().substring(text); // text of the part we're working with
|
||||
|
||||
@@ -19,13 +19,14 @@ package com.jetbrains.python.psi;
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.codeInsight.lookup.LookupElementFactory;
|
||||
import com.intellij.codeInsight.lookup.LookupItem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
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;
|
||||
|
||||
@@ -307,30 +308,47 @@ public class PyResolveUtil {
|
||||
public static class ResolveProcessor implements PyScopeProcessor {
|
||||
private String myName;
|
||||
private PsiElement myResult = null;
|
||||
private Set<String> mySeen;
|
||||
/*private Set<String> mySeen;*/
|
||||
private List<NameDefiner> myDefiners;
|
||||
|
||||
public ResolveProcessor(final String name) {
|
||||
myName = name;
|
||||
mySeen = new HashSet<String>();
|
||||
/*mySeen = new HashSet<String>();*/
|
||||
myDefiners = new ArrayList<NameDefiner>(2); // 1 is typical, 2 is sometimes, more is rare.
|
||||
}
|
||||
|
||||
public PsiElement getResult() {
|
||||
return myResult;
|
||||
}
|
||||
|
||||
|
||||
@NonNls
|
||||
static String _nvl(Object s) {
|
||||
if (s != null) return "'" + s.toString() + "'";
|
||||
else return "null"; // TODO: move to PyNames
|
||||
else return "null";
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
public Set<String> getSeen() {
|
||||
return mySeen;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* Adds a NameDefiner point which is a secondary resolution target. E.g. import statement for imported name.
|
||||
* @param definer
|
||||
*/
|
||||
protected void addNameDefiner(NameDefiner definer) {
|
||||
myDefiners.add(definer);
|
||||
}
|
||||
|
||||
public List<NameDefiner>getDefiners() {
|
||||
return myDefiners;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return _nvl(myName) + ", " + _nvl(myResult);
|
||||
}
|
||||
|
||||
|
||||
public boolean execute(PsiElement element, ResolveState substitutor) {
|
||||
if (element instanceof PyFile) {
|
||||
final VirtualFile file = ((PyFile)element).getVirtualFile();
|
||||
@@ -367,6 +385,9 @@ public class PyResolveUtil {
|
||||
PsiElement by_name = definer.getElementNamed(myName);
|
||||
if (by_name != null) {
|
||||
myResult = by_name;
|
||||
if (!PsiTreeUtil.isAncestor(element, by_name, true)) { // non-trivial definer
|
||||
addNameDefiner(definer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.intellij.psi.ResolveResult;
|
||||
|
||||
/**
|
||||
* Provides a way to sort results of multi-resolve.
|
||||
* User: dcheryasov
|
||||
* Date: Dec 5, 2008 11:06:30 AM
|
||||
*/
|
||||
public interface RatedResolveResult extends ResolveResult {
|
||||
|
||||
RatedResolveResult[] EMPTY_ARRAY = new RatedResolveResult[0];
|
||||
|
||||
/**
|
||||
* Results with higher rate are shown higher in the list of multiResolve().
|
||||
* @see com.intellij.psi.PsiPolyVariantReference#multiResolve(boolean)
|
||||
* @return desired rate. If in doubt, use 0.
|
||||
*/
|
||||
int getRate();
|
||||
|
||||
/**
|
||||
* For unusual items that need to be on top.
|
||||
*/
|
||||
int RATE_HIGH = 1000;
|
||||
|
||||
/**
|
||||
* For regular references.
|
||||
*/
|
||||
int RATE_NORMAL = 0;
|
||||
|
||||
/**
|
||||
* For additional, less important results.
|
||||
*/
|
||||
int RATE_LOW = -1000;
|
||||
}
|
||||
@@ -19,6 +19,7 @@ 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.ResolveResult;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -146,12 +147,15 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
public PyType getType() {
|
||||
PyExpression callee = getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PsiElement target = ((PyReferenceExpression)callee).resolve();
|
||||
if (target instanceof PyClass) {
|
||||
return new PyClassType((PyClass) target, false); // we call a class name, that is, the constructor, we get an instance.
|
||||
ResolveResult[] targets = ((PyReferenceExpression)callee).multiResolve(false);
|
||||
if (targets.length == 0) {
|
||||
PsiElement target = targets[0].getElement();
|
||||
if (target instanceof PyClass) {
|
||||
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);
|
||||
}
|
||||
// TODO: look at well-known functions and their return types
|
||||
return PyReferenceExpressionImpl.getReferenceTypeFromProviders(target);
|
||||
}
|
||||
return callee.getType();
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
@@ -26,6 +28,7 @@ import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -89,6 +92,63 @@ public class PyImportElementImpl extends PyElementImpl implements PyImportElemen
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemPresentation getPresentation() {
|
||||
return new ItemPresentation() {
|
||||
|
||||
@NotNull
|
||||
private String getRefName(String default_name) {
|
||||
PyReferenceExpression ref = getImportReference();
|
||||
if (ref != null) {
|
||||
String refname = ref.getName();
|
||||
if (refname != null) return refname;
|
||||
}
|
||||
return default_name;
|
||||
}
|
||||
|
||||
public String getPresentableText() {
|
||||
return getRefName("<none>");
|
||||
}
|
||||
|
||||
public String getLocationString() {
|
||||
PyElement elt = PsiTreeUtil.getParentOfType(PyImportElementImpl.this, PyImportStatement.class, PyFromImportStatement.class);
|
||||
StringBuffer buf = new StringBuffer("| ");
|
||||
if (elt != null) { // always? who knows :)
|
||||
if (elt instanceof PyFromImportStatement) { // from ... import ...
|
||||
buf.append("from ");
|
||||
PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource();
|
||||
if (imp_src != null) {
|
||||
buf.append(PyResolveUtil.toPath(imp_src, "."));
|
||||
}
|
||||
else buf.append("<?>");
|
||||
buf.append(" import ");
|
||||
}
|
||||
else { // "import ... "
|
||||
buf.append("import ");
|
||||
}
|
||||
buf.append(getRefName("<?>"));
|
||||
}
|
||||
else {
|
||||
buf.append("import?.. ");
|
||||
}
|
||||
// are we the name or the 'as'?
|
||||
PyTargetExpression as_part = getAsName();
|
||||
if (as_part != null) {
|
||||
buf.append(" as ").append(as_part.getName());
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public Icon getIcon(final boolean open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public TextAttributesKey getTextAttributesKey() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Iterable<PyElement> iterateNames() {
|
||||
PyElement ret = getAsName();
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.intellij.psi.impl.source.resolve.ResolveCache;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.containers.SortedList;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyIcons;
|
||||
import com.jetbrains.python.PyNames;
|
||||
@@ -121,16 +122,39 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
return results.length == 1 ? results[0].getElement() : null;
|
||||
}
|
||||
|
||||
|
||||
private static class ResultList extends ArrayList<RatedResolveResult> {
|
||||
// Allows to add non-null elements and discard nulls in a hassle-free way.
|
||||
public boolean poke(final PsiElement what, final int rate) {
|
||||
if (what == null) return false;
|
||||
super.add(new RatedResolveResult() {
|
||||
public int getRate() { return rate; }
|
||||
|
||||
public PsiElement getElement() { return what; }
|
||||
|
||||
public boolean isValidResult() { return true; }
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public void pokeAll(Collection<PsiElement> elts, int rate) {
|
||||
for (PsiElement elt : elts) poke(elt, rate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does actual resolution of resolve().
|
||||
* @return resolution result.
|
||||
* @see #resolve()
|
||||
*/
|
||||
private
|
||||
@Nullable
|
||||
PsiElement resolveInner() {
|
||||
@NotNull
|
||||
List<RatedResolveResult> resolveInner() {
|
||||
//List<PsiElement> ret = new ArrayList<PsiElement>();
|
||||
ResultList ret = new ResultList();
|
||||
|
||||
final String referencedName = getReferencedName();
|
||||
if (referencedName == null) return null;
|
||||
if (referencedName == null) return ret;
|
||||
|
||||
if (PsiTreeUtil.getParentOfType(this, PyImportElement.class, PyFromImportStatement.class) != null) {
|
||||
PsiElement target = ResolveImportUtil.resolveImportReference(this);
|
||||
@@ -145,9 +169,13 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
If we ever need to exactly tell a dir from __init__.py, that logic has to change.
|
||||
*/
|
||||
}
|
||||
else return null; // dir without __init__.py does not resolve
|
||||
else {
|
||||
ret.clear();
|
||||
return ret; // dir without __init__.py does not resolve
|
||||
}
|
||||
}
|
||||
return target;
|
||||
ret.poke(target, RatedResolveResult.RATE_HIGH);
|
||||
return ret;
|
||||
}
|
||||
|
||||
final PyExpression qualifier = getQualifier();
|
||||
@@ -159,42 +187,67 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
List<PyQualifiedExpression> qualifier_path = PyResolveUtil.unwindQualifiers((PyQualifiedExpression)qualifier);
|
||||
if (qualifier_path != null) {
|
||||
for (PyExpression ex : collectAssignedAttributes((PyQualifiedExpression)qualifier)) {
|
||||
if (referencedName.equals(ex.getName())) return ex;
|
||||
if (referencedName.equals(ex.getName())) {
|
||||
ret.poke(ex, RatedResolveResult.RATE_NORMAL);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// resolve within the type proper
|
||||
return qualifierType.resolveMember(referencedName);
|
||||
PsiElement ref_elt = qualifierType.resolveMember(referencedName);
|
||||
if (ref_elt != null) ret.poke(ref_elt, RatedResolveResult.RATE_NORMAL);
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// here we have an unqualified expr. it may be defined:
|
||||
// ...in current file
|
||||
PsiElement ret = PyResolveUtil.treeCrawlUp(new PyResolveUtil.ResolveProcessor(referencedName), this);
|
||||
if ((ret != null) && (ret instanceof PyClass)) {
|
||||
// is it a case of the bizarre "class Foo(Foo)" construct?
|
||||
PyClass cls = (PyClass)ret;
|
||||
for (PyExpression base_expr : cls.getSuperClassExpressions()){
|
||||
if (base_expr == this) return null; // cannot resolve us, the base class ref, to the class being defined
|
||||
PyResolveUtil.ResolveProcessor processor = new PyResolveUtil.ResolveProcessor(referencedName);
|
||||
PsiElement uexpr = PyResolveUtil.treeCrawlUp(processor, this);
|
||||
if ((uexpr != null)) {
|
||||
if ((uexpr instanceof PyClass)) {
|
||||
// is it a case of the bizarre "class Foo(Foo)" construct?
|
||||
PyClass cls = (PyClass)uexpr;
|
||||
for (PyExpression base_expr : cls.getSuperClassExpressions()){
|
||||
if (base_expr == this) {
|
||||
ret.clear();
|
||||
return ret; // cannot resolve us, the base class ref, to the class being defined
|
||||
}
|
||||
}
|
||||
}
|
||||
// sort what we got
|
||||
for (NameDefiner hit : processor.getDefiners()) {
|
||||
ret.poke(hit, getRate(hit));
|
||||
}
|
||||
}
|
||||
if (ret == null) {
|
||||
if (uexpr == null) {
|
||||
// ...as a part of current module
|
||||
PyType otype = PyBuiltinCache.getInstance(this.getProject()).getObjectType(); // "object" as a closest kin to "module"
|
||||
if (otype != null) ret = otype.resolveMember(getName());
|
||||
PyType otype = PyBuiltinCache.getInstance(getProject()).getObjectType(); // "object" as a closest kin to "module"
|
||||
if (otype != null) uexpr = otype.resolveMember(getName());
|
||||
}
|
||||
if (ret == null) {
|
||||
if (uexpr == null) {
|
||||
// ...as a builtin symbol
|
||||
PyFile bfile = PyBuiltinCache.getInstance(this.getProject()).getBuiltinsFile();
|
||||
ret = PyResolveUtil.treeCrawlUp(new PyResolveUtil.ResolveProcessor(referencedName), true, bfile);
|
||||
PyFile bfile = PyBuiltinCache.getInstance(getProject()).getBuiltinsFile();
|
||||
uexpr = PyResolveUtil.treeCrawlUp(new PyResolveUtil.ResolveProcessor(referencedName), true, bfile);
|
||||
}
|
||||
if (ret == null) {
|
||||
ret = PyResolveUtil.resolveOffContext(this);
|
||||
if (uexpr == null) {
|
||||
uexpr = PyResolveUtil.resolveOffContext(this);
|
||||
}
|
||||
ret.poke(uexpr, getRate(uexpr));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// NOTE: very crude
|
||||
private static int getRate(PsiElement elt) {
|
||||
int rate;
|
||||
if (elt instanceof PyImportElement || elt instanceof PyStarImportElement) rate = RatedResolveResult.RATE_LOW;
|
||||
else if (elt instanceof PyFile) rate = RatedResolveResult.RATE_HIGH;
|
||||
else rate = RatedResolveResult.RATE_NORMAL;
|
||||
return rate;
|
||||
}
|
||||
|
||||
private static Collection<PyExpression> collectAssignedAttributes(PyQualifiedExpression qualifier) {
|
||||
List<PyQualifiedExpression> qualifier_path = PyResolveUtil.unwindQualifiers(qualifier);
|
||||
if (qualifier_path != null) {
|
||||
@@ -213,25 +266,21 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
|
||||
// crude logic right here to see it work
|
||||
|
||||
PsiElement target = resolveInner();
|
||||
if (target == null) return ResolveResult.EMPTY_ARRAY;
|
||||
List<RatedResolveResult> targets = resolveInner();
|
||||
if (targets.size() == 0) return ResolveResult.EMPTY_ARRAY;
|
||||
|
||||
List<ResolveResult> ret = new ArrayList<ResolveResult>();
|
||||
ret.add(new PsiElementResolveResult(target));
|
||||
|
||||
if (target instanceof PsiDirectory) {
|
||||
final PsiDirectory dir = (PsiDirectory)target;
|
||||
final PsiFile file = dir.findFile(ResolveImportUtil.INIT_PY);
|
||||
if (file != null) {
|
||||
ret.add(0, new PsiElementResolveResult(file));
|
||||
List<RatedResolveResult> ret = new SortedList<RatedResolveResult>(new Comparator<RatedResolveResult>() {
|
||||
public int compare(final RatedResolveResult one, final RatedResolveResult another) {
|
||||
return another.getRate() - one.getRate();
|
||||
}
|
||||
}
|
||||
});
|
||||
ret.addAll(targets);
|
||||
|
||||
return ret.toArray(new ResolveResult[ret.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves reference to possible referred elements.
|
||||
* Reso lves reference to possible referred elements.
|
||||
* First element is always what resolve() would return.
|
||||
* Imported module names: to module file, or {directory, '___init__.py}' for a qualifier.
|
||||
* @todo Local identifiers: a list of definitions in the most recent compound statement
|
||||
@@ -402,7 +451,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
return PyNoneType.INSTANCE;
|
||||
}
|
||||
}
|
||||
PsiElement target = resolve();
|
||||
ResolveResult[] targets = multiResolve(false);
|
||||
if (targets.length == 0) return null;
|
||||
PsiElement target = targets[0].getElement();
|
||||
if (target == this) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,9 +4,13 @@ import com.jetbrains.python.psi.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.navigation.ItemPresentation;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Simplest PyStarImportElement possible.
|
||||
* User: dcheryasov
|
||||
@@ -37,4 +41,39 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
|
||||
public boolean mustResolveOutside() {
|
||||
return true; // we don't have children, but...
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemPresentation getPresentation() {
|
||||
return new ItemPresentation() {
|
||||
|
||||
private String getName() {
|
||||
PyElement elt = PsiTreeUtil.getParentOfType(PyStarImportElementImpl.this, PyFromImportStatement.class);
|
||||
if (elt instanceof PyFromImportStatement) { // always? who knows :)
|
||||
PyReferenceExpression imp_src = ((PyFromImportStatement)elt).getImportSource();
|
||||
if (imp_src != null) {
|
||||
return PyResolveUtil.toPath(imp_src, ".");
|
||||
}
|
||||
}
|
||||
return "<?>";
|
||||
}
|
||||
|
||||
public String getPresentableText() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public String getLocationString() {
|
||||
StringBuffer buf = new StringBuffer("| ");
|
||||
buf.append("from ").append(getName()).append(" import *");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public Icon getIcon(final boolean open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public TextAttributesKey getTextAttributesKey() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.jetbrains.python.validation;
|
||||
|
||||
import static com.jetbrains.python.PyBundle.message;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
|
||||
/**
|
||||
@@ -61,50 +63,53 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
}
|
||||
|
||||
private class ExprVisitor extends PyElementVisitor {
|
||||
private Operation _op;
|
||||
private Operation _op;
|
||||
private final String DELETING_NONE = message("ANN.deleting.none");
|
||||
private final String ASSIGNMENT_TO_NONE = message("ANN.assign.to.none");
|
||||
private final String CANT_ASSIGN_TO_FUNCTION_CALL = message("ANN.cant.assign.to.call");
|
||||
private final String CANT_DELETE_FUNCTION_CALL = message("ANN.cant.delete.call");
|
||||
|
||||
public ExprVisitor(Operation op) {
|
||||
public ExprVisitor(Operation op) {
|
||||
_op = op;
|
||||
}
|
||||
|
||||
@Override public void visitPyReferenceExpression(final PyReferenceExpression node) {
|
||||
String referencedName = node.getReferencedName();
|
||||
if (referencedName != null && referencedName.equals("None")) {
|
||||
if (referencedName != null && referencedName.equals(PyNames.NONE)) {
|
||||
getHolder().createErrorAnnotation(node,
|
||||
(_op == Operation.Delete) ? "deleting None" : "assignment to None");
|
||||
(_op == Operation.Delete) ? DELETING_NONE : ASSIGNMENT_TO_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitPyTargetExpression(final PyTargetExpression node) {
|
||||
String targetName = node.getName();
|
||||
if (targetName != null && targetName.equals("None")) {
|
||||
if (targetName != null && targetName.equals(PyNames.NONE)) {
|
||||
getHolder().createErrorAnnotation(node,
|
||||
(_op == Operation.Delete) ? "deleting None" : "assignment to None");
|
||||
(_op == Operation.Delete) ? DELETING_NONE : ASSIGNMENT_TO_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void visitPyCallExpression(final PyCallExpression node) {
|
||||
getHolder().createErrorAnnotation(node,
|
||||
(_op == Operation.Delete) ? "can't delete function call" : "can't assign to function call");
|
||||
(_op == Operation.Delete) ? CANT_DELETE_FUNCTION_CALL : CANT_ASSIGN_TO_FUNCTION_CALL);
|
||||
}
|
||||
|
||||
@Override public void visitPyGeneratorExpression(final PyGeneratorExpression node) {
|
||||
getHolder().createErrorAnnotation(node,
|
||||
(_op == Operation.AugAssign)
|
||||
? "augmented assign to generator expression not possible"
|
||||
: "assign to generator expression not possible");
|
||||
message(_op == Operation.AugAssign ? "ANN.cant.aug.assign.to.generator" : "ANN.cant.assign.to.generator")
|
||||
);
|
||||
}
|
||||
|
||||
@Override public void visitPyBinaryExpression(final PyBinaryExpression node) {
|
||||
getHolder().createErrorAnnotation(node, "can't assign to operator");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.operator"));
|
||||
}
|
||||
|
||||
@Override public void visitPyTupleExpression(final PyTupleExpression node) {
|
||||
if (node.getElements().length == 0) {
|
||||
getHolder().createErrorAnnotation(node, "can't assign to ()");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.parens"));
|
||||
}
|
||||
else if (_op == Operation.AugAssign) {
|
||||
getHolder().createErrorAnnotation(node, "augmented assign to tuple literal or generator expression not possible");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.aug.assign.to.tuple.or.generator"));
|
||||
}
|
||||
else {
|
||||
node.acceptChildren(this);
|
||||
@@ -113,7 +118,7 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
|
||||
@Override public void visitPyParenthesizedExpression(final PyParenthesizedExpression node) {
|
||||
if (_op == Operation.AugAssign) {
|
||||
getHolder().createErrorAnnotation(node, "augmented assign to tuple literal or generator expression not possible");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.aug.assign.to.tuple.or.generator"));
|
||||
}
|
||||
else {
|
||||
node.acceptChildren(this);
|
||||
@@ -122,10 +127,10 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
|
||||
@Override public void visitPyListLiteralExpression(final PyListLiteralExpression node) {
|
||||
if (node.getElements().length == 0) {
|
||||
getHolder().createErrorAnnotation(node, "can't assign to []");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.brackets"));
|
||||
}
|
||||
else if (_op == Operation.AugAssign) {
|
||||
getHolder().createErrorAnnotation(node, "augmented assign to list literal or comprehension not possible");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.aug.assign.to.list.or.comprh"));
|
||||
}
|
||||
else {
|
||||
node.acceptChildren(this);
|
||||
@@ -134,9 +139,8 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
|
||||
@Override public void visitPyListCompExpression(final PyListCompExpression node) {
|
||||
getHolder().createErrorAnnotation(node,
|
||||
_op == Operation.AugAssign
|
||||
? "augmented assign to list comprehension not possible"
|
||||
: "can't assign to list comprehension");
|
||||
message(_op == Operation.AugAssign ? "ANN.cant.aug.assign.to.comprh" : "ANN.cant.assign.to.comprh")
|
||||
);
|
||||
}
|
||||
|
||||
public void visitPyNumericLiteralExpression(final PyNumericLiteralExpression node) {
|
||||
@@ -148,11 +152,11 @@ public class AssignTargetAnnotator extends PyAnnotator {
|
||||
}
|
||||
|
||||
private void checkLiteral(PyExpression node) {
|
||||
getHolder().createErrorAnnotation(node, "can't assign to literal");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.literal"));
|
||||
}
|
||||
|
||||
public void visitPyLambdaExpression(final PyLambdaExpression node) {
|
||||
getHolder().createErrorAnnotation(node, "can't assign to lambda");
|
||||
getHolder().createErrorAnnotation(node, message("ANN.cant.assign.to.lambda"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.*;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -20,17 +18,41 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
|
||||
assertEquals("ImportedFile.py", ((PyFile) element).getName());
|
||||
}
|
||||
|
||||
/*
|
||||
public void testFromImport() throws Exception {
|
||||
PsiElement element = doResolve();
|
||||
assertTrue(element instanceof PyFunction);
|
||||
assertEquals("func", ((PyFunction) element).getName());
|
||||
}
|
||||
*/
|
||||
|
||||
public void testFromImport() throws Exception {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import stmt
|
||||
PsiElement func_elt = results[0].getElement();
|
||||
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
|
||||
assertEquals("named 'func'?", "func", ((PyFunction) func_elt).getName());
|
||||
PsiElement import_elt = results[1].getElement();
|
||||
assertTrue("is import?", import_elt instanceof PyImportElement);
|
||||
}
|
||||
|
||||
/*
|
||||
public void testFromImportStar() throws Exception {
|
||||
PsiElement element = doResolve();
|
||||
assertTrue(element instanceof PyFunction);
|
||||
assertEquals("func", ((PyFunction) element).getName());
|
||||
}
|
||||
*/
|
||||
|
||||
public void testFromImportStar() throws Exception {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import-* stmt
|
||||
PsiElement func_elt = results[0].getElement();
|
||||
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
|
||||
assertEquals("named 'func'?", "func", ((PyFunction) func_elt).getName());
|
||||
PsiElement import_elt = results[1].getElement();
|
||||
assertTrue("is import?", import_elt instanceof PyStarImportElement);
|
||||
}
|
||||
|
||||
protected void _checkInitPyDir(PsiElement elt, String dirname) throws Exception {
|
||||
assertTrue(elt instanceof PyFile);
|
||||
@@ -79,15 +101,36 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
|
||||
assertTrue(element instanceof PyFunction);
|
||||
}
|
||||
|
||||
/*
|
||||
public void testTransitiveImport() throws Exception {
|
||||
PsiElement element = doResolve();
|
||||
assertTrue(element instanceof PyTargetExpression);
|
||||
}
|
||||
*/
|
||||
|
||||
public void testTransitiveImport() throws Exception {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import stmt
|
||||
PsiElement elt = results[0].getElement();
|
||||
assertTrue("is target?", elt instanceof PyTargetExpression);
|
||||
}
|
||||
|
||||
/*
|
||||
public void testResolveInPkg() throws Exception {
|
||||
PsiElement element = doResolve();
|
||||
assertTrue(element instanceof PyFunction);
|
||||
}
|
||||
*/
|
||||
|
||||
public void testResolveInPkg() throws Exception {
|
||||
ResolveResult[] results = doMultiResolve();
|
||||
assertTrue(results.length == 2); // func and import stmt
|
||||
PsiElement func_elt = results[0].getElement();
|
||||
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
|
||||
assertEquals("named 'token'?", "token", ((PyFunction) func_elt).getName());
|
||||
PsiElement import_elt = results[1].getElement();
|
||||
assertTrue("is import?", import_elt instanceof PyImportElement);
|
||||
}
|
||||
|
||||
// Currently fails due to inadequate stubs
|
||||
public void testCircularImport() throws Exception {
|
||||
@@ -96,7 +139,7 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
|
||||
}
|
||||
|
||||
|
||||
private PsiElement doResolve() throws Exception {
|
||||
private PsiFile prepareFile() throws Exception {
|
||||
String testName = getTestName(true);
|
||||
String fileName = getTestName(false) + ".py";
|
||||
String root = PathManager.getHomePath() + "/plugins/python/testData/resolve/multiFile/" + testName;
|
||||
@@ -107,11 +150,23 @@ public class PyMultiFileResolveTest extends CodeInsightTestCase {
|
||||
VirtualFile sourceFile = rootDir.findChild(fileName);
|
||||
assert sourceFile != null;
|
||||
PsiFile psiFile = myPsiManager.findFile(sourceFile);
|
||||
return psiFile;
|
||||
}
|
||||
|
||||
private PsiElement doResolve() throws Exception {
|
||||
PsiFile psiFile = prepareFile();
|
||||
int offset = findMarkerOffset(psiFile);
|
||||
final PsiReference ref = psiFile.findReferenceAt(offset);
|
||||
return ref.resolve();
|
||||
}
|
||||
|
||||
private ResolveResult[] doMultiResolve() throws Exception {
|
||||
PsiFile psiFile = prepareFile();
|
||||
int offset = findMarkerOffset(psiFile);
|
||||
final PsiPolyVariantReference ref = (PsiPolyVariantReference)psiFile.findReferenceAt(offset);
|
||||
return ref.multiResolve(false);
|
||||
}
|
||||
|
||||
private int findMarkerOffset(final PsiFile psiFile) {
|
||||
Document document = PsiDocumentManager.getInstance(myProject).getDocument(psiFile);
|
||||
assert document != null;
|
||||
|
||||
Reference in New Issue
Block a user