mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
IDEA-CR-49176: function inline: import elements from function scope with alias if there is a name clash (PY-36642)
GitOrigin-RevId: 992b1d44d248725f31f599d53efdd4e4b854824a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f6c70470d6
commit
faf4d0b5cc
@@ -57,6 +57,7 @@ public final class PyClassRefactoringUtil {
|
||||
private static final Key<PsiNamedElement> ENCODED_IMPORT = Key.create("PyEncodedImport");
|
||||
private static final Key<Boolean> ENCODED_USE_FROM_IMPORT = Key.create("PyEncodedUseFromImport");
|
||||
private static final Key<String> ENCODED_IMPORT_AS = Key.create("PyEncodedImportAs");
|
||||
private static final Key<PyReferenceExpression> REPLACEMENT_EXPRESSION = Key.create("PyReplacementExpression");
|
||||
|
||||
|
||||
private PyClassRefactoringUtil() {
|
||||
@@ -251,6 +252,7 @@ public final class PyClassRefactoringUtil {
|
||||
PsiNamedElement target = sourceNode.getCopyableUserData(ENCODED_IMPORT);
|
||||
final String asName = sourceNode.getCopyableUserData(ENCODED_IMPORT_AS);
|
||||
final Boolean useFromImport = sourceNode.getCopyableUserData(ENCODED_USE_FROM_IMPORT);
|
||||
final PyReferenceExpression replacement = sourceNode.getCopyableUserData(REPLACEMENT_EXPRESSION);
|
||||
if (target instanceof PsiDirectory) {
|
||||
target = (PsiNamedElement)PyUtil.getPackageElement((PsiDirectory)target, sourceNode);
|
||||
}
|
||||
@@ -270,6 +272,9 @@ public final class PyClassRefactoringUtil {
|
||||
else {
|
||||
insertImport(targetNode, target, asName, true);
|
||||
}
|
||||
if (replacement != null) {
|
||||
sourceNode.replace(replacement);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
sourceNode.putCopyableUserData(ENCODED_IMPORT, null);
|
||||
@@ -428,6 +433,22 @@ public final class PyClassRefactoringUtil {
|
||||
return ContainerUtil.mapNotNull(expr.getReference().multiResolve(false), result -> result.getElement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the use of 'import as' when restoring references (i.e. if there are name clashes). Takes an optional replacement expression
|
||||
* to insert in place of the node after import.
|
||||
* @param node with encoded import
|
||||
* @param asName new alias for import
|
||||
* @param replacement reference after import
|
||||
*/
|
||||
public static void forceAsName(@NotNull PyReferenceExpression node, @NotNull String asName, @Nullable PyReferenceExpression replacement) {
|
||||
if (node.getCopyableUserData(ENCODED_IMPORT) == null) {
|
||||
LOG.warn("As name is forced on the referenceExpression, that has no encoded import. Forcing it will likely be ignored.");
|
||||
}
|
||||
node.putCopyableUserData(ENCODED_IMPORT_AS, asName);
|
||||
if (replacement != null) {
|
||||
node.putCopyableUserData(REPLACEMENT_EXPRESSION, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasEncodedTarget(@NotNull PyReferenceExpression node) {
|
||||
return node.getCopyableUserData(ENCODED_IMPORT) != null;
|
||||
|
||||
@@ -148,7 +148,9 @@ class PyInlineFunctionProcessor(project: Project,
|
||||
|
||||
val argumentReplacements = mutableMapOf<PyReferenceExpression, PyExpression>()
|
||||
val nameClashes = mutableSetOf<String>()
|
||||
val importAsTargets = mutableSetOf<String>()
|
||||
val nameClashRefs = MultiMap.create<String, PyExpression>()
|
||||
val importAsRefs = MultiMap.create<String, PyReferenceExpression>()
|
||||
val returnStatements = mutableListOf<PyReturnStatement>()
|
||||
|
||||
val mappedArguments = prepareArguments(callSite, declarations, generatedNames, scopeAnchor, reference, languageLevel)
|
||||
@@ -157,11 +159,12 @@ class PyInlineFunctionProcessor(project: Project,
|
||||
override fun visitPyReferenceExpression(node: PyReferenceExpression) {
|
||||
if (!node.isQualified) {
|
||||
val name = node.name!!
|
||||
if (name in namesInOuterScope && name !in mappedArguments && !PyClassRefactoringUtil.hasEncodedTarget(node)) {
|
||||
if (name in namesInOuterScope && name !in mappedArguments) {
|
||||
val resolved = node.reference.resolve()
|
||||
val target = (resolved as? PyFunction)?.containingClass ?: resolved
|
||||
if (!builtinCache.isBuiltin(target) && target !in PyResolveUtil.resolveLocally(refScopeOwner, name)) {
|
||||
nameClashes.add(name)
|
||||
if (PyClassRefactoringUtil.hasEncodedTarget(node)) importAsTargets.add(name)
|
||||
else nameClashes.add(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,6 +195,7 @@ class PyInlineFunctionProcessor(project: Project,
|
||||
when (val name = node.name) {
|
||||
in mappedArguments -> argumentReplacements[node] = mappedArguments[name]!!
|
||||
in nameClashes -> nameClashRefs.putValue(name!!, node)
|
||||
in importAsTargets -> importAsRefs.putValue(name!!, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,6 +227,10 @@ class PyInlineFunctionProcessor(project: Project,
|
||||
}
|
||||
}
|
||||
|
||||
importAsRefs.entrySet().forEach { (name, elements) ->
|
||||
val newRef = generateUniqueAssignment(languageLevel, name, generatedNames, scopeAnchor).assignedValue as PyReferenceExpression
|
||||
elements.forEach { PyClassRefactoringUtil.forceAsName(it, newRef.name!!, newRef) }
|
||||
}
|
||||
|
||||
if (returnStatements.size == 1 && returnStatements[0].expression !is PyTupleExpression) {
|
||||
// replace single return with expression itself
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from source import foo as foo1
|
||||
|
||||
foo = 1
|
||||
|
||||
x = foo1()
|
||||
res = x + 2
|
||||
@@ -0,0 +1,5 @@
|
||||
from source import bar
|
||||
|
||||
foo = 1
|
||||
|
||||
res = ba<caret>r(2)
|
||||
@@ -0,0 +1,7 @@
|
||||
def foo():
|
||||
return 1
|
||||
|
||||
|
||||
def bar(y):
|
||||
x = foo()
|
||||
return x + y
|
||||
@@ -59,6 +59,7 @@ class PyInlineFunctionTest : PyTestCase() {
|
||||
fun testArgumentExtraction() = doTest()
|
||||
fun testMultipleReturns() = doTest()
|
||||
fun testImporting() = doTest()
|
||||
fun testImportAs() = doTest()
|
||||
//fun testExistingImports() = doTest()
|
||||
fun testMethodInsideClass() = doTest()
|
||||
fun testMethodOutsideClass() = doTest()
|
||||
|
||||
Reference in New Issue
Block a user