mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-19989 Don't store results of implicit relative import in PythonPathCache
This commit is contained in:
@@ -19,7 +19,6 @@ import com.google.common.base.Preconditions;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Couple;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
@@ -604,6 +603,14 @@ public class PyPsiUtils {
|
||||
Preconditions.checkArgument(!module.isDisposed(), String.format("Module %s is disposed", module));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static PsiFileSystemItem getFileSystemItem(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiFileSystemItem) {
|
||||
return (PsiFileSystemItem)element;
|
||||
}
|
||||
return element.getContainingFile();
|
||||
}
|
||||
|
||||
private static abstract class TopLevelVisitor extends PyRecursiveElementVisitor {
|
||||
public void visitPyElement(final PyElement node) {
|
||||
super.visitPyElement(node);
|
||||
|
||||
@@ -28,15 +28,13 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.FileIndexFacade;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsUtil;
|
||||
import com.jetbrains.python.console.PydevConsoleRunner;
|
||||
import com.jetbrains.python.facet.PythonPathContributingFacet;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
@@ -44,6 +42,7 @@ import com.jetbrains.python.psi.impl.PyImportResolver;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.sdk.PySdkUtil;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -52,6 +51,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* Resolves the specified qualified name in the specified context (module, all modules or a file) to a file or directory.
|
||||
*
|
||||
@@ -235,6 +236,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
|
||||
|
||||
final PsiFile footholdFile = myContext.getFootholdFile();
|
||||
checkValidForTests(footholdFile);
|
||||
boolean foundRelativeImport = false;
|
||||
if (myRelativeLevel >= 0 && footholdFile != null && !PyUserSkeletonsUtil.isUnderUserSkeletonsDirectory(footholdFile)) {
|
||||
PsiDirectory dir = footholdFile.getContainingDirectory();
|
||||
checkValidForTests(dir);
|
||||
@@ -246,12 +248,13 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
|
||||
PsiElement module = resolveModuleAt(dir);
|
||||
checkValidForTests(module);
|
||||
if (module != null) {
|
||||
foundRelativeImport = isRelativeImportResult(dir, module);
|
||||
addRoot(module, true);
|
||||
}
|
||||
}
|
||||
|
||||
final PythonPathCache cache = findMyCache();
|
||||
final boolean mayCache = cache != null && !myWithoutRoots && !myWithoutForeign;
|
||||
final boolean mayCache = cache != null && !myWithoutRoots && !myWithoutForeign && !foundRelativeImport;
|
||||
if (mayCache) {
|
||||
final List<PsiElement> cachedResults = cache.get(myQualifiedName);
|
||||
if (cachedResults != null) {
|
||||
@@ -293,6 +296,22 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
private boolean isRelativeImportResult(@NotNull PsiDirectory rootDirectory, @NotNull PsiElement result) {
|
||||
if (myRelativeLevel > 0) {
|
||||
return true;
|
||||
}
|
||||
final boolean isPython2 = LanguageLevel.forElement(rootDirectory).isOlderThan(LanguageLevel.PYTHON30);
|
||||
if (myRelativeLevel == 0 && isPython2 && PyUtil.isPackage(rootDirectory, false, null)) {
|
||||
// Candidate for implicit relative import doesn't necessarily means that the same module cannot be imported absolutely
|
||||
final PsiFileSystemItem moduleOrPackage = as(result, PsiFileSystemItem.class);
|
||||
if (moduleOrPackage != null && !myQualifiedName.equals(QualifiedNameFinder.findShortestImportableQName(moduleOrPackage))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve relative imports from sdk root to the skeleton dir
|
||||
*/
|
||||
@@ -421,6 +440,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
|
||||
*
|
||||
* @param directory where to start from; top qualifier will be searched for here.
|
||||
*/
|
||||
@Contract("null -> null")
|
||||
@Nullable
|
||||
public PsiElement resolveModuleAt(@Nullable PsiDirectory directory) {
|
||||
// prerequisites
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
import mod
|
||||
<ref>
|
||||
@@ -0,0 +1,2 @@
|
||||
import mod
|
||||
<ref>
|
||||
@@ -18,6 +18,8 @@ package com.jetbrains.python;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ModuleRootManager;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
@@ -26,10 +28,14 @@ import com.jetbrains.python.fixtures.PyMultiFileResolveTestCase;
|
||||
import com.jetbrains.python.fixtures.PyResolveTestCase;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -459,4 +465,29 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PY-19989
|
||||
public void testAmbiguousImplicitRelativeImport() {
|
||||
prepareTestDirectory();
|
||||
assertSameElements(doMultiResolveAndGetFileUrls("pkg2/__init__.py"), "pkg2/mod.py");
|
||||
assertSameElements(doMultiResolveAndGetFileUrls("pkg/__init__.py"), "pkg/mod.py");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<String> doMultiResolveAndGetFileUrls(@NotNull String currentFilePath) {
|
||||
myFixture.configureByFile(currentFilePath);
|
||||
final PsiReference reference = PyResolveTestCase.findReferenceByMarker(myFixture.getFile());
|
||||
final VirtualFile root = ModuleRootManager.getInstance(myFixture.getModule()).getSourceRoots()[0];
|
||||
|
||||
final Stream<PsiFileSystemItem> fileSystemItems;
|
||||
if (reference instanceof PsiPolyVariantReference) {
|
||||
final ResolveResult[] results = ((PsiPolyVariantReference)reference).multiResolve(false);
|
||||
fileSystemItems = Arrays.stream(results).map(r -> PyPsiUtils.getFileSystemItem(r.getElement()));
|
||||
}
|
||||
else {
|
||||
fileSystemItems = Stream.of(PyPsiUtils.getFileSystemItem(reference.resolve()));
|
||||
}
|
||||
return fileSystemItems.map(f -> VfsUtilCore.getRelativeLocation(f.getVirtualFile(), root)).collect(Collectors.toList());
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user