From 5ff23220752f6cd5b238331970a3d7dd06dce7a2 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 30 Jun 2010 20:49:49 +0400 Subject: [PATCH] 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 --- .../dataflow/scope/impl/ScopeImpl.java | 3 ++- .../PyUnboundLocalVariableInspection.java | 5 +---- .../python/parsing/StatementParsing.java | 4 ++-- .../python/psi/PyGlobalStatement.java | 2 +- .../python/psi/PyTargetExpression.java | 5 +++++ .../psi/impl/PyGlobalStatementImpl.java | 7 +++++-- .../psi/impl/PyGlobalStatementNavigator.java | 2 +- .../python/psi/impl/PyReferenceImpl.java | 19 ++++++++++++++++++- .../psi/impl/PyTargetExpressionImpl.java | 6 ++++++ .../python/validation/GlobalAnnotator.java | 4 ++-- python/testData/findUsages/GlobalUsages.py | 5 +++++ python/testData/findUsages/GlobalUsages2.py | 5 +++++ python/testData/psi/Nonlocal.txt | 4 ++-- .../jetbrains/python/PyFindUsagesTest.java | 13 ++++++++++++- 14 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 python/testData/findUsages/GlobalUsages.py create mode 100644 python/testData/findUsages/GlobalUsages2.py diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java index 87be1a85790f..6c3fcbccf306 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/impl/ScopeImpl.java @@ -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()); } } diff --git a/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java b/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java index 928031df66e6..3078798e8f1f 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnboundLocalVariableInspection.java @@ -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 diff --git a/python/src/com/jetbrains/python/parsing/StatementParsing.java b/python/src/com/jetbrains/python/parsing/StatementParsing.java index 8c466b741a59..aac72088b29c 100644 --- a/python/src/com/jetbrains/python/parsing/StatementParsing.java +++ b/python/src/com/jetbrains/python/parsing/StatementParsing.java @@ -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); diff --git a/python/src/com/jetbrains/python/psi/PyGlobalStatement.java b/python/src/com/jetbrains/python/psi/PyGlobalStatement.java index 47390b8db8ad..8c04bc8f6b15 100644 --- a/python/src/com/jetbrains/python/psi/PyGlobalStatement.java +++ b/python/src/com/jetbrains/python/psi/PyGlobalStatement.java @@ -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); } diff --git a/python/src/com/jetbrains/python/psi/PyTargetExpression.java b/python/src/com/jetbrains/python/psi/PyTargetExpression.java index d48d7c74bf02..222c4d0b4d86 100644 --- a/python/src/com/jetbrains/python/psi/PyTargetExpression.java +++ b/python/src/com/jetbrains/python/psi/PyTargetExpression.java @@ -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(); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementImpl.java index f1a251c35f7e..e8cdf54e30fd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementImpl.java @@ -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, diff --git a/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementNavigator.java b/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementNavigator.java index c0e2c52282a3..30e2aa43fe21 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementNavigator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyGlobalStatementNavigator.java @@ -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; diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java index 28ffb96c9a2a..3121cd0e7483 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceImpl.java @@ -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; diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index cef8844c78b8..d9b0225a17fd 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -208,6 +208,7 @@ public class PyTargetExpressionImpl extends PyPresentableElementImplsearch_variable = 1 + +def function(): + global search_variable + search_variable = 2 \ No newline at end of file diff --git a/python/testData/findUsages/GlobalUsages2.py b/python/testData/findUsages/GlobalUsages2.py new file mode 100644 index 000000000000..6c6f4d9e5392 --- /dev/null +++ b/python/testData/findUsages/GlobalUsages2.py @@ -0,0 +1,5 @@ +search_variable = 1 + +def function(): + global search_variable + search_variable = 2 \ No newline at end of file diff --git a/python/testData/psi/Nonlocal.txt b/python/testData/psi/Nonlocal.txt index d93b3a9eb6eb..5c8c0998d496 100644 --- a/python/testData/psi/Nonlocal.txt +++ b/python/testData/psi/Nonlocal.txt @@ -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') \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java b/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java index 67cdcf73d77e..d3189914a6fd 100644 --- a/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java +++ b/python/testSrc/com/jetbrains/python/PyFindUsagesTest.java @@ -33,4 +33,15 @@ public class PyFindUsagesTest extends PyLightFixtureTestCase { final Collection usages = myFixture.testFindUsages("findUsages/QualifiedVsUnqualifiedUsages.py"); assertEquals(1, usages.size()); } -} + + public void testGlobalUsages() { // PY-1167 + final Collection usages = myFixture.testFindUsages("findUsages/GlobalUsages.py"); + assertEquals(3, usages.size()); + } + + public void testGlobalUsages2() { // PY-1167 + // same text as GlobalUsages, different caret location + final Collection usages = myFixture.testFindUsages("findUsages/GlobalUsages2.py"); + assertEquals(3, usages.size()); + } + }