172: Revert "Refactoring: Use modifier list in CallableInfo where possible"
This reverts commit 99be75c
This commit is contained in:
committed by
Nikolay Krasko
parent
577e6e5206
commit
deb2ffb49f
+1149
File diff suppressed because it is too large
Load Diff
+281
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.ArrayUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.ClassInfo
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.idea.util.getResolvableApproximations
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Represents a concrete type or a set of types yet to be inferred from an expression.
|
||||
*/
|
||||
abstract class TypeInfo(val variance: Variance) {
|
||||
object Empty: TypeInfo(Variance.INVARIANT) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> = Collections.emptyList()
|
||||
}
|
||||
|
||||
class ByExpression(val expression: KtExpression, variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> {
|
||||
return KotlinNameSuggester.suggestNamesByExpressionOnly(expression, bindingContext, { true }).toTypedArray()
|
||||
}
|
||||
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
expression.guessTypes(
|
||||
context = builder.currentFileContext,
|
||||
module = builder.currentFileModule,
|
||||
pseudocode = builder.pseudocode
|
||||
).flatMap { it.getPossibleSupertypes(variance, builder) }
|
||||
}
|
||||
|
||||
class ByTypeReference(val typeReference: KtTypeReference, variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
builder.currentFileContext[BindingContext.TYPE, typeReference].getPossibleSupertypes(variance, builder)
|
||||
}
|
||||
|
||||
class ByType(val theType: KotlinType, variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
theType.getPossibleSupertypes(variance, builder)
|
||||
}
|
||||
|
||||
class ByReceiverType(variance: Variance): TypeInfo(variance) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
|
||||
}
|
||||
|
||||
class ByExplicitCandidateTypes(val types: List<KotlinType>) : 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)
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> = delegate.getPossibleTypes(builder)
|
||||
}
|
||||
|
||||
class NoSubstitutions(delegate: TypeInfo): DelegatingTypeInfo(delegate) {
|
||||
override val substitutionsAllowed: Boolean = false
|
||||
}
|
||||
|
||||
class StaticContextRequired(delegate: TypeInfo): DelegatingTypeInfo(delegate) {
|
||||
override val staticContextRequired: Boolean = true
|
||||
}
|
||||
|
||||
class OfThis(delegate: TypeInfo) : DelegatingTypeInfo(delegate)
|
||||
|
||||
val isOfThis: Boolean
|
||||
get() = when (this) {
|
||||
is OfThis -> true
|
||||
is DelegatingTypeInfo -> delegate.isOfThis
|
||||
else -> false
|
||||
}
|
||||
|
||||
open val substitutionsAllowed: Boolean = true
|
||||
open val staticContextRequired: Boolean = false
|
||||
open fun getPossibleNamesFromExpression(bindingContext: BindingContext): Array<String> = ArrayUtil.EMPTY_STRING_ARRAY
|
||||
abstract fun getPossibleTypes(builder: CallableBuilder): List<KotlinType>
|
||||
|
||||
private fun getScopeForTypeApproximation(config: CallableBuilderConfiguration, placement: CallablePlacement?): LexicalScope? {
|
||||
if (placement == null) return config.originalElement.getResolutionScope()
|
||||
|
||||
val containingElement = when (placement) {
|
||||
is CallablePlacement.NoReceiver -> {
|
||||
placement.containingElement
|
||||
}
|
||||
is CallablePlacement.WithReceiver -> {
|
||||
val receiverClassDescriptor =
|
||||
placement.receiverTypeCandidate.theType.constructor.declarationDescriptor
|
||||
val classDeclaration = receiverClassDescriptor?.let { DescriptorToSourceUtils.getSourceFromDescriptor(it) }
|
||||
if (!config.isExtension && classDeclaration != null) classDeclaration else config.currentFile
|
||||
}
|
||||
}
|
||||
return when (containingElement) {
|
||||
is KtClassOrObject -> (containingElement.resolveToDescriptorIfAny() as? ClassDescriptorWithResolutionScopes)?.scopeForMemberDeclarationResolution
|
||||
is KtBlockExpression -> (containingElement.statements.firstOrNull() ?: containingElement).getResolutionScope()
|
||||
is KtElement -> containingElement.containingKtFile.getResolutionScope()
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
protected fun KotlinType?.getPossibleSupertypes(variance: Variance, callableBuilder: CallableBuilder): List<KotlinType> {
|
||||
if (this == null || ErrorUtils.containsErrorType(this)) {
|
||||
return Collections.singletonList(callableBuilder.currentFileModule.builtIns.anyType)
|
||||
}
|
||||
val scope = getScopeForTypeApproximation(callableBuilder.config, callableBuilder.placement)
|
||||
val approximations = getResolvableApproximations(scope, false, true)
|
||||
return when (variance) {
|
||||
Variance.IN_VARIANCE -> approximations.toList()
|
||||
else -> listOf(approximations.firstOrNull() ?: this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TypeInfo(expressionOfType: KtExpression, variance: Variance): TypeInfo = TypeInfo.ByExpression(expressionOfType, variance)
|
||||
fun TypeInfo(typeReference: KtTypeReference, variance: Variance): TypeInfo = TypeInfo.ByTypeReference(typeReference, variance)
|
||||
fun TypeInfo(theType: KotlinType, variance: Variance): TypeInfo = TypeInfo.ByType(theType, variance)
|
||||
|
||||
fun TypeInfo.noSubstitutions(): TypeInfo = (this as? TypeInfo.NoSubstitutions) ?: TypeInfo.NoSubstitutions(this)
|
||||
|
||||
fun TypeInfo.forceNotNull(): TypeInfo {
|
||||
class ForcedNotNull(delegate: TypeInfo): TypeInfo.DelegatingTypeInfo(delegate) {
|
||||
override fun getPossibleTypes(builder: CallableBuilder): List<KotlinType> =
|
||||
super.getPossibleTypes(builder).map { it.makeNotNullable() }
|
||||
}
|
||||
|
||||
return (this as? ForcedNotNull) ?: ForcedNotNull(this)
|
||||
}
|
||||
|
||||
fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
|
||||
|
||||
/**
|
||||
* Encapsulates information about a function parameter that is going to be created.
|
||||
*/
|
||||
class ParameterInfo(
|
||||
val typeInfo: TypeInfo,
|
||||
val nameSuggestions: List<String>
|
||||
) {
|
||||
constructor(typeInfo: TypeInfo, preferredName: String? = null): this(typeInfo, listOfNotNull(preferredName))
|
||||
}
|
||||
|
||||
enum class CallableKind {
|
||||
FUNCTION,
|
||||
CLASS_WITH_PRIMARY_CONSTRUCTOR,
|
||||
CONSTRUCTOR,
|
||||
PROPERTY
|
||||
}
|
||||
|
||||
abstract class CallableInfo (
|
||||
val name: String,
|
||||
val receiverTypeInfo: TypeInfo,
|
||||
val returnTypeInfo: TypeInfo,
|
||||
val possibleContainers: List<KtElement>,
|
||||
val typeParameterInfos: List<TypeInfo>,
|
||||
val isAbstract: Boolean = false,
|
||||
val isForCompanion: Boolean = false,
|
||||
val modifierList: KtModifierList? = null
|
||||
) {
|
||||
abstract val kind: CallableKind
|
||||
abstract val parameterInfos: List<ParameterInfo>
|
||||
|
||||
abstract fun copy(receiverTypeInfo: TypeInfo = this.receiverTypeInfo,
|
||||
possibleContainers: List<KtElement> = this.possibleContainers,
|
||||
isAbstract: Boolean = this.isAbstract): CallableInfo
|
||||
}
|
||||
|
||||
class FunctionInfo(name: String,
|
||||
receiverTypeInfo: TypeInfo,
|
||||
returnTypeInfo: TypeInfo,
|
||||
possibleContainers: List<KtElement> = Collections.emptyList(),
|
||||
override val parameterInfos: List<ParameterInfo> = Collections.emptyList(),
|
||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||
val isOperator: Boolean = false,
|
||||
val isInfix: Boolean = false,
|
||||
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<KtElement>, isAbstract: Boolean) = FunctionInfo(
|
||||
name,
|
||||
receiverTypeInfo,
|
||||
returnTypeInfo,
|
||||
possibleContainers,
|
||||
parameterInfos,
|
||||
typeParameterInfos,
|
||||
isOperator,
|
||||
isInfix,
|
||||
isAbstract
|
||||
)
|
||||
}
|
||||
|
||||
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<ParameterInfo> get() = classInfo.parameterInfos
|
||||
|
||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
class ConstructorInfo(
|
||||
override val parameterInfos: List<ParameterInfo>,
|
||||
val targetClass: PsiElement,
|
||||
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<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
class PropertyInfo(name: String,
|
||||
receiverTypeInfo: TypeInfo,
|
||||
returnTypeInfo: TypeInfo,
|
||||
val writable: Boolean,
|
||||
possibleContainers: List<KtElement> = Collections.emptyList(),
|
||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||
isAbstract: Boolean = false,
|
||||
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<ParameterInfo> get() = Collections.emptyList()
|
||||
|
||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) =
|
||||
copyProperty(receiverTypeInfo, possibleContainers, isAbstract)
|
||||
|
||||
fun copyProperty(
|
||||
receiverTypeInfo: TypeInfo = this.receiverTypeInfo,
|
||||
possibleContainers: List<KtElement> = this.possibleContainers,
|
||||
isAbstract: Boolean = this.isAbstract,
|
||||
isLateinitPreferred: Boolean = this.isLateinitPreferred
|
||||
) = PropertyInfo(
|
||||
name,
|
||||
receiverTypeInfo,
|
||||
returnTypeInfo,
|
||||
writable,
|
||||
possibleContainers,
|
||||
typeParameterInfos,
|
||||
isAbstract,
|
||||
isLateinitPreferred,
|
||||
isForCompanion,
|
||||
modifierList,
|
||||
withInitializer
|
||||
)
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateBinaryOperationActionFactory : CreateCallableMemberFromUsageFactory<KtBinaryExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtBinaryExpression? {
|
||||
return diagnostic.psiElement.parent as? KtBinaryExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtBinaryExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val token = element.operationToken as KtToken
|
||||
val operationName = when (token) {
|
||||
KtTokens.IDENTIFIER -> element.operationReference.getReferencedName()
|
||||
else -> OperatorConventions.getNameForOperationSymbol(token, false, true)?.asString()
|
||||
} ?: return null
|
||||
val inOperation = token in OperatorConventions.IN_OPERATIONS
|
||||
val comparisonOperation = token in OperatorConventions.COMPARISON_OPERATIONS
|
||||
|
||||
val leftExpr = element.left ?: return null
|
||||
val rightExpr = element.right ?: return null
|
||||
|
||||
val receiverExpr = if (inOperation) rightExpr else leftExpr
|
||||
val argumentExpr = if (inOperation) leftExpr else rightExpr
|
||||
|
||||
val builtIns = element.builtIns
|
||||
val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE)
|
||||
val returnType = when {
|
||||
inOperation -> TypeInfo.ByType(builtIns.booleanType, Variance.INVARIANT).noSubstitutions()
|
||||
comparisonOperation -> TypeInfo.ByType(builtIns.intType, Variance.INVARIANT).noSubstitutions()
|
||||
else -> TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
}
|
||||
val parameters = Collections.singletonList(ParameterInfo(TypeInfo(argumentExpr, Variance.IN_VARIANCE)))
|
||||
val isOperator = token != KtTokens.IDENTIFIER
|
||||
return FunctionInfo(operationName, receiverType, returnType, parameterInfos = parameters,
|
||||
isOperator = isOperator,
|
||||
isInfix = !isOperator)
|
||||
}
|
||||
}
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.analyzer.AnalysisResult
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.getExtractionContainers
|
||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
||||
import org.jetbrains.kotlin.idea.util.isAbstract
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classValueType
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.*
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import java.lang.AssertionError
|
||||
import java.util.*
|
||||
|
||||
sealed class CreateCallableFromCallActionFactory<E : KtExpression>(
|
||||
extensionsEnabled: Boolean = true
|
||||
) : CreateCallableMemberFromUsageFactory<E>(extensionsEnabled) {
|
||||
protected abstract fun doCreateCallableInfo(
|
||||
expression: E,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo?
|
||||
|
||||
protected fun getExpressionOfInterest(diagnostic: Diagnostic): KtExpression? {
|
||||
val diagElement = diagnostic.psiElement
|
||||
if (PsiTreeUtil.getParentOfType(
|
||||
diagElement,
|
||||
KtTypeReference::class.java, KtAnnotationEntry::class.java, KtImportDirective::class.java
|
||||
) != null) return null
|
||||
|
||||
return when (diagnostic.factory) {
|
||||
in Errors.UNRESOLVED_REFERENCE_DIAGNOSTICS, Errors.EXPRESSION_EXPECTED_PACKAGE_FOUND -> {
|
||||
val parent = diagElement.parent
|
||||
if (parent is KtCallExpression && parent.calleeExpression == diagElement) parent else diagElement
|
||||
}
|
||||
|
||||
Errors.NO_VALUE_FOR_PARAMETER,
|
||||
Errors.TOO_MANY_ARGUMENTS,
|
||||
Errors.NONE_APPLICABLE -> diagElement.getNonStrictParentOfType<KtCallExpression>()
|
||||
|
||||
Errors.TYPE_MISMATCH -> (diagElement.parent as? KtValueArgument)?.getStrictParentOfType<KtCallExpression>()
|
||||
|
||||
else -> throw AssertionError("Unexpected diagnostic: ${diagnostic.factory}")
|
||||
} as? KtExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: E, diagnostic: Diagnostic): CallableInfo? {
|
||||
val project = element.project
|
||||
|
||||
val calleeExpr = when (element) {
|
||||
is KtCallExpression -> element.calleeExpression
|
||||
is KtSimpleNameExpression -> element
|
||||
else -> null
|
||||
} as? KtSimpleNameExpression ?: return null
|
||||
|
||||
if (calleeExpr.getReferencedNameElementType() != KtTokens.IDENTIFIER) return null
|
||||
|
||||
val analysisResult = calleeExpr.analyzeAndGetResult()
|
||||
val receiver = element.getCall(analysisResult.bindingContext)?.explicitReceiver
|
||||
val receiverType = getReceiverTypeInfo(analysisResult.bindingContext, project, receiver) ?: return null
|
||||
|
||||
val possibleContainers =
|
||||
if (receiverType is TypeInfo.Empty) {
|
||||
val containers = with(element.getQualifiedExpressionForSelectorOrThis().getExtractionContainers()) {
|
||||
if (element is KtCallExpression) this else filter { it is KtClassBody || it is KtFile }
|
||||
}
|
||||
if (containers.isNotEmpty()) containers else return null
|
||||
}
|
||||
else Collections.emptyList()
|
||||
|
||||
return doCreateCallableInfo(element, analysisResult, calleeExpr.getReferencedName(), receiverType, possibleContainers)
|
||||
}
|
||||
|
||||
private fun getReceiverTypeInfo(context: BindingContext, project: Project, receiver: Receiver?): TypeInfo? {
|
||||
return when (receiver) {
|
||||
null -> TypeInfo.Empty
|
||||
is Qualifier -> {
|
||||
val qualifierType = context.getType(receiver.expression)
|
||||
if (qualifierType != null) return TypeInfo(qualifierType, Variance.IN_VARIANCE)
|
||||
|
||||
if (receiver !is ClassQualifier) return null
|
||||
val classifierType = receiver.descriptor.classValueType
|
||||
if (classifierType != null) return TypeInfo(classifierType, Variance.IN_VARIANCE)
|
||||
|
||||
val javaClassifier = receiver.descriptor as? JavaClassDescriptor ?: return null
|
||||
val javaClass = DescriptorToSourceUtilsIde.getAnyDeclaration(project, javaClassifier) as? PsiClass
|
||||
if (javaClass == null || !javaClass.canRefactor()) return null
|
||||
TypeInfo.StaticContextRequired(TypeInfo(javaClassifier.defaultType, Variance.IN_VARIANCE))
|
||||
}
|
||||
is ReceiverValue -> TypeInfo(receiver.type, Variance.IN_VARIANCE)
|
||||
else -> throw AssertionError("Unexpected receiver: $receiver")
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getAbstractCallableInfo(mainCallable: CallableInfo, originalExpression: KtExpression): CallableInfo? {
|
||||
val receiverTypeInfo: TypeInfo
|
||||
val receiverType: KotlinType
|
||||
|
||||
val originalReceiverTypeInfo = mainCallable.receiverTypeInfo
|
||||
if (originalReceiverTypeInfo != TypeInfo.Empty) {
|
||||
if (originalReceiverTypeInfo !is TypeInfo.ByType) return null
|
||||
receiverTypeInfo = originalReceiverTypeInfo
|
||||
receiverType = receiverTypeInfo.theType
|
||||
}
|
||||
else {
|
||||
val containingClass = originalExpression.getStrictParentOfType<KtClassOrObject>() as? KtClass ?: return null
|
||||
if (containingClass is KtEnumEntry) return null
|
||||
receiverType = (containingClass.unsafeResolveToDescriptor() as ClassDescriptor).defaultType
|
||||
receiverTypeInfo = TypeInfo(receiverType, Variance.IN_VARIANCE).ofThis()
|
||||
}
|
||||
|
||||
if (!receiverType.isAbstract() && TypeUtils.getAllSupertypes(receiverType).all { !it.isAbstract() }) return null
|
||||
|
||||
return mainCallable.copy(receiverTypeInfo = receiverTypeInfo, possibleContainers = emptyList(), isAbstract = true)
|
||||
}
|
||||
|
||||
protected fun getCallableWithReceiverInsideExtension(
|
||||
mainCallable: CallableInfo,
|
||||
originalExpression: KtExpression,
|
||||
context: BindingContext,
|
||||
receiverType: TypeInfo
|
||||
): CallableInfo? {
|
||||
if (receiverType != TypeInfo.Empty) return null
|
||||
val callable = (originalExpression.getParentOfTypeAndBranch<KtFunction> { bodyExpression }
|
||||
?: originalExpression.getParentOfTypeAndBranches<KtProperty> { listOf(getter, setter) })
|
||||
?: return null
|
||||
if (callable !is KtFunctionLiteral && callable.receiverTypeReference == null) return null
|
||||
val callableDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, callable] as? CallableDescriptor ?: return null
|
||||
val extensionReceiverType = callableDescriptor.extensionReceiverParameter?.type ?: return null
|
||||
val newReceiverTypeInfo = TypeInfo(extensionReceiverType, Variance.IN_VARIANCE)
|
||||
return mainCallable.copy(receiverTypeInfo = newReceiverTypeInfo, possibleContainers = emptyList())
|
||||
}
|
||||
|
||||
sealed class Property: CreateCallableFromCallActionFactory<KtSimpleNameExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtSimpleNameExpression? {
|
||||
val refExpr = getExpressionOfInterest(diagnostic) as? KtNameReferenceExpression ?: return null
|
||||
if (refExpr.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } != null) return null
|
||||
return refExpr
|
||||
}
|
||||
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
val fullCallExpr = expression.getQualifiedExpressionForSelectorOrThis()
|
||||
val varExpected = fullCallExpr.getAssignmentByLHS() != null
|
||||
val expressionForTypeGuess = fullCallExpr.getExpressionForTypeGuess()
|
||||
val returnTypes = expressionForTypeGuess.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor)
|
||||
val returnTypeInfo = TypeInfo(expressionForTypeGuess, if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE)
|
||||
val canBeLateinit =
|
||||
varExpected
|
||||
&& returnTypes.any { !it.isMarkedNullable && !KotlinBuiltIns.isPrimitiveType(it) }
|
||||
&& fullCallExpr.parents.firstOrNull { it is KtDeclarationWithBody || it is KtClassInitializer } is KtDeclarationWithBody
|
||||
return PropertyInfo(name, receiverType, returnTypeInfo, varExpected, possibleContainers, isLateinitPreferred = canBeLateinit)
|
||||
}
|
||||
|
||||
object Default : Property() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
return super.doCreateCallableInfo(
|
||||
expression,
|
||||
analysisResult,
|
||||
name,
|
||||
receiverType,
|
||||
possibleContainers.filterNot { it is KtClassBody && (it.parent as KtClassOrObject).isInterfaceClass() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object Abstract : Property() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
getAbstractCallableInfo(it, expression)
|
||||
}
|
||||
}
|
||||
|
||||
object ByImplicitExtensionReceiver : Property() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtSimpleNameExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
ByImplicitExtensionReceiver.getCallableWithReceiverInsideExtension(it, expression, analysisResult.bindingContext, receiverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Function: CreateCallableFromCallActionFactory<KtCallExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallExpression? {
|
||||
return getExpressionOfInterest(diagnostic) as? KtCallExpression
|
||||
}
|
||||
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
val parameters = expression.getParameterInfos()
|
||||
val typeParameters = expression.getTypeInfoForTypeArguments()
|
||||
val fullCallExpression = expression.getQualifiedExpressionForSelectorOrThis()
|
||||
val expectedType = fullCallExpression.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor).singleOrNull()
|
||||
val returnType = if (expectedType != null) {
|
||||
TypeInfo(expectedType, Variance.OUT_VARIANCE)
|
||||
} else {
|
||||
TypeInfo(fullCallExpression, Variance.OUT_VARIANCE)
|
||||
}
|
||||
return FunctionInfo(name, receiverType, returnType, possibleContainers, parameters, typeParameters)
|
||||
}
|
||||
|
||||
object Default : Function()
|
||||
|
||||
object Abstract : Function() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
getAbstractCallableInfo(it, expression)
|
||||
}
|
||||
}
|
||||
|
||||
object ByImplicitExtensionReceiver : Function() {
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
) = super.doCreateCallableInfo(expression, analysisResult, name, receiverType, possibleContainers)?.let {
|
||||
getCallableWithReceiverInsideExtension(it, expression, analysisResult.bindingContext, receiverType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Constructor: CreateCallableFromCallActionFactory<KtCallExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallExpression? {
|
||||
return getExpressionOfInterest(diagnostic) as? KtCallExpression
|
||||
}
|
||||
|
||||
override fun doCreateCallableInfo(
|
||||
expression: KtCallExpression,
|
||||
analysisResult: AnalysisResult,
|
||||
name: String,
|
||||
receiverType: TypeInfo,
|
||||
possibleContainers: List<KtElement>
|
||||
): CallableInfo? {
|
||||
if (expression.typeArguments.isNotEmpty()) return null
|
||||
|
||||
val classDescriptor = expression
|
||||
.calleeExpression
|
||||
?.getReferenceTargets(analysisResult.bindingContext)
|
||||
?.mapNotNull { (it as? ConstructorDescriptor)?.containingDeclaration }
|
||||
?.distinct()
|
||||
?.singleOrNull() as? ClassDescriptor
|
||||
val klass = classDescriptor?.source?.getPsi()
|
||||
if ((klass !is KtClass && klass !is PsiClass) || !klass.canRefactor()) return null
|
||||
|
||||
val expectedType = analysisResult.bindingContext[BindingContext.EXPECTED_EXPRESSION_TYPE, expression.getQualifiedExpressionForSelectorOrThis()]
|
||||
?: classDescriptor.builtIns.nullableAnyType
|
||||
if (!classDescriptor.defaultType.isSubtypeOf(expectedType)) return null
|
||||
|
||||
val parameters = expression.getParameterInfos()
|
||||
|
||||
return ConstructorInfo(parameters, klass)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FUNCTIONS = arrayOf(Function.Default,
|
||||
Function.Abstract,
|
||||
Function.ByImplicitExtensionReceiver,
|
||||
Constructor)
|
||||
val INSTANCES = arrayOf(Function.Default,
|
||||
Function.Abstract,
|
||||
Function.ByImplicitExtensionReceiver,
|
||||
Constructor,
|
||||
Property.Default,
|
||||
Property.Abstract,
|
||||
Property.ByImplicitExtensionReceiver)
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
object CreateComponentFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtDestructuringDeclaration>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtDestructuringDeclaration? {
|
||||
QuickFixUtil.getParentElementOfType(diagnostic, KtDestructuringDeclaration::class.java)?.let { return it }
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)?.destructuringDeclaration
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtDestructuringDeclaration, diagnostic: Diagnostic): CallableInfo? {
|
||||
val diagnosticWithParameters = Errors.COMPONENT_FUNCTION_MISSING.cast(diagnostic)
|
||||
|
||||
val name = diagnosticWithParameters.a
|
||||
if (!DataClassDescriptorResolver.isComponentLike(name)) return null
|
||||
|
||||
val componentNumber = DataClassDescriptorResolver.getComponentIndex(name.asString()) - 1
|
||||
|
||||
val targetType = diagnosticWithParameters.b
|
||||
val targetClassDescriptor = targetType.constructor.declarationDescriptor as? ClassDescriptor
|
||||
if (targetClassDescriptor != null && targetClassDescriptor.isData) return null
|
||||
|
||||
val ownerTypeInfo = TypeInfo(targetType, Variance.IN_VARIANCE)
|
||||
val entries = element.entries
|
||||
|
||||
val entry = entries[componentNumber]
|
||||
val returnTypeInfo = TypeInfo(entry, Variance.OUT_VARIANCE)
|
||||
|
||||
return FunctionInfo(name.identifier, ownerTypeInfo, returnTypeInfo, isOperator = true)
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtArrayAccessExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateGetFunctionActionFactory : CreateGetSetFunctionActionFactory(isGet = true) {
|
||||
override fun createCallableInfo(element: KtArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val arrayExpr = element.arrayExpression ?: return null
|
||||
|
||||
val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE)
|
||||
val parameters = element.indexExpressions.map { ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE)) }
|
||||
val returnType = TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(
|
||||
OperatorNameConventions.GET.asString(), arrayType, returnType, Collections.emptyList(), parameters, isOperator = true
|
||||
)
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreateHasNextFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtForExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtForExpression? {
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtForExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val diagnosticWithParameters =
|
||||
DiagnosticFactory.cast(diagnostic, Errors.HAS_NEXT_MISSING, Errors.HAS_NEXT_FUNCTION_NONE_APPLICABLE)
|
||||
val ownerType = TypeInfo(diagnosticWithParameters.a, Variance.IN_VARIANCE)
|
||||
val returnType = TypeInfo(element.builtIns.booleanType, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.HAS_NEXT.asString(), ownerType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreateInvokeFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtCallExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallExpression? {
|
||||
return diagnostic.psiElement.parent as? KtCallExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtCallExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val expectedType = Errors.FUNCTION_EXPECTED.cast(diagnostic).b
|
||||
if (expectedType.isError) return null
|
||||
|
||||
val receiverType = TypeInfo(expectedType, Variance.IN_VARIANCE)
|
||||
|
||||
val anyType = element.builtIns.nullableAnyType
|
||||
val parameters = element.valueArguments.map {
|
||||
ParameterInfo(
|
||||
it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE),
|
||||
it.getArgumentName()?.referenceExpression?.getReferencedName()
|
||||
)
|
||||
}
|
||||
|
||||
val returnType = TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.INVOKE.asString(), receiverType, returnType, parameterInfos = parameters, isOperator = true)
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithAllCompilerChecks
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.guessTypes
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.TypeProjectionImpl
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateIteratorFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtForExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtForExpression? {
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtForExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val file = diagnostic.psiFile as? KtFile ?: return null
|
||||
val iterableExpr = element.loopRange ?: return null
|
||||
val variableExpr: KtExpression = ((element.loopParameter ?: element.destructuringDeclaration) ?: return null) as KtExpression
|
||||
val iterableType = TypeInfo(iterableExpr, Variance.IN_VARIANCE)
|
||||
|
||||
val (bindingContext, moduleDescriptor) = file.analyzeWithAllCompilerChecks()
|
||||
|
||||
val returnJetType = moduleDescriptor.builtIns.iterator.defaultType
|
||||
val returnJetTypeParameterTypes = variableExpr.guessTypes(bindingContext, moduleDescriptor)
|
||||
if (returnJetTypeParameterTypes.size != 1) return null
|
||||
|
||||
val returnJetTypeParameterType = TypeProjectionImpl(returnJetTypeParameterTypes[0])
|
||||
val returnJetTypeArguments = Collections.singletonList(returnJetTypeParameterType)
|
||||
val newReturnJetType = KotlinTypeFactory.simpleTypeWithNonTrivialMemberScope(returnJetType.annotations,
|
||||
returnJetType.constructor,
|
||||
returnJetTypeArguments,
|
||||
returnJetType.isMarkedNullable,
|
||||
returnJetType.memberScope)
|
||||
val returnType = TypeInfo(newReturnJetType, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.ITERATOR.asString(), iterableType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreateNextFunctionActionFactory : CreateCallableMemberFromUsageFactory<KtForExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtForExpression? {
|
||||
return QuickFixUtil.getParentElementOfType(diagnostic, KtForExpression::class.java)
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtForExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val diagnosticWithParameters = DiagnosticFactory.cast(diagnostic, Errors.NEXT_MISSING, Errors.NEXT_NONE_APPLICABLE)
|
||||
val ownerType = TypeInfo(diagnosticWithParameters.a, Variance.IN_VARIANCE)
|
||||
|
||||
val variableExpr = element.loopParameter ?: element.destructuringDeclaration ?: return null
|
||||
val returnType = TypeInfo(variableExpr as KtExpression, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(OperatorNameConventions.NEXT.asString(), ownerType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.builtins.ReflectionTypes
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.VariableAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
object CreatePropertyDelegateAccessorsActionFactory : CreateCallableMemberFromUsageFactory<KtExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtExpression? {
|
||||
return diagnostic.psiElement as? KtExpression
|
||||
}
|
||||
|
||||
override fun extractFixData(element: KtExpression, diagnostic: Diagnostic): List<CallableInfo> {
|
||||
val context = element.analyze()
|
||||
|
||||
fun isApplicableForAccessor(accessor: VariableAccessorDescriptor?): Boolean =
|
||||
accessor != null && context[BindingContext.DELEGATED_PROPERTY_RESOLVED_CALL, accessor] == null
|
||||
|
||||
val property = element.getNonStrictParentOfType<KtProperty>() ?: return emptyList()
|
||||
val propertyDescriptor = context[BindingContext.DECLARATION_TO_DESCRIPTOR, property] as? VariableDescriptorWithAccessors
|
||||
?: return emptyList()
|
||||
|
||||
if (propertyDescriptor is LocalVariableDescriptor
|
||||
&& !element.languageVersionSettings.supportsFeature(LanguageFeature.LocalDelegatedProperties)) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val propertyReceiver = propertyDescriptor.extensionReceiverParameter ?: propertyDescriptor.dispatchReceiverParameter
|
||||
val propertyType = propertyDescriptor.type
|
||||
|
||||
val accessorReceiverType = TypeInfo(element, Variance.IN_VARIANCE)
|
||||
val builtIns = propertyDescriptor.builtIns
|
||||
val thisRefParam = ParameterInfo(TypeInfo(propertyReceiver?.type ?: builtIns.nullableNothingType, Variance.IN_VARIANCE))
|
||||
val kPropertyStarType = ReflectionTypes.createKPropertyStarType(propertyDescriptor.module) ?: return emptyList()
|
||||
val metadataParam = ParameterInfo(TypeInfo(kPropertyStarType, Variance.IN_VARIANCE), "property")
|
||||
|
||||
val callableInfos = SmartList<CallableInfo>()
|
||||
|
||||
if (isApplicableForAccessor(propertyDescriptor.getter)) {
|
||||
val getterInfo = FunctionInfo(
|
||||
name = OperatorNameConventions.GET_VALUE.asString(),
|
||||
receiverTypeInfo = accessorReceiverType,
|
||||
returnTypeInfo = TypeInfo(propertyType, Variance.OUT_VARIANCE),
|
||||
parameterInfos = listOf(thisRefParam, metadataParam),
|
||||
isOperator = true
|
||||
)
|
||||
callableInfos.add(getterInfo)
|
||||
}
|
||||
|
||||
if (propertyDescriptor.isVar && isApplicableForAccessor(propertyDescriptor.setter)) {
|
||||
val newValueParam = ParameterInfo(TypeInfo(propertyType, Variance.IN_VARIANCE))
|
||||
val setterInfo = FunctionInfo(
|
||||
name = OperatorNameConventions.SET_VALUE.asString(),
|
||||
receiverTypeInfo = accessorReceiverType,
|
||||
returnTypeInfo = TypeInfo(builtIns.unitType, Variance.OUT_VARIANCE),
|
||||
parameterInfos = listOf(thisRefParam, metadataParam, newValueParam),
|
||||
isOperator = true
|
||||
)
|
||||
callableInfos.add(setterInfo)
|
||||
}
|
||||
|
||||
return callableInfos
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.core.quickfix.QuickFixUtil
|
||||
import org.jetbrains.kotlin.idea.project.builtIns
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.ParameterInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import java.util.*
|
||||
|
||||
object CreateSetFunctionActionFactory : CreateGetSetFunctionActionFactory(isGet = false) {
|
||||
override fun createCallableInfo(element: KtArrayAccessExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val arrayExpr = element.arrayExpression ?: return null
|
||||
val arrayType = TypeInfo(arrayExpr, Variance.IN_VARIANCE)
|
||||
|
||||
val builtIns = element.builtIns
|
||||
|
||||
val parameters = element.indexExpressions.mapTo(ArrayList<ParameterInfo>()) {
|
||||
ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE))
|
||||
}
|
||||
val assignmentExpr = QuickFixUtil.getParentElementOfType(diagnostic, KtOperationExpression::class.java) ?: return null
|
||||
val valType = when (assignmentExpr) {
|
||||
is KtBinaryExpression -> {
|
||||
TypeInfo(assignmentExpr.right ?: return null, Variance.IN_VARIANCE)
|
||||
}
|
||||
is KtUnaryExpression -> {
|
||||
if (assignmentExpr.operationToken !in OperatorConventions.INCREMENT_OPERATIONS) return null
|
||||
|
||||
val rhsType = assignmentExpr.resolveToCall()?.resultingDescriptor?.returnType
|
||||
TypeInfo(if (rhsType == null || ErrorUtils.containsErrorType(rhsType)) builtIns.anyType else rhsType, Variance.IN_VARIANCE)
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
parameters.add(ParameterInfo(valType, "value"))
|
||||
|
||||
val returnType = TypeInfo(builtIns.unitType, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(
|
||||
OperatorNameConventions.SET.asString(), arrayType, returnType, Collections.emptyList(), parameters, isOperator = true
|
||||
)
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.CallableInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.FunctionInfo
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.TypeInfo
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.psi.KtUnaryExpression
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
object CreateUnaryOperationActionFactory: CreateCallableMemberFromUsageFactory<KtUnaryExpression>() {
|
||||
override fun getElementOfInterest(diagnostic: Diagnostic): KtUnaryExpression? {
|
||||
return diagnostic.psiElement.parent as? KtUnaryExpression
|
||||
}
|
||||
|
||||
override fun createCallableInfo(element: KtUnaryExpression, diagnostic: Diagnostic): CallableInfo? {
|
||||
val token = element.operationToken as KtToken
|
||||
val operationName = OperatorConventions.getNameForOperationSymbol(token, true, false) ?: return null
|
||||
val incDec = token in OperatorConventions.INCREMENT_OPERATIONS
|
||||
|
||||
val receiverExpr = element.baseExpression ?: return null
|
||||
|
||||
val receiverType = TypeInfo(receiverExpr, Variance.IN_VARIANCE)
|
||||
val returnType = if (incDec) TypeInfo.ByReceiverType(Variance.OUT_VARIANCE) else TypeInfo(element, Variance.OUT_VARIANCE)
|
||||
return FunctionInfo(operationName.asString(), receiverType, returnType, isOperator = true)
|
||||
}
|
||||
}
|
||||
+390
@@ -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<JvmModifier>) {
|
||||
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<KtElement>(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 <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
|
||||
|
||||
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
|
||||
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<PsiTypeParameter> {
|
||||
val results = ArrayList<PsiTypeParameter>()
|
||||
accept(
|
||||
object : PsiTypeVisitor<Unit>() {
|
||||
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<KotlinType>()) {
|
||||
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<IntentionAction> {
|
||||
val kModifierOwner = target.toKtElement<KtModifierListOwner>() ?: 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<IntentionAction> {
|
||||
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<KtElement>(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<IntentionAction> {
|
||||
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<IntentionAction> {
|
||||
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<IntentionAction> {
|
||||
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<KtElement>(targetContainer, listOf(functionInfo)) {
|
||||
override fun getFamilyName() = "Add method"
|
||||
override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'"
|
||||
}
|
||||
return listOf(action)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user