IDEA-CR-49176: function inline: qualifier is not extracted into declaration if the method does not use 'self' (PY-36471)

GitOrigin-RevId: 94a3604d54fd957e9744e7f3f6b632cb7924e4fb
This commit is contained in:
Aleksei Kniazev
2019-07-08 18:05:36 +03:00
committed by intellij-monorepo-bot
parent 1bb63560ee
commit 1b5d272f4e
4 changed files with 49 additions and 20 deletions
@@ -41,6 +41,7 @@ class PyInlineFunctionProcessor(project: Project,
private val myFunctionClass = myFunction.containingClass
private val myGenerator = PyElementGenerator.getInstance(myProject)
private var myRemoveDeclaration = !myInlineThis && removeDeclaration
private var mySelfUsed: Boolean? = null
override fun preprocessUsages(refUsages: Ref<Array<UsageInfo>>): Boolean {
if (refUsages.isNull) return false
@@ -284,24 +285,23 @@ class PyInlineFunctionProcessor(project: Project,
val context = PyResolveContext.noImplicits().withTypeEvalContext(TypeEvalContext.userInitiated(myProject, reference.containingFile))
val mapping = PyCallExpressionHelper.mapArguments(callSite, context).firstOrNull() ?: error("Can't map arguments for ${reference.name}")
val mappedParams = mapping.mappedParameters
val firstImplicit = mapping.implicitParameters.firstOrNull()
val self = mapping.implicitParameters.firstOrNull()?.let { first ->
val implicitName = first.name!!
val selfReplacement = reference.qualifier?.let { qualifier ->
myFunctionClass?.let {
when {
qualifier is PyReferenceExpression && !qualifier.isQualified -> qualifier
else -> {
val qualifierDeclaration = generateUniqueAssignment(languageLevel, myFunctionClass.name!!, generatedNames, scopeAnchor)
val newRef = qualifierDeclaration.assignedValue!!.copy() as PyExpression
qualifierDeclaration.assignedValue!!.replace(qualifier)
declarations.add(qualifierDeclaration)
newRef
}
}
}
if (mySelfUsed == null && firstImplicit != null) {
mySelfUsed = SyntaxTraverser.psiTraverser(myFunction.statementList).traverse()
.filter(PyReferenceExpression::class.java)
.filter { !it.isQualified }
.any { it.name == firstImplicit.name }
}
val self = firstImplicit?.let { first ->
val qualifier = reference.qualifier ?: error("Function $myFunction has first implicit parameter, but no qualifier")
val selfReplacement = when {
mySelfUsed == false -> qualifier
qualifier is PyReferenceExpression && !qualifier.isQualified -> qualifier
else -> extractDeclaration(myFunctionClass?.name!!, qualifier, declarations, generatedNames, scopeAnchor, languageLevel).second
}
mapOf(implicitName to selfReplacement!!)
mapOf(first.name!! to selfReplacement)
} ?: emptyMap()
val passedArguments = mappedParams.asSequence()
@@ -323,13 +323,17 @@ class PyInlineFunctionProcessor(project: Project,
private fun tryExtractDeclaration(paramName: String, arg: PyExpression, declarations: MutableList<PyAssignmentStatement>, generatedNames: MutableSet<String>,
scopeAnchor: PsiElement, languageLevel: LanguageLevel): Pair<String, PyExpression> {
if (arg !is PyReferenceExpression && arg !is PyLiteralExpression) {
val statement = generateUniqueAssignment(languageLevel, paramName, generatedNames, scopeAnchor)
statement.assignedValue!!.replace(arg)
declarations.add(statement)
return paramName to statement.targets[0]
return extractDeclaration(paramName, arg, declarations, generatedNames, scopeAnchor, languageLevel)
}
return paramName to arg
}
private fun extractDeclaration(paramName: String, arg: PyExpression, declarations: MutableList<PyAssignmentStatement>, generatedNames: MutableSet<String>,
scopeAnchor: PsiElement, languageLevel: LanguageLevel): Pair<String, PyExpression> {
val statement = generateUniqueAssignment(languageLevel, paramName, generatedNames, scopeAnchor)
statement.assignedValue!!.replace(arg)
declarations.add(statement)
return paramName to statement.targets[0]
}
private fun generateUniqueAssignment(level: LanguageLevel, name: String, previouslyGeneratedNames: MutableSet<String>, scopeAnchor: PsiElement): PyAssignmentStatement {
@@ -0,0 +1,12 @@
class MyClass:
def __init__(self, attr):
self.attr = attr
def __add__(self, other):
return MyClass(self.attr + other.attr)
def method_no_self(self):
print('Can actually be static')
print('Can actually be static')
@@ -0,0 +1,12 @@
class MyClass:
def __init__(self, attr):
self.attr = attr
def __add__(self, other):
return MyClass(self.attr + other.attr)
def method_no_self(self):
print('Can actually be static')
(MyClass(1) + MyClass(2)).met<caret>hod_no_self()
@@ -73,6 +73,7 @@ class PyInlineFunctionTest : PyTestCase() {
fun testCallFromStaticMethod() = doTest()
fun testCallFromClassMethod() = doTest()
fun testComplexQualifier() = doTest()
fun testRedundantQualifier() = doTest()
fun testFunctionWithLambda() = doTest()
fun testRefInDunderAll() = doTest(inlineThis = false, remove = true)
fun testRemovingDocstring() = doTest()