improve interaction of Find Usages and global statements (PY-1167); changed PSI so that names declared in global statement are now PyTargetExpression rather than PyReferenceExpression instances

This commit is contained in:
Dmitry Jemerov
2010-06-30 22:02:15 +04:00
parent 88a19e9bf5
commit 5ff2322075
14 changed files with 67 additions and 17 deletions
@@ -14,6 +14,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable;
import com.jetbrains.python.psi.PyGlobalStatement;
import com.jetbrains.python.psi.PyRecursiveElementVisitor;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.jetbrains.python.psi.PyTargetExpression;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -98,7 +99,7 @@ public class ScopeImpl implements Scope {
owner.accept(new PyRecursiveElementVisitor(){
@Override
public void visitPyGlobalStatement(final PyGlobalStatement node) {
for (PyReferenceExpression expression : node.getGlobals()) {
for (PyTargetExpression expression : node.getGlobals()) {
names.add(expression.getReferencedName());
}
}
@@ -1,7 +1,5 @@
package com.jetbrains.python.inspections;
import com.intellij.codeHighlighting.HighlightDisplayLevel;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
@@ -17,7 +15,6 @@ import com.jetbrains.python.console.PydevConsoleRunner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.*;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
/**
@@ -43,7 +40,7 @@ public class PyUnboundLocalVariableInspection extends PyInspection {
return;
}
// Ignore global statements arguments
if (PyGlobalStatementNavigator.getPyGlobalStatementByArgument(node) != null){
if (PyGlobalStatementNavigator.getByArgument(node) != null){
return;
}
// Ignore qualifier inspections
@@ -500,10 +500,10 @@ public class StatementParsing extends Parsing implements ITokenTypeRemapper {
private void parseNameDefiningStatement(boolean inSuite, final PyElementType elementType) {
final PsiBuilder.Marker globalStatement = myBuilder.mark();
myBuilder.advanceLexer();
parseIdentifier(PyElementTypes.REFERENCE_EXPRESSION);
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
while (myBuilder.getTokenType() == PyTokenTypes.COMMA) {
myBuilder.advanceLexer();
parseIdentifier(PyElementTypes.REFERENCE_EXPRESSION);
parseIdentifier(PyElementTypes.TARGET_EXPRESSION);
}
checkEndOfStatement(inSuite);
globalStatement.done(elementType);
@@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull;
* @author yole
*/
public interface PyGlobalStatement extends PyStatement, NameDefiner {
@NotNull PyReferenceExpression[] getGlobals();
@NotNull PyTargetExpression[] getGlobals();
void addGlobal(String name);
}
@@ -2,9 +2,11 @@ package com.jetbrains.python.psi;
import com.intellij.psi.PsiNameIdentifierOwner;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.StubBasedPsiElement;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.stubs.PyTargetExpressionStub;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -24,4 +26,7 @@ public interface PyTargetExpression extends PyQualifiedExpression, PsiNamedEleme
@Nullable
PyQualifiedName getAssignedQName();
@NotNull
PsiReference getReference();
}
@@ -4,6 +4,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.TokenSet;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
@@ -14,6 +15,8 @@ import java.util.Arrays;
* @author yole
*/
public class PyGlobalStatementImpl extends PyElementImpl implements PyGlobalStatement {
private static final TokenSet TARGET_EXPRESSION_SET = TokenSet.create(PyElementTypes.TARGET_EXPRESSION);
public PyGlobalStatementImpl(ASTNode astNode) {
super(astNode);
}
@@ -24,8 +27,8 @@ public class PyGlobalStatementImpl extends PyElementImpl implements PyGlobalStat
}
@NotNull
public PyReferenceExpression[] getGlobals() {
return childrenToPsi(PyElementTypes.REFERENCE_EXPRESSION_SET, PyReferenceExpression.EMPTY_ARRAY);
public PyTargetExpression[] getGlobals() {
return childrenToPsi(TARGET_EXPRESSION_SET, PyTargetExpression.EMPTY_ARRAY);
}
public boolean processDeclarations(@NotNull PsiScopeProcessor processor,
@@ -13,7 +13,7 @@ public class PyGlobalStatementNavigator {
}
@Nullable
public static PyGlobalStatement getPyGlobalStatementByArgument(final PsiElement element){
public static PyGlobalStatement getByArgument(final PsiElement element){
final PsiElement parent = element.getParent();
if (parent instanceof PyGlobalStatement){
final PyGlobalStatement statement = (PyGlobalStatement)parent;
@@ -283,7 +283,24 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
return true;
}
}
return resolve() == element; // TODO: handle multi-resolve
final PsiElement resolveResult = resolve();
if (resolveResult == element) {
return true;
}
// TODO support nonlocal statement
final PyGlobalStatement ourGlobal = PyGlobalStatementNavigator.getByArgument(resolveResult);
final PyGlobalStatement theirGlobal = PyGlobalStatementNavigator.getByArgument(element);
if (ourGlobal != null || theirGlobal != null) {
PsiElement ourContainer = PsiTreeUtil.getParentOfType(getElement(), PsiNamedElement.class);
PsiElement theirContainer = PsiTreeUtil.getParentOfType(element, PsiNamedElement.class);
if (ourGlobal != null && ourContainer != null && PsiTreeUtil.isAncestor(theirContainer, ourContainer, false)) {
return true;
}
if (theirGlobal != null && theirContainer != null && PsiTreeUtil.isAncestor(ourContainer, theirContainer, false)) {
return true;
}
}
return false; // TODO: handle multi-resolve
}
}
return false;
@@ -208,6 +208,7 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
return value instanceof PyReferenceExpression ? ((PyReferenceExpression) value).asQualifiedName() : null;
}
@NotNull
@Override
public PsiReference getReference() {
if (getQualifier() != null) {
@@ -219,6 +220,11 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
@NotNull
@Override
public SearchScope getUseScope() {
final PyGlobalStatement globalStatement = PyGlobalStatementNavigator.getByArgument(this);
if (globalStatement != null) {
return super.getUseScope();
}
// find highest level function containing our var
PyElement container = this;
while(true) {
@@ -34,13 +34,13 @@ public class GlobalAnnotator extends PyAnnotator {
// check globals
final AnnotationHolder holder = getHolder();
for (PyReferenceExpression expr : node.getGlobals()) {
for (PyTargetExpression expr : node.getGlobals()) {
final String expr_name = expr.getReferencedName();
if (paramNames.contains(expr_name)) {
holder.createErrorAnnotation(expr.getTextRange(), PyBundle.message("ANN.$0.both.global.and.param", expr_name));
}
PsiElement resolvedElement = expr.getReference().resolve();
if (resolvedElement != null && PsiTreeUtil.isAncestor(function, resolvedElement, true)) {
if (resolvedElement != null && resolvedElement != expr && PsiTreeUtil.isAncestor(function, resolvedElement, true)) {
getHolder().createWarningAnnotation(expr.getTextRange(),PyBundle.message("ANN.$0.both.global.and.param", expr_name));
}
}
@@ -0,0 +1,5 @@
<caret>search_variable = 1
def function():
global search_variable
search_variable = 2
@@ -0,0 +1,5 @@
search_variable = 1
def function():
global search_variable
<caret>search_variable = 2
+2 -2
View File
@@ -2,9 +2,9 @@ PyFile:Nonlocal.py
PyNonlocalStatement
PsiElement(Py:NONLOCAL_KEYWORD)('nonlocal')
PsiWhiteSpace(' ')
PyReferenceExpression: a
PyTargetExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyReferenceExpression: b
PyTargetExpression: b
PsiElement(Py:IDENTIFIER)('b')
@@ -33,4 +33,15 @@ public class PyFindUsagesTest extends PyLightFixtureTestCase {
final Collection<UsageInfo> usages = myFixture.testFindUsages("findUsages/QualifiedVsUnqualifiedUsages.py");
assertEquals(1, usages.size());
}
}
public void testGlobalUsages() { // PY-1167
final Collection<UsageInfo> usages = myFixture.testFindUsages("findUsages/GlobalUsages.py");
assertEquals(3, usages.size());
}
public void testGlobalUsages2() { // PY-1167
// same text as GlobalUsages, different caret location
final Collection<UsageInfo> usages = myFixture.testFindUsages("findUsages/GlobalUsages2.py");
assertEquals(3, usages.size());
}
}