PY-36374 Enforce that ImportCandidateHolder stores a PsiNamedElement

Making it straightforward to retrieve this name from an element and not
have to pass it from the outside.

Deprecated stubs for AutoImportQuickFix.addImport() overloads, accepting
a "raw" PSI element, were provided for existing plugins.

GitOrigin-RevId: 899752ae9aa78a139eb0b6632ad9ca0d68e53337
This commit is contained in:
Mikhail Golubev
2021-01-07 17:25:30 +00:00
committed by intellij-monorepo-bot
parent 3ba2747c51
commit 57251de36e
3 changed files with 49 additions and 21 deletions
@@ -15,6 +15,7 @@ import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyImportElement;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -69,7 +70,7 @@ public class AutoImportQuickFix extends LocalQuickFixOnPsiElement implements Hig
* @param file the file which is the source of the importable
* @param importElement an existing import element that can be a source for the importable.
*/
public void addImport(@NotNull PsiElement importable, @NotNull PsiFile file, @Nullable PyImportElement importElement) {
public void addImport(@NotNull PsiNamedElement importable, @NotNull PsiFile file, @Nullable PyImportElement importElement) {
myImports.add(new ImportCandidateHolder(importable, file, importElement, null));
}
@@ -79,14 +80,42 @@ public class AutoImportQuickFix extends LocalQuickFixOnPsiElement implements Hig
* @param file the file which is the source of the importable
* @param path import path for the file, as a qualified name (a.b.c)
*/
public void addImport(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file, @Nullable QualifiedName path) {
public void addImport(@NotNull PsiNamedElement importable, @NotNull PsiFileSystemItem file, @Nullable QualifiedName path) {
myImports.add(new ImportCandidateHolder(importable, file, null, path));
}
public void addImport(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file, @Nullable QualifiedName path, @Nullable String asName) {
/**
* @deprecated Use {@link #addImport(PsiNamedElement, PsiFileSystemItem, QualifiedName)} accepting a named element.
*/
@ApiStatus.ScheduledForRemoval(inVersion = "2021.3")
@Deprecated
public void addImport(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file, @Nullable QualifiedName path) {
if (importable instanceof PsiNamedElement) {
addImport((PsiNamedElement)importable, file, path);
}
}
public void addImport(@NotNull PsiNamedElement importable,
@NotNull PsiFileSystemItem file,
@Nullable QualifiedName path,
@Nullable String asName) {
myImports.add(new ImportCandidateHolder(importable, file, null, path, asName));
}
/**
* @deprecated Use {@link #addImport(PsiNamedElement, PsiFileSystemItem, QualifiedName, String)} accepting a named element.
*/
@ApiStatus.ScheduledForRemoval(inVersion = "2021.3")
@Deprecated
public void addImport(@NotNull PsiElement importable,
@NotNull PsiFileSystemItem file,
@Nullable QualifiedName path,
@Nullable String asName) {
if (importable instanceof PsiNamedElement) {
addImport((PsiNamedElement)importable, file, path, asName);
}
}
@Override
@NotNull
public String getText() {
@@ -31,11 +31,11 @@ import java.util.List;
// visibility is intentionally package-level
public class ImportCandidateHolder implements Comparable<ImportCandidateHolder> {
private static final Logger LOG = Logger.getInstance(ImportCandidateHolder.class);
@NotNull private final SmartPsiElementPointer<PsiElement> myImportable;
@NotNull private final SmartPsiElementPointer<PsiNamedElement> myImportable;
@Nullable private final SmartPsiElementPointer<PyImportElement> myImportElement;
@NotNull private final SmartPsiElementPointer<PsiFileSystemItem> myFile;
@Nullable private final QualifiedName myPath;
private final String myImportableName;
@NotNull private final String myImportableName;
@Nullable private final String myAsName;
private final int myRelevance;
@@ -51,12 +51,13 @@ public class ImportCandidateHolder implements Comparable<ImportCandidateHolder>
* (empty for modules and packages located at source roots).
*
*/
public ImportCandidateHolder(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file,
public ImportCandidateHolder(@NotNull PsiNamedElement importable, @NotNull PsiFileSystemItem file,
@Nullable PyImportElement importElement, @Nullable QualifiedName path, @Nullable String asName) {
SmartPointerManager pointerManager = SmartPointerManager.getInstance(importable.getProject());
myFile = pointerManager.createSmartPsiElementPointer(file);
myImportable = pointerManager.createSmartPsiElementPointer(importable);
myImportableName = importable instanceof PsiNamedElement ? PyUtil.getElementNameWithoutExtension(((PsiNamedElement)importable)) : null;
myImportableName = PyUtil.getElementNameWithoutExtension(importable);
assert myImportableName != null;
myImportElement = importElement != null ? pointerManager.createSmartPsiElementPointer(importElement) : null;
myPath = path;
myAsName = asName;
@@ -65,17 +66,17 @@ public class ImportCandidateHolder implements Comparable<ImportCandidateHolder>
assert importElement != null || path != null; // one of these must be present
}
public ImportCandidateHolder(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file,
public ImportCandidateHolder(@NotNull PsiNamedElement importable, @NotNull PsiFileSystemItem file,
@Nullable PyImportElement importElement, @Nullable QualifiedName path) {
this(importable, file, importElement, path, null);
}
@Nullable
public PsiElement getImportable() {
public PsiNamedElement getImportable() {
return myImportable.getElement();
}
public String getImportableName() {
public @NotNull String getImportableName() {
return myImportableName;
}
@@ -87,16 +87,17 @@ public class PyImportCollector {
PyFile sourceFile = as(PyUtil.turnDirIntoInit(source), PyFile.class);
if (sourceFile instanceof PyFileImpl) {
PsiElement res = sourceFile.findExportedName(myRefText);
final String name = res instanceof PyQualifiedNameOwner ? ((PyQualifiedNameOwner)res).getQualifiedName() : null;
PsiElement variant = sourceFile.findExportedName(myRefText);
final String name = variant instanceof PyQualifiedNameOwner ? ((PyQualifiedNameOwner)variant).getQualifiedName() : null;
if (name != null && seenCandidateNames.contains(name)) {
return existingImportFile;
}
PsiNamedElement definition = as(variant, PsiNamedElement.class);
// allow importing from this source if it either declares the name itself or represents a higher-level package that reexports the name
if (res != null && !(res instanceof PyFile) && !(res instanceof PyImportElement) && res.getContainingFile() != null &&
PsiTreeUtil.isAncestor(source, res.getContainingFile(), false)) {
if (definition != null && !(definition instanceof PyFile || definition instanceof PyImportElement) &&
definition.getContainingFile() != null && PsiTreeUtil.isAncestor(source, definition.getContainingFile(), false)) {
existingImportFile = sourceFile;
fix.addImport(res, sourceFile, importElement);
fix.addImport(definition, sourceFile, importElement);
if (name != null) {
seenCandidateNames.add(name);
}
@@ -107,7 +108,7 @@ public class PyImportCollector {
private void addSymbolImportCandidates(PsiFile existingImportFile) {
Project project = myNode.getProject();
List<PsiElement> symbols = new ArrayList<>(PyClassNameIndex.find(myRefText, project, true));
List<PsiNamedElement> symbols = new ArrayList<>(PyClassNameIndex.find(myRefText, project, true));
GlobalSearchScope scope = PySearchUtilBase.excludeSdkTestsScope(myNode);
if (!isQualifier()) {
symbols.addAll(PyFunctionNameIndex.find(myRefText, project, scope));
@@ -120,7 +121,7 @@ public class PyImportCollector {
symbols.addAll(findImportableModules(packageQName, project, scope));
}
}
for (PsiElement symbol : symbols) {
for (PsiNamedElement symbol : symbols) {
if (isIndexableTopLevel(symbol)) { // we only want top-level symbols
PsiFileSystemItem srcfile =
symbol instanceof PsiFileSystemItem ? ((PsiFileSystemItem)symbol).getParent() : symbol.getContainingFile();
@@ -132,10 +133,7 @@ public class PyImportCollector {
if (symbol instanceof PsiFileSystemItem) {
importPath = importPath.removeTail(1);
}
if (!(symbol instanceof PsiNamedElement)) {
continue;
}
String name = PyUtil.getElementNameWithoutExtension((PsiNamedElement)symbol);
String name = PyUtil.getElementNameWithoutExtension(symbol);
final String symbolImportQName = importPath.append(name).toString();
if (seenCandidateNames.add(symbolImportQName)) {
String alias = name.equals(myRefText) ? null : myRefText;