mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Resolve PyReferenceImpl refs to all outer definitions in the current file
This applies to all outer definitions except imports where we resolve only to the latest imports as we used to do. This is required for detecting and optimizing unused imports and adding imports during refactorings. The implementation of ResolveProcessor has been rewritten as PyResolveProcessor.
This commit is contained in:
@@ -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<RatedResolveResult> resultList = getResultsFromProcessor(referencedName, processor, psiFile, psiFile);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -45,8 +45,8 @@ public interface Scope {
|
||||
@NotNull
|
||||
List<PyImportedNameDefiner> getImportedNameDefiners();
|
||||
|
||||
@Nullable
|
||||
PsiNamedElement getNamedElement(String name, boolean includeNestedGlobals);
|
||||
@NotNull
|
||||
Collection<PsiNamedElement> getNamedElements(String name, boolean includeNestedGlobals);
|
||||
|
||||
@NotNull
|
||||
Collection<PsiNamedElement> getNamedElements();
|
||||
|
||||
@@ -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<String> myNonlocals;
|
||||
private volatile List<Scope> myNestedScopes;
|
||||
private final ScopeOwner myFlowOwner;
|
||||
private volatile Map<String, PsiNamedElement> myNamedElements;
|
||||
private volatile Map<String, Collection<PsiNamedElement>> myNamedElements;
|
||||
private volatile List<PyImportedNameDefiner> myImportedNameDefiners; // Declarations which declare unknown set of imported names
|
||||
private volatile Set<String> myAugAssignments;
|
||||
private List<PyTargetExpression> 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<PsiNamedElement> 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<PsiNamedElement> 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<PsiNamedElement> results = Lists.newArrayList();
|
||||
for (Collection<PsiNamedElement> elements : myNamedElements.values()) {
|
||||
results.addAll(elements);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -181,7 +185,7 @@ public class ScopeImpl implements Scope {
|
||||
}
|
||||
|
||||
private void collectDeclarations() {
|
||||
final Map<String, PsiNamedElement> namedElements = new HashMap<String, PsiNamedElement>();
|
||||
final Map<String, Collection<PsiNamedElement>> namedElements = new HashMap<String, Collection<PsiNamedElement>>();
|
||||
final List<PyImportedNameDefiner> importedNameDefiners = new ArrayList<PyImportedNameDefiner>();
|
||||
final List<Scope> nestedScopes = new ArrayList<Scope>();
|
||||
final Set<String> globals = new HashSet<String>();
|
||||
@@ -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.<PsiNamedElement>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;
|
||||
|
||||
+16
-16
@@ -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<Pair<PsiElement, PsiElement>> findDefinitions(
|
||||
String name, Collection<? extends PsiReference> references, @Nullable Collection<? extends PsiElement> ignored
|
||||
) {
|
||||
public static List<Pair<PsiElement, PsiElement>> findDefinitions(@NotNull String name,
|
||||
@NotNull Collection<PsiReference> references,
|
||||
@NotNull Set<PsiElement> ignored) {
|
||||
List<Pair<PsiElement, PsiElement>> conflicts = new ArrayList<Pair<PsiElement, PsiElement>>();
|
||||
REF_LOOP:
|
||||
for (PsiReference ref : references) {
|
||||
ResolveProcessor processor = new ResolveProcessor(name);
|
||||
PyResolveUtil.treeCrawlUp(processor, ref.getElement());
|
||||
PsiElement result = processor.getResult();
|
||||
if (result != null) {
|
||||
List<PsiElement> 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;
|
||||
|
||||
+4
-7
@@ -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<PsiElement> ignored = Sets.<PsiElement>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
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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.<PsiElement>emptySet()), target_name, null)) {
|
||||
return; // got conflicts
|
||||
}
|
||||
|
||||
|
||||
@@ -64,11 +64,11 @@ public class PyDocReference extends PyReferenceImpl {
|
||||
final List<Pair<PsiElement,TextRange>> files = languageManager.getInjectedPsiFiles(host);
|
||||
if (files != null) {
|
||||
for (Pair<PsiElement, TextRange> pair : files) {
|
||||
ResolveProcessor processor = new ResolveProcessor(referencedName);
|
||||
final PyResolveProcessor processor = new PyResolveProcessor(referencedName);
|
||||
|
||||
PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)pair.getFirst(), referencedName, pair.getFirst());
|
||||
final List<RatedResolveResult> resultList = getResultsFromProcessor(referencedName, processor, pair.getFirst(),
|
||||
pair.getFirst());
|
||||
pair.getFirst());
|
||||
if (resultList.size() > 0) {
|
||||
List<RatedResolveResult> 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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MType> {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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. <code>if X: a = 1; else: a = 2</code> has two definitions of <code>a</code>.).
|
||||
* 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<ReadWriteInstruction> instructions, @NotNull PsiElement element, @NotNull String name) {
|
||||
private static ResolveResultList resolveToLatestDefs(@NotNull List<ReadWriteInstruction> 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.<PsiElement>singletonList(definer)));
|
||||
ret.add(new ImportedResolveResult(definition, getRate(definition, context), Collections.<PsiElement>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<RatedResolveResult> getResultsFromProcessor(String referencedName,
|
||||
ResolveProcessor processor,
|
||||
PsiElement realContext, PsiElement roof) {
|
||||
ResolveResultList ret = new ResolveResultList();
|
||||
PsiElement uexpr = processor.getResult();
|
||||
List<PsiElement> 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<RatedResolveResult> 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<PsiElement> resolvedElements = processor.getElements();
|
||||
final Scope resolvedScope = ControlFlowCache.getScope(resolvedOwner);
|
||||
|
||||
if (!resolvedScope.isGlobal(referencedName)) {
|
||||
if (resolvedOwner == referenceOwner) {
|
||||
final List<ReadWriteInstruction> 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<ReadWriteInstruction> 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<RatedResolveResult> 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<PsiElement, PsiElement> 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<PsiElement> 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.<PsiElement>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) {
|
||||
|
||||
@@ -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<PsiElement, PsiElement> 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> T getHint(@NotNull Key<T> hintKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleEvent(@NotNull Event event, @Nullable Object associated) {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<PsiElement, PsiElement> getResults() {
|
||||
return myResults;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<PsiElement> getElements() {
|
||||
return myResults.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Collection<PsiElement> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<NameDefiner> definers = new ArrayList<NameDefiner>(scope.getImportedNameDefiners());
|
||||
if (anchor != null && ScopeUtil.getScopeOwner(anchor) == scopeOwner) {
|
||||
final Comparator<NameDefiner> nearestDefinerComparator = new Comparator<NameDefiner>() {
|
||||
@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) {
|
||||
|
||||
+5
-2
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<PsiElement> myDefiners;
|
||||
private boolean myLocalResolve = false;
|
||||
|
||||
public ResolveProcessor(@NotNull final String name) {
|
||||
myName = name;
|
||||
myDefiners = new ArrayList<PsiElement>(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<PsiElement> 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> T getHint(@NotNull Key<T> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<Set<PyClassType>> CTX_VISITED = Key.create("PyClassType.Visited");
|
||||
|
||||
@@ -60,8 +60,10 @@ public class PyImportedModuleType implements PyType {
|
||||
final PsiFile containingFile = location != null ? location.getContainingFile() : null;
|
||||
List<PsiElement> 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<PsiElement> importedSubmodules = PyModuleType.collectImportedSubmodules((PsiFileSystemItem)resolved, location);
|
||||
if (importedSubmodules != null) {
|
||||
final Set<PsiElement> imported = Sets.newHashSet(importedSubmodules);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
from my_package.my_module import <error descr="Unresolved reference 'eggs'">eggs</error>
|
||||
<warning descr="Unused import statement">from my_package.my_module import <error descr="Unresolved reference 'eggs'">eggs</error></warning>
|
||||
from my_package.my_module import <error descr="Unresolved reference 'eggs'">eggs</error>
|
||||
|
||||
eggs()
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import p1
|
||||
<warning descr="Unused import statement">import p1</warning>
|
||||
import p1.m1
|
||||
|
||||
print(p1.m1.a1)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import p1
|
||||
import p1.m1
|
||||
from p1 import m1
|
||||
from p1.m1 import foo
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
def f():
|
||||
global foo
|
||||
return foo
|
||||
# <ref>
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user