Inspection (and quickfix) for extension propeties conflicting with synthetic ones
This commit is contained in:
+3
-1
@@ -68,6 +68,8 @@ public object DirectiveBasedActionUtils {
|
|||||||
"Edit intention settings",
|
"Edit intention settings",
|
||||||
"Edit inspection profile setting",
|
"Edit inspection profile setting",
|
||||||
"Inject language or reference",
|
"Inject language or reference",
|
||||||
"Suppress '"
|
"Suppress '",
|
||||||
|
"Run inspection on",
|
||||||
|
"Inspection '"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
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.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1138,6 +1138,13 @@
|
|||||||
level="WARNING"
|
level="WARNING"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection"
|
||||||
|
displayName="Extension property conflicts with synthetic one"
|
||||||
|
groupName="Kotlin"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="WARNING"
|
||||||
|
/>
|
||||||
|
|
||||||
<project.converterProvider implementation="org.jetbrains.kotlin.idea.converters.JetRunConfigurationSettingsFormatConverterProvider"/>
|
<project.converterProvider implementation="org.jetbrains.kotlin.idea.converters.JetRunConfigurationSettingsFormatConverterProvider"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
+195
@@ -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<JetProperty>(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<JetSimpleNameReference>()
|
||||||
|
.map { ref -> ref.expression.getStrictParentOfType<JetImportDirective>() }
|
||||||
|
.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() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
<problems>
|
||||||
|
<problem>
|
||||||
|
<file>File.kt</file>
|
||||||
|
<line>4</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="File.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Extension property conflicts with automatically produced one</problem_class>
|
||||||
|
<description>This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler</description>
|
||||||
|
</problem>
|
||||||
|
|
||||||
|
<problem>
|
||||||
|
<file>File.kt</file>
|
||||||
|
<line>10</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="File.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Extension property conflicts with automatically produced one</problem_class>
|
||||||
|
<description>This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler</description>
|
||||||
|
</problem>
|
||||||
|
|
||||||
|
<problem>
|
||||||
|
<file>File.kt</file>
|
||||||
|
<line>15</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="File.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Extension property conflicts with automatically produced one</problem_class>
|
||||||
|
<description>This property conflicts with synthetic extension and should be removed to avoid breaking code by future changes in the compiler</description>
|
||||||
|
</problem>
|
||||||
|
</problems>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
// "class org.jetbrains.kotlin.idea.quickfix.DeprecatedSymbolUsageFix" "false"
|
// "class org.jetbrains.kotlin.idea.quickfix.DeprecatedSymbolUsageFix" "false"
|
||||||
// ACTION: Inspection 'DEPRECATED_SYMBOL_WITH_MESSAGE' options
|
|
||||||
|
|
||||||
fun foo() {
|
fun foo() {
|
||||||
val c = JavaClass()
|
val c = JavaClass()
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
|
||||||
|
var Thread.<caret>priority: Int
|
||||||
|
get() = this.getPriority()
|
||||||
|
set(value) {
|
||||||
|
this.setPriority(value)
|
||||||
|
}
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class C {
|
||||||
|
val File.<caret>name: String
|
||||||
|
get() = getName()
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class C {
|
||||||
|
}
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
}
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import utils.*
|
||||||
|
|
||||||
|
fun foo(file: File) {
|
||||||
|
print(file.name)
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
fun foo(file: File) {
|
||||||
|
print(file.name)
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
// WITH_RUNTIME
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import utils.name
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val File.<caret>name: String
|
||||||
|
get() = getName()
|
||||||
|
|
||||||
|
// WITH_RUNTIME
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import utils.*
|
||||||
|
|
||||||
|
fun foo(file: File) {
|
||||||
|
print(file.name)
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import utils.name
|
||||||
|
|
||||||
|
fun foo(file: File) {
|
||||||
|
print(file.name)
|
||||||
|
}
|
||||||
Vendored
+9
@@ -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)
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val Thread.name: String
|
||||||
|
get() = getName()
|
||||||
|
|
||||||
|
// WITH_RUNTIME
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val File.<caret>name: String
|
||||||
|
get() = getName()
|
||||||
|
|
||||||
|
val Thread.name: String
|
||||||
|
get() = getName()
|
||||||
|
|
||||||
|
// WITH_RUNTIME
|
||||||
Vendored
+9
@@ -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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val File.<caret>name: String
|
||||||
|
get() { return getName() }
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val File.<caret>name: String
|
||||||
|
get() = getName()
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
|
||||||
|
val Thread.<caret>priority: Int
|
||||||
|
get() = getPriority()
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Delete redundant extension property" "false"
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
var File.<caret>name: String
|
||||||
|
get() = getName()
|
||||||
|
set(value) {}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
|
||||||
|
var Thread.<caret>priority: Int
|
||||||
|
get() = getPriority()
|
||||||
|
set(value) {
|
||||||
|
setPriority(value)
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
// "Delete redundant extension property" "true"
|
||||||
|
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Delete redundant extension property" "false"
|
||||||
|
// ACTION: Convert property to function
|
||||||
|
|
||||||
|
class C : Thread() {
|
||||||
|
val Thread.<caret>priority: Int
|
||||||
|
get() = this@C.getPriority()
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
// "Delete redundant extension property" "false"
|
||||||
|
|
||||||
|
class C : Thread() {
|
||||||
|
var Thread.<caret>priority: Int
|
||||||
|
get() = getPriority()
|
||||||
|
set(value) {
|
||||||
|
this@C.setPriority(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Delete redundant extension property" "false"
|
||||||
|
|
||||||
|
var Thread.<caret>priority: Int
|
||||||
|
get() = getPriority() + 1
|
||||||
|
set(value) {
|
||||||
|
setPriority(value)
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// "Delete redundant extension property" "false"
|
||||||
|
// ACTION: Convert property to function
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
public val File.<caret>parent: File?
|
||||||
|
get() = getParentFile()
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// "Delete redundant extension property" "false"
|
||||||
|
|
||||||
|
var Thread.<caret>priority: Int
|
||||||
|
get() = this.getPriority()
|
||||||
|
set(value) {
|
||||||
|
this.setPriority(value)
|
||||||
|
System.out.print("set")
|
||||||
|
}
|
||||||
@@ -94,6 +94,12 @@ public class JetInspectionTestGenerated extends AbstractJetInspectionTest {
|
|||||||
JetTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/inspections"), Pattern.compile("^(inspections\\.test)$"));
|
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")
|
@TestMetadata("redundantSamConstructor/inspectionData/inspections.test")
|
||||||
public void testRedundantSamConstructor_inspectionData_Inspections_test() throws Exception {
|
public void testRedundantSamConstructor_inspectionData_Inspections_test() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/redundantSamConstructor/inspectionData/inspections.test");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/inspections/redundantSamConstructor/inspectionData/inspections.test");
|
||||||
|
|||||||
@@ -21,18 +21,24 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
|||||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
|
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
|
||||||
import com.intellij.codeInsight.intention.IntentionAction;
|
import com.intellij.codeInsight.intention.IntentionAction;
|
||||||
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
|
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.command.CommandProcessor;
|
||||||
|
import com.intellij.openapi.extensions.Extensions;
|
||||||
import com.intellij.openapi.projectRoots.Sdk;
|
import com.intellij.openapi.projectRoots.Sdk;
|
||||||
import com.intellij.openapi.util.Pair;
|
import com.intellij.openapi.util.Pair;
|
||||||
import com.intellij.openapi.util.io.FileUtil;
|
import com.intellij.openapi.util.io.FileUtil;
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile;
|
||||||
import com.intellij.util.ArrayUtil;
|
import com.intellij.util.ArrayUtil;
|
||||||
|
import com.intellij.util.containers.ContainerUtil;
|
||||||
import com.intellij.util.ui.UIUtil;
|
import com.intellij.util.ui.UIUtil;
|
||||||
import junit.framework.ComparisonFailure;
|
import junit.framework.ComparisonFailure;
|
||||||
import kotlin.KotlinPackage;
|
import kotlin.KotlinPackage;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.kotlin.idea.KotlinDaemonAnalyzerTestCase;
|
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.ConfigLibraryUtil;
|
||||||
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
|
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||||
@@ -42,6 +48,7 @@ import org.jetbrains.kotlin.test.JetTestUtils;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FilenameFilter;
|
import java.io.FilenameFilter;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -65,9 +72,35 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void doTestWithExtraFile(String beforeFileName) throws Exception {
|
protected void doTestWithExtraFile(String beforeFileName) throws Exception {
|
||||||
|
enableInspections(beforeFileName);
|
||||||
doTest(beforeFileName, true);
|
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<InspectionEP> 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
|
@Override
|
||||||
protected void setUp() throws Exception {
|
protected void setUp() throws Exception {
|
||||||
super.setUp();
|
super.setUp();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.idea.KotlinLightQuickFixTestCase;
|
import org.jetbrains.kotlin.idea.KotlinLightQuickFixTestCase;
|
||||||
import org.jetbrains.kotlin.idea.js.KotlinJavaScriptLibraryManager;
|
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.ConfigLibraryUtil;
|
||||||
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
|
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
|
||||||
@@ -64,19 +65,6 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
|
|||||||
((StartupManagerImpl) StartupManager.getInstance(getProject())).runPostStartupActivities();
|
((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 {
|
protected void doTest(@NotNull String beforeFileName) throws Exception {
|
||||||
try {
|
try {
|
||||||
configureRuntimeIfNeeded(beforeFileName);
|
configureRuntimeIfNeeded(beforeFileName);
|
||||||
@@ -184,7 +172,7 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void enableInspections(String beforeFileName) throws IOException, ClassNotFoundException {
|
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) {
|
if (inspectionFile != null) {
|
||||||
String className = FileUtil.loadFile(inspectionFile).trim();
|
String className = FileUtil.loadFile(inspectionFile).trim();
|
||||||
Class<?> inspectionClass = Class.forName(className);
|
Class<?> inspectionClass = Class.forName(className);
|
||||||
|
|||||||
@@ -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);
|
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")
|
@TestMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -3923,6 +3923,87 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
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")
|
@TestMetadata("idea/testData/quickfix/migration/lambdaSyntax")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user