diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt index 91edf3324ae..48dff6bef0d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt @@ -310,6 +310,8 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m return createProperty(text + " val x").modifierList!! } + fun createEmptyModifierList() = createModifierList(KtTokens.PRIVATE_KEYWORD).apply { firstChild.delete() } + fun createModifier(modifier: KtModifierKeywordToken): PsiElement { return createModifierList(modifier.value).getModifier(modifier)!! } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/addRemoveModifier.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/addRemoveModifier.kt index ac53aeae11c..b88c1ecb95a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/addRemoveModifier.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/addRemoveModifier.kt @@ -143,20 +143,20 @@ private val MODIFIERS_TO_REPLACE = mapOf( ACTUAL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD) ) -private val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD, - HEADER_KEYWORD, IMPL_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD, - FINAL_KEYWORD, OPEN_KEYWORD, ABSTRACT_KEYWORD, SEALED_KEYWORD, - CONST_KEYWORD, - EXTERNAL_KEYWORD, - OVERRIDE_KEYWORD, - LATEINIT_KEYWORD, - TAILREC_KEYWORD, - VARARG_KEYWORD, - SUSPEND_KEYWORD, - INNER_KEYWORD, - ENUM_KEYWORD, ANNOTATION_KEYWORD, - COMPANION_KEYWORD, - INLINE_KEYWORD, - INFIX_KEYWORD, - OPERATOR_KEYWORD, - DATA_KEYWORD) +val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD, + HEADER_KEYWORD, IMPL_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD, + FINAL_KEYWORD, OPEN_KEYWORD, ABSTRACT_KEYWORD, SEALED_KEYWORD, + CONST_KEYWORD, + EXTERNAL_KEYWORD, + OVERRIDE_KEYWORD, + LATEINIT_KEYWORD, + TAILREC_KEYWORD, + VARARG_KEYWORD, + SUSPEND_KEYWORD, + INNER_KEYWORD, + ENUM_KEYWORD, ANNOTATION_KEYWORD, + COMPANION_KEYWORD, + INLINE_KEYWORD, + INFIX_KEYWORD, + OPERATOR_KEYWORD, + DATA_KEYWORD) diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt index 104e8a5e9a2..513e8c74512 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/psiUtil/ktPsiUtil.kt @@ -411,18 +411,24 @@ fun KtLambdaArgument.getLambdaArgumentName(bindingContext: BindingContext): Name fun KtExpression.asAssignment(): KtBinaryExpression? = if (KtPsiUtil.isAssignment(this)) this as KtBinaryExpression else null -private fun KtDeclaration.modifierFromTokenSet(set: TokenSet): PsiElement? { - val modifierList = modifierList ?: return null +private fun KtModifierList.modifierFromTokenSet(set: TokenSet): PsiElement? { return set.types .asSequence() - .map { modifierList.getModifier(it as KtModifierKeywordToken) } + .map { getModifier(it as KtModifierKeywordToken) } .firstOrNull { it != null } } -fun KtDeclaration.visibilityModifier() = modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS) +private fun KtModifierListOwner.modifierFromTokenSet(set: TokenSet) = modifierList?.modifierFromTokenSet(set) -fun KtDeclaration.visibilityModifierType(): KtModifierKeywordToken? +fun KtModifierList.visibilityModifier() = modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS) + +fun KtModifierList.visibilityModifierType(): KtModifierKeywordToken? + = visibilityModifier()?.node?.elementType as KtModifierKeywordToken? + +fun KtModifierListOwner.visibilityModifier() = modifierList?.modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS) + +fun KtModifierListOwner.visibilityModifierType(): KtModifierKeywordToken? = visibilityModifier()?.node?.elementType as KtModifierKeywordToken? private val MODALITY_MODIFIERS = TokenSet.create( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt index 13fe1844a4d..f1640cbef90 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationUtil.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.constants.ErrorValue -private val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic") +val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic") val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField") diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt index c279cdad4d7..91604887e54 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/psiModificationUtils.kt @@ -31,10 +31,8 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject -import org.jetbrains.kotlin.psi.psiUtil.getLambdaArgumentName -import org.jetbrains.kotlin.psi.psiUtil.hasBody -import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType +import org.jetbrains.kotlin.psi.addRemoveModifier.MODIFIERS_ORDER +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.OverridingUtil @@ -42,6 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParenthese import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.isError +import org.jetbrains.kotlin.utils.SmartList @Suppress("UNCHECKED_CAST") inline fun PsiElement.replaced(newElement: T): T { @@ -406,3 +405,27 @@ fun KtParameter.setDefaultValue(newDefaultValue: KtExpression): PsiElement? { val eq = equalsToken ?: add(psiFactory.createEQ()) return addAfter(newDefaultValue, eq) as KtExpression } + +fun KtModifierList.appendModifier(modifier: KtModifierKeywordToken) { + add(KtPsiFactory(this).createModifier(modifier)) +} + +fun KtModifierList.normalize(): KtModifierList { + val psiFactory = KtPsiFactory(this) + return psiFactory.createEmptyModifierList().also { newList -> + val modifiers = SmartList() + allChildren.forEach { + val elementType = it.node.elementType + when { + it is KtAnnotation || it is KtAnnotationEntry -> newList.add(it) + elementType is KtModifierKeywordToken -> { + if (elementType == KtTokens.DEFAULT_VISIBILITY_KEYWORD) return@forEach + if (elementType == KtTokens.FINALLY_KEYWORD && !hasModifier(KtTokens.OVERRIDE_KEYWORD)) return@forEach + modifiers.add(it) + } + } + } + modifiers.sortBy { MODIFIERS_ORDER.indexOf(it.node.elementType) } + modifiers.forEach { newList.add(it) } + } +} \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 77da28a49f4..d62c02d6bf0 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -782,6 +782,8 @@ + + org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention Kotlin diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinCrossLanguageQuickFixAction.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinCrossLanguageQuickFixAction.kt new file mode 100644 index 00000000000..7e3f065422d --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinCrossLanguageQuickFixAction.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2010-2017 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.codeInsight.FileModificationService +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile + +abstract class KotlinCrossLanguageQuickFixAction(element: T) : QuickFixActionBase(element) { + override val isCrossLanguageFix: Boolean + get() = true + + override final fun invoke(project: Project, editor: Editor?, file: PsiFile) { + val element = element + if (element != null && FileModificationService.getInstance().prepareFileForWrite(element.containingFile)) { + invokeImpl(project, editor, file) + } + } + + protected abstract fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) +} diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinQuickFixAction.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinQuickFixAction.kt index 8c2adc970dc..20b88d006c6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinQuickFixAction.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/KotlinQuickFixAction.kt @@ -17,42 +17,18 @@ package org.jetbrains.kotlin.idea.quickfix import com.intellij.codeInsight.FileModificationService -import com.intellij.codeInsight.intention.IntentionAction -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.editor.Editor import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT -import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer -abstract class KotlinQuickFixAction(element: T) : IntentionAction { - private val elementPointer = element.createSmartPointer() +abstract class KotlinQuickFixAction(element: T) : QuickFixActionBase(element) { + protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile) = true - protected val element: T? - get() = elementPointer.element - - final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { - if (ApplicationManager.getApplication().isUnitTestMode) { - CREATEBYPATTERN_MAY_NOT_REFORMAT = true - } - try { - val element = element ?: return false - return element.isValid && - !element.project.isDisposed && - (file.manager.isInProject(file) || file is KtCodeFragment) && - (file is KtFile) && - isAvailable(project, editor, file) - } - finally { - CREATEBYPATTERN_MAY_NOT_REFORMAT = false - } - } - - protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean { - return true + override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean { + val ktFile = file as? KtFile ?: return false + return isAvailable(project, editor, ktFile) } final override fun invoke(project: Project, editor: Editor?, file: PsiFile) { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixActionBase.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixActionBase.kt new file mode 100644 index 00000000000..21ddc45ef75 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixActionBase.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2017 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.codeInsight.intention.IntentionAction +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT +import org.jetbrains.kotlin.psi.KtCodeFragment +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer + +abstract class QuickFixActionBase(element: T) : IntentionAction { + private val elementPointer = element.createSmartPointer() + + protected val element: T? + get() = elementPointer.element + + open val isCrossLanguageFix: Boolean = false + + protected open fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile) = true + + final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean { + if (ApplicationManager.getApplication().isUnitTestMode) { + CREATEBYPATTERN_MAY_NOT_REFORMAT = true + } + try { + val element = element ?: return false + return element.isValid && + !element.project.isDisposed && + (file.manager.isInProject(file) || file is KtCodeFragment) && + (file is KtFile || isCrossLanguageFix) && + isAvailableImpl(project, editor, file) + } + finally { + CREATEBYPATTERN_MAY_NOT_REFORMAT = false + } + } + + override fun startInWriteAction() = true +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index f1294c81a85..b50f47d6f85 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -452,16 +452,34 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { val psiFactory = KtPsiFactory(currentFile) - val modifiers = - if (callableInfo.isAbstract) { - if (containingElement is KtClass && containingElement.isInterface()) "" else "abstract " - } - else if (containingElement is KtClassOrObject - && !(containingElement is KtClass && containingElement.isInterface()) - && containingElement.isAncestor(config.originalElement) - && callableInfo.kind != CallableKind.CONSTRUCTOR) "private " - else if (isExtension) "private " - else "" + val modifiers = buildString { + val modifierList = callableInfo.modifierList?.copied() ?: psiFactory.createEmptyModifierList() + val visibilityKeyword = modifierList.visibilityModifierType() + if (visibilityKeyword == null) { + val defaultVisibility = + if (callableInfo.isAbstract) "" + else if (containingElement is KtClassOrObject + && !(containingElement is KtClass && containingElement.isInterface()) + && containingElement.isAncestor(config.originalElement) + && callableInfo.kind != CallableKind.CONSTRUCTOR) "private " + else if (isExtension) "private " + else "" + append(defaultVisibility) + } + + // TODO: Get rid of isAbstract + if (callableInfo.isAbstract + && containingElement is KtClass + && !containingElement.isInterface() + && !modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) { + modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD) + } + + val text = modifierList.normalize().text + if (text.isNotEmpty()) { + append("$text ") + } + } val isExpectClassMember by lazy { containingElement is KtClassOrObject && (containingElement.resolveToDescriptorIfAny() as? ClassDescriptor)?.isExpect ?: false @@ -470,7 +488,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { val declaration: KtNamedDeclaration = when (callableInfo.kind) { CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> { val body = when { - callableInfo.kind == CallableKind.CONSTRUCTOR -> "" + callableInfo is ConstructorInfo -> if (callableInfo.withBody) "{\n\n}" else "" callableInfo.isAbstract -> "" containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> "" containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> "" @@ -478,7 +496,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { && containingElement.parent.parent is KtClass && (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> "" isExpectClassMember -> "" - else -> "{}" + else -> "{\n\n}" } @Suppress("USELESS_CAST") // KT-10755 @@ -544,7 +562,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { return assignmentToReplace.replace(declaration) as KtCallableDeclaration } - val declarationInPlace = placeDeclarationInContainer(declaration, containingElement, config.originalElement, jetFileToEdit) + val container = if (containingElement is KtClass && callableInfo.isForCompanion) { + containingElement.getOrCreateCompanionObject() + } + else containingElement + val declarationInPlace = placeDeclarationInContainer(declaration, container, config.originalElement, jetFileToEdit) if (declarationInPlace is KtSecondaryConstructor) { val containingClass = declarationInPlace.containingClassOrObject!! @@ -581,14 +603,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration, - parameterTypeExpressions: List): List { - val typeRefsToShorten = ArrayList() - + parameterTypeExpressions: List) { if (config.isExtension) { val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first() val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText) - val newTypeRef = (declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!! - typeRefsToShorten.add(newTypeRef) + (declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!! } val returnTypeRefs = declaration.getReturnTypeReferences() @@ -599,7 +618,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { if (returnType != null) { // user selected a given type replaceWithLongerName(returnTypeRefs, returnType) - typeRefsToShorten.addAll(declaration.getReturnTypeReferences()) } } @@ -618,11 +636,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } } } - - val expandedValueParameters = declaration.getValueParameters() - parameterIndicesToShorten.mapNotNullTo(typeRefsToShorten) { expandedValueParameters[it].typeReference } - - return typeRefsToShorten } private fun postprocessDeclaration(declaration: KtNamedDeclaration) { @@ -636,15 +649,12 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { } private fun setupDeclarationBody(func: KtDeclarationWithBody) { + if (func !is KtNamedFunction && func !is KtPropertyAccessor) return + if (skipReturnType && callableInfo is FunctionInfo && callableInfo.preferEmptyBody) return val oldBody = func.bodyExpression ?: return - val templateKind = when (func) { - is KtSecondaryConstructor -> TemplateKind.SECONDARY_CONSTRUCTOR - is KtNamedFunction, is KtPropertyAccessor -> TemplateKind.FUNCTION - else -> throw AssertionError("Unexpected declaration: " + func.getElementTextWithContext()) - } val bodyText = getFunctionBodyTextFromTemplate( func.project, - templateKind, + TemplateKind.FUNCTION, if (callableInfo.name.isNotEmpty()) callableInfo.name else null, if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "", receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) } @@ -751,13 +761,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { // add parameter name to the template val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext) - val preferredName = parameter.preferredName - val possibleNames = if (preferredName != null) { - arrayOf(preferredName, *possibleNamesFromExpression) - } - else { - possibleNamesFromExpression - } + val possibleNames = arrayOf(*parameter.nameSuggestions.toTypedArray(), *possibleNamesFromExpression) // figure out suggested names for each type option val parameterTypeToNamesMap = HashMap>() @@ -951,6 +955,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { if (newDeclaration is KtProperty) { newDeclaration.getter?.let { setupDeclarationBody(it) } + + if (newDeclaration.getter == null + && newDeclaration.initializer == null + && callableInfo is PropertyInfo + && callableInfo.withInitializer + && !callableInfo.isLateinitPreferred) { + newDeclaration.initializer = KtPsiFactory(newDeclaration).createExpression("TODO(\"initialize me\")") + } } val callElement = config.originalElement as? KtCallElement @@ -961,9 +973,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { CodeStyleManager.getInstance(project).reformat(newDeclaration) // change short type names to fully qualified ones (to be shortened below) - val typeRefsToShorten = setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions) + setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions) if (!transformToJavaMemberIfApplicable(newDeclaration)) { - elementsToShorten.addAll(typeRefsToShorten) + elementsToShorten.add(newDeclaration) setupEditor(newDeclaration) } } @@ -1090,17 +1102,24 @@ internal fun placeDeclarationInContainer( } container is KtClassOrObject -> { - insertMember(null, container, declaration, container.declarations.lastOrNull()) + var sibling: PsiElement? = container.declarations.lastOrNull { it::class == declaration::class } + if (sibling == null && declaration is KtProperty) { + sibling = container.getBody()?.lBrace + } + + insertMember(null, container, declaration, sibling) } else -> throw AssertionError("Invalid containing element: ${container.text}") } - val parent = declarationInPlace.parent - calcNecessaryEmptyLines(declarationInPlace, false).let { - if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace) - } - calcNecessaryEmptyLines(declarationInPlace, true).let { - if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace) + if (declaration !is KtPrimaryConstructor) { + val parent = declarationInPlace.parent + calcNecessaryEmptyLines(declarationInPlace, false).let { + if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace) + } + calcNecessaryEmptyLines(declarationInPlace, true).let { + if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace) + } } return declarationInPlace } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt index aaaf48b106e..8e2258f286a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt @@ -70,6 +70,10 @@ abstract class TypeInfo(val variance: Variance) { (builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder) } + class ByExplicitCandidateTypes(val types: List) : TypeInfo(Variance.INVARIANT) { + override fun getPossibleTypes(builder: CallableBuilder) = types + } + abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) { override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext) @@ -155,8 +159,10 @@ fun TypeInfo.ofThis() = TypeInfo.OfThis(this) */ class ParameterInfo( val typeInfo: TypeInfo, - val preferredName: String? = null -) + val nameSuggestions: List +) { + constructor(typeInfo: TypeInfo, preferredName: String? = null): this(typeInfo, listOfNotNull(preferredName)) +} enum class CallableKind { FUNCTION, @@ -171,7 +177,9 @@ abstract class CallableInfo ( val returnTypeInfo: TypeInfo, val possibleContainers: List, val typeParameterInfos: List, - val isAbstract: Boolean = false + val isAbstract: Boolean = false, + val isForCompanion: Boolean = false, + val modifierList: KtModifierList? = null ) { abstract val kind: CallableKind abstract val parameterInfos: List @@ -189,8 +197,11 @@ class FunctionInfo(name: String, typeParameterInfos: List = Collections.emptyList(), val isOperator: Boolean = false, val isInfix: Boolean = false, - isAbstract: Boolean = false -) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) { + isAbstract: Boolean = false, + isForCompanion: Boolean = false, + modifierList: KtModifierList? = null, + val preferEmptyBody: Boolean = false +) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) { override val kind: CallableKind get() = CallableKind.FUNCTION override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List, isAbstract: Boolean) = FunctionInfo( @@ -206,8 +217,12 @@ class FunctionInfo(name: String, ) } -class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo( - classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false +class ClassWithPrimaryConstructorInfo( + val classInfo: ClassInfo, + expectedTypeInfo: TypeInfo, + modifierList: KtModifierList? = null +): CallableInfo( + classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false, modifierList = modifierList ) { override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR override val parameterInfos: List get() = classInfo.parameterInfos @@ -218,8 +233,10 @@ class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo class ConstructorInfo( override val parameterInfos: List, val targetClass: PsiElement, - val isPrimary: Boolean = false -): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false) { + val isPrimary: Boolean = false, + modifierList: KtModifierList? = null, + val withBody: Boolean = false +): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false, modifierList = modifierList) { override val kind: CallableKind get() = CallableKind.CONSTRUCTOR override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List, isAbstract: Boolean) = throw UnsupportedOperationException() @@ -232,12 +249,23 @@ class PropertyInfo(name: String, possibleContainers: List = Collections.emptyList(), typeParameterInfos: List = Collections.emptyList(), isAbstract: Boolean = false, - val isLateinitPreferred: Boolean = false -) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) { + val isLateinitPreferred: Boolean = false, + isForCompanion: Boolean = false, + modifierList: KtModifierList? = null, + val withInitializer: Boolean = false +) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) { override val kind: CallableKind get() = CallableKind.PROPERTY override val parameterInfos: List get() = Collections.emptyList() - override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List, isAbstract: Boolean) = PropertyInfo( + override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List, isAbstract: Boolean) = + copyProperty(receiverTypeInfo, possibleContainers, isAbstract) + + fun copyProperty( + receiverTypeInfo: TypeInfo = this.receiverTypeInfo, + possibleContainers: List = this.possibleContainers, + isAbstract: Boolean = this.isAbstract, + isLateinitPreferred: Boolean = this.isLateinitPreferred + ) = PropertyInfo( name, receiverTypeInfo, returnTypeInfo, @@ -245,6 +273,9 @@ class PropertyInfo(name: String, possibleContainers, typeParameterInfos, isAbstract, - isLateinitPreferred + isLateinitPreferred, + isForCompanion, + modifierList, + withInitializer ) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/generationUtils.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/generationUtils.kt index d708c8b2cc3..44d2ade8dcd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/generationUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/generationUtils.kt @@ -29,11 +29,13 @@ fun setupEditorSelection(editor: Editor, declaration: KtNamedDeclaration) { val caretModel = editor.caretModel val selectionModel = editor.selectionModel - if (declaration is KtSecondaryConstructor) { - caretModel.moveToOffset(declaration.getConstructorKeyword().endOffset) + val offset = when (declaration) { + is KtPrimaryConstructor -> declaration.getConstructorKeyword()?.endOffset ?: declaration.valueParameterList?.startOffset + is KtSecondaryConstructor -> declaration.getConstructorKeyword().endOffset + else -> declaration.nameIdentifier?.endOffset } - else { - caretModel.moveToOffset(declaration.nameIdentifier!!.endOffset) + if (offset != null) { + caretModel.moveToOffset(offset) } fun positionBetween(left: PsiElement, right: PsiElement) { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt index bf1e1d9d369..6f147f7fb53 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromUsageFix.kt @@ -17,14 +17,19 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable import com.intellij.codeInsight.intention.LowPriorityAction +import com.intellij.codeInsight.navigation.NavigationUtil +import com.intellij.ide.util.EditorHelper import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.descriptors.ClassifierDescriptor +import org.jetbrains.kotlin.idea.KotlinBundle import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase +import org.jetbrains.kotlin.idea.quickfix.KotlinCrossLanguageQuickFixAction import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* import org.jetbrains.kotlin.idea.refactoring.canRefactor import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary @@ -35,7 +40,7 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import java.util.* -class CreateCallableFromUsageFix( +open class CreateCallableFromUsageFix( originalExpression: E, callableInfos: List ) : CreateCallableFromUsageFixBase(originalExpression, callableInfos, false) @@ -47,9 +52,9 @@ class CreateExtensionCallableFromUsageFix( abstract class CreateCallableFromUsageFixBase( originalExpression: E, - private val callableInfos: List, + protected val callableInfos: List, val isExtension: Boolean -) : CreateFromUsageFixBase(originalExpression) { +) : KotlinCrossLanguageQuickFixAction(originalExpression) { init { assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" } if (callableInfos.size > 1) { @@ -77,6 +82,8 @@ abstract class CreateCallableFromUsageFixBase( return if (isExtension || declaration.canRefactor()) declaration else null } + override fun getFamilyName(): String = KotlinBundle.message("create.from.usage.family") + override fun getText(): String { val element = element ?: return "" val receiverTypeInfo = callableInfos.first().receiverTypeInfo @@ -141,7 +148,7 @@ abstract class CreateCallableFromUsageFixBase( }.toString() } - override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean { + override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean { val element = element ?: return false val receiverInfo = callableInfos.first().receiverTypeInfo @@ -173,12 +180,26 @@ abstract class CreateCallableFromUsageFixBase( } } - override fun invoke(project: Project, editor: Editor?, file: KtFile) { + override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) { + if (editor == null) return + val element = element ?: return val callableInfo = callableInfos.first() + val fileForBuilder: KtFile + val editorForBuilder: Editor + if (file is KtFile) { + fileForBuilder = file + editorForBuilder = editor + } + else { + fileForBuilder = element.containingKtFile + EditorHelper.openInEditor(element) + editorForBuilder = FileEditorManager.getInstance(project).selectedTextEditor!! + } + val callableBuilder = - CallableBuilderConfiguration(callableInfos, element as KtElement, file, editor!!, isExtension).createBuilder() + CallableBuilderConfiguration(callableInfos, element as KtElement, fileForBuilder, editorForBuilder, isExtension).createBuilder() fun runBuilder(placement: CallablePlacement) { callableBuilder.placement = placement diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt index 173fbad81eb..87ffd358942 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableMemberFromUsageFactory.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable +import com.intellij.codeInsight.intention.IntentionAction import com.intellij.psi.SmartPsiElementPointer import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority @@ -35,7 +36,7 @@ abstract class CreateCallableMemberFromUsageFactory( originalElementPointer: SmartPsiElementPointer, priority: IntentionActionPriority, quickFixDataFactory: () -> List?, - quickFixFactory: (E, List) -> CreateFromUsageFixBase? + quickFixFactory: (E, List) -> IntentionAction? ): QuickFixWithDelegateFactory { return QuickFixWithDelegateFactory(priority) { val data = quickFixDataFactory().orEmpty() diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt new file mode 100644 index 00000000000..20656a3e3e3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/crossLanguage/KotlinElementActionsFactory.kt @@ -0,0 +1,390 @@ +/* + * Copyright 2010-2017 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.crossLanguage + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInsight.intention.QuickFixFactory +import com.intellij.lang.jvm.* +import com.intellij.lang.jvm.actions.* +import com.intellij.openapi.project.Project +import com.intellij.psi.* +import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl +import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade +import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration +import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.asJava.unwrapped +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.appendModifier +import org.jetbrains.kotlin.idea.quickfix.AddModifierFix +import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.* +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix +import org.jetbrains.kotlin.idea.resolve.ResolutionFacade +import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes +import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.load.java.components.TypeUsage +import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents +import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext +import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver +import org.jetbrains.kotlin.load.java.lazy.child +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor +import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes +import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver +import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter +import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl +import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.supertypes + +class KotlinElementActionsFactory : JvmElementActionsFactory() { + companion object { + val javaPsiModifiersMapping = mapOf( + JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD, + JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD, + JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD, + JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD + ) + } + + private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() { + override fun getText(): String = psiParam.name!! + override fun getProject(): Project = psiParam.project + override fun getParent(): PsiElement = psiParam.parent + override fun getType(): PsiType? = psiParam.type + override fun isValid(): Boolean = true + override fun getContainingFile(): PsiFile = psiParam.containingFile + override fun getReferenceName(): String? = psiParam.name + override fun resolve(): PsiElement? = psiParam + } + + private class ModifierBuilder( + private val targetContainer: KtElement, + private val allowJvmStatic: Boolean = true + ) { + private val psiFactory = KtPsiFactory(targetContainer.project) + + val modifierList = psiFactory.createEmptyModifierList() + + private fun JvmModifier.transformAndAppend(): Boolean { + javaPsiModifiersMapping[this]?.let { + modifierList.appendModifier(it) + return true + } + + when (this) { + JvmModifier.STATIC -> { + if (allowJvmStatic && targetContainer is KtClassOrObject) { + addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME) + } + } + JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD) + JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD) + else -> return false + } + + return true + } + + var isValid = true + private set + + fun addJvmModifier(modifier: JvmModifier) { + isValid = isValid && modifier.transformAndAppend() + } + + fun addJvmModifiers(modifiers: Iterable) { + modifiers.forEach { addJvmModifier(it) } + } + + fun addAnnotation(fqName: FqName) { + if (!isValid) return + modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}")) + } + } + + class CreatePropertyFix( + private val targetClass: JvmClass, + contextElement: KtElement, + propertyInfo: PropertyInfo + ) : CreateCallableFromUsageFix(contextElement, listOf(propertyInfo)) { + override fun getFamilyName() = "Add property" + override fun getText(): String { + val info = callableInfos.first() as PropertyInfo + return buildString { + append("Add '") + if (info.isLateinitPreferred) { + append("lateinit ") + } + append(if (info.writable) "var" else "val") + append("' property '${info.name}' to '${targetClass.name}'") + } + } + } + + private fun JvmClass.toKtClassOrFile(): KtElement? { + val psi = sourceElement + return when (psi) { + is KtClassOrObject -> psi + is KtLightClassForSourceDeclaration -> psi.kotlinOrigin + is KtLightClassForFacade -> psi.files.firstOrNull() + else -> null + } + } + + private inline fun JvmElement.toKtElement() = sourceElement?.unwrapped as? T + + private fun fakeParametersExpressions(parameters: List, project: Project): Array? = + when { + parameters.isEmpty() -> emptyArray() + else -> JavaPsiFacade + .getElementFactory(project) + .createParameterList( + parameters.map { it.name }.toTypedArray(), + parameters.map { it.type as? PsiType ?: return null }.toTypedArray() + ) + .parameters + .map(::FakeExpressionFromParameter) + .toTypedArray() + } + + private fun PsiType.collectTypeParameters(): List { + val results = ArrayList() + accept( + object : PsiTypeVisitor() { + override fun visitArrayType(arrayType: PsiArrayType) { + arrayType.componentType.accept(this) + } + + override fun visitClassType(classType: PsiClassType) { + (classType.resolve() as? PsiTypeParameter)?.let { results += it } + classType.parameters.forEach { it.accept(this) } + } + + override fun visitWildcardType(wildcardType: PsiWildcardType) { + wildcardType.bound?.accept(this) + } + } + ) + return results + } + + private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? { + val typeParameters = collectTypeParameters() + val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java) + val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null } + val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy")) + val dummyClassDescriptor = ClassDescriptorImpl( + dummyPackageDescriptor, + Name.identifier("Dummy"), + Modality.FINAL, + ClassKind.CLASS, + emptyList(), + SourceElement.NO_SOURCE, + false + ) + val typeParameterResolver = object : TypeParameterResolver { + override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? { + val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi + val index = typeParameters.indexOf(psiTypeParameter) + if (index < 0) return null + return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor) + } + } + val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver) + val attributes = JavaTypeAttributes(TypeUsage.COMMON) + return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true) + } + + private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo { + val candidateTypes = flatMapTo(LinkedHashSet()) { + val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList() + when (it.theKind) { + ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType) + ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes() + } + } + if (candidateTypes.isEmpty()) { + val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType + return TypeInfo(nullableAnyType, Variance.INVARIANT) + } + return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList()) + } + + override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List { + val kModifierOwner = target.toKtElement() ?: return emptyList() + + val modifier = request.modifier + val shouldPresent = request.shouldPresent + val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier) + KtTokens.OPEN_KEYWORD to !shouldPresent + else + javaPsiModifiersMapping[modifier] to shouldPresent + if (kToken == null) return emptyList() + + val action = if (shouldPresentMapped) + AddModifierFix.createIfApplicable(kModifierOwner, kToken) + else + RemoveModifierFix(kModifierOwner, kToken, false) + return listOfNotNull(action) + } + + override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List { + val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList() + + if (request.typeParameters.isNotEmpty()) return emptyList() + + val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetKtClass.getResolutionFacade() + val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType + val parameterInfos = request.parameters.mapIndexed { index, param -> + val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + val name = param.name ?: "arg${index + 1}" + ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name)) + } + val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor() + val constructorInfo = ConstructorInfo( + parameterInfos, + targetKtClass, + isPrimary = needPrimary, + modifierList = modifierBuilder.modifierList, + withBody = true + ) + val addConstructorAction = object : CreateCallableFromUsageFix(targetKtClass, listOf(constructorInfo)) { + override fun getFamilyName() = "Add method" + override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'" + } + + val changePrimaryConstructorAction = run { + val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null + val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null + val project = targetKtClass.project + val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null + QuickFixFactory.getInstance() + .createChangeMethodSignatureFromUsageFix( + lightMethod, + fakeParametersExpressions, + PsiSubstitutor.EMPTY, + targetKtClass, + false, + 2 + ).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) } + } + + return listOfNotNull(changePrimaryConstructorAction, addConstructorAction) + } + + override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List { + val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + + val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetContainer.getResolutionFacade() + val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType + val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType + val propertyInfo = PropertyInfo( + request.propertyName, + TypeInfo.Empty, + TypeInfo(ktType, Variance.INVARIANT), + request.setterRequired, + listOf(targetContainer), + modifierList = modifierBuilder.modifierList, + withInitializer = true + ) + val propertyInfos = if (request.setterRequired) { + listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true)) + } + else { + listOf(propertyInfo) + } + return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + } + + override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List { + val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + + val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply { + addJvmModifiers(request.modifiers) + addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) + } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetContainer.getResolutionFacade() + val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade) + val writable = JvmModifier.FINAL !in request.modifiers + val propertyInfo = PropertyInfo( + request.fieldName, + TypeInfo.Empty, + typeInfo, + writable, + listOf(targetContainer), + isForCompanion = JvmModifier.STATIC in request.modifiers, + modifierList = modifierBuilder.modifierList, + withInitializer = true + ) + val propertyInfos = if (writable) { + listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true)) + } + else { + listOf(propertyInfo) + } + return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) } + } + + override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List { + val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList() + + val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) } + if (!modifierBuilder.isValid) return emptyList() + + val resolutionFacade = targetContainer.getResolutionFacade() + val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade) + val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) -> + ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList()) + } + val functionInfo = FunctionInfo( + request.methodName, + TypeInfo.Empty, + returnTypeInfo, + listOf(targetContainer), + parameterInfos, + isAbstract = JvmModifier.ABSTRACT in request.modifiers, + isForCompanion = JvmModifier.STATIC in request.modifiers, + modifierList = modifierBuilder.modifierList, + preferEmptyBody = true + ) + val action = object : CreateCallableFromUsageFix(targetContainer, listOf(functionInfo)) { + override fun getFamilyName() = "Add method" + override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'" + } + return listOf(action) + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.after.Dependency.kt new file mode 100644 index 00000000000..e9ff13191a8 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.after.Dependency.kt @@ -0,0 +1,6 @@ +class K { + fun foo(i: Int, s: String): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.after.java b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.after.java new file mode 100644 index 00000000000..b01f1c586d0 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.after.java @@ -0,0 +1,6 @@ +// "Add method 'foo' to 'K'" "true" +class J { + void test(K k) { + boolean b = k.foo(1, "2"); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Dependency.kt new file mode 100644 index 00000000000..907ff90fef4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Dependency.kt @@ -0,0 +1,3 @@ +class K { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Main.java b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Main.java new file mode 100644 index 00000000000..b01f1c586d0 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Main.java @@ -0,0 +1,6 @@ +// "Add method 'foo' to 'K'" "true" +class J { + void test(K k) { + boolean b = k.foo(1, "2"); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.after.Dependency.kt new file mode 100644 index 00000000000..117a207b18c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.after.Dependency.kt @@ -0,0 +1,9 @@ +class K { + companion object { + @JvmStatic + fun foo(i: Int, s: String): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + } + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.after.java b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.after.java new file mode 100644 index 00000000000..4aab11b500d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.after.java @@ -0,0 +1,7 @@ +// "Add method 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test() { + boolean b = K.foo(1, "2"); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Dependency.kt new file mode 100644 index 00000000000..907ff90fef4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Dependency.kt @@ -0,0 +1,3 @@ +class K { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Main.java b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Main.java new file mode 100644 index 00000000000..4aab11b500d --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Main.java @@ -0,0 +1,7 @@ +// "Add method 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test() { + boolean b = K.foo(1, "2"); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.after.Dependency.kt new file mode 100644 index 00000000000..349691ba628 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.after.Dependency.kt @@ -0,0 +1,7 @@ +@file:JvmName("TestKt") + +fun test() {} + +fun foo(i: Int, s: String): Boolean { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. +} diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.after.java b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.after.java new file mode 100644 index 00000000000..63e6e12237c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.after.java @@ -0,0 +1,6 @@ +// "Add method 'foo' to 'TestKt'" "true" +class J { + void test() { + boolean b = TestKt.foo(1, "2"); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Dependency.kt new file mode 100644 index 00000000000..8bd4f93dbde --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Dependency.kt @@ -0,0 +1,3 @@ +@file:JvmName("TestKt") + +fun test() {} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Main.java b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Main.java new file mode 100644 index 00000000000..63e6e12237c --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Main.java @@ -0,0 +1,6 @@ +// "Add method 'foo' to 'TestKt'" "true" +class J { + void test() { + boolean b = TestKt.foo(1, "2"); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.after.Dependency.kt new file mode 100644 index 00000000000..ddd5babef24 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.after.Dependency.kt @@ -0,0 +1,5 @@ +class K { + @JvmField + lateinit var foo: String + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.after.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.after.java new file mode 100644 index 00000000000..e33fc2546a7 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.after.java @@ -0,0 +1,7 @@ +// "Add 'lateinit var' property 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test(K k) { + String s = k.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Dependency.kt new file mode 100644 index 00000000000..907ff90fef4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Dependency.kt @@ -0,0 +1,3 @@ +class K { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Main.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Main.java new file mode 100644 index 00000000000..e33fc2546a7 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Main.java @@ -0,0 +1,7 @@ +// "Add 'lateinit var' property 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test(K k) { + String s = k.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.after.Dependency.kt new file mode 100644 index 00000000000..22b7f4538f8 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.after.Dependency.kt @@ -0,0 +1,5 @@ +class K { + @JvmField + var foo: String = TODO("initialize me") + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.after.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.after.java new file mode 100644 index 00000000000..44ad12b9336 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.after.java @@ -0,0 +1,7 @@ +// "Add 'var' property 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test(K k) { + String s = k.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Dependency.kt new file mode 100644 index 00000000000..907ff90fef4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Dependency.kt @@ -0,0 +1,3 @@ +class K { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Main.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Main.java new file mode 100644 index 00000000000..44ad12b9336 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Main.java @@ -0,0 +1,7 @@ +// "Add 'var' property 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test(K k) { + String s = k.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.after.Dependency.kt new file mode 100644 index 00000000000..e66bda39bde --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.after.Dependency.kt @@ -0,0 +1,7 @@ +class K { + companion object { + @JvmField + var foo: String = TODO("initialize me") + } + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.after.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.after.java new file mode 100644 index 00000000000..e290f80f508 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.after.java @@ -0,0 +1,7 @@ +// "Add 'var' property 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test() { + String s = K.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Dependency.kt new file mode 100644 index 00000000000..907ff90fef4 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Dependency.kt @@ -0,0 +1,3 @@ +class K { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Main.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Main.java new file mode 100644 index 00000000000..e290f80f508 --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Main.java @@ -0,0 +1,7 @@ +// "Add 'var' property 'foo' to 'K'" "true" +// WITH_RUNTIME +class J { + void test() { + String s = K.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.after.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.after.Dependency.kt new file mode 100644 index 00000000000..6c823a5596f --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.after.Dependency.kt @@ -0,0 +1,6 @@ +@file:JvmName("TestKt") + +fun test() {} + +@JvmField +var foo: String = TODO("initialize me") diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.after.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.after.java new file mode 100644 index 00000000000..b521d9f427b --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.after.java @@ -0,0 +1,7 @@ +// "Add 'var' property 'foo' to 'TestKt'" "true" +// WITH_RUNTIME +class J { + void test() { + String s = TestKt.foo; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Dependency.kt b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Dependency.kt new file mode 100644 index 00000000000..8bd4f93dbde --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Dependency.kt @@ -0,0 +1,3 @@ +@file:JvmName("TestKt") + +fun test() {} \ No newline at end of file diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Main.java b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Main.java new file mode 100644 index 00000000000..b521d9f427b --- /dev/null +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Main.java @@ -0,0 +1,7 @@ +// "Add 'var' property 'foo' to 'TestKt'" "true" +// WITH_RUNTIME +class J { + void test() { + String s = TestKt.foo; + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index d9182f7f858..d3d0f43c4f3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1339,6 +1339,33 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } } + + @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FromJava extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInFromJava() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/fromJava"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("classMember.before.Main.java") + public void testClassMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionMember.before.Main.java") + public void testCompanionMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("topLevel.before.Main.java") + public void testTopLevel() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Main.java"); + doTestWithExtraFile(fileName); + } + } } @TestMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor") @@ -1509,6 +1536,39 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/varOnJavaType.before.Main.kt"); doTestWithExtraFile(fileName); } + + @TestMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FieldFromJava extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInFieldFromJava() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true); + } + + @TestMetadata("classMemberLateinitVar.before.Main.java") + public void testClassMemberLateinitVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("classMemberVar.before.Main.java") + public void testClassMemberVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionMemberVar.before.Main.java") + public void testCompanionMemberVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("topLevelVar.before.Main.java") + public void testTopLevelVar() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Main.java"); + doTestWithExtraFile(fileName); + } + } } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 475cd18e977..d9481682ec4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -3467,6 +3467,15 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { } } + @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FromJava extends AbstractQuickFixTest { + public void testAllFilesPresentInFromJava() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/fromJava"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true); + } + } + @TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/delegateAccessors") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt index 25ebdf5fb69..8c02d755f58 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUClass.kt @@ -72,6 +72,8 @@ open class KotlinUClass private constructor( override val psi = unwrap(psi) + override fun getSourceElement() = sourcePsi ?: this + override fun getOriginalElement(): PsiElement? = super.getOriginalElement() override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass) diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt index 3e45ea0ba7d..be3fccb5c36 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUMethod.kt @@ -42,6 +42,8 @@ open class KotlinUMethod( override val sourcePsi = psi.kotlinOrigin + override fun getSourceElement() = sourcePsi ?: this + override val uastDefaultValue by lz { val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null val defaultValue = annotationParameter.defaultValue ?: return@lz null diff --git a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt index 6a335d66ad1..78a1665e80c 100644 --- a/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt +++ b/plugins/uast-kotlin/src/org/jetbrains/uast/kotlin/declarations/KotlinUVariable.kt @@ -269,6 +269,7 @@ open class KotlinUField( override val sourcePsi: KtElement?, givenParent: UElement? ) : AbstractKotlinUVariable(givenParent), UField, PsiField by psi { + override fun getSourceElement() = sourcePsi ?: this override val javaPsi = unwrap(psi)