diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt
index 75d7cbae09a..c6ba21f6025 100644
--- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt
+++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt
@@ -68,6 +68,8 @@ public object DirectiveBasedActionUtils {
"Edit intention settings",
"Edit inspection profile setting",
"Inject language or reference",
- "Suppress '"
+ "Suppress '",
+ "Run inspection on",
+ "Inspection '"
)
}
diff --git a/idea/resources/inspectionDescriptions/ConflictingExtensionProperty.html b/idea/resources/inspectionDescriptions/ConflictingExtensionProperty.html
new file mode 100644
index 00000000000..1cb77ded0a8
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/ConflictingExtensionProperty.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports extension properties that conflict with synthetic ones automatically produced from Java get/set-methods and that should be either removed or renamed to avoid breaking code by future changes in the compiler.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 81963e66ff2..221ac8b7412 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -1138,6 +1138,13 @@
level="WARNING"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt
new file mode 100644
index 00000000000..d73b0fd5663
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ConflictingExtensionPropertyInspection.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2010-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.inspections
+
+import com.intellij.codeInspection.*
+import com.intellij.openapi.diagnostic.Logger
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.progress.ProgressIndicator
+import com.intellij.openapi.progress.ProgressManager
+import com.intellij.openapi.progress.Task
+import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiElementVisitor
+import com.intellij.psi.search.GlobalSearchScope
+import com.intellij.psi.search.searches.ReferencesSearch
+import com.intellij.util.ui.UIUtil
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.descriptors.PropertyDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyze
+import org.jetbrains.kotlin.idea.caches.resolve.getFileTopLevelScope
+import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
+import org.jetbrains.kotlin.idea.core.targetDescriptors
+import org.jetbrains.kotlin.idea.imports.importableFqName
+import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction
+import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
+import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope
+import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
+import org.jetbrains.kotlin.idea.util.application.runReadAction
+import org.jetbrains.kotlin.incremental.components.NoLookupLocation
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+import org.jetbrains.kotlin.resolve.scopes.FileScope
+import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
+import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
+
+public class ConflictingExtensionPropertyInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
+ val file = session.file as? JetFile ?: return PsiElementVisitor.EMPTY_VISITOR
+ val fileScope = file.getResolutionFacade().getFileTopLevelScope(file)
+
+ return object : JetVisitorVoid() {
+ override fun visitProperty(property: JetProperty) {
+ super.visitProperty(property)
+
+ if (property.receiverTypeReference != null) {
+ val nameElement = property.nameIdentifier ?: return
+ val conflictingExtension = conflictingSyntheticExtension(property.resolveToDescriptor() as PropertyDescriptor, fileScope)
+ if (conflictingExtension != null) {
+ val problemDescriptor = holder.manager.createProblemDescriptor(
+ nameElement,
+ //TODO: review message
+ "This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler",
+ createQuickFix(property, conflictingExtension),
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ true
+ )
+ holder.registerProblem(problemDescriptor)
+ }
+ }
+ }
+ }
+ }
+
+ private fun conflictingSyntheticExtension(descriptor: PropertyDescriptor, fileScope: FileScope): SyntheticJavaPropertyDescriptor? {
+ val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return null
+ return fileScope
+ .getSyntheticExtensionProperties(listOf(extensionReceiverType), descriptor.name, NoLookupLocation.FROM_IDE)
+ .firstIsInstanceOrNull()
+ }
+
+ private fun createQuickFix(declaration: JetProperty, syntheticProperty: SyntheticJavaPropertyDescriptor): LocalQuickFix? {
+ val getter = declaration.getter ?: return null
+ val setter = declaration.setter
+
+ if (!checkGetterBodyIsGetMethodCall(getter, syntheticProperty.getMethod)) return null
+
+ if (setter != null) {
+ val setMethod = syntheticProperty.setMethod ?: return null // synthetic property is val but our property is var
+ if (!checkSetterBodyIsSetMethodCall(setter, setMethod)) return null
+ }
+
+ return IntentionWrapper(DeleteRedundantExtensionAction(declaration), declaration.containingFile)
+ }
+
+ private fun checkGetterBodyIsGetMethodCall(getter: JetPropertyAccessor, getMethod: FunctionDescriptor): Boolean {
+ if (getter.hasBlockBody()) {
+ val statement = (getter.bodyExpression as? JetBlockExpression)?.statements?.singleOrNull() ?: return false
+ return (statement as? JetReturnExpression)?.returnedExpression.isGetMethodCall(getMethod)
+ }
+ else {
+ return getter.bodyExpression.isGetMethodCall(getMethod)
+ }
+ }
+
+ private fun checkSetterBodyIsSetMethodCall(setter: JetPropertyAccessor, setMethod: FunctionDescriptor): Boolean {
+ val valueParameterName = setter.valueParameters.singleOrNull()?.nameAsName ?: return false
+ if (!setter.hasBlockBody()) return false
+ val statement = (setter.bodyExpression as? JetBlockExpression)?.statements?.singleOrNull() ?: return false
+ return statement.isSetMethodCall(setMethod, valueParameterName)
+ }
+
+ private fun JetExpression?.isGetMethodCall(getMethod: FunctionDescriptor): Boolean {
+ when (this) {
+ is JetCallExpression -> {
+ val resolvedCall = getResolvedCall(analyze())
+ return resolvedCall != null && resolvedCall.status.isSuccess && resolvedCall.resultingDescriptor.original == getMethod.original
+ }
+
+ is JetQualifiedExpression -> {
+ val receiver = receiverExpression
+ return receiver is JetThisExpression && receiver.labelQualifier == null && selectorExpression.isGetMethodCall(getMethod)
+ }
+
+ else -> return false
+ }
+ }
+
+ private fun JetExpression?.isSetMethodCall(setMethod: FunctionDescriptor, valueParameterName: Name): Boolean {
+ when (this) {
+ is JetCallExpression -> {
+ if ((valueArguments.singleOrNull()?.getArgumentExpression() as? JetSimpleNameExpression)?.getReferencedNameAsName() != valueParameterName) return false
+ val resolvedCall = getResolvedCall(analyze())
+ return resolvedCall != null && resolvedCall.status.isSuccess && resolvedCall.resultingDescriptor.original == setMethod.original
+ }
+
+ is JetQualifiedExpression -> {
+ val receiver = receiverExpression
+ return receiver is JetThisExpression && receiver.labelQualifier == null && selectorExpression.isSetMethodCall(setMethod, valueParameterName)
+ }
+
+ else -> return false
+ }
+ }
+
+ private class DeleteRedundantExtensionAction(property: JetProperty) : JetIntentionAction(property) {
+ private val LOG = Logger.getInstance(DeleteRedundantExtensionAction::class.java);
+
+ override fun getFamilyName() = "Delete redundant extension property"
+ override fun getText() = familyName
+
+ override fun startInWriteAction() = false
+
+ override fun invoke(project: Project, editor: Editor?, file: JetFile) {
+ val declaration = element
+ val fqName = declaration.resolveToDescriptor().importableFqName
+ if (fqName != null) {
+ ProgressManager.getInstance().run(
+ object : Task.Modal(project, "Searching for imports to delete", true) {
+ override fun run(indicator: ProgressIndicator) {
+ val importsToDelete = runReadAction {
+ val searchScope = JetSourceFilterScope.kotlinSources(GlobalSearchScope.projectScope(project), project)
+ ReferencesSearch.search(declaration, searchScope)
+ .filterIsInstance()
+ .map { ref -> ref.expression.getStrictParentOfType() }
+ .filterNotNull()
+ .filter { import -> !import.isAllUnder && import.targetDescriptors().size() == 1 }
+ }
+ UIUtil.invokeLaterIfNeeded {
+ project.executeWriteCommand(text) {
+ importsToDelete.forEach { import ->
+ try {
+ import.delete()
+ }
+ catch(e: Exception) {
+ LOG.error(e)
+ }
+ }
+ declaration.delete()
+ }
+ }
+ }
+ })
+ }
+ else {
+ project.executeWriteCommand(text) { declaration.delete() }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/conflictingExtensionProperty/File.kt b/idea/testData/inspections/conflictingExtensionProperty/File.kt
new file mode 100644
index 00000000000..9c47d84d7ed
--- /dev/null
+++ b/idea/testData/inspections/conflictingExtensionProperty/File.kt
@@ -0,0 +1,16 @@
+import java.io.File
+import java.io.Serializable
+
+val File.name: String
+ get() = getName()
+
+val Serializable.name: String
+ get() = ""
+
+val File.parent: File
+ get() = getParentFile()
+
+class MyFile : File("")
+
+val MyFile.isFile: Boolean
+ get() = isFile()
\ No newline at end of file
diff --git a/idea/testData/inspections/conflictingExtensionProperty/inspectionData/expected.xml b/idea/testData/inspections/conflictingExtensionProperty/inspectionData/expected.xml
new file mode 100644
index 00000000000..d26edca2533
--- /dev/null
+++ b/idea/testData/inspections/conflictingExtensionProperty/inspectionData/expected.xml
@@ -0,0 +1,28 @@
+
+
+ File.kt
+ 4
+ light_idea_test_case
+
+ Extension property conflicts with automatically produced one
+ This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler
+
+
+
+ File.kt
+ 10
+ light_idea_test_case
+
+ Extension property conflicts with automatically produced one
+ This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler
+
+
+
+ File.kt
+ 15
+ light_idea_test_case
+
+ Extension property conflicts with automatically produced one
+ This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler
+
+
diff --git a/idea/testData/inspections/conflictingExtensionProperty/inspectionData/inspections.test b/idea/testData/inspections/conflictingExtensionProperty/inspectionData/inspections.test
new file mode 100644
index 00000000000..11fa203cb2e
--- /dev/null
+++ b/idea/testData/inspections/conflictingExtensionProperty/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection
diff --git a/idea/testData/quickfix/deprecatedSymbolUsage/javaDeprecated.before.Main.kt b/idea/testData/quickfix/deprecatedSymbolUsage/javaDeprecated.before.Main.kt
index 887988f0aa8..4c5a64968db 100644
--- a/idea/testData/quickfix/deprecatedSymbolUsage/javaDeprecated.before.Main.kt
+++ b/idea/testData/quickfix/deprecatedSymbolUsage/javaDeprecated.before.Main.kt
@@ -1,5 +1,4 @@
// "class org.jetbrains.kotlin.idea.quickfix.DeprecatedSymbolUsageFix" "false"
-// ACTION: Inspection 'DEPRECATED_SYMBOL_WITH_MESSAGE' options
fun foo() {
val c = JavaClass()
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/.inspection b/idea/testData/quickfix/migration/deleteRedundantExtension/.inspection
new file mode 100644
index 00000000000..eed53fe3f39
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt
new file mode 100644
index 00000000000..18d56b3d5a1
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt
@@ -0,0 +1,7 @@
+// "Delete redundant extension property" "true"
+
+var Thread.priority: Int
+ get() = this.getPriority()
+ set(value) {
+ this.setPriority(value)
+ }
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt.after b/idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt.after
new file mode 100644
index 00000000000..1aceb5e4c7e
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt.after
@@ -0,0 +1,2 @@
+// "Delete redundant extension property" "true"
+
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt
new file mode 100644
index 00000000000..5e17941aba2
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt
@@ -0,0 +1,7 @@
+// "Delete redundant extension property" "true"
+import java.io.File
+
+class C {
+ val File.name: String
+ get() = getName()
+}
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt.after b/idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt.after
new file mode 100644
index 00000000000..c1444359acd
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt.after
@@ -0,0 +1,5 @@
+// "Delete redundant extension property" "true"
+import java.io.File
+
+class C {
+}
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.JustImport.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.JustImport.kt
new file mode 100644
index 00000000000..8387e08ec93
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.JustImport.kt
@@ -0,0 +1,4 @@
+package test
+
+fun foo() {
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.StarImport.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.StarImport.kt
new file mode 100644
index 00000000000..5faa41b8874
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.StarImport.kt
@@ -0,0 +1,8 @@
+package test
+
+import java.io.File
+import utils.*
+
+fun foo(file: File) {
+ print(file.name)
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.Usages.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.Usages.kt
new file mode 100644
index 00000000000..b897040cdff
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.Usages.kt
@@ -0,0 +1,7 @@
+package test
+
+import java.io.File
+
+fun foo(file: File) {
+ print(file.name)
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.kt
new file mode 100644
index 00000000000..3465328a649
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.after.kt
@@ -0,0 +1,6 @@
+// "Delete redundant extension property" "true"
+package utils
+
+import java.io.File
+
+// WITH_RUNTIME
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.JustImport.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.JustImport.kt
new file mode 100644
index 00000000000..f259524280f
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.JustImport.kt
@@ -0,0 +1,6 @@
+package test
+
+import utils.name
+
+fun foo() {
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Main.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Main.kt
new file mode 100644
index 00000000000..91a5430aba4
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Main.kt
@@ -0,0 +1,9 @@
+// "Delete redundant extension property" "true"
+package utils
+
+import java.io.File
+
+val File.name: String
+ get() = getName()
+
+// WITH_RUNTIME
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.StarImport.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.StarImport.kt
new file mode 100644
index 00000000000..5faa41b8874
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.StarImport.kt
@@ -0,0 +1,8 @@
+package test
+
+import java.io.File
+import utils.*
+
+fun foo(file: File) {
+ print(file.name)
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Usages.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Usages.kt
new file mode 100644
index 00000000000..22aacc11d4b
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Usages.kt
@@ -0,0 +1,8 @@
+package test
+
+import java.io.File
+import utils.name
+
+fun foo(file: File) {
+ print(file.name)
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.after.Usages.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.after.Usages.kt
new file mode 100644
index 00000000000..8c558377f65
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.after.Usages.kt
@@ -0,0 +1,9 @@
+package test
+
+import java.io.File
+import utils.name
+
+fun foo(file: File, thread: Thread) {
+ print(file.name)
+ print(thread.name)
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.after.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.after.kt
new file mode 100644
index 00000000000..ae4de13e553
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.after.kt
@@ -0,0 +1,9 @@
+// "Delete redundant extension property" "true"
+package utils
+
+import java.io.File
+
+val Thread.name: String
+ get() = getName()
+
+// WITH_RUNTIME
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Main.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Main.kt
new file mode 100644
index 00000000000..a309f46c08b
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Main.kt
@@ -0,0 +1,12 @@
+// "Delete redundant extension property" "true"
+package utils
+
+import java.io.File
+
+val File.name: String
+ get() = getName()
+
+val Thread.name: String
+ get() = getName()
+
+// WITH_RUNTIME
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Usages.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Usages.kt
new file mode 100644
index 00000000000..8c558377f65
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Usages.kt
@@ -0,0 +1,9 @@
+package test
+
+import java.io.File
+import utils.name
+
+fun foo(file: File, thread: Thread) {
+ print(file.name)
+ print(thread.name)
+}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt
new file mode 100644
index 00000000000..4271fe88d04
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt
@@ -0,0 +1,5 @@
+// "Delete redundant extension property" "true"
+import java.io.File
+
+val File.name: String
+ get() { return getName() }
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt.after b/idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt.after
new file mode 100644
index 00000000000..c2fe797d149
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt.after
@@ -0,0 +1,3 @@
+// "Delete redundant extension property" "true"
+import java.io.File
+
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt
new file mode 100644
index 00000000000..4c2f0541488
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt
@@ -0,0 +1,5 @@
+// "Delete redundant extension property" "true"
+import java.io.File
+
+val File.name: String
+ get() = getName()
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt.after b/idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt.after
new file mode 100644
index 00000000000..c2fe797d149
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt.after
@@ -0,0 +1,3 @@
+// "Delete redundant extension property" "true"
+import java.io.File
+
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt
new file mode 100644
index 00000000000..1cde1940de0
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt
@@ -0,0 +1,4 @@
+// "Delete redundant extension property" "true"
+
+val Thread.priority: Int
+ get() = getPriority()
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt.after b/idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt.after
new file mode 100644
index 00000000000..1aceb5e4c7e
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt.after
@@ -0,0 +1,2 @@
+// "Delete redundant extension property" "true"
+
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/varInsteadOfVal.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/varInsteadOfVal.kt
new file mode 100644
index 00000000000..a8b1430e0d4
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/varInsteadOfVal.kt
@@ -0,0 +1,6 @@
+// "Delete redundant extension property" "false"
+import java.io.File
+
+var File.name: String
+ get() = getName()
+ set(value) {}
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt
new file mode 100644
index 00000000000..83e0f50a7d8
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt
@@ -0,0 +1,7 @@
+// "Delete redundant extension property" "true"
+
+var Thread.priority: Int
+ get() = getPriority()
+ set(value) {
+ setPriority(value)
+ }
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt.after b/idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt.after
new file mode 100644
index 00000000000..1aceb5e4c7e
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt.after
@@ -0,0 +1,2 @@
+// "Delete redundant extension property" "true"
+
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis.kt
new file mode 100644
index 00000000000..b46dea7f309
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis.kt
@@ -0,0 +1,7 @@
+// "Delete redundant extension property" "false"
+// ACTION: Convert property to function
+
+class C : Thread() {
+ val Thread.priority: Int
+ get() = this@C.getPriority()
+}
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis2.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis2.kt
new file mode 100644
index 00000000000..b492247ef7c
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis2.kt
@@ -0,0 +1,9 @@
+// "Delete redundant extension property" "false"
+
+class C : Thread() {
+ var Thread.priority: Int
+ get() = getPriority()
+ set(value) {
+ this@C.setPriority(value)
+ }
+}
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter.kt
new file mode 100644
index 00000000000..5afe2572e81
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter.kt
@@ -0,0 +1,7 @@
+// "Delete redundant extension property" "false"
+
+var Thread.priority: Int
+ get() = getPriority() + 1
+ set(value) {
+ setPriority(value)
+ }
\ No newline at end of file
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter2.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter2.kt
new file mode 100644
index 00000000000..ee8bb758fa8
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter2.kt
@@ -0,0 +1,6 @@
+// "Delete redundant extension property" "false"
+// ACTION: Convert property to function
+import java.io.File
+
+public val File.parent: File?
+ get() = getParentFile()
diff --git a/idea/testData/quickfix/migration/deleteRedundantExtension/wrongSetter.kt b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongSetter.kt
new file mode 100644
index 00000000000..5ac5e1e55fc
--- /dev/null
+++ b/idea/testData/quickfix/migration/deleteRedundantExtension/wrongSetter.kt
@@ -0,0 +1,8 @@
+// "Delete redundant extension property" "false"
+
+var Thread.priority: Int
+ get() = this.getPriority()
+ set(value) {
+ this.setPriority(value)
+ System.out.print("set")
+ }
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java
index fdb1874eaf8..6ffca985581 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/JetInspectionTestGenerated.java
@@ -94,6 +94,12 @@ public class JetInspectionTestGenerated extends AbstractJetInspectionTest {
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/inspections"), Pattern.compile("^(inspections\\.test)$"));
}
+ @TestMetadata("conflictingExtensionProperty/inspectionData/inspections.test")
+ public void testConflictingExtensionProperty_inspectionData_Inspections_test() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/conflictingExtensionProperty/inspectionData/inspections.test");
+ doTest(fileName);
+ }
+
@TestMetadata("redundantSamConstructor/inspectionData/inspections.test")
public void testRedundantSamConstructor_inspectionData_Inspections_test() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/redundantSamConstructor/inspectionData/inspections.test");
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java
index ca35d7439d4..de264b7e0a3 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java
@@ -21,18 +21,24 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
+import com.intellij.codeInspection.InspectionEP;
+import com.intellij.codeInspection.InspectionProfileEntry;
+import com.intellij.codeInspection.LocalInspectionEP;
import com.intellij.openapi.command.CommandProcessor;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.PsiFile;
import com.intellij.util.ArrayUtil;
+import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import junit.framework.ComparisonFailure;
import kotlin.KotlinPackage;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.idea.KotlinDaemonAnalyzerTestCase;
+import org.jetbrains.kotlin.idea.quickfix.utils.UtilsPackage;
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil;
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
@@ -42,6 +48,7 @@ import org.jetbrains.kotlin.test.JetTestUtils;
import java.io.File;
import java.io.FilenameFilter;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -65,9 +72,35 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer
}
protected void doTestWithExtraFile(String beforeFileName) throws Exception {
+ enableInspections(beforeFileName);
doTest(beforeFileName, true);
}
+ private void enableInspections(String beforeFileName) throws IOException, ClassNotFoundException {
+ File inspectionFile = UtilsPackage.findInspectionFile(new File(beforeFileName).getParentFile());
+ if (inspectionFile != null) {
+ String className = FileUtil.loadFile(inspectionFile).trim();
+ Class> inspectionClass = Class.forName(className);
+ enableInspectionTools(inspectionClass);
+ }
+ }
+
+ private void enableInspectionTools(@NotNull Class> klass) {
+ List eps = ContainerUtil.newArrayList();
+ ContainerUtil.addAll(eps, Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION));
+ ContainerUtil.addAll(eps, Extensions.getExtensions(InspectionEP.GLOBAL_INSPECTION));
+
+ InspectionProfileEntry tool = null;
+ for (InspectionEP ep : eps) {
+ if (klass.getName().equals(ep.implementationClass)) {
+ tool = ep.instantiateTool();
+ }
+ }
+ assert tool != null : "Could not find inspection tool for class: " + klass;
+
+ enableInspectionTools(tool);
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
index 2ca6ddf98b9..8103b4971c9 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixTest.java
@@ -41,6 +41,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinLightQuickFixTestCase;
import org.jetbrains.kotlin.idea.js.KotlinJavaScriptLibraryManager;
+import org.jetbrains.kotlin.idea.quickfix.utils.UtilsPackage;
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil;
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
@@ -64,19 +65,6 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
((StartupManagerImpl) StartupManager.getInstance(getProject())).runPostStartupActivities();
}
- @Nullable
- private static File findInspectionFile(@NotNull File startDir) {
- File currentDir = startDir;
- while (currentDir != null) {
- File inspectionFile = new File(currentDir, ".inspection");
- if (inspectionFile.exists()) {
- return inspectionFile;
- }
- currentDir = currentDir.getParentFile();
- }
- return null;
- }
-
protected void doTest(@NotNull String beforeFileName) throws Exception {
try {
configureRuntimeIfNeeded(beforeFileName);
@@ -184,7 +172,7 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
}
private void enableInspections(String beforeFileName) throws IOException, ClassNotFoundException {
- File inspectionFile = findInspectionFile(new File(beforeFileName).getParentFile());
+ File inspectionFile = UtilsPackage.findInspectionFile(new File(beforeFileName).getParentFile());
if (inspectionFile != null) {
String className = FileUtil.loadFile(inspectionFile).trim();
Class> inspectionClass = Class.forName(className);
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java
index 47d08f13e66..27f34f95418 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java
@@ -990,6 +990,27 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
+ @TestMetadata("idea/testData/quickfix/migration/deleteRedundantExtension")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class DeleteRedundantExtension extends AbstractQuickFixMultiFileTest {
+ public void testAllFilesPresentInDeleteRedundantExtension() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/deleteRedundantExtension"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
+ }
+
+ @TestMetadata("removeImports.before.Main.kt")
+ public void testRemoveImports() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/removeImports.before.Main.kt");
+ doTestWithExtraFile(fileName);
+ }
+
+ @TestMetadata("removeImportsOverloads.before.Main.kt")
+ public void testRemoveImportsOverloads() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/removeImportsOverloads.before.Main.kt");
+ doTestWithExtraFile(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
index 34924b1ae30..af86ddf1757 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java
@@ -3923,6 +3923,87 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
+ @TestMetadata("idea/testData/quickfix/migration/deleteRedundantExtension")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class DeleteRedundantExtension extends AbstractQuickFixTest {
+ public void testAllFilesPresentInDeleteRedundantExtension() throws Exception {
+ JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/deleteRedundantExtension"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
+ }
+
+ @TestMetadata("explicitThis.kt")
+ public void testExplicitThis() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/explicitThis.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("memberExtension.kt")
+ public void testMemberExtension() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/memberExtension.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("returnInGetter.kt")
+ public void testReturnInGetter() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/returnInGetter.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("simple.kt")
+ public void testSimple() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/simple.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("valInsteadOfVar.kt")
+ public void testValInsteadOfVar() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/valInsteadOfVar.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("varInsteadOfVal.kt")
+ public void testVarInsteadOfVal() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/varInsteadOfVal.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("withSetter.kt")
+ public void testWithSetter() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/withSetter.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("wrongExplicitThis.kt")
+ public void testWrongExplicitThis() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("wrongExplicitThis2.kt")
+ public void testWrongExplicitThis2() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/wrongExplicitThis2.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("wrongGetter.kt")
+ public void testWrongGetter() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("wrongGetter2.kt")
+ public void testWrongGetter2() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/wrongGetter2.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("wrongSetter.kt")
+ public void testWrongSetter() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deleteRedundantExtension/wrongSetter.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/quickfix/migration/lambdaSyntax")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/quickfixTestUtils.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/quickfixTestUtils.kt
new file mode 100644
index 00000000000..87121b05021
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/quickfixTestUtils.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2010-2015 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.quickfix.utils
+
+import java.io.File
+
+fun findInspectionFile(startDir: File): File? {
+ var currentDir: File? = startDir
+ while (currentDir != null) {
+ val inspectionFile = File(currentDir, ".inspection")
+ if (inspectionFile.exists()) {
+ return inspectionFile
+ }
+ currentDir = currentDir.parentFile
+ }
+ return null
+}