mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
subpackage names are visible in the __init__.py of a package even if something was imported from the subpackages, and the subpackages themselves weren't imported (related to PY-3201)
This commit is contained in:
+1
-2
@@ -9,7 +9,6 @@ import com.intellij.usages.UsageTarget;
|
||||
import com.intellij.usages.UsageViewManager;
|
||||
import com.intellij.usages.UsageViewPresentation;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.NameDefiner;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.resolve.ResolveProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -46,7 +45,7 @@ public class DeclarationConflictChecker {
|
||||
PyResolveUtil.treeCrawlUp(processor, ref.getElement());
|
||||
PsiElement result = processor.getResult();
|
||||
if (result != null) {
|
||||
List<NameDefiner> definers = processor.getDefiners();
|
||||
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.
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
}
|
||||
|
||||
public static class Visitor extends PyInspectionVisitor {
|
||||
private Set<NameDefiner> myUsedImports = Collections.synchronizedSet(new HashSet<NameDefiner>());
|
||||
private Set<PsiElement> myUsedImports = Collections.synchronizedSet(new HashSet<PsiElement>());
|
||||
private Set<NameDefiner> myAllImports = Collections.synchronizedSet(new HashSet<NameDefiner>());
|
||||
private final ImmutableSet<String> myIgnoredIdentifiers;
|
||||
|
||||
|
||||
@@ -610,6 +610,14 @@ public class PyUtil {
|
||||
} // don't touch non-dirs
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement turnInitIntoDir(PsiElement target) {
|
||||
if (target instanceof PyFile && PyNames.INIT_DOT_PY.equals(((PyFile) target).getName())) {
|
||||
return ((PyFile) target).getContainingDirectory();
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts initial underscores of an identifier.
|
||||
*
|
||||
|
||||
@@ -15,8 +15,6 @@ import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import com.jetbrains.python.*;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
|
||||
@@ -32,7 +30,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.*;
|
||||
|
||||
public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
@@ -248,12 +245,12 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findNameInFromImportStatementStub(String name, PyFromImportStatementStub child) {
|
||||
if (child.isStarImport()) {
|
||||
private PsiElement findNameInFromImportStatementStub(String name, PyFromImportStatementStub stub) {
|
||||
if (stub.isStarImport()) {
|
||||
if (PyUtil.isClassPrivateName(name)) {
|
||||
return null;
|
||||
}
|
||||
final PyFromImportStatement statement = child.getPsi();
|
||||
final PyFromImportStatement statement = stub.getPsi();
|
||||
PsiElement starImportSource = ResolveImportUtil.resolveFromImportStatementSource(statement);
|
||||
if (starImportSource != null) {
|
||||
starImportSource = PyUtil.turnDirIntoInit(starImportSource);
|
||||
@@ -266,7 +263,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
}
|
||||
}
|
||||
else {
|
||||
final List<StubElement> importElements = child.getChildrenStubs();
|
||||
final List<StubElement> importElements = stub.getChildrenStubs();
|
||||
for (StubElement importElement : importElements) {
|
||||
final PsiElement psi = importElement.getPsi();
|
||||
if (psi instanceof PyImportElement && name.equals(((PyImportElement)psi).getVisibleName())) {
|
||||
@@ -277,6 +274,16 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
}
|
||||
}
|
||||
}
|
||||
// http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible
|
||||
if (PyNames.INIT_DOT_PY.equals(getName())) {
|
||||
final PyQualifiedName qName = stub.getImportSourceQName();
|
||||
if (qName.endsWith(name)) {
|
||||
final PsiElement element = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveFromImportStatementSource(stub.getPsi()));
|
||||
if (element != null && element.getParent() == getContainingDirectory()) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -302,10 +309,11 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
}
|
||||
return new PyImportedModule(this, PyQualifiedName.fromComponents(name));
|
||||
}
|
||||
if (name.equals(((PyImportElement)psi).getVisibleName())) {
|
||||
final PsiElement resolved = importElement.getElementNamed(name);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
// http://stackoverflow.com/questions/6048786/from-module-import-in-init-py-makes-module-name-visible
|
||||
if (qName.getComponentCount() > 1 && name.equals(qName.getLastComponent()) && PyNames.INIT_DOT_PY.equals(getName())) {
|
||||
final PsiElement element = ResolveImportUtil.resolveImportElement(importElement, qName.removeLastComponent());
|
||||
if (PyUtil.turnDirIntoInit(element) == this) {
|
||||
return importElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference
|
||||
PsiElement uexpr = PyResolveUtil.treeCrawlUp(processor, false, realContext, roof);
|
||||
if ((uexpr != null)) {
|
||||
// sort what we got
|
||||
for (NameDefiner hit : processor.getDefiners()) {
|
||||
for (PsiElement hit : processor.getDefiners()) {
|
||||
ret.poke(hit, getRate(hit));
|
||||
}
|
||||
uexpr = PyUtil.turnDirIntoInit(uexpr); // an import statement may have returned a dir
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.jetbrains.python.psi.resolve;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.NameDefiner;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -9,14 +8,14 @@ import java.util.List;
|
||||
* @author yole
|
||||
*/
|
||||
public class ImportedResolveResult extends RatedResolveResult {
|
||||
private final List<NameDefiner> myNameDefiners;
|
||||
private final List<PsiElement> myNameDefiners;
|
||||
|
||||
public ImportedResolveResult(PsiElement element, int rate, List<NameDefiner> nameDefiners) {
|
||||
public ImportedResolveResult(PsiElement element, int rate, List<PsiElement> nameDefiners) {
|
||||
super(rate, element);
|
||||
myNameDefiners = nameDefiners;
|
||||
}
|
||||
|
||||
public List<NameDefiner> getNameDefiners() {
|
||||
public List<PsiElement> getNameDefiners() {
|
||||
return myNameDefiners;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,7 +91,9 @@ public class PyResolveUtil {
|
||||
PyElementTypes.ASSIGNMENT_STATEMENT,
|
||||
PyElementTypes.PARAMETER_LIST,
|
||||
PyElementTypes.EXCEPT_PART,
|
||||
PyElementTypes.FOR_STATEMENT);
|
||||
PyElementTypes.FOR_STATEMENT,
|
||||
PyElementTypes.FROM_IMPORT_STATEMENT); // this is not exactly a NameDefiner but needed anyway
|
||||
// in mypackage/__init__.py, 'from mypackage.foo import bar' makes 'foo' name visible
|
||||
|
||||
/**
|
||||
* Crawls up the PSI tree, checking nodes as if crawling backwards through source lexemes.
|
||||
|
||||
@@ -190,7 +190,7 @@ public class ResolveImportUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<PsiElement> resolveFromImportStatementSource(PyFromImportStatement from_import_statement, PyQualifiedName qName) {
|
||||
public static List<PsiElement> resolveFromImportStatementSource(PyFromImportStatement from_import_statement, PyQualifiedName qName) {
|
||||
boolean absolute_import_enabled = isAbsoluteImportEnabledFor(from_import_statement);
|
||||
PsiFile file = from_import_statement.getContainingFile();
|
||||
return resolveModule(qName, file, absolute_import_enabled, from_import_statement.getRelativeLevel());
|
||||
|
||||
@@ -20,11 +20,11 @@ import java.util.List;
|
||||
public class ResolveProcessor implements PsiScopeProcessor {
|
||||
@NotNull private final String myName;
|
||||
private PsiElement myResult = null;
|
||||
private final List<NameDefiner> myDefiners;
|
||||
private final List<PsiElement> myDefiners;
|
||||
|
||||
public ResolveProcessor(@NotNull final String name) {
|
||||
myName = name;
|
||||
myDefiners = new ArrayList<NameDefiner>(2); // 1 is typical, 2 is sometimes, more is rare.
|
||||
myDefiners = new ArrayList<PsiElement>(2); // 1 is typical, 2 is sometimes, more is rare.
|
||||
}
|
||||
|
||||
public PsiElement getResult() {
|
||||
@@ -36,11 +36,11 @@ public class ResolveProcessor implements PsiScopeProcessor {
|
||||
*
|
||||
* @param definer
|
||||
*/
|
||||
protected void addNameDefiner(NameDefiner definer) {
|
||||
protected void addNameDefiner(PsiElement definer) {
|
||||
myDefiners.add(definer);
|
||||
}
|
||||
|
||||
public List<NameDefiner> getDefiners() {
|
||||
public List<PsiElement> getDefiners() {
|
||||
return myDefiners;
|
||||
}
|
||||
|
||||
@@ -98,11 +98,21 @@ public class ResolveProcessor implements PsiScopeProcessor {
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyImportElement) {
|
||||
// name is resolved to unresolved import (PY-956)
|
||||
final PyImportElement importElement = (PyImportElement) element;
|
||||
final PyQualifiedName 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(ResolveImportUtil.resolveImportElement(importElement));
|
||||
addNameDefiner(importElement);
|
||||
}
|
||||
}
|
||||
|
||||
// name is resolved to unresolved import (PY-956)
|
||||
String definedName = importElement.getAsName();
|
||||
if (definedName == null) {
|
||||
final PyQualifiedName qName = importElement.getImportedQName();
|
||||
if (qName != null && qName.getComponentCount() == 1) {
|
||||
definedName = qName.getComponents().get(0);
|
||||
}
|
||||
@@ -112,6 +122,17 @@ public class ResolveProcessor implements PsiScopeProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyFromImportStatement && PyNames.INIT_DOT_PY.equals(element.getContainingFile().getName())) {
|
||||
final PyFromImportStatement fromImportStatement = (PyFromImportStatement)element;
|
||||
final PyQualifiedName qName = fromImportStatement.getImportSourceQName();
|
||||
if (qName != null && qName.endsWith(myName)) {
|
||||
final PsiElement source = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveFromImportStatementSource(fromImportStatement));
|
||||
if (source != null && source.getParent() == element.getContainingFile().getContainingDirectory()) {
|
||||
myResult = source;
|
||||
addNameDefiner(fromImportStatement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import mygame
|
||||
|
||||
mygame.display.foo()
|
||||
# <ref>
|
||||
@@ -0,0 +1,5 @@
|
||||
from display import foo
|
||||
|
||||
print('calling display.foo()')
|
||||
display.foo()
|
||||
# <ref>
|
||||
@@ -0,0 +1 @@
|
||||
def foo(): print 'foo'
|
||||
@@ -0,0 +1,4 @@
|
||||
import mygame
|
||||
|
||||
mygame.display.foo()
|
||||
# <ref>
|
||||
@@ -0,0 +1,5 @@
|
||||
import mygame.display
|
||||
|
||||
display.foo()
|
||||
# <ref>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
def foo(): pass
|
||||
@@ -13,6 +13,7 @@ import com.jetbrains.python.psi.*;
|
||||
* @author yole
|
||||
*/
|
||||
public class PyMultiFileResolveTest extends PyResolveTestCase {
|
||||
protected String myTestFileName;
|
||||
|
||||
private static void checkInitPyDir(PsiElement elt, String dirname) {
|
||||
assertTrue(elt instanceof PyFile);
|
||||
@@ -223,6 +224,27 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
|
||||
assertResolvesTo(PyFunction.class, "foo", "/src/mygame/display.py");
|
||||
}
|
||||
|
||||
public void testImportPackageIntoSelfInit() {
|
||||
myTestFileName = "mygame/__init__.py";
|
||||
try {
|
||||
assertResolvesTo(PyFile.class, "display.py");
|
||||
}
|
||||
finally {
|
||||
myTestFileName = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void testFromImportPackageIntoSelf() {
|
||||
myTestFileName = "mygame/__init__.py";
|
||||
try {
|
||||
assertResolvesTo(PyFile.class, "display.py");
|
||||
}
|
||||
finally {
|
||||
myTestFileName = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testImportPrivateNameWithStar() { // PY-2717
|
||||
PsiElement psiElement = doResolve();
|
||||
assertNull(psiElement);
|
||||
@@ -230,7 +252,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
|
||||
|
||||
private PsiFile prepareFile() {
|
||||
String testName = getTestName(true);
|
||||
String fileName = getTestName(false) + ".py";
|
||||
String fileName = myTestFileName != null ? myTestFileName : getTestName(false) + ".py";
|
||||
myFixture.copyDirectoryToProject(testName, "");
|
||||
PsiDocumentManager.getInstance(myFixture.getProject()).commitAllDocuments();
|
||||
|
||||
|
||||
@@ -279,6 +279,22 @@ public class PyStubsTest extends PyLightFixtureTestCase {
|
||||
assertNotParsed(file);
|
||||
}
|
||||
|
||||
public void testInitPyExportsSubpackageImport() {
|
||||
myFixture.copyDirectoryToProject("../resolve/multiFile/importPackageIntoSelf", "");
|
||||
final PyFile initPy = (PyFile) myFixture.getPsiManager().findFile(myFixture.findFileInTempDir("mygame/__init__.py"));
|
||||
final PsiElement element = initPy.findExportedName("display");
|
||||
assertTrue(element instanceof PyImportElement);
|
||||
assertNotParsed(initPy);
|
||||
}
|
||||
|
||||
public void testInitPyExportsSubpackageFromImport() {
|
||||
myFixture.copyDirectoryToProject("../resolve/multiFile/fromImportPackageIntoSelf", "");
|
||||
final PyFile initPy = (PyFile) myFixture.getPsiManager().findFile(myFixture.findFileInTempDir("mygame/__init__.py"));
|
||||
final PsiElement element = initPy.findExportedName("display");
|
||||
assertTrue(element instanceof PyFile);
|
||||
assertNotParsed(initPy);
|
||||
}
|
||||
|
||||
public void testVariableIndex() {
|
||||
getTestFile();
|
||||
GlobalSearchScope scope = GlobalSearchScope.allScope(myFixture.getProject());
|
||||
|
||||
Reference in New Issue
Block a user