diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/generate/generateDependenciesUtils.kt.172 b/ultimate/src/org/jetbrains/kotlin/idea/spring/generate/generateDependenciesUtils.kt.172 new file mode 100644 index 00000000000..485c856fef9 --- /dev/null +++ b/ultimate/src/org/jetbrains/kotlin/idea/spring/generate/generateDependenciesUtils.kt.172 @@ -0,0 +1,512 @@ +/* + * 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.generate + +import com.intellij.codeInsight.lookup.LookupElement +import com.intellij.codeInsight.lookup.LookupElementBuilder +import com.intellij.codeInsight.template.Expression +import com.intellij.codeInsight.template.ExpressionContext +import com.intellij.codeInsight.template.TemplateBuilderImpl +import com.intellij.codeInsight.template.TextResult +import com.intellij.codeInspection.SmartHashMap +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.runWriteAction +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Key +import com.intellij.psi.* +import com.intellij.psi.util.PropertyUtil +import com.intellij.psi.util.PsiTypesUtil +import com.intellij.spring.CommonSpringModel +import com.intellij.spring.model.CommonSpringBean +import com.intellij.spring.model.SpringBeanPointer +import com.intellij.spring.model.SpringModelSearchParameters +import com.intellij.spring.model.actions.generate.GenerateSpringBeanDependenciesUtil +import com.intellij.spring.model.actions.generate.GenerateSpringBeanDependenciesUtil.* +import com.intellij.spring.model.actions.generate.SpringBeanClassMember +import com.intellij.spring.model.highlighting.SpringConstructorArgResolveUtil +import com.intellij.spring.model.utils.SpringBeanCoreUtils +import com.intellij.spring.model.utils.SpringBeanUtils +import com.intellij.spring.model.utils.SpringModelSearchers +import com.intellij.spring.model.utils.SpringModelUtils +import com.intellij.spring.model.xml.DomSpringBean +import com.intellij.spring.model.xml.beans.Beans +import com.intellij.spring.model.xml.beans.SpringBean +import com.intellij.util.IncorrectOperationException +import com.intellij.util.xml.DomElement +import com.intellij.util.xml.DomUtil +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.asJava.classes.KtLightClass +import org.jetbrains.kotlin.asJava.elements.KtLightMethod +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.idea.caches.project.getModuleInfo +import org.jetbrains.kotlin.idea.caches.project.moduleInfo +import org.jetbrains.kotlin.idea.caches.resolve.* +import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaOrKotlinMemberDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.util.getParameterDescriptor +import org.jetbrains.kotlin.idea.completion.BasicLookupElementFactory +import org.jetbrains.kotlin.idea.completion.InsertHandlerProvider +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator +import org.jetbrains.kotlin.idea.core.ShortenReferences +import org.jetbrains.kotlin.idea.core.getOrCreateCompanionObject +import org.jetbrains.kotlin.idea.editor.BatchTemplateRunner +import org.jetbrains.kotlin.idea.spring.beanClass +import org.jetbrains.kotlin.idea.spring.effectiveBeanClasses +import org.jetbrains.kotlin.idea.util.CallType +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.load.java.propertyNameBySetMethodName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.isIdentifier +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.expressions.TypeReconstructionUtil +import org.jetbrains.kotlin.utils.SmartList +import org.jetbrains.kotlin.utils.ifEmpty +import java.util.* + +// TODO: GenerateSpringBeanDependenciesUtil.ensureFileWritable() is not accessible here +private fun DomElement.ensureFileWritable() = ensureFileWritable(DomUtil.getFile(this).virtualFile, manager.project) + +// TODO: GenerateSpringBeanDependenciesUtil.getReferenceName() is not accessible here +private fun getReferencedName(currentBean: SpringBean, bean: SpringBeanPointer): String? { + val model = SpringModelUtils.getInstance().getSpringModel(currentBean) + return SpringBeanCoreUtils.getReferencedName(bean, model.allCommonBeans) +} + +// TODO: GenerateSpringBeanDependenciesUtil.getExistedSetter() is not accessible here +private fun getExistedSetter(currentBeanClass: PsiClass, setterPsiClass: PsiClass): PsiMethod? { + val psiClassType = JavaPsiFacade.getInstance(setterPsiClass.project).elementFactory.createType(setterPsiClass) + return currentBeanClass.allMethods.firstOrNull { + PropertyUtil.isSimplePropertySetter(it) && it.parameterList.parameters.first().type.isAssignableFrom(psiClassType) + } +} + +// TODO: GenerateSpringBeanDependenciesUtil.findConstructor() is not accessible here +private fun findConstructor(constructors: Array, psiParameterTypes: List): PsiMethod? { + return constructors.firstOrNull { + val parameters = it.parameterList.parameters + if (it.parameterList.parametersCount != psiParameterTypes.size) return@firstOrNull false + (psiParameterTypes zip parameters).all { it.first.isAssignableFrom(it.second.type) } + } +} + +// TODO: GenerateSpringBeanDependenciesUtil.findExistedConstructor() is not accessible here +private fun findExistedConstructor( + currentBean: SpringBean, + currentBeanClass: PsiClass, + candidateParameterClasses: Array +): PsiMethod? { + val constructors = SpringConstructorArgResolveUtil.findMatchingMethods(currentBean) + for (candidateBeanClass in candidateParameterClasses) { + for (constructor in constructors) { + val psiParameterTypes = SmartList().apply { + constructor.parameterList.parameters.mapTo(this) { it.type } + add(PsiTypesUtil.getClassType(candidateBeanClass)) + } + findConstructor(currentBeanClass.constructors, psiParameterTypes)?.let { return it } + } + } + + return null +} + +// TODO: GenerateSpringBeanDependenciesUtil.createSpringBean() is not accessible here +private fun createSpringBean(parentBeans: Beans, psiClass: PsiClass): SpringBean? { + if (!((parentBeans as DomElement)).ensureFileWritable()) return null + + return parentBeans.addBean().apply { + clazz.stringValue = psiClass.qualifiedName + id.stringValue = SpringBeanCoreUtils.suggestBeanNames(this).firstOrNull() ?: "" + } +} + +private fun SpringBean.getFactoryFunctionName(factoryBeanClass: KtClass): String { + factoryMethod.stringValue?.let { if (!it.isBlank()) return it } + + val existingNames = factoryBeanClass.declarations.mapNotNull { if (it is KtFunction || it is KtClassOrObject) it.name else null } + return KotlinNameSuggester.suggestNameByName("create${beanClass()!!.name}") { it !in existingNames } +} + +internal val PsiClass.defaultTypeText: String + get() { + val qName = qualifiedName ?: return "Any" + val typeParameters = typeParameters + if (typeParameters.isEmpty()) return qName + return TypeReconstructionUtil.getTypeNameAndStarProjectionsString(qName, typeParameters.size) + } + +private fun createSettable( + candidateBean: SpringBeanPointer, + currentBeanClass: KtLightClass, + candidateBeanClasses: Array, + injectionKind: SpringDependencyInjectionKind +): KtNamedDeclaration { + val project = currentBeanClass.project + val psiFactory = KtPsiFactory(project) + val beanName = candidateBean.name + try { + val candidateClass = candidateBeanClasses.first() + val propertyName = if (beanName != null && beanName.isIdentifier()) beanName else candidateClass.name!! + + val prototype: KtNamedDeclaration = when (injectionKind) { + SpringDependencyInjectionKind.SETTER -> { + psiFactory.createFunction("fun set${propertyName.capitalize()}(${propertyName.decapitalize()}: ${candidateClass.defaultTypeText}) { }") + } + + SpringDependencyInjectionKind.LATEINIT_PROPERTY -> { + psiFactory.createProperty("lateinit var ${propertyName.decapitalize()}: ${candidateClass.defaultTypeText}") + } + + else -> error("Unexpected injection kind: $injectionKind") + } + return currentBeanClass.kotlinOrigin!!.addDeclaration(prototype).apply { ShortenReferences.DEFAULT.process(this) } + } + catch (e: IncorrectOperationException) { + throw RuntimeException(e) + } +} + +internal fun getSuggestedNames( + beanPointer: SpringBeanPointer, + declaration: KtCallableDeclaration, + excludedInValidator: List = listOf(declaration), + existingNames: Collection = emptyList(), + getType: CallableDescriptor.() -> KotlinType? +): Collection { + val names = LinkedHashSet() + + val newDeclarationNameValidator = + NewDeclarationNameValidator(declaration.parent, null, NewDeclarationNameValidator.Target.VARIABLES, excludedInValidator) + fun validate(name: String) = name !in existingNames && newDeclarationNameValidator(name) + + SpringBeanUtils.getInstance() + .findBeanNames(beanPointer.springBean) + .asSequence() + .filter { it.isIdentifier() } + .mapTo(names) { KotlinNameSuggester.suggestNameByName(it, ::validate) } + + (declaration.resolveToDescriptor() as CallableDescriptor).getType()?.let { + names += KotlinNameSuggester.suggestNamesByType(it, ::validate) + } + + return names +} + +internal fun TemplateBuilderImpl.appendVariableTemplate( + variable: KtCallableDeclaration, + candidateBeanClasses: Array, + computeSuggestions: (() -> Collection)? +) { + if (computeSuggestions != null) { + val suggestedNames = computeSuggestions() + val defaultName = variable.name ?: "" + val nameExpression = object : Expression() { + override fun calculateResult(context: ExpressionContext) = TextResult(defaultName) + + override fun calculateQuickResult(context: ExpressionContext) = calculateResult(context) + + override fun calculateLookupItems(context: ExpressionContext): Array? { + PsiDocumentManager.getInstance(context.project).commitAllDocuments() + return suggestedNames.map { LookupElementBuilder.create(it) }.toTypedArray() + + } + } + replaceElement(variable.nameIdentifier, "names", nameExpression, true) + } + + val superTypeVariants = getSuperTypeVariants(candidateBeanClasses) + if (superTypeVariants.size > 1) { + val lookupFactory = BasicLookupElementFactory(variable.project, InsertHandlerProvider(CallType.TYPE) { emptyList() }) + val typeReferenceExpression = object : Expression() { + override fun calculateResult(context: ExpressionContext) = TextResult(candidateBeanClasses.first().qualifiedName ?: "") + + override fun calculateQuickResult(context: ExpressionContext) = calculateResult(context) + + override fun calculateLookupItems(context: ExpressionContext): Array? { + return superTypeVariants + .mapTo(LinkedHashSet()) { lookupFactory.createLookupElementForJavaClass(it, qualifyNestedClasses = true) } + .toTypedArray() + } + } + replaceElement(variable.typeReference, "type", typeReferenceExpression, true) + } +} + +private fun BatchTemplateRunner.addCreateFunctionTemplate( + function: KtFunction, + candidateBeanClasses: Map>, + dependency: SpringBeanPointer +) { + val parameterList = function.valueParameterList!! + val builder = TemplateBuilderImpl(parameterList) + addTemplateFactory(parameterList) { + for ((paramIndex, candidateBeanClassesForParam) in candidateBeanClasses) { + builder.appendVariableTemplate(function.valueParameters[paramIndex], candidateBeanClassesForParam) { + getSuggestedNames(dependency, function, listOf(function.valueParameters[paramIndex], function)) { + valueParameters[paramIndex].type + } + } + } + + builder.buildInlineTemplate() + } +} + +private fun BatchTemplateRunner.addCreatePropertyTemplate(property: KtProperty, candidateBeanClasses: Array) { + val builder = TemplateBuilderImpl(property) + addTemplateFactory(property) { + builder.appendVariableTemplate(property, candidateBeanClasses, null) + builder.buildInlineTemplate() + } +} + +private fun BatchTemplateRunner.addCreateFunctionTemplate( + function: KtFunction, + paramIndex: Int, + candidateBeanClasses: Array, + dependency: SpringBeanPointer +) { + addCreateFunctionTemplate(function, mapOf(paramIndex to candidateBeanClasses), dependency) +} + +private fun getOrCreateSetter( + candidateBean: SpringBeanPointer, + currentBeanClass: KtLightClass, + candidateBeanClasses: Array, + templatesHolder: BatchTemplateRunner, + injectionKind: SpringDependencyInjectionKind +): PsiNamedElement? { + for (candidateBeanClass in candidateBeanClasses) { + getExistedSetter(currentBeanClass, candidateBeanClass)?.let { return it } + } + + if (!ensureFileWritable(currentBeanClass)) return null + val settable = createSettable(candidateBean, currentBeanClass, candidateBeanClasses, injectionKind) + when (settable) { + is KtNamedFunction -> templatesHolder.addCreateFunctionTemplate(settable, 0, candidateBeanClasses, candidateBean) + is KtProperty -> templatesHolder.addCreatePropertyTemplate(settable, candidateBeanClasses) + } + + return settable +} + +private fun createSetterDependency( + currentBean: SpringBean, + dependency: SpringBeanPointer, + injectionKind: SpringDependencyInjectionKind +): BatchTemplateRunner? { + val templatesHolder = BatchTemplateRunner(currentBean.manager.project) + val currentBeanClass = currentBean.beanClass() as? KtLightClass ?: return null + val candidateBeanClasses = dependency.effectiveBeanClasses().ifEmpty { return null } + val setter = getOrCreateSetter(dependency, currentBeanClass, candidateBeanClasses, templatesHolder, injectionKind) ?: return null + currentBean.addProperty().apply { + name.ensureXmlElementExists() + name.stringValue = when (setter) { + is PsiMethod, is KtFunction -> propertyNameBySetMethodName(Name.identifier(setter.name!!), false)!!.asString() + else -> setter.name + } + refAttr.setStringValue(getReferencedName(currentBean, dependency)) + } + return templatesHolder +} + +private fun addConstructorParameter( + currentBeanClass: KtLightClass, + candidateBeanClass: PsiClass, + constructor: KtFunction +) { + val psiFactory = KtPsiFactory(currentBeanClass) + + val validator = NewDeclarationNameValidator(constructor, null, NewDeclarationNameValidator.Target.VARIABLES) + val resolutionFacade = currentBeanClass.kotlinOrigin!!.getResolutionFacade() + val defaultType = (candidateBeanClass.getJavaOrKotlinMemberDescriptor(resolutionFacade) as ClassDescriptor).defaultType + val name = KotlinNameSuggester.suggestNamesByType(defaultType, validator).first() + + val parameter = psiFactory.createParameter("$name: ${candidateBeanClass.defaultTypeText}") + constructor.getValueParameterList()!!.addParameter(parameter).apply { ShortenReferences.DEFAULT.process(this) } +} + +private fun findProperConstructorAndAddParameter( + currentBean: SpringBean, + dependency: SpringBeanPointer, + currentBeanClass: KtLightClass, + candidateBeanClass: PsiClass, + holder: BatchTemplateRunner +): PsiMethod? { + val lightConstructor = currentBean.resolvedConstructorArgs.resolvedMethod as? KtLightMethod ?: return null + val constructorOrigin = lightConstructor.kotlinOrigin + val properConstructor = when (constructorOrigin) { + is KtFunction -> constructorOrigin + is KtClass -> constructorOrigin.createPrimaryConstructorIfAbsent() + else -> return null + } + addConstructorParameter(currentBeanClass, candidateBeanClass, properConstructor) + holder.addCreateFunctionTemplate(properConstructor, properConstructor.getValueParameters().lastIndex, arrayOf(candidateBeanClass), dependency) + return lightConstructor +} + +private fun createConstructorWithTemplate( + currentBean: SpringBean, + dependency: SpringBeanPointer, + templatesHolder: BatchTemplateRunner +) { + val beanClass = currentBean.beanClass() as? KtLightClass ?: return + val ktBeanClass = beanClass.kotlinOrigin as? KtClass ?: return + + try { + val psiFactory = KtPsiFactory(beanClass) + val factoryFunction = when { + currentBean.factoryBean.exists() -> { + // TODO: Support non-Kotlin factory beans + val factoryClass = currentBean.factoryBean.value?.beanClass as? KtLightClass ?: return + val ktFactoryClass = factoryClass.kotlinOrigin as? KtClass ?: return + val funName = currentBean.getFactoryFunctionName(ktFactoryClass) + val funText = "fun $funName(): ${beanClass.defaultTypeText} {\n return null \n}" + ktFactoryClass.addDeclaration(psiFactory.createFunction(funText)) + } + currentBean.factoryMethod.exists() -> { + val funName = currentBean.getFactoryFunctionName(ktBeanClass) + val funText = "@JvmStatic fun $funName(): ${beanClass.defaultTypeText} {\n return null \n}" + ktBeanClass.getOrCreateCompanionObject().addDeclaration(psiFactory.createFunction(funText)) + } + else -> { + if (ktBeanClass.hasPrimaryConstructor() || ktBeanClass.getSecondaryConstructors().isNotEmpty()) { + ktBeanClass.addDeclaration(psiFactory.createSecondaryConstructor("constructor()")) + } + else { + ktBeanClass.createPrimaryConstructorIfAbsent() + } + } + } as KtFunction + + ShortenReferences.DEFAULT.process(factoryFunction) + + val dummyMethod = JavaPsiFacade.getElementFactory(beanClass.project).createMethod("foo", PsiType.VOID) + dummyMethod.containingFile.moduleInfo = ktBeanClass.getModuleInfo() + SpringConstructorArgResolveUtil.suggestParamsForConstructorArgs(currentBean).forEach { dummyMethod.parameterList.add(it) } + + val parameterList = factoryFunction.valueParameterList!! + val indexOffset = parameterList.parameters.size + val candidateClassMap = SmartHashMap>() + val resolutionFacade = ktBeanClass.getResolutionFacade() + for ((i, psiParam) in dummyMethod.parameterList.parameters.withIndex()) { + val descriptor = psiParam.getParameterDescriptor(resolutionFacade)!! + val ktParameter = psiFactory.createParameter( + "${psiParam.name}: ${IdeDescriptorRenderers.SOURCE_CODE_NOT_NULL_TYPE_APPROXIMATION.renderType(descriptor.type)}" + ) + parameterList.addParameter(ktParameter) + (psiParam.type as? PsiClassType)?.resolve()?.let { candidateClassMap[i + indexOffset] = arrayOf(it) } + } + + templatesHolder.addCreateFunctionTemplate(factoryFunction, candidateClassMap, dependency) + } + catch (e: IncorrectOperationException) { + throw RuntimeException(e) + } +} + +private fun createConstructorDependency( + currentBean: SpringBean, + dependency: SpringBeanPointer +): BatchTemplateRunner? { + val templatesHolder = BatchTemplateRunner(currentBean.manager.project) + val currentBeanClass = currentBean.beanClass() as? KtLightClass ?: return null + val candidateBeanClasses = dependency.effectiveBeanClasses().ifEmpty { return null } + var existedConstructor = findExistedConstructor(currentBean, currentBeanClass, candidateBeanClasses) + if (existedConstructor == null) { + if (!ensureFileWritable(currentBeanClass)) return null + existedConstructor = findProperConstructorAndAddParameter(currentBean, dependency, currentBeanClass, candidateBeanClasses.first(), templatesHolder) + } + + val newConstructorArg = currentBean.addConstructorArg() + newConstructorArg.refAttr.stringValue = getReferencedName(currentBean, dependency) + if (existedConstructor == null && SpringConstructorArgResolveUtil.findMatchingMethods(currentBean).isEmpty()) { + createConstructorWithTemplate(currentBean, dependency, templatesHolder) + } + + return templatesHolder +} + +private fun generateDependency( + springBean: SpringBean, + dependency: SpringBeanPointer, + injectionKind: SpringDependencyInjectionKind +): BatchTemplateRunner? { + return runWriteAction { + if (injectionKind.isSetter) { + createSetterDependency(springBean, dependency, injectionKind) + } + else { + createConstructorDependency(springBean, dependency) + } + } +} + +enum class SpringDependencyInjectionKind(val isSetter: Boolean) { + CONSTRUCTOR(false), + SETTER(true), + LATEINIT_PROPERTY(true) +} + +@set:TestOnly +var Project.beanFilter: (SpringBeanPointer) -> Boolean + by NotNullableUserDataProperty(Key.create("BEAN_CHOOSER")) { true } + +@NotNull +private fun chooseDependentBeans( + candidates: Set, + project: Project, + isSetterDependency: Boolean +): List> { + return if (ApplicationManager.getApplication().isUnitTestMode) { + candidates.map { it.springBean }.filter(project.beanFilter) + } + else { + GenerateSpringBeanDependenciesUtil.chooseDependentBeans(candidates, project, isSetterDependency) + } +} + +fun generateDependenciesFor( + springModel: CommonSpringModel, + klass: KtLightClass, + injectionKind: SpringDependencyInjectionKind +): List { + val project = klass.project + val isSetter = injectionKind.isSetter + + SpringModelSearchers + .findBeans(springModel, SpringModelSearchParameters.byClass(klass)) + .asSequence() + .map { it.springBean } + .filterIsInstance() + .firstOrNull { acceptBean(it, false) } + ?.let { springBean -> + if (!springBean.ensureFileWritable()) return emptyList() + if (springBean.beanClass() == null) return emptyList() + val dependencies = chooseDependentBeans(getCandidates(springBean, isSetter), project, isSetter) + return dependencies.mapNotNull { generateDependency(springBean, it, injectionKind) } + } + + val dependencies = chooseDependentBeans(getCandidates(springModel, klass, isSetter), project, isSetter) + val beanXml = dependencies.singleOrNull()?.springBean as? DomSpringBean ?: return emptyList() + val beansXml = beanXml.getParentOfType(Beans::class.java, false) ?: return emptyList() + val newBean = createSpringBean(beansXml, klass) ?: return emptyList() + + return dependencies.mapNotNull { generateDependency(newBean, it, injectionKind) } +} diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiredMembersInspection.kt.172 b/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiredMembersInspection.kt.172 new file mode 100644 index 00000000000..ee388c55681 --- /dev/null +++ b/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiredMembersInspection.kt.172 @@ -0,0 +1,39 @@ +/* + * 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.codeInspection.ProblemsHolder +import com.intellij.psi.PsiElementVisitor +import com.intellij.spring.model.highlighting.SpringJavaAutowiredMembersInspection +import org.jetbrains.kotlin.asJava.toLightClass +import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection +import org.jetbrains.kotlin.idea.inspections.registerWithElementsUnwrapped +import org.jetbrains.kotlin.psi.KtClassOrObject +import org.jetbrains.kotlin.psi.KtVisitorVoid + +class SpringKotlinAutowiredMembersInspection : AbstractKotlinInspection() { + private val javaInspection by lazy { SpringJavaAutowiredMembersInspection() } + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return object: KtVisitorVoid() { + override fun visitClassOrObject(classOrObject: KtClassOrObject) { + val lightClass = classOrObject.toLightClass() ?: return + javaInspection.checkClass(lightClass, holder.manager, isOnTheFly)?.registerWithElementsUnwrapped(holder, isOnTheFly) + } + } + } +} diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt.172 b/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt.172 index 60c48347c02..2d7f9ff7c38 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt.172 +++ b/ultimate/src/org/jetbrains/kotlin/idea/spring/inspections/SpringKotlinAutowiringInspection.kt.172 @@ -16,8 +16,8 @@ import com.intellij.spring.CommonSpringModel import com.intellij.spring.SpringBundle import com.intellij.spring.model.SpringBeanPointer import com.intellij.spring.model.converters.SpringConverterUtil -import com.intellij.spring.model.highlighting.autowire.SpringJavaInjectionPointsAutowiringInspection -import com.intellij.spring.model.utils.SpringAutowireUtil +import com.intellij.spring.model.highlighting.SpringAutowireUtil +import com.intellij.spring.model.highlighting.SpringJavaAutowiringInspection import org.jetbrains.kotlin.asJava.elements.KtLightField import org.jetbrains.kotlin.asJava.elements.KtLightMethod import org.jetbrains.kotlin.asJava.toLightClass @@ -30,7 +30,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* class SpringKotlinAutowiringInspection : AbstractKotlinInspection() { - // Based on SpringJavaInjectionPointsAutowiringInspection.AddSpringBeanQualifierFix + // Based on SpringJavaAutowiringInspection.AddSpringBeanQualifierFix class AddQualifierFix( modifierListOwner: KtModifierListOwner, private val beanPointers: Collection>, @@ -86,16 +86,16 @@ class SpringKotlinAutowiringInspection : AbstractKotlinInspection() { } } - private val javaInspection by lazy { SpringJavaInjectionPointsAutowiringInspection() } + private val javaInspection by lazy { SpringJavaAutowiringInspection() } override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = object : KtVisitorVoid() { - // TODO: SpringJavaInjectionPointsAutowiringInspection.checkAutowiredMethod() is not accessible here + // TODO: SpringJavaAutowiringInspection.checkAutowiredMethod() is not accessible here private fun checkAutowiredMethod(psiMethod: PsiMethod, holder: ProblemsHolder, springModel: CommonSpringModel, required: Boolean) { val resourceAnnotation = SpringAutowireUtil.getResourceAnnotation(psiMethod) when { resourceAnnotation != null -> { val propertyType = PropertyUtil.getPropertyType(psiMethod) ?: return - SpringJavaInjectionPointsAutowiringInspection.checkInjectionPoint(psiMethod, propertyType, holder, springModel, required) + SpringJavaAutowiringInspection.checkAutowiredPsiMember(psiMethod, propertyType, holder, springModel, required) } psiMethod.parameterList.parametersCount == 0 && SpringAutowireUtil.isAutowiredByAnnotation(psiMethod) -> { @@ -107,7 +107,7 @@ class SpringKotlinAutowiringInspection : AbstractKotlinInspection() { else -> { for (parameter in psiMethod.parameterList.parameters) { if (AnnotationUtil.isAnnotated(parameter, "org.springframework.beans.factory.annotation.Value", true)) continue - SpringJavaInjectionPointsAutowiringInspection.checkInjectionPoint(parameter, parameter.type, holder, springModel, required) + SpringJavaAutowiringInspection.checkAutowiredPsiMember(parameter, parameter.type, holder, springModel, required) } } } @@ -153,7 +153,7 @@ class SpringKotlinAutowiringInspection : AbstractKotlinInspection() { private fun PsiField.processLightField() { if (!SpringAutowireUtil.isAutowiredByAnnotation(this)) return processLightMember { holder, model, required -> - SpringJavaInjectionPointsAutowiringInspection.checkInjectionPoint(this, type, holder, model, required) + SpringJavaAutowiringInspection.checkAutowiredPsiMember(this, type, holder, model, required) } } diff --git a/ultimate/src/org/jetbrains/kotlin/idea/spring/references/KtSpringQualifierReference.kt.172 b/ultimate/src/org/jetbrains/kotlin/idea/spring/references/KtSpringQualifierReference.kt.172 index eb9e900a2cf..17c5d6bb0ab 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/spring/references/KtSpringQualifierReference.kt.172 +++ b/ultimate/src/org/jetbrains/kotlin/idea/spring/references/KtSpringQualifierReference.kt.172 @@ -20,8 +20,8 @@ import com.intellij.codeInsight.lookup.LookupElement import com.intellij.psi.* import com.intellij.spring.model.DefaultSpringBeanQualifier import com.intellij.spring.model.converters.SpringConverterUtil +import com.intellij.spring.model.highlighting.SpringAutowireUtil import com.intellij.spring.model.jam.qualifiers.SpringJamQualifier -import com.intellij.spring.model.utils.SpringAutowireUtil import com.intellij.spring.model.utils.SpringModelSearchers import com.intellij.spring.references.SpringQualifierReference import org.jetbrains.kotlin.asJava.elements.KtLightElement