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 inspection profile setting",
|
||||
"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"
|
||||
/>
|
||||
|
||||
<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"/>
|
||||
|
||||
<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"
|
||||
// ACTION: Inspection 'DEPRECATED_SYMBOL_WITH_MESSAGE' options
|
||||
|
||||
fun foo() {
|
||||
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)$"));
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
@@ -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<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
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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