Spring Support: Inspection for final Spring-annotated classes/functions
#KT-11098 Fixed
This commit is contained in:
@@ -96,6 +96,7 @@ New features:
|
|||||||
- Kotlin Education Plugin (for IDEA 2016)
|
- 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-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-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:
|
General issues fixed:
|
||||||
|
|
||||||
|
|||||||
@@ -775,7 +775,13 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
testGroup("idea/idea-ultimate/tests", "idea/testData") {
|
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") {
|
testGroup("idea/tests", "compiler/testData") {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<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>
|
<idea-plugin>
|
||||||
<extensions defaultExtensionNs="com.intellij">
|
<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>
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
||||||
+92
@@ -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) }
|
||||||
|
}
|
||||||
+43
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
+97
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-3
@@ -53,9 +53,12 @@ class SpringTestFixtureExtension() : TestFixtureExtension {
|
|||||||
override fun tearDown() {
|
override fun tearDown() {
|
||||||
try {
|
try {
|
||||||
// clear existing SpringFacet configuration before running next test
|
// clear existing SpringFacet configuration before running next test
|
||||||
module?.let { SpringFacet.getInstance(it) }?.let {
|
module?.let { module ->
|
||||||
it.removeFileSets()
|
SpringFacet.getInstance(module)?.let { facet ->
|
||||||
FacetUtil.deleteFacet(it)
|
facet.removeFileSets()
|
||||||
|
FacetUtil.deleteFacet(facet)
|
||||||
|
}
|
||||||
|
ConfigLibraryUtil.removeLibrary(module, "spring" + SpringFramework.FRAMEWORK_4_2_0.version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
|||||||
idea/testData/ultimateInspections/spring/finalSpringAnnotatedDeclaration/inspectionData/expected.xml
Vendored
+74
@@ -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>
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.spring.inspections.KotlinFinalClassOrFunSpringInspection
|
||||||
|
// FIXTURE_CLASS: org.jetbrains.kotlin.idea.spring.tests.SpringTestFixtureExtension
|
||||||
+65
@@ -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()
|
||||||
|
}
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.spring.inspections.KotlinFinalClassOrFunSpringInspection
|
||||||
idea/testData/ultimateQuickFixes/spring/finalSpringAnnotatedDeclaration/classWithComponentRuntime.kt
Vendored
+7
@@ -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
|
||||||
+7
@@ -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
|
||||||
+7
@@ -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
|
||||||
+7
@@ -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
|
||||||
+10
@@ -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
|
||||||
+10
@@ -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
|
||||||
+9
@@ -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() = ""
|
||||||
|
}
|
||||||
+9
@@ -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() = ""
|
||||||
|
}
|
||||||
+9
@@ -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() = ""
|
||||||
|
}
|
||||||
+9
@@ -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() = ""
|
||||||
|
}
|
||||||
+12
@@ -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() = ""
|
||||||
|
}
|
||||||
+12
@@ -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() = ""
|
||||||
|
}
|
||||||
+12
@@ -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() = ""
|
||||||
|
}
|
||||||
+12
@@ -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.codeInspection.ex.LocalInspectionToolWrapper
|
||||||
import com.intellij.openapi.command.CommandProcessor
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import com.intellij.psi.PsiFile
|
|
||||||
import com.intellij.testFramework.IdeaTestUtil
|
import com.intellij.testFramework.IdeaTestUtil
|
||||||
import com.intellij.testFramework.InspectionTestUtil
|
import com.intellij.testFramework.InspectionTestUtil
|
||||||
import com.intellij.testFramework.LightProjectDescriptor
|
import com.intellij.testFramework.LightProjectDescriptor
|
||||||
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
import org.jetbrains.kotlin.idea.test.*
|
||||||
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.util.application.runWriteAction
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.test.assertFalse
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase() {
|
abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -64,6 +58,8 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
|
|||||||
val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, "// INSPECTION_CLASS: ")!!)
|
val inspectionClass = Class.forName(InTextDirectivesUtils.findStringWithPrefixes(options, "// INSPECTION_CLASS: ")!!)
|
||||||
val toolWrapper = LocalInspectionToolWrapper(inspectionClass.newInstance() as LocalInspectionTool)
|
val toolWrapper = LocalInspectionToolWrapper(inspectionClass.newInstance() as LocalInspectionTool)
|
||||||
|
|
||||||
|
val fixtureClasses = InTextDirectivesUtils.findListWithPrefixes(options, "// FIXTURE_CLASS: ")
|
||||||
|
|
||||||
val inspectionsTestDir = optionsFile.parentFile!!
|
val inspectionsTestDir = optionsFile.parentFile!!
|
||||||
val srcDir = inspectionsTestDir.parentFile!!
|
val srcDir = inspectionsTestDir.parentFile!!
|
||||||
|
|
||||||
@@ -74,7 +70,7 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
|
|||||||
val psiFiles = srcDir.walkTopDown().onEnter { it.name != "inspectionData" }.mapNotNull {
|
val psiFiles = srcDir.walkTopDown().onEnter { it.name != "inspectionData" }.mapNotNull {
|
||||||
file ->
|
file ->
|
||||||
if (file.isDirectory) {
|
if (file.isDirectory) {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
else if (file.extension != "kt") {
|
else if (file.extension != "kt") {
|
||||||
val filePath = file.relativeTo(srcDir).invariantSeparatorsPath
|
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!! })
|
val scope = AnalysisScope(project, psiFiles.map { it.virtualFile!! })
|
||||||
scope.invalidate()
|
scope.invalidate()
|
||||||
|
|
||||||
@@ -139,6 +137,8 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
|
|||||||
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
fixtureClasses.forEach { TestFixtureExtension.unloadFixture(it) }
|
||||||
|
|
||||||
if (isWithRuntime) {
|
if (isWithRuntime) {
|
||||||
ConfigLibraryUtil.unConfigureKotlinRuntimeAndSdk(myFixture.module, IdeaTestUtil.getMockJdk17())
|
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.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;
|
||||||
|
import org.jetbrains.kotlin.idea.test.TestFixtureExtension;
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.psi.KtFile;
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
import org.jetbrains.kotlin.test.KotlinTestUtils;
|
||||||
@@ -57,6 +58,7 @@ import org.junit.Assert;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -132,8 +134,15 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
|
|||||||
public void run() {
|
public void run() {
|
||||||
String fileText = "";
|
String fileText = "";
|
||||||
String expectedErrorMessage = "";
|
String expectedErrorMessage = "";
|
||||||
|
List<String> fixtureClasses = Collections.emptyList();
|
||||||
try {
|
try {
|
||||||
fileText = FileUtil.loadFile(testFile, CharsetToolkit.UTF8_CHARSET);
|
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: ");
|
expectedErrorMessage = InTextDirectivesUtils.findStringWithPrefixes(fileText, "// SHOULD_FAIL_WITH: ");
|
||||||
String contents = StringUtil.convertLineSeparators(fileText);
|
String contents = StringUtil.convertLineSeparators(fileText);
|
||||||
quickFixTestCase.configureFromFileText(testFile.getName(), contents);
|
quickFixTestCase.configureFromFileText(testFile.getName(), contents);
|
||||||
@@ -157,6 +166,9 @@ public abstract class AbstractQuickFixTest extends KotlinLightQuickFixTestCase {
|
|||||||
fail(testName);
|
fail(testName);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
for (String fixtureClass : fixtureClasses) {
|
||||||
|
TestFixtureExtension.Companion.unloadFixture(fixtureClass);
|
||||||
|
}
|
||||||
ConfigLibraryUtil.unconfigureLibrariesByDirective(getModule(), fileText);
|
ConfigLibraryUtil.unconfigureLibrariesByDirective(getModule(), fileText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user