Spring Support: Inspection for final Spring-annotated classes/functions

#KT-11098 Fixed
This commit is contained in:
Alexey Sedunov
2016-02-20 23:01:27 +03:00
parent 161d11d9ec
commit 72a17b0671
30 changed files with 582 additions and 12 deletions
+1
View File
@@ -96,6 +96,7 @@ New features:
- Kotlin Education Plugin (for IDEA 2016)
- [KT-9752](https://youtrack.jetbrains.com/issue/KT-9752) More usable file chooser for "Move declaration to another file"
- [KT-9697](https://youtrack.jetbrains.com/issue/KT-9697) Move method to companion object and back
- [KT-11098](https://youtrack.jetbrains.com/issue/KT-11098) Inspection on final classes/functions annotated with Spring @Configuration/@Component/@Bean
General issues fixed:
@@ -775,7 +775,13 @@ fun main(args: Array<String>) {
}
testGroup("idea/idea-ultimate/tests", "idea/testData") {
testClass<AbstractInspectionTest>("UltimateInspectionTestGenerated") {
model("ultimateInspections", pattern = "^(inspections\\.test)$", singleClass = true)
}
testClass<AbstractQuickFixTest>("UltimateQuickFixTestGenerated") {
model("ultimateQuickFixes", pattern = "^([\\w\\-_]+)\\.kt$", filenameStartsLowerCase = true)
}
}
testGroup("idea/tests", "compiler/testData") {
+1
View File
@@ -5,6 +5,7 @@
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
@@ -0,0 +1,5 @@
<html>
<body>
This inspection reports final Kotlin classes and functions annotated with Spring @Component, @Configuration or @Bean annotations
</body>
</html>
@@ -1,5 +1,11 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<localInspection implementationClass="org.jetbrains.kotlin.idea.spring.inspections.KotlinFinalClassOrFunSpringInspection"
displayName="Final Kotlin class or function with Spring annotation"
groupBundle="resources.messages.SpringBundle"
groupKey="inspection.group.code"
groupPath="Spring,Spring Core"
enabledByDefault="true"
level="WARNING"/>
</extensions>
</idea-plugin>
@@ -0,0 +1,92 @@
/*
* Copyright 2010-2016 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.spring.inspections
import com.intellij.codeInsight.highlighting.HighlightUsagesDescriptionLocation
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.psi.ElementDescriptionUtil
import com.intellij.psi.PsiElementVisitor
import com.intellij.spring.constants.SpringAnnotationsConstants
import com.intellij.spring.model.jam.stereotype.SpringComponent
import com.intellij.spring.model.jam.stereotype.SpringConfiguration
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
import org.jetbrains.kotlin.idea.spring.isAnnotatedWith
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.isInheritable
import org.jetbrains.kotlin.psi.psiUtil.isOverridable
class KotlinFinalClassOrFunSpringInspection : AbstractKotlinInspection() {
class QuickFix<T: KtModifierListOwner>(private val element: T) : LocalQuickFix {
override fun getName(): String {
return "Make ${ElementDescriptionUtil.getElementDescription(element, HighlightUsagesDescriptionLocation.INSTANCE)} open"
}
override fun getFamilyName() = "Make declaration open"
override fun applyFix(project: Project, problemDescriptor: ProblemDescriptor) {
(element as? KtNamedDeclaration)?.containingClassOrObject?.addModifier(KtTokens.OPEN_KEYWORD)
element.addModifier(KtTokens.OPEN_KEYWORD)
}
}
private fun getMessage(declaration: KtNamedDeclaration): String? {
when (declaration) {
is KtClass -> {
val lightClass = declaration.toLightClass() ?: return null
when {
SpringConfiguration.META.getJamElement(lightClass) != null -> return "@Configuration class should be declared open"
SpringComponent.META.getJamElement(lightClass) != null -> return "@Component class should be declared open"
}
}
is KtNamedFunction -> {
val lightMethod = declaration.toLightMethods().firstOrNull() ?: return null
if (lightMethod.isAnnotatedWith(SpringAnnotationsConstants.JAVA_SPRING_BEAN)) return "@Bean function should be declared open"
}
}
return null
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object: KtVisitorVoid() {
override fun visitNamedDeclaration(declaration: KtNamedDeclaration) {
when (declaration) {
is KtClass -> if (declaration.isInheritable()) return
is KtNamedFunction -> if (declaration.isOverridable()) return
else -> return
}
val message = getMessage(declaration) ?: return
holder.registerProblem(
declaration.nameIdentifier ?: declaration,
message,
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
QuickFix(declaration)
)
}
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2016 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.spring
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiModifierListOwner
import com.intellij.spring.model.jam.utils.JamAnnotationTypeUtil
internal fun PsiModifierListOwner.isAnnotatedWith(annotationFqName: String): Boolean {
val module = ModuleUtilCore.findModuleForPsiElement(this) ?: return false
return JamAnnotationTypeUtil.getInstance(module)
.getAnnotationTypesWithChildren(annotationFqName)
.mapNotNull { it.qualifiedName }
.any { AnnotationUtil.isAnnotated(this, it, true) }
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2016 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.codeInsight;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/ultimateInspections")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class UltimateInspectionTestGenerated extends AbstractInspectionTest {
public void testAllFilesPresentInUltimateInspections() throws Exception {
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/ultimateInspections"), Pattern.compile("^(inspections\\.test)$"));
}
@TestMetadata("spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test")
public void testSpring_finalSpringAnnotatedDeclaration_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateInspections/spring/finalSpringAnnotatedDeclaration/inspectionData/inspections.test");
doTest(fileName);
}
}
@@ -0,0 +1,97 @@
/*
* Copyright 2010-2016 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;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.JUnit3RunnerWithInners;
import org.jetbrains.kotlin.test.KotlinTestUtils;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.runner.RunWith;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("idea/testData/ultimateQuickFixes")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public class UltimateQuickFixTestGenerated extends AbstractQuickFixTest {
public void testAllFilesPresentInUltimateQuickFixes() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/ultimateQuickFixes"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("idea/testData/ultimateQuickFixes/spring")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Spring extends AbstractQuickFixTest {
public void testAllFilesPresentInSpring() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/ultimateQuickFixes/spring"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FinalSpringAnnotatedDeclaration extends AbstractQuickFixTest {
public void testAllFilesPresentInFinalSpringAnnotatedDeclaration() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("classWithComponentRuntime.kt")
public void testClassWithComponentRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt");
doTest(fileName);
}
@TestMetadata("classWithConfigurationRuntime.kt")
public void testClassWithConfigurationRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/classWithConfigurationRuntime.kt");
doTest(fileName);
}
@TestMetadata("classWithCustomConfigurationRuntime.kt")
public void testClassWithCustomConfigurationRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/classWithCustomConfigurationRuntime.kt");
doTest(fileName);
}
@TestMetadata("funWithBeanFinalClassRuntime.kt")
public void testFunWithBeanFinalClassRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanFinalClassRuntime.kt");
doTest(fileName);
}
@TestMetadata("funWithBeanOpenClassRuntime.kt")
public void testFunWithBeanOpenClassRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/funWithBeanOpenClassRuntime.kt");
doTest(fileName);
}
@TestMetadata("funWithCustomBeanFinalClassRuntime.kt")
public void testFunWithCustomBeanFinalClassRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanFinalClassRuntime.kt");
doTest(fileName);
}
@TestMetadata("funWithCustomBeanOpenClassRuntime.kt")
public void testFunWithCustomBeanOpenClassRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/funWithCustomBeanOpenClassRuntime.kt");
doTest(fileName);
}
}
}
}
@@ -53,9 +53,12 @@ class SpringTestFixtureExtension() : TestFixtureExtension {
override fun tearDown() {
try {
// clear existing SpringFacet configuration before running next test
module?.let { SpringFacet.getInstance(it) }?.let {
it.removeFileSets()
FacetUtil.deleteFacet(it)
module?.let { module ->
SpringFacet.getInstance(module)?.let { facet ->
facet.removeFileSets()
FacetUtil.deleteFacet(facet)
}
ConfigLibraryUtil.removeLibrary(module, "spring" + SpringFramework.FRAMEWORK_4_2_0.version)
}
}
finally {
@@ -0,0 +1,74 @@
<problems>
<problem>
<file>test.kt</file>
<line>18</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Configuration class should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>21</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Configuration class should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>32</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Component class should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>41</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Bean function should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>44</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Bean function should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>47</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Bean function should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>50</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Bean function should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>55</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Bean function should be declared open</description>
</problem>
<problem>
<file>test.kt</file>
<line>58</line>
<module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Final Kotlin class or function with Spring annotation</problem_class>
<description>@Bean function should be declared open</description>
</problem>
</problems>
@@ -0,0 +1,2 @@
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.spring.inspections.KotlinFinalClassOrFunSpringInspection
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
@@ -0,0 +1,65 @@
// WITH_RUNTIME
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Component
@Configuration
annotation class MyConfiguration
@Bean
annotation class MyBean
// @Configuration
@Configuration
class Application1
@MyConfiguration
class Application2
@Configuration
open class Application3
@MyConfiguration
open class Application4
// @Component
@Component
class Component1
@Component
open class Component2
// @Bean
class Utils1 {
@Bean
fun foo1() = Component1()
@MyBean
fun foo2() = Component2()
@Bean
open fun foo3() = Component3()
@MyBean
open fun foo4() = Component4()
}
open class Utils2 {
@Bean
fun foo1() = Component1()
@MyBean
fun foo2() = Component2()
@Bean
open fun foo3() = Component3()
@MyBean
open fun foo4() = Component4()
}
@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.spring.inspections.KotlinFinalClassOrFunSpringInspection
@@ -0,0 +1,7 @@
// "Make class Bean open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.stereotype.Component
@Component
class <caret>Bean
@@ -0,0 +1,7 @@
// "Make class Bean open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.stereotype.Component
@Component
open class <caret>Bean
@@ -0,0 +1,7 @@
// "Make class Application open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Configuration
@Configuration
class <caret>Application
@@ -0,0 +1,7 @@
// "Make class Application open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Configuration
@Configuration
open class <caret>Application
@@ -0,0 +1,10 @@
// "Make class Application open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Configuration
@Configuration
annotation class MyConfiguration
@MyConfiguration
class <caret>Application
@@ -0,0 +1,10 @@
// "Make class Application open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Configuration
@Configuration
annotation class MyConfiguration
@MyConfiguration
open class <caret>Application
@@ -0,0 +1,9 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
class Foo {
@Bean
fun <caret>foo() = ""
}
@@ -0,0 +1,9 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
open class Foo {
@Bean
open fun <caret>foo() = ""
}
@@ -0,0 +1,9 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
open class Foo {
@Bean
fun <caret>foo() = ""
}
@@ -0,0 +1,9 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
open class Foo {
@Bean
open fun <caret>foo() = ""
}
@@ -0,0 +1,12 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
@Bean
annotation class MyBean
class Foo {
@MyBean
fun <caret>foo() = ""
}
@@ -0,0 +1,12 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
@Bean
annotation class MyBean
open class Foo {
@MyBean
open fun <caret>foo() = ""
}
@@ -0,0 +1,12 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
@Bean
annotation class MyBean
open class Foo {
@MyBean
fun <caret>foo() = ""
}
@@ -0,0 +1,12 @@
// "Make function foo open" "true"
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
// DISABLE-ERRORS
import org.springframework.context.annotation.Bean
@Bean
annotation class MyBean
open class Foo {
@MyBean
open fun <caret>foo() = ""
}
@@ -24,21 +24,15 @@ import com.intellij.codeInspection.ex.InspectionManagerEx
import com.intellij.codeInspection.ex.LocalInspectionToolWrapper
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiFile
import com.intellij.testFramework.IdeaTestUtil
import com.intellij.testFramework.InspectionTestUtil
import com.intellij.testFramework.LightProjectDescriptor
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.test.KotlinLightProjectDescriptor
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
import org.jetbrains.kotlin.idea.test.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import kotlin.test.assertFalse
import java.util.*
abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase() {
companion object {
@@ -64,6 +58,8 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, "// INSPECTION_CLASS: ")!!)
val toolWrapper = LocalInspectionToolWrapper(inspectionClass.newInstance() as LocalInspectionTool)
val fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(options, "// FIXTURE_CLASS: ")
val inspectionsTestDir = optionsFile.parentFile!!
val srcDir = inspectionsTestDir.parentFile!!
@@ -74,7 +70,7 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
val psiFiles = srcDir.walkTopDown().onEnter { it.name != "inspectionData" }.mapNotNull {
file ->
if (file.isDirectory) {
null
null
}
else if (file.extension != "kt") {
val filePath = file.relativeTo(srcDir).invariantSeparatorsPath
@@ -112,6 +108,8 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
)
}
fixtureClasses.forEach { TestFixtureExtension.loadFixture(it, myFixture.module) }
val scope = AnalysisScope(project, psiFiles.map { it.virtualFile!! })
scope.invalidate()
@@ -139,6 +137,8 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
}
finally {
fixtureClasses.forEach { TestFixtureExtension.unloadFixture(it) }
if (isWithRuntime) {
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myFixture.module, IdeaTestUtil.getMockJdk17())
}
@@ -49,6 +49,7 @@ import org.jetbrains.kotlin.idea.quickfix.utils.QuickfixTestUtilsKt;
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil;
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.idea.test.TestFixtureExtension;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
import org.jetbrains.kotlin.test.KotlinTestUtils;
@@ -57,6 +58,7 @@ import org.junit.Assert;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -132,8 +134,15 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
public void run() {
String fileText = "";
String expectedErrorMessage = "";
List<String> fixtureClasses = Collections.emptyList();
try {
fileText = FileUtil.loadFile(testFile, CharsetToolkit.UTF8_CHARSET);
fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(fileText, "// FIXTURE_CLASS: ");
for (String fixtureClass : fixtureClasses) {
TestFixtureExtension.Companion.loadFixture(fixtureClass, getModule());
}
expectedErrorMessage = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// SHOULD_FAIL_WITH: ");
String contents = StringUtil.convertLineSeparators(fileText);
quickFixTestCase.configureFromFileText(testFile.getName(), contents);
@@ -157,6 +166,9 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
fail(testName);
}
} finally {
for (String fixtureClass : fixtureClasses) {
TestFixtureExtension.Companion.unloadFixture(fixtureClass);
}
ConfigLibraryUtil.unconfigureLibrariesByDirective(getModule(), fileText);
}
}