mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-31299 Fix resolution of built-in classes' names in PyPsiFacade
Additionally, I extracted tests on PyPsiFacade implementation into a dedicated test case.
This commit is contained in:
@@ -64,6 +64,10 @@ public abstract class PyPsiFacade {
|
||||
/**
|
||||
* Retrieve a top-level class by its qualified name. The name provided is supposed to be <em>fully qualified absolute name</em>
|
||||
* of the class, neither relative to the containing file of the anchor element, nor dependent on its imports.
|
||||
* <p>
|
||||
* The only exception to the rule above are built-in classes as it's too cumbersome to explicitly specify "__builtin__" or "builtins"
|
||||
* prefix for them each time, and, overall, it's rather intuitive that these classes can be found solely by their short names.
|
||||
* <p>
|
||||
* The anchor element is needed only to detect the corresponding module and its SDK.
|
||||
*
|
||||
* @param qName qualified name of the required class
|
||||
|
||||
@@ -91,12 +91,18 @@ public class PyPsiFacadeImpl extends PyPsiFacade {
|
||||
@Nullable
|
||||
@Override
|
||||
public final PyClass createClassByQName(@NotNull final String qName, @NotNull final PsiElement anchor) {
|
||||
final QualifiedName qualifiedName = QualifiedName.fromDottedString(qName);
|
||||
// Only built-in classes can be found by their unqualified names.
|
||||
if (qualifiedName.getComponentCount() == 1) {
|
||||
return PyBuiltinCache.getInstance(anchor).getClass(qName);
|
||||
}
|
||||
|
||||
final Module module = ModuleUtilCore.findModuleForPsiElement(ObjectUtils.notNull(anchor.getContainingFile(), anchor));
|
||||
if (module == null) return null;
|
||||
// Don't use PyResolveImportUtil.fromFoothold here as setting foothold file is going to affect resolve results
|
||||
// particularly if the anchor element happens to be in the same file as the target class.
|
||||
final PyQualifiedNameResolveContext resolveContext = PyResolveImportUtil.fromModule(module).copyWithMembers();
|
||||
return StreamEx.of(resolveQualifiedName(QualifiedName.fromDottedString(qName), resolveContext))
|
||||
return StreamEx.of(resolveQualifiedName(qualifiedName, resolveContext))
|
||||
.select(PyClass.class)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
@@ -496,8 +496,8 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase {
|
||||
final List<PsiElement> elements = PyUtil.multiResolveTopPriority((PsiPolyVariantReference)ref);
|
||||
assertEquals(2, elements.size());
|
||||
final Set<String> parentNames = elements.stream()
|
||||
.filter(e -> e instanceof PyFile)
|
||||
.map(e -> ((PyFile)e).getVirtualFile().getParent().getName()).collect(Collectors.toSet());
|
||||
.filter(e -> e instanceof PyFile)
|
||||
.map(e -> ((PyFile)e).getVirtualFile().getParent().getName()).collect(Collectors.toSet());
|
||||
assertContainsElements(parentNames, "root", "ext");
|
||||
});
|
||||
}
|
||||
@@ -588,13 +588,4 @@ public class PyMultiFileResolveTest extends PyMultiFileResolveTestCase {
|
||||
public void testIncompleteFromImport() {
|
||||
assertUnresolved();
|
||||
}
|
||||
|
||||
public void testCreateClassByQNameDoesntDependOnExistingImports() {
|
||||
prepareTestDirectory();
|
||||
runWithSourceRoots(Collections.singletonList(myFixture.findFileInTempDir("src")), () -> {
|
||||
myFixture.configureByFile("src/" + getTestName(false) + ".py");
|
||||
final PyPsiFacade facade = PyPsiFacade.getInstance(myFixture.getProject());
|
||||
assertNotNull(facade.createClassByQName("foo.bar.MyClass", myFixture.getFile()));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyPsiFacade;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class PyPsiFacadeTest extends PyTestCase {
|
||||
public void testCreateClassByQNameDoesntDependOnExistingImports() {
|
||||
myFixture.copyDirectoryToProject(getTestName(true), "");
|
||||
runWithSourceRoots(Collections.singletonList(myFixture.findFileInTempDir("src")), () -> {
|
||||
myFixture.configureByFile("src/" + getTestName(false) + ".py");
|
||||
final PyPsiFacade facade = PyPsiFacade.getInstance(myFixture.getProject());
|
||||
assertNotNull(facade.createClassByQName("foo.bar.MyClass", myFixture.getFile()));
|
||||
});
|
||||
}
|
||||
|
||||
public void testCreateClassByQNameCanResolveUnqualifiedNamesOfBuiltinClasses() {
|
||||
final PsiFile file = myFixture.configureByText("a.py", "");
|
||||
final PyPsiFacade facade = PyPsiFacade.getInstance(myFixture.getProject());
|
||||
final PyClass builtinInt = facade.createClassByQName("int", file);
|
||||
assertNotNull(builtinInt);
|
||||
assertTrue(PyBuiltinCache.getInstance(file).isBuiltin(builtinInt));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return super.getTestDataPath() + "/psiFacade/";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user