Code inliner: keep main property if it can have side effects

So #KT-24165 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-05-23 14:20:24 +03:00
parent 93a510c05b
commit f3c2dd0526
13 changed files with 204 additions and 12 deletions
@@ -76,6 +76,7 @@ class CodeInliner<TCallElement : KtElement>(
// if the value to be inlined is not used and has no side effects we may drop it
if (codeToInline.mainExpression != null
&& !codeToInline.alwaysKeepMainExpression
&& assignment == null
&& elementToBeReplaced is KtExpression
&& !elementToBeReplaced.isUsedAsExpression(bindingContext)
@@ -35,7 +35,8 @@ import org.jetbrains.kotlin.psi.KtExpression
class CodeToInline(
val mainExpression: KtExpression?,
val statementsBefore: List<KtExpression>,
val fqNamesToImport: Collection<FqName>
val fqNamesToImport: Collection<FqName>,
val alwaysKeepMainExpression: Boolean
) {
companion object {
val PARAMETER_USAGE_KEY: Key<Name> = Key("PARAMETER_USAGE")
@@ -16,9 +16,7 @@
package org.jetbrains.kotlin.idea.codeInliner
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.asExpression
@@ -59,7 +57,17 @@ class CodeToInlineBuilder(
): CodeToInline {
var bindingContext = analyze()
val codeToInline = MutableCodeToInline(mainExpression, statementsBefore.toMutableList(), mutableSetOf())
val descriptor = mainExpression.getResolvedCall(bindingContext)?.resultingDescriptor
val alwaysKeepMainExpression = when (descriptor) {
is PropertyDescriptor -> descriptor.getter?.isDefault == false
else -> false
}
val codeToInline = MutableCodeToInline(
mainExpression,
statementsBefore.toMutableList(),
mutableSetOf(),
alwaysKeepMainExpression
)
bindingContext = insertExplicitTypeArguments(codeToInline, bindingContext, analyze)
@@ -29,9 +29,10 @@ import org.jetbrains.kotlin.psi.psiUtil.isAncestor
private val POST_INSERTION_ACTION: Key<(KtElement) -> Unit> = Key("POST_INSERTION_ACTION")
internal class MutableCodeToInline(
var mainExpression: KtExpression?,
val statementsBefore: MutableList<KtExpression>,
val fqNamesToImport: MutableCollection<FqName>
var mainExpression: KtExpression?,
val statementsBefore: MutableList<KtExpression>,
val fqNamesToImport: MutableCollection<FqName>,
val alwaysKeepMainExpression: Boolean
) {
fun <TElement : KtElement> addPostInsertionAction(element: TElement, action: (TElement) -> Unit) {
assert(element in this)
@@ -82,13 +83,15 @@ internal class MutableCodeToInline(
internal fun CodeToInline.toMutable(): MutableCodeToInline {
return MutableCodeToInline(
mainExpression?.copied(),
statementsBefore.map { it.copied() }.toMutableList(),
fqNamesToImport.toMutableSet())
mainExpression?.copied(),
statementsBefore.map { it.copied() }.toMutableList(),
fqNamesToImport.toMutableSet(),
alwaysKeepMainExpression
)
}
internal fun MutableCodeToInline.toNonMutable(): CodeToInline {
return CodeToInline(mainExpression, statementsBefore, fqNamesToImport)
return CodeToInline(mainExpression, statementsBefore, fqNamesToImport, alwaysKeepMainExpression)
}
internal inline fun <reified T : PsiElement> MutableCodeToInline.collectDescendantsOfType(noinline predicate: (T) -> Boolean = { true }): List<T> {
@@ -0,0 +1,8 @@
// "Replace with 'FOO'" "true"
const val FOO = 1
@Deprecated("always const", ReplaceWith("FOO"))
fun foo() = 1
fun test(){
val x = foo<caret>()
}
@@ -0,0 +1,8 @@
// "Replace with 'FOO'" "true"
const val FOO = 1
@Deprecated("always const", ReplaceWith("FOO"))
fun foo() = 1
fun test(){
val x = FOO
}
@@ -0,0 +1,8 @@
// "Replace with 'bar'" "true"
val bar get() = 1
@Deprecated("use property instead", ReplaceWith("bar"))
fun foo() = 1
fun test(){
foo<caret>()
}
@@ -0,0 +1,8 @@
// "Replace with 'bar'" "true"
val bar get() = 1
@Deprecated("use property instead", ReplaceWith("bar"))
fun foo() = 1
fun test(){
bar
}
@@ -0,0 +1,8 @@
// "Replace with 'FOO'" "true"
const val FOO = 1
@Deprecated("always const", ReplaceWith("FOO"))
fun foo() = 1
fun test(){
foo<caret>()
}
@@ -0,0 +1,7 @@
// "Replace with 'FOO'" "true"
const val FOO = 1
@Deprecated("always const", ReplaceWith("FOO"))
fun foo() = 1
fun test(){
}
@@ -2353,6 +2353,19 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/properties")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Properties extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInProperties() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/properties"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1578,6 +1578,97 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/keepComments")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class KeepComments extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInKeepComments() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/keepComments"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class KeepLineBreaks extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInKeepLineBreaks() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/keepLineBreaks"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class OperatorCalls extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInOperatorCalls() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/operatorCalls"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class OptionalParameters extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInOptionalParameters() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/optionalParameters"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/properties")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Properties extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInProperties() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/properties"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class PublishedApi extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInPublishedApi() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/safeCall")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SafeCall extends AbstractQuickFixMultiFileTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTestWithExtraFile, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInSafeCall() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/safeCall"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/typeAliases")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -5653,6 +5653,34 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/properties")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Properties extends AbstractQuickFixTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
public void testAllFilesPresentInProperties() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/properties"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("callInAssignmentToProperty.kt")
public void testCallInAssignmentToProperty() throws Exception {
runTest("idea/testData/quickfix/deprecatedSymbolUsage/properties/callInAssignmentToProperty.kt");
}
@TestMetadata("callToCustomProperty.kt")
public void testCallToCustomProperty() throws Exception {
runTest("idea/testData/quickfix/deprecatedSymbolUsage/properties/callToCustomProperty.kt");
}
@TestMetadata("callToProperty.kt")
public void testCallToProperty() throws Exception {
runTest("idea/testData/quickfix/deprecatedSymbolUsage/properties/callToProperty.kt");
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/publishedApi")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)