diff --git a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java index 6188dcc88f3e..94b60ac6c50c 100644 --- a/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java +++ b/python/ipnb/src/org/jetbrains/plugins/ipnb/psi/IpnbPyReference.java @@ -69,7 +69,7 @@ public class IpnbPyReference extends PyReferenceImpl { final Editor editor = ((IpnbCodePanel)editablePanel).getEditor(); final IpnbPyFragment psiFile = (IpnbPyFragment)PsiDocumentManager.getInstance(myElement.getProject()).getPsiFile(editor.getDocument()); if (psiFile == null) continue; - ResolveProcessor processor = new ResolveProcessor(referencedName); + final PyResolveProcessor processor = new PyResolveProcessor(referencedName); PyResolveUtil.scopeCrawlUp(processor, psiFile, referencedName, psiFile); final List resultList = getResultsFromProcessor(referencedName, processor, psiFile, psiFile); diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index fe3c86e35ff6..143936d53912 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -223,16 +223,25 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { @Override public void visitPyFromImportStatement(PyFromImportStatement node) { visitPyImportStatementBase(node); + final PyStarImportElement starImportElement = node.getStarImportElement(); + if (starImportElement != null) { + starImportElement.accept(this); + } + } + + @Override + public void visitPyStarImportElement(PyStarImportElement node) { + final ReadWriteInstruction instruction = ReadWriteInstruction.importStar(myBuilder, node); + myBuilder.addNode(instruction); + myBuilder.checkPending(instruction); } private void visitPyImportStatementBase(PyImportStatementBase node) { myBuilder.startNode(node); for (PyImportElement importElement : node.getImportElements()) { final ReadWriteInstruction instruction = ReadWriteInstruction.write(myBuilder, importElement, importElement.getVisibleName()); - if (instruction != null) { - myBuilder.addNode(instruction); - myBuilder.checkPending(instruction); - } + myBuilder.addNode(instruction); + myBuilder.checkPending(instruction); } } diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java index e7c2d05a2bea..2f3e7e3dcf9f 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ReadWriteInstruction.java @@ -38,7 +38,8 @@ public class ReadWriteInstruction extends InstructionImpl { READ(true, false, false), WRITE(false, true, false), ASSERTTYPE(false, false, true), - READWRITE(true, true, false); + READWRITE(true, true, false), + IMPORT_STAR(false, false, false); private final boolean isWrite; private final boolean isRead; @@ -119,6 +120,10 @@ public class ReadWriteInstruction extends InstructionImpl { return new ReadWriteInstruction(builder, element, name, ACCESS.ASSERTTYPE, getType); } + public static ReadWriteInstruction importStar(ControlFlowBuilder builder, PsiElement element) { + return new ReadWriteInstruction(builder, element, null, ACCESS.IMPORT_STAR); + } + @Nullable public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) { return myGetType.getType(context, anchor); diff --git a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java index 2467f1235098..207688fb53cd 100644 --- a/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java +++ b/python/src/com/jetbrains/python/codeInsight/dataflow/scope/Scope.java @@ -45,8 +45,8 @@ public interface Scope { @NotNull List getImportedNameDefiners(); - @Nullable - PsiNamedElement getNamedElement(String name, boolean includeNestedGlobals); + @NotNull + Collection getNamedElements(String name, boolean includeNestedGlobals); @NotNull Collection getNamedElements(); 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 92b99eb8c502..732d45bf6d71 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 @@ -15,6 +15,8 @@ */ package com.jetbrains.python.codeInsight.dataflow.scope.impl; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; import com.intellij.codeInsight.controlflow.Instruction; import com.intellij.codeInsight.dataflow.DFALimitExceededException; import com.intellij.codeInsight.dataflow.map.DFAMap; @@ -30,7 +32,6 @@ import com.jetbrains.python.codeInsight.dataflow.scope.ScopeVariable; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.*; @@ -44,7 +45,7 @@ public class ScopeImpl implements Scope { private volatile Set myNonlocals; private volatile List myNestedScopes; private final ScopeOwner myFlowOwner; - private volatile Map myNamedElements; + private volatile Map> myNamedElements; private volatile List myImportedNameDefiners; // Declarations which declare unknown set of imported names private volatile Set myAugAssignments; private List myTargetExpressions; @@ -118,7 +119,7 @@ public class ScopeImpl implements Scope { if (isNonlocal(name)) { return false; } - if (getNamedElement(name, true) != null) { + if (!getNamedElements(name, true).isEmpty()) { return true; } if (isAugAssignment(name)) { @@ -141,25 +142,24 @@ public class ScopeImpl implements Scope { return myImportedNameDefiners; } - @Nullable + @NotNull @Override - public PsiNamedElement getNamedElement(String name, boolean includeNestedGlobals) { + public Collection getNamedElements(String name, boolean includeNestedGlobals) { if (myNamedElements == null) { collectDeclarations(); } - final PsiNamedElement element = myNamedElements.get(name); - if (element != null) { - return element; + if (myNamedElements.containsKey(name)) { + return myNamedElements.get(name); } if (includeNestedGlobals && isGlobal(name)) { for (Scope scope : myNestedScopes) { - final PsiNamedElement global = scope.getNamedElement(name, true); - if (global != null) { - return global; + final Collection globals = scope.getNamedElements(name, true); + if (!globals.isEmpty()) { + return globals; } } } - return null; + return Collections.emptyList(); } @NotNull @@ -168,7 +168,11 @@ public class ScopeImpl implements Scope { if (myNamedElements == null) { collectDeclarations(); } - return myNamedElements.values(); + final List results = Lists.newArrayList(); + for (Collection elements : myNamedElements.values()) { + results.addAll(elements); + } + return results; } @NotNull @@ -181,7 +185,7 @@ public class ScopeImpl implements Scope { } private void collectDeclarations() { - final Map namedElements = new HashMap(); + final Map> namedElements = new HashMap>(); final List importedNameDefiners = new ArrayList(); final List nestedScopes = new ArrayList(); final Set globals = new HashSet(); @@ -211,7 +215,6 @@ public class ScopeImpl implements Scope { for (PyTargetExpression expression : node.getGlobals()) { final String name = expression.getReferencedName(); globals.add(name); - namedElements.put(name, expression); } super.visitPyGlobalStatement(node); } @@ -238,7 +241,11 @@ public class ScopeImpl implements Scope { @Override public void visitPyElement(PyElement node) { if (node instanceof PsiNamedElement && !(node instanceof PyKeywordArgument)) { - namedElements.put(node.getName(), (PsiNamedElement)node); + final String name = node.getName(); + if (!namedElements.containsKey(name)) { + namedElements.put(name, Sets.newLinkedHashSet()); + } + namedElements.get(name).add((PsiNamedElement)node); } if (node instanceof PyImportedNameDefiner) { importedNameDefiners.add((PyImportedNameDefiner)node); @@ -253,8 +260,6 @@ public class ScopeImpl implements Scope { } }); - Collections.reverse(importedNameDefiners); - myNamedElements = namedElements; myImportedNameDefiners = importedNameDefiners; myNestedScopes = nestedScopes; diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/DeclarationConflictChecker.java b/python/src/com/jetbrains/python/codeInsight/intentions/DeclarationConflictChecker.java index cac318dc1a51..4b75dd59f1cd 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/DeclarationConflictChecker.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/DeclarationConflictChecker.java @@ -24,14 +24,17 @@ import com.intellij.usages.UsageTarget; import com.intellij.usages.UsageViewManager; import com.intellij.usages.UsageViewPresentation; import com.jetbrains.python.PyBundle; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; +import com.jetbrains.python.psi.resolve.PyResolveProcessor; import com.jetbrains.python.psi.resolve.PyResolveUtil; -import com.jetbrains.python.psi.resolve.ResolveProcessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Set; /** * An utility class that checks local definitions of a given name and can show a conflicts panel. @@ -50,24 +53,21 @@ public class DeclarationConflictChecker { * @return a list of pairs (referring element, element that defines name). */ @NotNull - public static List> findDefinitions( - String name, Collection references, @Nullable Collection ignored - ) { + public static List> findDefinitions(@NotNull String name, + @NotNull Collection references, + @NotNull Set ignored) { List> conflicts = new ArrayList>(); - REF_LOOP: for (PsiReference ref : references) { - ResolveProcessor processor = new ResolveProcessor(name); - PyResolveUtil.treeCrawlUp(processor, ref.getElement()); - PsiElement result = processor.getResult(); - if (result != null) { - List definers = processor.getDefiners(); - if (definers != null && definers.size() > 0) { - result = definers.get(0); // in this case, processor's result is one hop of resolution too far from what we want. + final PsiElement refElement = ref.getElement(); + final ScopeOwner owner = ScopeUtil.getScopeOwner(refElement); + final PyResolveProcessor processor = new PyResolveProcessor(name, true); + if (owner != null) { + PyResolveUtil.scopeCrawlUp(processor, owner, name, null); + for (PsiElement element : processor.getElements()) { + if (!ignored.contains(element)) { + conflicts.add(Pair.create(refElement, element)); + } } - if (ignored != null) for (PsiElement ignorable : ignored) { - if (result == ignorable) continue REF_LOOP; - } - conflicts.add(Pair.create(ref.getElement(), result)); } } return conflicts; diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java index d0eda3752e99..a65b67fa0c61 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportFromToImportIntention.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.codeInsight.intentions; +import com.google.common.collect.Sets; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.lang.ASTNode; import com.intellij.openapi.editor.Document; @@ -242,13 +243,9 @@ public class ImportFromToImportIntention implements IntentionAction { possible_targets.addAll(references.keySet()); possible_targets.addAll(star_references); } - if ( - showConflicts( - project, - findDefinitions(top_name, possible_targets, Arrays.asList(info.myFromImportStatement.getImportElements())), - top_name, info.myModuleName - ) - ) { + final Set ignored = Sets.newHashSet(Arrays.asList(info.myFromImportStatement.getImportElements())); + if (top_name != null && showConflicts(project, findDefinitions(top_name, possible_targets, ignored), + top_name, info.myModuleName)) { return; // got conflicts } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java index d3736c23e58f..e3bbba818f31 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToggleAliasIntention.java @@ -43,6 +43,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import static com.jetbrains.python.codeInsight.intentions.DeclarationConflictChecker.findDefinitions; @@ -209,7 +210,7 @@ public class ImportToggleAliasIntention implements IntentionAction { } }); // no references here is OK by us. - if (showConflicts(project, findDefinitions(target_name, references, null), target_name, null)) { + if (showConflicts(project, findDefinitions(target_name, references, Collections.emptySet()), target_name, null)) { return; // got conflicts } diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java index 2a2b669422f0..dcbdc7f3988e 100644 --- a/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java +++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocReference.java @@ -64,11 +64,11 @@ public class PyDocReference extends PyReferenceImpl { final List> files = languageManager.getInjectedPsiFiles(host); if (files != null) { for (Pair pair : files) { - ResolveProcessor processor = new ResolveProcessor(referencedName); + final PyResolveProcessor processor = new PyResolveProcessor(referencedName); PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)pair.getFirst(), referencedName, pair.getFirst()); final List resultList = getResultsFromProcessor(referencedName, processor, pair.getFirst(), - pair.getFirst()); + pair.getFirst()); if (resultList.size() > 0) { List ret = RatedResolveResult.sorted(resultList); return ret.toArray(new RatedResolveResult[ret.size()]); @@ -77,7 +77,7 @@ public class PyDocReference extends PyReferenceImpl { } } - ResolveProcessor processor = new ResolveProcessor(referencedName); + final PyResolveProcessor processor = new PyResolveProcessor(referencedName); if (file instanceof ScopeOwner) PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)file, referencedName, file); diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java index 494096480f89..795aab4b5a23 100644 --- a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java @@ -28,8 +28,8 @@ import com.jetbrains.python.codeInsight.dataflow.scope.Scope; import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.resolve.PyResolveProcessor; import com.jetbrains.python.psi.resolve.PyResolveUtil; -import com.jetbrains.python.psi.resolve.ResolveProcessor; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -98,20 +98,22 @@ public class PyShadowingNamesInspection extends PyInspection { if (owner != null) { final ScopeOwner nextOwner = ScopeUtil.getScopeOwner(owner); if (nextOwner != null) { - final ResolveProcessor processor = new ResolveProcessor(name); - PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null, null); - final PsiElement resolved = processor.getResult(); - if (resolved != null) { - final PyComprehensionElement comprehension = PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class); - if (comprehension != null && PyUtil.isOwnScopeComprehension(comprehension)) { + final PyResolveProcessor processor = new PyResolveProcessor(name); + PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null); + for (PsiElement resolved : processor.getElements()) { + if (resolved != null) { + final PyComprehensionElement comprehension = PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class); + if (comprehension != null && PyUtil.isOwnScopeComprehension(comprehension)) { + return; + } + final Scope scope = ControlFlowCache.getScope(owner); + if (scope.isGlobal(name) || scope.isNonlocal(name)) { + return; + } + registerProblem(problemElement, String.format("Shadows name '%s' from outer scope", name), + ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix()); return; } - final Scope scope = ControlFlowCache.getScope(owner); - if (scope.isGlobal(name) || scope.isNonlocal(name)) { - return; - } - registerProblem(problemElement, String.format("Shadows name '%s' from outer scope", name), - ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix()); } } } diff --git a/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java b/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java index badcdfe0a83b..88c4e65cbf74 100644 --- a/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java +++ b/python/src/com/jetbrains/python/psi/impl/PropertyBunch.java @@ -15,12 +15,13 @@ */ package com.jetbrains.python.psi.impl; +import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.ArrayUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.resolve.PyResolveProcessor; import com.jetbrains.python.psi.resolve.PyResolveUtil; -import com.jetbrains.python.psi.resolve.ResolveProcessor; import com.jetbrains.python.toolbox.Maybe; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -102,10 +103,13 @@ public abstract class PropertyBunch { protected static boolean resolvesLocally(@NotNull PyReferenceExpression ref) { final String name = ref.getName(); if (name != null) { - final ResolveProcessor processor = new ResolveProcessor(name); - processor.setLocalResolve(); + final PyResolveProcessor processor = new PyResolveProcessor(name, true); PyResolveUtil.scopeCrawlUp(processor, ref, name, null); - return processor.getResult() != null; + for (PsiElement element : processor.getElements()) { + if (element != null) { + return true; + } + } } return false; } diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index bfb5e81e5262..b27ad43ea6d3 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -48,10 +48,7 @@ import com.jetbrains.python.refactoring.PyDefUseUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.ListIterator; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; /** @@ -105,7 +102,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference * (e.g. if X: a = 1; else: a = 2 has two definitions of a.). * todo Identifiers not found locally: similar definitions in imported files and builtins. * - * @see com.intellij.psi.PsiPolyVariantReference#multiResolve(boolean) + * @see PsiPolyVariantReference#multiResolve(boolean) */ @Override @NotNull @@ -162,7 +159,10 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } @NotNull - private static ResolveResultList resolveToLatestDefs(@NotNull List instructions, @NotNull PsiElement element, @NotNull String name) { + private static ResolveResultList resolveToLatestDefs(@NotNull List instructions, + @NotNull PsiElement element, + @NotNull String name, + @NotNull TypeEvalContext context) { final ResolveResultList ret = new ResolveResultList(); for (ReadWriteInstruction instruction : instructions) { PsiElement definition = instruction.getElement(); @@ -175,10 +175,10 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } if (definer != null) { if (definer instanceof PyImportElement || definer instanceof PyStarImportElement || definer instanceof PyImportedModule) { - ret.add(new ImportedResolveResult(definition, getRate(definition), Collections.singletonList(definer))); + ret.add(new ImportedResolveResult(definition, getRate(definition, context), Collections.singletonList(definer))); } else { - ret.poke(definition, getRate(definition)); + ret.poke(definition, getRate(definition, context)); } // TODO this kind of resolve contract is quite stupid if (definition != null) { @@ -186,7 +186,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference } } else { - ret.poke(definition, getRate(definition)); + ret.poke(definition, getRate(definition, context)); } } final ResolveResultList results = new ResolveResultList(); @@ -195,7 +195,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference if (e == element) { continue; } - if (element instanceof PyTargetExpression && PyPsiUtils.isBefore(element, e)) { + if (element instanceof PyTargetExpression && e != null && PyPsiUtils.isBefore(element, e)) { continue; } else { @@ -237,14 +237,14 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference if (myElement instanceof PyTargetExpression) { if (PsiTreeUtil.getParentOfType(myElement, PyComprehensionElement.class) != null) { - ret.poke(myElement, getRate(myElement)); + ret.poke(myElement, getRate(myElement, myContext.getTypeEvalContext())); return ret; } } // here we have an unqualified expr. it may be defined: // ...in current file - ResolveProcessor processor = new ResolveProcessor(referencedName); + final PyResolveProcessor processor = new PyResolveProcessor(referencedName); // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment final PsiElement realContext = PyPsiUtils.getRealContext(myElement); @@ -254,84 +254,103 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return getResultsFromProcessor(referencedName, processor, realContext, roof); } - protected List getResultsFromProcessor(String referencedName, - ResolveProcessor processor, - PsiElement realContext, PsiElement roof) { - ResolveResultList ret = new ResolveResultList(); - PsiElement uexpr = processor.getResult(); - List definers = processor.getDefiners(); - if (uexpr != null) { - if (definers.isEmpty()) { - final ScopeOwner originalOwner = ScopeUtil.getScopeOwner(realContext); - final ScopeOwner owner = ScopeUtil.getScopeOwner(uexpr); - if (owner != null) { - final Scope scope = ControlFlowCache.getScope(owner); - if (uexpr == originalOwner && originalOwner instanceof PyClass) { - uexpr = null; + protected List getResultsFromProcessor(@NotNull String referencedName, + @NotNull PyResolveProcessor processor, + @Nullable PsiElement realContext, + @Nullable PsiElement resolveRoof) { + boolean unreachableLocalDeclaration = false; + boolean resolveInParentScope = false; + final ResolveResultList resultList = new ResolveResultList(); + final ScopeOwner referenceOwner = ScopeUtil.getScopeOwner(realContext); + final TypeEvalContext typeEvalContext = myContext.getTypeEvalContext(); + ScopeOwner resolvedOwner = processor.getOwner(); + + if (resolvedOwner != null && !processor.isImplicitName() && !processor.getResults().isEmpty()) { + final Collection resolvedElements = processor.getElements(); + final Scope resolvedScope = ControlFlowCache.getScope(resolvedOwner); + + if (!resolvedScope.isGlobal(referencedName)) { + if (resolvedOwner == referenceOwner) { + final List instructions = PyDefUseUtil.getLatestDefs(resolvedOwner, referencedName, realContext, false); + // TODO: Use the results from the processor as a cache for resolving to latest defs + final ResolveResultList latestDefs = resolveToLatestDefs(instructions, realContext, referencedName, typeEvalContext); + if (!latestDefs.isEmpty()) { + return latestDefs; } - else if (owner == originalOwner && !scope.isGlobal(referencedName)) { - final List instructions = PyDefUseUtil.getLatestDefs(owner, referencedName, realContext, false); - final ResolveResultList latest = resolveToLatestDefs(instructions, realContext, referencedName); - if (!latest.isEmpty()) { - return latest; - } - if (owner instanceof PyClass || (instructions.isEmpty() && isInOwnScopeComprehension(uexpr))) { - final ScopeOwner parentOwner = ScopeUtil.getScopeOwner(owner); - if (parentOwner != null) { - processor = new ResolveProcessor(referencedName); - PyResolveUtil.scopeCrawlUp(processor, parentOwner, referencedName, roof); - uexpr = processor.getResult(); - definers = processor.getDefiners(); - } - } - else { - uexpr = null; - } + else if (resolvedOwner instanceof PyClass || instructions.isEmpty() && allInOwnScopeComprehensions(resolvedElements)) { + resolveInParentScope = true; } - else if (owner != originalOwner && originalOwner != null && !scope.isGlobal(referencedName)) { - final Scope originalScope = ControlFlowCache.getScope(originalOwner); - if (originalScope.containsDeclaration(referencedName)) { - uexpr = null; - } + else { + unreachableLocalDeclaration = true; } } - } - // sort what we got - for (PsiElement hit : definers) { - ret.poke(hit, getRate(hit)); - } - final PsiElement packageInit = PyUtil.turnDirIntoInit(uexpr); - if (packageInit != null) { - uexpr = packageInit; // an import statement may have returned a dir - } - } - else if (!definers.isEmpty()) { - ret.add(new ImportedResolveResult(null, RatedResolveResult.RATE_LOW, definers)); - } - PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(realContext); - if (uexpr == null) { - // ...as a part of current module - String name = myElement.getName(); - if (PyModuleType.MODULE_MEMBERS.contains(name)) { - PyType objectType = builtinCache.getObjectType(); // "object" as a closest kin to "module" - if (objectType != null && name != null) { - ret.addAll(objectType.resolveMember(name, null, AccessDirection.READ, myContext)); - } - } - } - if (uexpr != null) { - ret.add(new ImportedResolveResult(uexpr, getRate(uexpr), definers)); - } - else { - for (PyReferenceResolveProvider provider : Extensions.getExtensions(PyReferenceResolveProvider.EP_NAME)) { - final List results = provider.resolveName(myElement, definers); - for (RatedResolveResult res : results) { - ret.add(res); + else if (referenceOwner != null) { + final Scope referenceScope = ControlFlowCache.getScope(referenceOwner); + if (referenceScope.containsDeclaration(referencedName)) { + unreachableLocalDeclaration = true; + } } } } - return ret; + // TODO: Try resolve to latest defs for outer scopes starting from the last element in CFG (=> no need for a special rate for globals) + + if (!unreachableLocalDeclaration) { + if (resolveInParentScope) { + processor = new PyResolveProcessor(referencedName); + resolvedOwner = ScopeUtil.getScopeOwner(resolvedOwner); + if (resolvedOwner != null) { + PyResolveUtil.scopeCrawlUp(processor, resolvedOwner, referencedName, resolveRoof); + } + } + + for (Map.Entry entry : processor.getResults().entrySet()) { + final PsiElement resolved = entry.getKey(); + final PsiElement definer = entry.getValue(); + if (resolved != null) { + if (typeEvalContext.maySwitchToAST(resolved) && isInnerComprehension(realContext, resolved)) { + continue; + } + if (resolved == referenceOwner && referenceOwner instanceof PyClass) { + continue; + } + if (definer == null) { + resultList.poke(resolved, getRate(resolved, typeEvalContext)); + } + else { + resultList.poke(definer, getRate(definer, typeEvalContext)); + resultList.add(new ImportedResolveResult(resolved, getRate(resolved, typeEvalContext), Collections.singletonList(definer))); + } + } + else if (definer != null) { + resultList.add(new ImportedResolveResult(null, RatedResolveResult.RATE_LOW, Collections.singletonList(definer))); + } + } + + if (!resultList.isEmpty()) { + return resultList; + } + } + + return resolveByReferenceResolveProviders(); + } + + private static boolean allInOwnScopeComprehensions(@NotNull Collection elements) { + for (PsiElement element : elements) { + if (!isInOwnScopeComprehension(element)) { + return false; + } + } + return true; + } + + @NotNull + private ResolveResultList resolveByReferenceResolveProviders() { + final ResolveResultList results = new ResolveResultList(); + for (PyReferenceResolveProvider provider : Extensions.getExtensions(PyReferenceResolveProvider.EP_NAME)) { + results.addAll(provider.resolveName(myElement, Collections.emptyList())); + } + return results; } private PsiElement findResolveRoof(String referencedName, PsiElement realContext) { @@ -370,11 +389,18 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference return realContext.getContainingFile(); } - // NOTE: very crude - - public static int getRate(PsiElement elt) { + public static int getRate(PsiElement elt, @NotNull TypeEvalContext context) { int rate; - if (elt instanceof PyImportedNameDefiner || elt instanceof PyReferenceExpression) { + if (elt instanceof PyTargetExpression && context.maySwitchToAST(elt)) { + final PsiElement parent = elt.getParent(); + if (parent instanceof PyGlobalStatement || parent instanceof PyNonlocalStatement) { + rate = RatedResolveResult.RATE_LOW; + } + else { + rate = RatedResolveResult.RATE_NORMAL; + } + } + else if (elt instanceof PyImportedNameDefiner || elt instanceof PyReferenceExpression) { rate = RatedResolveResult.RATE_LOW; } else if (elt instanceof PyFile) { diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java new file mode 100644 index 000000000000..2656a74dff3d --- /dev/null +++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveProcessor.java @@ -0,0 +1,173 @@ +/* + * Copyright 2000-2016 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.psi.resolve; + +import com.google.common.collect.Maps; +import com.intellij.openapi.util.Key; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiNamedElement; +import com.intellij.psi.ResolveState; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.QualifiedName; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; +import com.jetbrains.python.psi.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; + +/** + * @author vlan + */ +public class PyResolveProcessor implements PsiScopeProcessor { + @NotNull private final String myName; + private final boolean myLocalResolve; + @NotNull private final Map myResults = Maps.newLinkedHashMap(); + @Nullable private ScopeOwner myOwner; + private boolean myImplicitName = false; + + public PyResolveProcessor(@NotNull String name) { + this(name, false); + } + + public PyResolveProcessor(@NotNull String name, boolean localResolve) { + myName = name; + myLocalResolve = localResolve; + } + + @Override + public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { + final PsiNamedElement namedElement = PyUtil.as(element, PsiNamedElement.class); + if (namedElement != null && myName.equals(namedElement.getName())) { + return tryAddResult(element, null); + } + final NameDefiner nameDefiner = PyUtil.as(element, NameDefiner.class); + if (nameDefiner != null) { + final PsiElement resolved = resolveInNameDefiner(nameDefiner); + if (resolved != null) { + return tryAddResult(resolved, nameDefiner); + } + final PyImportElement importElement = PyUtil.as(element, PyImportElement.class); + if (importElement != null) { + if (addResultIfImportMakesNameVisibleInInit(importElement)) { + myImplicitName = true; + } + else { + final String importName = importElement.getVisibleName(); + if (myName.equals(importName)) { + return tryAddResult(null, importElement); + } + } + } + } + return myOwner == null || myOwner == ScopeUtil.getScopeOwner(element); + } + + @Nullable + @Override + public T getHint(@NotNull Key hintKey) { + return null; + } + + @Override + public void handleEvent(@NotNull Event event, @Nullable Object associated) { + } + + @NotNull + public Map getResults() { + return myResults; + } + + @NotNull + public Collection getElements() { + return myResults.keySet(); + } + + @NotNull + public Collection getDefiners() { + return myResults.values(); + } + + @Nullable + public ScopeOwner getOwner() { + return myOwner; + } + + public boolean isImplicitName() { + return myImplicitName; + } + + @Nullable + private PsiElement resolveInNameDefiner(@NotNull NameDefiner definer) { + if (myLocalResolve) { + final PyImportElement importElement = PyUtil.as(definer, PyImportElement.class); + if (importElement != null) { + return importElement.getElementNamed(myName, false); + } + else if (definer instanceof PyStarImportElement) { + return null; + } + } + return definer.getElementNamed(myName); + } + + private boolean tryAddResult(@Nullable PsiElement element, @Nullable PsiElement definer) { + final ScopeOwner owner = ScopeUtil.getScopeOwner(definer != null ? definer : element); + if (myOwner == null) { + myOwner = owner; + } + final boolean sameScope = owner == myOwner; + if (sameScope) { + myResults.put(element, definer != null ? definer : null); + } + return sameScope; + } + + /** + * @see http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible + */ + private boolean addResultIfImportMakesNameVisibleInInit(@NotNull PyImportElement element) { + final QualifiedName importedQName = element.getImportedQName(); + final PsiFile file = element.getContainingFile(); + if (file != null && PyUtil.isPackage(file)) { + if (importedQName != null && importedQName.getComponentCount() > 1 && myName.equals(importedQName.getLastComponent())) { + final QualifiedName packageQName = importedQName.removeLastComponent(); + final PsiElement resolvedImport = PyUtil.turnDirIntoInit(ResolveImportUtil.resolveImportElement(element, packageQName)); + if (resolvedImport == file) { + tryAddResult(element.resolve(), element); + myImplicitName = true; + return true; + } + } + final PyFromImportStatement fromImport = PyUtil.as(element.getContainingImportStatement(), PyFromImportStatement.class); + if (fromImport != null) { + final QualifiedName importSourceQName = fromImport.getImportSourceQName(); + if (importSourceQName != null && importSourceQName.endsWith(myName)) { + final PsiElement resolvedImportSource = PyUtil.turnInitIntoDir(fromImport.resolveImportSource()); + if (resolvedImportSource != null && resolvedImportSource.getParent() == file.getContainingDirectory()) { + tryAddResult(resolvedImportSource, fromImport); + myImplicitName = true; + return true; + } + } + } + } + return false; + } +} diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java index 5c6f93811661..b199e0644c15 100644 --- a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java @@ -38,11 +38,6 @@ import com.jetbrains.python.psi.impl.PyPsiUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - /** * Ref resolution routines. * User: dcheryasov @@ -117,71 +112,39 @@ public class PyResolveUtil { owner = outerScopeOwner; } } - scopeCrawlUp(processor, owner, originalOwner, name, roof, realContext); + scopeCrawlUp(processor, owner, originalOwner, name, roof); } public static void scopeCrawlUp(@NotNull PsiScopeProcessor processor, @NotNull ScopeOwner scopeOwner, @Nullable String name, @Nullable PsiElement roof) { - scopeCrawlUp(processor, scopeOwner, scopeOwner, name, roof, null); + scopeCrawlUp(processor, scopeOwner, scopeOwner, name, roof); } public static void scopeCrawlUp(@NotNull PsiScopeProcessor processor, @Nullable ScopeOwner scopeOwner, - @Nullable ScopeOwner originalScopeOwner, @Nullable String name, @Nullable PsiElement roof, - @Nullable final PsiElement anchor) { + @Nullable ScopeOwner originalScopeOwner, @Nullable String name, @Nullable PsiElement roof) { while (scopeOwner != null) { if (!(scopeOwner instanceof PyClass) || scopeOwner == originalScopeOwner) { final Scope scope = ControlFlowCache.getScope(scopeOwner); - boolean found = false; if (name != null) { final boolean includeNestedGlobals = scopeOwner instanceof PyFile; - final PsiElement resolved = scope.getNamedElement(name, includeNestedGlobals); - if (resolved != null) { + for (PsiNamedElement resolved : scope.getNamedElements(name, includeNestedGlobals)) { if (!processor.execute(resolved, ResolveState.initial())) { - found = true; + return; } } } else { for (PsiNamedElement element : scope.getNamedElements()) { if (!processor.execute(element, ResolveState.initial())) { - found = true; - break; + return; } } } - List definers = new ArrayList(scope.getImportedNameDefiners()); - if (anchor != null && ScopeUtil.getScopeOwner(anchor) == scopeOwner) { - final Comparator nearestDefinerComparator = new Comparator() { - @Override - public int compare(NameDefiner a, NameDefiner b) { - final boolean aIsBefore = PyPsiUtils.isBefore(a, anchor); - final boolean bIsBefore = PyPsiUtils.isBefore(b, anchor); - final int diff = a.getTextOffset() - b.getTextOffset(); - if (aIsBefore && bIsBefore) { - return -diff; - } - else if (aIsBefore) { - return -1; - } - else if (bIsBefore) { - return 1; - } - else { - return diff; - } - } - }; - Collections.sort(definers, nearestDefinerComparator); - } - for (NameDefiner definer : definers) { + for (NameDefiner definer : scope.getImportedNameDefiners()) { if (!processor.execute(definer, ResolveState.initial())) { - found = true; - break; + return; } } - if (found) { - return; - } } if (scopeOwner == roof) { return; @@ -195,14 +158,12 @@ public class PyResolveUtil { * * @param processor a visitor that says when the crawl is done and collects info. * @param elt element from which we start (not checked by processor); if null, the search immediately returns null. - * @return first element that the processor accepted. * * @deprecated Use {@link #scopeCrawlUp} instead. */ @Deprecated - @Nullable - public static PsiElement treeCrawlUp(PsiScopeProcessor processor, PsiElement elt) { - if (elt == null || !elt.isValid()) return null; // can't find anyway. + public static void treeCrawlUp(PsiScopeProcessor processor, PsiElement elt) { + if (elt == null || !elt.isValid()) return; PsiElement seeker = elt; PsiElement cap = PyUtil.getConcealingParent(elt); PyFunction capFunction = cap != null ? PsiTreeUtil.getParentOfType(cap, PyFunction.class, false) : null; @@ -244,27 +205,18 @@ public class PyResolveUtil { // check what we got if (seeker != null) { if (!processor.execute(seeker, ResolveState.initial())) { - if (processor instanceof ResolveProcessor) { - return ((ResolveProcessor)processor).getResult(); - } - else { - return seeker; - } // can't point to exact element, but somewhere here + return; } } } while (seeker != null); - if (processor instanceof ResolveProcessor) { - return ((ResolveProcessor)processor).getResult(); - } - return null; } /** * @param innerFunction a method, presumably inside the class * @param outer an element presumably in the class context. * @return true if an outer element is in a class context, while the inner is a method or function inside it. - * @see com.jetbrains.python.psi.PyUtil#getConcealingParent(com.intellij.psi.PsiElement) + * @see PyUtil#getConcealingParent(PsiElement) */ private static boolean refersFromMethodToClass(final PyFunction innerFunction, final PsiElement outer) { if (innerFunction == null) { diff --git a/python/src/com/jetbrains/python/psi/resolve/PythonBuiltinReferenceResolveProvider.java b/python/src/com/jetbrains/python/psi/resolve/PythonBuiltinReferenceResolveProvider.java index 47f3dd3c0383..3701566a20e8 100644 --- a/python/src/com/jetbrains/python/psi/resolve/PythonBuiltinReferenceResolveProvider.java +++ b/python/src/com/jetbrains/python/psi/resolve/PythonBuiltinReferenceResolveProvider.java @@ -22,6 +22,7 @@ import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.psi.impl.references.PyReferenceImpl; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; @@ -46,8 +47,10 @@ public class PythonBuiltinReferenceResolveProvider implements PyReferenceResolve if (resultElement == null && "__builtins__".equals(referencedName)) { resultElement = bfile; // resolve __builtins__ reference } - if (resultElement != null) - result.add(new ImportedResolveResult(resultElement, PyReferenceImpl.getRate(resultElement), definers)); + if (resultElement != null) { + final TypeEvalContext typeEvalContext = TypeEvalContext.codeInsightFallback(element.getProject()); + result.add(new ImportedResolveResult(resultElement, PyReferenceImpl.getRate(resultElement, typeEvalContext), definers)); + } } return result; } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveProcessor.java b/python/src/com/jetbrains/python/psi/resolve/ResolveProcessor.java deleted file mode 100644 index 4f00bfa399f1..000000000000 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveProcessor.java +++ /dev/null @@ -1,194 +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.psi.resolve; - -import com.intellij.openapi.util.Key; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiNamedElement; -import com.intellij.psi.ResolveState; -import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.psi.util.PsiTreeUtil; -import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.impl.PyImportedModule; -import com.intellij.psi.util.QualifiedName; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -import static com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil.getScopeOwner; - -public class ResolveProcessor implements PsiScopeProcessor { - @NotNull private final String myName; - private PsiElement myResult = null; - private final List myDefiners; - private boolean myLocalResolve = false; - - public ResolveProcessor(@NotNull final String name) { - myName = name; - myDefiners = new ArrayList(2); // 1 is typical, 2 is sometimes, more is rare. - } - - public PsiElement getResult() { - return myResult; - } - - /** - * Adds a NameDefiner point which is a secondary resolution target. E.g. import statement for imported name. - * - * @param definer - */ - protected void addNameDefiner(PsiElement definer) { - myDefiners.add(definer); - } - - public List getDefiners() { - return myDefiners; - } - - public void setLocalResolve() { - myLocalResolve = true; - } - - public String toString() { - return PyUtil.nvl(myName) + ", " + PyUtil.nvl(myResult); - } - - @Override - public boolean execute(@NotNull PsiElement element, @NotNull ResolveState substitutor) { - if (element instanceof PyFile) { - final VirtualFile file = ((PyFile)element).getVirtualFile(); - if (file != null) { - if (myName.equals(file.getNameWithoutExtension())) { - return setResult(element, null); - } - else if (PyNames.INIT_DOT_PY.equals(file.getName())) { - VirtualFile dir = file.getParent(); - if ((dir != null) && myName.equals(dir.getName())) { - return setResult(element, null); - } - } - } - } - if (element instanceof PsiNamedElement) { - if (myName.equals(((PsiNamedElement)element).getName())) { - return setResult(element, null); - } - } - if (element instanceof PyReferenceExpression) { - PyReferenceExpression expr = (PyReferenceExpression)element; - String referencedName = expr.getReferencedName(); - if (referencedName != null && referencedName.equals(myName)) { - return setResult(element, null); - } - } - if (element instanceof NameDefiner) { - final NameDefiner definer = (NameDefiner)element; - PsiElement byName = resolveFromNameDefiner(definer); - if (byName != null) { - // prefer more specific imported modules to less specific ones - if (byName instanceof PyImportedModule && myResult instanceof PyImportedModule && - ((PyImportedModule)byName).isAncestorOf((PyImportedModule)myResult)) { - return false; - } - - setResult(byName, definer); - if (!PsiTreeUtil.isAncestor(element, byName, true)) { - addNameDefiner(definer); - } - // we can have same module imported directly and as part of chain (import os; import os.path) - // direct imports always take precedence over imported modules - // also, if some name is defined both in 'try' and 'except' parts of the same try/except statement, - // we prefer the declaration in the 'try' part - if (!(myResult instanceof PyImportedModule) && PsiTreeUtil.getParentOfType(element, PyExceptPart.class) == null) { - return false; - } - } - else if (element instanceof PyImportElement) { - final PyImportElement importElement = (PyImportElement) element; - final QualifiedName qName = importElement.getImportedQName(); - // http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible - if (qName != null && qName.getComponentCount() > 1 && myName.equals(qName.getLastComponent()) && - PyNames.INIT_DOT_PY.equals(importElement.getContainingFile().getName())) { - final PsiElement packageElement = ResolveImportUtil.resolveImportElement(importElement, qName.removeLastComponent()); - if (PyUtil.turnDirIntoInit(packageElement) == importElement.getContainingFile()) { - myResult = PyUtil.turnDirIntoInit(importElement.resolve()); - addNameDefiner(importElement); - } - } - - // name is resolved to unresolved import (PY-956) - String definedName = importElement.getAsName(); - if (definedName == null) { - if (qName != null && qName.getComponentCount() == 1) { - definedName = qName.getComponents().get(0); - } - } - if (myName.equals(definedName)) { - addNameDefiner(importElement); - } - element = importElement.getContainingImportStatement(); - } - } - if (element instanceof PyFromImportStatement && PyNames.INIT_DOT_PY.equals(element.getContainingFile().getName())) { - final PyFromImportStatement fromImportStatement = (PyFromImportStatement)element; - final QualifiedName qName = fromImportStatement.getImportSourceQName(); - if (qName != null && qName.endsWith(myName)) { - final PsiElement source = PyUtil.turnInitIntoDir(fromImportStatement.resolveImportSource()); - if (source != null && source.getParent() == element.getContainingFile().getContainingDirectory()) { - myResult = source; - addNameDefiner(fromImportStatement); - } - } - } - - return true; - } - - @Nullable - private PsiElement resolveFromNameDefiner(NameDefiner definer) { - if (myLocalResolve) { - if (definer instanceof PyImportElement) { - return ((PyImportElement) definer).getElementNamed(myName, false); - } - else if (definer instanceof PyStarImportElement) { - return null; - } - } - return definer.getElementNamed(myName); - } - - @Override - @Nullable - public T getHint(@NotNull Key hintKey) { - return null; - } - - @Override - public void handleEvent(@NotNull Event event, Object associated) { - } - - private boolean setResult(PsiElement result, @Nullable PsiElement definer) { - if (myResult == null || getScopeOwner(myResult) == getScopeOwner(result) || - (definer != null && getScopeOwner(myResult) == getScopeOwner(definer))) { - myResult = result; - } - return false; - } -} diff --git a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java index 69c5391700b6..8e57776277bb 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassTypeImpl.java @@ -15,6 +15,7 @@ */ package com.jetbrains.python.psi.types; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.intellij.codeInsight.completion.CompletionUtil; @@ -445,14 +446,14 @@ public class PyClassTypeImpl extends UserDataHolderBase implements PyClassType { boolean isDefinition, @NotNull String name, @Nullable PyExpression location) { - final ResolveProcessor processor = new ResolveProcessor(name); + final PyResolveProcessor processor = new PyResolveProcessor(name); if (!isDefinition) { if (!cls.processInstanceLevelDeclarations(processor, location)) { - return processor.getResult(); + return Iterables.getFirst(processor.getElements(), null); } } cls.processClassLevelDeclarations(processor); - return processor.getResult(); + return Iterables.getFirst(processor.getElements(), null); } private static Key> CTX_VISITED = Key.create("PyClassType.Visited"); diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java index 0aaa054c8b6c..74e693f65fb4 100644 --- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java @@ -60,8 +60,10 @@ public class PyImportedModuleType implements PyType { final PsiFile containingFile = location != null ? location.getContainingFile() : null; List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, containingFile, false, true)); final PyImportElement importElement = myImportedModule.getImportElement(); + final PyFile resolvedFile = PyUtil.as(resolved, PyFile.class); if (location != null && importElement != null && PyUtil.inSameFile(location, importElement) && - ResolveImportUtil.getPointInImport(location) == PointInImport.NONE && resolved instanceof PsiFileSystemItem) { + ResolveImportUtil.getPointInImport(location) == PointInImport.NONE && resolved instanceof PsiFileSystemItem && + (resolvedFile == null || !PyUtil.isPackage(resolvedFile) || resolvedFile.getElementNamed(name) == null)) { final List importedSubmodules = PyModuleType.collectImportedSubmodules((PsiFileSystemItem)resolved, location); if (importedSubmodules != null) { final Set imported = Sets.newHashSet(importedSubmodules); diff --git a/python/src/com/jetbrains/python/refactoring/PyDefUseUtil.java b/python/src/com/jetbrains/python/refactoring/PyDefUseUtil.java index a19d53ee22f0..579a4bb423fe 100644 --- a/python/src/com/jetbrains/python/refactoring/PyDefUseUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyDefUseUtil.java @@ -80,6 +80,13 @@ public class PyDefUseUtil { return ControlFlowUtil.Operation.CONTINUE; } } + else if (access == ReadWriteInstruction.ACCESS.IMPORT_STAR) { + final PyStarImportElement starImport = PyUtil.as(element, PyStarImportElement.class); + if (starImport != null && starImport.getElementNamed(varName) != null) { + result.add(rwInstruction); + return ControlFlowUtil.Operation.CONTINUE; + } + } } return ControlFlowUtil.Operation.NEXT; } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UsedUnresolvedNameImportedSeveralTimes/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UsedUnresolvedNameImportedSeveralTimes/a.py index 767f9bf68131..820a0f0ad6f3 100644 --- a/python/testData/inspections/PyUnresolvedReferencesInspection/UsedUnresolvedNameImportedSeveralTimes/a.py +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UsedUnresolvedNameImportedSeveralTimes/a.py @@ -1,4 +1,4 @@ -from my_package.my_module import eggs +from my_package.my_module import eggs from my_package.my_module import eggs eggs() diff --git a/python/testData/inspections/unusedImport/unusedPackageAndSubmodule/test1.py b/python/testData/inspections/unusedImport/unusedPackageAndSubmodule/test1.py index c4ea15c0cde4..d5ac11d21f26 100644 --- a/python/testData/inspections/unusedImport/unusedPackageAndSubmodule/test1.py +++ b/python/testData/inspections/unusedImport/unusedPackageAndSubmodule/test1.py @@ -1,4 +1,4 @@ -import p1 +import p1 import p1.m1 print(p1.m1.a1) diff --git a/python/testData/optimizeImports/alphabeticalOrder.after.py b/python/testData/optimizeImports/alphabeticalOrder.after.py index 23f45f5cfcf3..edf016f2f333 100644 --- a/python/testData/optimizeImports/alphabeticalOrder.after.py +++ b/python/testData/optimizeImports/alphabeticalOrder.after.py @@ -12,7 +12,6 @@ import z from from a import C1 from alphabet import * -from alphabet import A from alphabet import B, A from alphabet import C from alphabet import D diff --git a/python/testData/refactoring/move/module/after/src/a.py b/python/testData/refactoring/move/module/after/src/a.py index 84c829adb049..9e7a4b733d13 100644 --- a/python/testData/refactoring/move/module/after/src/a.py +++ b/python/testData/refactoring/move/module/after/src/a.py @@ -1,5 +1,4 @@ import p1 -import p1.m1 from p1 import m1 from p1.m1 import foo diff --git a/python/testData/resolve/GlobalNotDefinedAtTopLevel.py b/python/testData/resolve/GlobalNotDefinedAtTopLevel.py new file mode 100644 index 000000000000..542137c1053d --- /dev/null +++ b/python/testData/resolve/GlobalNotDefinedAtTopLevel.py @@ -0,0 +1,4 @@ +def f(): + global foo + return foo +# \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index 64d1d5c7672f..00e9fc9b1c71 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -650,4 +650,8 @@ public class PyResolveTest extends PyResolveTestCase { assertTrue(target instanceof PyStringLiteralExpression); assertEquals("kwg", ((PyStringLiteralExpression)target).getStringValue()); } + + public void testGlobalNotDefinedAtTopLevel() { + assertResolvesTo(PyTargetExpression.class, "foo"); + } }