mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-83529 Do not resolve to empty skeleton package modules
(cherry picked from commit 699b50ea5e2be24852fc1c07acb4d372e760a251) GitOrigin-RevId: 7270c13b1973eb00d88c4f7f6c71ac1a54291428
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c6222fa129
commit
3a58a09cbe
@@ -68,7 +68,7 @@ fun resolveQualifiedName(name: QualifiedName, context: PyQualifiedNameResolveCon
|
||||
val key = cachePrefix(context).append(name)
|
||||
|
||||
if (mayCache) {
|
||||
val cachedResults = cache?.get(key)
|
||||
val cachedResults = cache.get(key)
|
||||
if (cachedResults != null) {
|
||||
return (relativeResults + cachedResults).distinct()
|
||||
}
|
||||
@@ -87,15 +87,19 @@ fun resolveQualifiedName(name: QualifiedName, context: PyQualifiedNameResolveCon
|
||||
}
|
||||
}
|
||||
|
||||
val results = if (relativeDirectory != null && PyUtil.isExplicitPackage(relativeDirectory)) {
|
||||
val allResults = if (relativeDirectory != null && PyUtil.isExplicitPackage(relativeDirectory)) {
|
||||
filterTopPriorityResultsWithFallback(notSameDirectoryPython3Results, sameDirectoryPython3Results, foreignResults, name, context)
|
||||
}
|
||||
else {
|
||||
filterTopPriorityResultsWithFallback(sameDirectoryPython3Results, notSameDirectoryPython3Results, foreignResults, name, context)
|
||||
}
|
||||
|
||||
val results = allResults
|
||||
.filterNot { it is PsiFileSystemItem && isInSkeletons(it) }
|
||||
.ifEmpty { allResults }
|
||||
|
||||
if (mayCache) {
|
||||
cache?.put(key, results)
|
||||
cache.put(key, results)
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@@ -10,6 +10,8 @@ import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.io.FileUtilRt;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.VfsUtilCore;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDirectory;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
@@ -27,6 +29,8 @@ import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.pyi.PyiStubSuppressor;
|
||||
import com.jetbrains.python.pyi.PyiUtil;
|
||||
import com.jetbrains.python.sdk.PythonSdkUtil;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -319,13 +323,22 @@ public final class ResolveImportUtil {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!withoutForeign && parent instanceof PsiFile) {
|
||||
final PsiElement foreign = resolveForeignImports((PsiFile)parent, referencedName);
|
||||
if (foreign != null) {
|
||||
final ResolveResultList results = new ResolveResultList();
|
||||
results.addAll(resolved);
|
||||
results.poke(foreign, RatedResolveResult.RATE_NORMAL);
|
||||
return results;
|
||||
if (!withoutForeign) {
|
||||
if (parent instanceof PsiFile) {
|
||||
final PsiElement foreign = resolveForeignImports((PsiFile)parent, referencedName);
|
||||
if (foreign != null) {
|
||||
final ResolveResultList results = new ResolveResultList();
|
||||
results.addAll(resolved);
|
||||
results.poke(foreign, RatedResolveResult.RATE_NORMAL);
|
||||
return results;
|
||||
}
|
||||
}
|
||||
else if (parent instanceof PsiDirectory dir) {
|
||||
PsiDirectory skeletonsDir = findCorrespondingSkeletonsDir(dir);
|
||||
if (skeletonsDir != null) {
|
||||
return ContainerUtil.concat(resolved, resolveInDirectory(referencedName, containingFile, skeletonsDir, fileOnly,
|
||||
checkForPackage, withoutStubs));
|
||||
}
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
@@ -339,6 +352,28 @@ public final class ResolveImportUtil {
|
||||
return !results.isEmpty() ? results.get(0) : null;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static @Nullable PsiDirectory findCorrespondingSkeletonsDir(@NotNull PsiDirectory packageDir) {
|
||||
Sdk sdk = PythonSdkUtil.findPythonSdk(packageDir);
|
||||
if (sdk == null) return null;
|
||||
|
||||
VirtualFile skeletonsDir = PythonSdkUtil.findSkeletonsDir(sdk);
|
||||
if (skeletonsDir == null) return null;
|
||||
|
||||
boolean isInSkeletons = VfsUtilCore.isAncestor(skeletonsDir, packageDir.getVirtualFile(), false);
|
||||
if (isInSkeletons) return null;
|
||||
|
||||
PsiDirectory skeletonsPsiDir = packageDir.getManager().findDirectory(skeletonsDir);
|
||||
if (skeletonsPsiDir == null) return null;
|
||||
|
||||
QualifiedName packageName = QualifiedNameFinder.findShortestImportableQName(packageDir);
|
||||
if (packageName == null) return null;
|
||||
|
||||
List<@NotNull PsiElement> elements =
|
||||
PyResolveImportUtil.resolveModuleAt(packageName, skeletonsPsiDir, PyResolveImportUtil.fromFoothold(skeletonsPsiDir));
|
||||
return ContainerUtil.getFirstItem(elements) instanceof PsiDirectory dir ? dir : null;
|
||||
}
|
||||
|
||||
private static @NotNull List<RatedResolveResult> resolveMemberFromReferenceTypeProviders(@NotNull PsiElement parent,
|
||||
@NotNull String referencedName) {
|
||||
final var context = TypeEvalContext.codeInsightFallback(parent.getProject());
|
||||
|
||||
@@ -381,23 +381,21 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
* @return a list of submodules of the specified module directory, either files or dirs, for easier naming; may contain file names
|
||||
* not suitable for import.
|
||||
*/
|
||||
private static @NotNull List<PsiFileSystemItem> getSubmodulesList(@Nullable PsiDirectory directory, @Nullable PsiElement anchor) {
|
||||
private static @NotNull List<PsiFileSystemItem> getSubmodulesList(@NotNull PsiDirectory directory, @Nullable PsiElement anchor) {
|
||||
final List<PsiFileSystemItem> result = new ArrayList<>();
|
||||
|
||||
if (directory != null) { // just in case
|
||||
// file modules
|
||||
for (PsiFile f : directory.getFiles()) {
|
||||
final String filename = f.getName();
|
||||
// if we have a binary module, we'll most likely also have a stub for it in site-packages
|
||||
if (!isExcluded(f) && (f instanceof PyFile && !filename.equals(PyNames.INIT_DOT_PY)) || isBinaryModule(filename)) {
|
||||
result.add(f);
|
||||
}
|
||||
// file modules
|
||||
for (PsiFile f : directory.getFiles()) {
|
||||
final String filename = f.getName();
|
||||
// if we have a binary module, we'll most likely also have a stub for it in site-packages
|
||||
if (!isExcluded(f) && (f instanceof PyFile && !filename.equals(PyNames.INIT_DOT_PY)) || isBinaryModule(filename)) {
|
||||
result.add(f);
|
||||
}
|
||||
// dir modules
|
||||
for (PsiDirectory dir : directory.getSubdirectories()) {
|
||||
if (!isExcluded(dir) && PyUtil.isPackage(dir, anchor)) {
|
||||
result.add(dir);
|
||||
}
|
||||
}
|
||||
// dir modules
|
||||
for (PsiDirectory dir : directory.getSubdirectories()) {
|
||||
if (!isExcluded(dir) && PyUtil.isPackage(dir, anchor)) {
|
||||
result.add(dir);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -530,8 +528,16 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
public static @NotNull List<LookupElement> getSubModuleVariants(@Nullable PsiDirectory directory,
|
||||
@NotNull PsiElement location,
|
||||
@Nullable Set<? super String> namesAlready) {
|
||||
if (directory == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<LookupElement> result = new ArrayList<>();
|
||||
for (PsiFileSystemItem item : getSubmodulesList(directory, location)) {
|
||||
List<PsiFileSystemItem> items = getSubmodulesList(directory, location);
|
||||
PsiDirectory skeletonsDir = ResolveImportUtil.findCorrespondingSkeletonsDir(directory);
|
||||
if (skeletonsDir != null) {
|
||||
items = ContainerUtil.concat(items, getSubmodulesList(skeletonsDir, location));
|
||||
}
|
||||
for (PsiFileSystemItem item : items) {
|
||||
if (item != location.getContainingFile().getOriginalFile()) {
|
||||
final LookupElement lookupElement = buildFileLookupElement(location.getContainingFile(), item, namesAlready);
|
||||
if (lookupElement != null) {
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import pkg.subpkg
|
||||
|
||||
print(pkg.subpkg.Foo)
|
||||
+1
@@ -0,0 +1 @@
|
||||
from pkg.subpkg.subpkg import Foo
|
||||
+1
@@ -0,0 +1 @@
|
||||
Foo = ...
|
||||
@@ -4179,6 +4179,23 @@ public class Py3TypeTest extends PyTestCase {
|
||||
""");
|
||||
}
|
||||
|
||||
// PY-83529
|
||||
public void testImportNestedBinarySubModule() {
|
||||
String testDir = TEST_DIRECTORY + getTestName(false);
|
||||
runWithAdditionalClassEntryInSdkRoots(testDir + "/site-packages", () -> {
|
||||
runWithAdditionalClassEntryInSdkRoots(testDir + "/python_stubs", () -> {
|
||||
doTest("imported module pkg", """
|
||||
import pkg.subpkg
|
||||
expr = pkg
|
||||
""");
|
||||
doTest("__init__.py", """
|
||||
import pkg.subpkg
|
||||
expr = pkg.subpkg
|
||||
""");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void doTest(final String expectedType, final String text) {
|
||||
myFixture.configureByText(PythonFileType.INSTANCE, text);
|
||||
final PyExpression expr = myFixture.findElementByText("expr", PyExpression.class);
|
||||
|
||||
+11
@@ -491,4 +491,15 @@ public class Py3UnresolvedReferencesInspectionTest extends PyInspectionTestCase
|
||||
public void testStrictUnionMemberExtendingAny() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-83529
|
||||
public void testPackageAttributeInPresenceOfBinarySkeleton() {
|
||||
runWithAdditionalClassEntryInSdkRoots(getTestDirectoryPath() + "/site-packages", () -> {
|
||||
runWithAdditionalClassEntryInSdkRoots(getTestDirectoryPath() + "/python_stubs", () -> {
|
||||
final PsiFile currentFile = myFixture.configureByFile(getTestDirectoryPath() + "/main.py");
|
||||
configureInspection();
|
||||
assertSdkRootsNotParsed(currentFile);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user