FIR IDE: add action to override/implement missing members
This commit adds the following actions: - quickfix to implement missing members - quickfix to implement missing members as constructor parameters - action to implement members (Code - Generate - Implement) - action to override members (Code - Generate - Override) The current implementation is still missing some pieces, which will be addressed in future changes. - fully qualified names are not shorten - some Kotlin types are not rendered correctly
This commit is contained in:
committed by
TeamCityServer
parent
34387e228f
commit
00031c8eb1
+109
-8
@@ -9,14 +9,12 @@ import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.modalityModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.overrideModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.visibilityModifier
|
||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirComponentCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
@@ -24,6 +22,7 @@ import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
|
||||
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.resolve.SessionHolder
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
||||
@@ -32,11 +31,7 @@ import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
|
||||
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -49,6 +44,8 @@ import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
|
||||
import org.jetbrains.kotlin.util.ImplementationStatus
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
private val INLINE_ONLY_ANNOTATION_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.InlineOnly"))
|
||||
@@ -428,3 +425,107 @@ fun isSubtypeOfForFunctionalTypeReturningUnit(context: ConeInferenceContext, sub
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun FirCallableMemberDeclaration<*>.isVisibleInClass(parentClass: FirClass<*>): Boolean {
|
||||
val classPackage = parentClass.symbol.classId.packageFqName
|
||||
if (visibility == Visibilities.Private ||
|
||||
!visibility.visibleFromPackage(classPackage, symbol.callableId.packageName)
|
||||
) return false
|
||||
if (visibility == Visibilities.Internal &&
|
||||
declarationSiteSession !== parentClass.declarationSiteSession
|
||||
) return false
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the [ImplementationStatus] for this member.
|
||||
*
|
||||
* @param parentClass the contextual class for this query.
|
||||
*/
|
||||
fun FirCallableMemberDeclaration<*>.getImplementationStatus(sessionHolder: SessionHolder, parentClass: FirClass<*>): ImplementationStatus {
|
||||
val containingClass = getContainingClass(sessionHolder)
|
||||
val symbol = this.symbol
|
||||
if (symbol is FirIntersectionCallableSymbol) {
|
||||
if (containingClass === parentClass && symbol.subjectToManyNotImplemented(sessionHolder)) {
|
||||
return ImplementationStatus.AMBIGUOUSLY_INHERITED
|
||||
}
|
||||
// In Java 8, non-abstract intersection overrides having abstract symbol from base class
|
||||
// still should be implemented in current class (even when they have default interface implementation)
|
||||
if (symbol.intersections.any {
|
||||
val fir = (it.fir as FirCallableMemberDeclaration).unwrapFakeOverrides()
|
||||
fir.isAbstract && (fir.getContainingClass(sessionHolder) as? FirRegularClass)?.classKind == ClassKind.CLASS
|
||||
}
|
||||
) {
|
||||
// Exception from the rule above: interface implementation via delegation
|
||||
if (symbol.intersections.none {
|
||||
val fir = (it.fir as FirCallableMemberDeclaration)
|
||||
fir.origin == FirDeclarationOrigin.Delegated && !fir.isAbstract
|
||||
}
|
||||
) {
|
||||
return ImplementationStatus.NOT_IMPLEMENTED
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this is FirSimpleFunction) {
|
||||
if (parentClass is FirRegularClass && parentClass.isData && matchesDataClassSyntheticMemberSignatures) {
|
||||
return ImplementationStatus.INHERITED_OR_SYNTHESIZED
|
||||
}
|
||||
// TODO: suspend function overridden by a Java class in the middle is not properly regarded as an override
|
||||
if (isSuspend) {
|
||||
return ImplementationStatus.INHERITED_OR_SYNTHESIZED
|
||||
}
|
||||
}
|
||||
return when {
|
||||
isFinal -> ImplementationStatus.CANNOT_BE_IMPLEMENTED
|
||||
containingClass === parentClass && origin == FirDeclarationOrigin.Source -> ImplementationStatus.ALREADY_IMPLEMENTED
|
||||
containingClass is FirRegularClass && containingClass.isExpect -> ImplementationStatus.CANNOT_BE_IMPLEMENTED
|
||||
isAbstract -> ImplementationStatus.NOT_IMPLEMENTED
|
||||
else -> ImplementationStatus.INHERITED_OR_SYNTHESIZED
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun FirIntersectionCallableSymbol.subjectToManyNotImplemented(sessionHolder: SessionHolder): Boolean {
|
||||
var nonAbstractCountInClass = 0
|
||||
var nonAbstractCountInInterface = 0
|
||||
var abstractCountInInterface = 0
|
||||
for (intersectionSymbol in intersections) {
|
||||
val intersection = intersectionSymbol.fir as FirCallableMemberDeclaration
|
||||
val containingClass = intersection.getContainingClass(sessionHolder) as? FirRegularClass
|
||||
val hasInterfaceContainer = containingClass?.classKind == ClassKind.INTERFACE
|
||||
if (intersection.modality != Modality.ABSTRACT) {
|
||||
if (hasInterfaceContainer) {
|
||||
nonAbstractCountInInterface++
|
||||
} else {
|
||||
nonAbstractCountInClass++
|
||||
}
|
||||
} else if (hasInterfaceContainer) {
|
||||
abstractCountInInterface++
|
||||
}
|
||||
if (nonAbstractCountInClass + nonAbstractCountInInterface > 1) {
|
||||
return true
|
||||
}
|
||||
if (nonAbstractCountInInterface > 0 && abstractCountInInterface > 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private val FirSimpleFunction.matchesDataClassSyntheticMemberSignatures: Boolean
|
||||
get() = (this.name == OperatorNameConventions.EQUALS && matchesEqualsSignature) ||
|
||||
(this.name == HASHCODE_NAME && matchesHashCodeSignature) ||
|
||||
(this.name == OperatorNameConventions.TO_STRING && matchesToStringSignature)
|
||||
|
||||
private fun FirSymbolOwner<*>.getContainingClass(sessionHolder: SessionHolder): FirClassLikeDeclaration<*>? =
|
||||
this.safeAs<FirCallableMemberDeclaration<*>>()?.containingClass()?.toSymbol(sessionHolder.session)?.fir
|
||||
|
||||
// NB: we intentionally do not check return types
|
||||
private val FirSimpleFunction.matchesEqualsSignature: Boolean
|
||||
get() = valueParameters.size == 1 && valueParameters[0].returnTypeRef.coneType.isNullableAny
|
||||
|
||||
private val FirSimpleFunction.matchesHashCodeSignature: Boolean
|
||||
get() = valueParameters.isEmpty()
|
||||
|
||||
private val FirSimpleFunction.matchesToStringSignature: Boolean
|
||||
get() = valueParameters.isEmpty()
|
||||
|
||||
+17
-127
@@ -8,12 +8,9 @@ package org.jetbrains.kotlin.fir.analysis.checkers.declaration
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED
|
||||
@@ -24,15 +21,14 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OVERRIDING_FINAL_MEMBER_BY_DELEGATION
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverridePropertySymbol
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.isNullableAny
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
||||
import org.jetbrains.kotlin.util.ImplementationStatus
|
||||
|
||||
object FirNotImplementedOverrideChecker : FirClassChecker() {
|
||||
|
||||
@@ -51,115 +47,24 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
||||
val notImplementedSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||
val notImplementedIntersectionSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||
val classPackage = declaration.symbol.classId.packageFqName
|
||||
|
||||
fun FirCallableMemberDeclaration<*>.isInvisible(): Boolean {
|
||||
if (visibility == Visibilities.Private ||
|
||||
!visibility.visibleFromPackage(classPackage, symbol.callableId.packageName)
|
||||
) return true
|
||||
if (visibility == Visibilities.Internal &&
|
||||
declarationSiteSession !== declaration.declarationSiteSession
|
||||
) return true
|
||||
return false
|
||||
}
|
||||
|
||||
fun FirIntersectionCallableSymbol.subjectToManyNotImplemented(): Boolean {
|
||||
var nonAbstractCountInClass = 0
|
||||
var nonAbstractCountInInterface = 0
|
||||
var abstractCountInInterface = 0
|
||||
for (intersectionSymbol in intersections) {
|
||||
val intersection = intersectionSymbol.fir as FirCallableMemberDeclaration
|
||||
val containingClass = intersection.getContainingClass(context) as? FirRegularClass
|
||||
val hasInterfaceContainer = containingClass?.classKind == ClassKind.INTERFACE
|
||||
if (intersection.modality != Modality.ABSTRACT) {
|
||||
if (hasInterfaceContainer) {
|
||||
nonAbstractCountInInterface++
|
||||
} else {
|
||||
nonAbstractCountInClass++
|
||||
}
|
||||
} else if (hasInterfaceContainer) {
|
||||
abstractCountInInterface++
|
||||
fun collectSymbol(symbol: FirCallableSymbol<*>) {
|
||||
val fir = symbol.fir as? FirCallableMemberDeclaration<*> ?: return
|
||||
when (fir.getImplementationStatus(context.sessionHolder, declaration)) {
|
||||
ImplementationStatus.AMBIGUOUSLY_INHERITED -> notImplementedIntersectionSymbols.add(symbol)
|
||||
ImplementationStatus.NOT_IMPLEMENTED -> when {
|
||||
fir.isVisibleInClass(declaration) -> notImplementedSymbols.add(symbol)
|
||||
else -> invisibleSymbols.add(symbol)
|
||||
}
|
||||
if (nonAbstractCountInClass + nonAbstractCountInInterface > 1) {
|
||||
return true
|
||||
}
|
||||
if (nonAbstractCountInInterface > 0 && abstractCountInInterface > 0) {
|
||||
return true
|
||||
else -> {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun FirIntersectionCallableSymbol.shouldBeImplemented(): Boolean {
|
||||
// In Java 8, non-abstract intersection overrides having abstract symbol from base class
|
||||
// still should be implemented in current class (even when they have default interface implementation)
|
||||
if (intersections.none {
|
||||
val fir = (it.fir as FirCallableMemberDeclaration).unwrapFakeOverrides()
|
||||
fir.isAbstract && (fir.getContainingClass(context) as? FirRegularClass)?.classKind == ClassKind.CLASS
|
||||
}
|
||||
) return false
|
||||
// Exception from the rule above: interface implementation via delegation
|
||||
if (intersections.any {
|
||||
val fir = (it.fir as FirCallableMemberDeclaration)
|
||||
fir.origin == FirDeclarationOrigin.Delegated && !fir.isAbstract
|
||||
}
|
||||
) return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun FirCallableMemberDeclaration<*>.shouldBeImplemented(): Boolean {
|
||||
val containingClass = getContainingClass(context)
|
||||
if (containingClass is FirRegularClass && containingClass.isExpect) return false
|
||||
if (!isAbstract) {
|
||||
val symbol = symbol as? FirIntersectionCallableSymbol ?: return false
|
||||
return symbol.shouldBeImplemented()
|
||||
}
|
||||
if (containingClass === declaration && origin == FirDeclarationOrigin.Source) return false
|
||||
return true
|
||||
}
|
||||
|
||||
for (name in classScope.getCallableNames()) {
|
||||
classScope.processFunctionsByName(name) { namedFunctionSymbol ->
|
||||
val simpleFunction = namedFunctionSymbol.fir
|
||||
if (namedFunctionSymbol is FirIntersectionOverrideFunctionSymbol) {
|
||||
if (simpleFunction.getContainingClass(context) === declaration &&
|
||||
namedFunctionSymbol.subjectToManyNotImplemented()
|
||||
) {
|
||||
notImplementedIntersectionSymbols += namedFunctionSymbol
|
||||
return@processFunctionsByName
|
||||
}
|
||||
}
|
||||
if (!simpleFunction.shouldBeImplemented()) return@processFunctionsByName
|
||||
if (declaration is FirRegularClass && declaration.isData && simpleFunction.matchesDataClassSyntheticMemberSignatures) {
|
||||
return@processFunctionsByName
|
||||
}
|
||||
|
||||
// TODO: suspend function overridden by a Java class in the middle is not properly regarded as an override
|
||||
if (simpleFunction.isSuspend) return@processFunctionsByName
|
||||
if (simpleFunction.isInvisible()) {
|
||||
invisibleSymbols += namedFunctionSymbol
|
||||
} else {
|
||||
notImplementedSymbols += namedFunctionSymbol
|
||||
}
|
||||
}
|
||||
classScope.processPropertiesByName(name) { propertySymbol ->
|
||||
val property = propertySymbol.fir as? FirProperty ?: return@processPropertiesByName
|
||||
if (propertySymbol is FirIntersectionOverridePropertySymbol) {
|
||||
if (property.getContainingClass(context) === declaration &&
|
||||
propertySymbol.subjectToManyNotImplemented()
|
||||
) {
|
||||
notImplementedIntersectionSymbols += propertySymbol
|
||||
return@processPropertiesByName
|
||||
}
|
||||
}
|
||||
if (!property.shouldBeImplemented()) return@processPropertiesByName
|
||||
|
||||
if (property.isInvisible()) {
|
||||
invisibleSymbols += propertySymbol
|
||||
} else {
|
||||
notImplementedSymbols += propertySymbol
|
||||
}
|
||||
}
|
||||
classScope.processFunctionsByName(name, ::collectSymbol)
|
||||
classScope.processPropertiesByName(name, ::collectSymbol)
|
||||
}
|
||||
|
||||
if (notImplementedSymbols.isNotEmpty()) {
|
||||
@@ -237,19 +142,4 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
||||
|
||||
private fun FirCallableDeclaration<*>.isFromInterfaceOrEnum(context: CheckerContext): Boolean =
|
||||
(getContainingClass(context) as? FirRegularClass)?.let { it.isInterface || it.isEnumClass } == true
|
||||
|
||||
private val FirSimpleFunction.matchesDataClassSyntheticMemberSignatures: Boolean
|
||||
get() = (this.name == OperatorNameConventions.EQUALS && matchesEqualsSignature) ||
|
||||
(this.name == HASHCODE_NAME && matchesHashCodeSignature) ||
|
||||
(this.name == OperatorNameConventions.TO_STRING && matchesToStringSignature)
|
||||
|
||||
// NB: we intentionally do not check return types
|
||||
private val FirSimpleFunction.matchesEqualsSignature: Boolean
|
||||
get() = valueParameters.size == 1 && valueParameters[0].returnTypeRef.coneType.isNullableAny
|
||||
|
||||
private val FirSimpleFunction.matchesHashCodeSignature: Boolean
|
||||
get() = valueParameters.isEmpty()
|
||||
|
||||
private val FirSimpleFunction.matchesToStringSignature: Boolean
|
||||
get() = valueParameters.isEmpty()
|
||||
}
|
||||
|
||||
@@ -57,6 +57,12 @@ inline val FirRegularClass.isFun get() = status.isFun
|
||||
inline val FirMemberDeclaration.modality get() = status.modality
|
||||
inline val FirMemberDeclaration.isAbstract get() = status.modality == Modality.ABSTRACT
|
||||
inline val FirMemberDeclaration.isOpen get() = status.modality == Modality.OPEN
|
||||
inline val FirMemberDeclaration.isFinal: Boolean
|
||||
get() {
|
||||
// member with unspecified modality is final
|
||||
val modality = status.modality ?: return true
|
||||
return modality == Modality.FINAL
|
||||
}
|
||||
|
||||
inline val FirMemberDeclaration.visibility: Visibility get() = status.visibility
|
||||
inline val FirMemberDeclaration.effectiveVisibility: EffectiveVisibility
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.util
|
||||
|
||||
/** Implementation status of a member symbol that is available inside a class scope. */
|
||||
enum class ImplementationStatus {
|
||||
/** This symbol is not implemented and should be implemented if the class is not abstract. */
|
||||
NOT_IMPLEMENTED,
|
||||
|
||||
/** The symbol is inheriting multiple non-abstract symbols and hence must be explicitly implemented. */
|
||||
AMBIGUOUSLY_INHERITED,
|
||||
|
||||
/**
|
||||
* This symbol has an inherited implementation, and it can be overridden if desired. For example, it's an open non-abstract member or
|
||||
* it's automatically synthesized by the Kotlin compiler.
|
||||
*/
|
||||
INHERITED_OR_SYNTHESIZED,
|
||||
|
||||
/** The symbol is already implemented in this class. */
|
||||
ALREADY_IMPLEMENTED,
|
||||
|
||||
/**
|
||||
* The symbol is not implemented in the class and it cannot be implemented. For example, it's final in super classes or the current
|
||||
* class is `expect`.
|
||||
*/
|
||||
CANNOT_BE_IMPLEMENTED;
|
||||
|
||||
val shouldBeImplemented: Boolean get() = this == NOT_IMPLEMENTED || this == AMBIGUOUSLY_INHERITED
|
||||
val isOverridable: Boolean get() = this != ALREADY_IMPLEMENTED && this != CANNOT_BE_IMPLEMENTED
|
||||
}
|
||||
|
||||
+5
-5
@@ -22,8 +22,8 @@ import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
|
||||
import org.jetbrains.kotlin.idea.util.expectedDescriptors
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
@@ -37,18 +37,18 @@ open class ImplementMembersHandler : GenerateMembersHandler(), IntentionAction {
|
||||
.map { OverrideMemberChooserObject.create(project, it, it, BodyType.FROM_TEMPLATE) }
|
||||
}
|
||||
|
||||
override fun getChooserTitle() = KotlinBundle.message("implement.members.handler.title")
|
||||
override fun getChooserTitle() = KotlinIdeaCoreBundle.message("implement.members.handler.title")
|
||||
|
||||
override fun getNoMembersFoundHint() = KotlinBundle.message("implement.members.handler.no.members.hint")
|
||||
override fun getNoMembersFoundHint() = KotlinIdeaCoreBundle.message("implement.members.handler.no.members.hint")
|
||||
|
||||
override fun getText() = familyName
|
||||
override fun getFamilyName() = KotlinBundle.message("implement.members.handler.family")
|
||||
override fun getFamilyName() = KotlinIdeaCoreBundle.message("implement.members.handler.family")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
|
||||
}
|
||||
|
||||
class ImplementAsConstructorParameter : ImplementMembersHandler() {
|
||||
override fun getText() = KotlinBundle.message("action.text.implement.as.constructor.parameters")
|
||||
override fun getText() = KotlinIdeaCoreBundle.message("action.text.implement.as.constructor.parameters")
|
||||
|
||||
override fun isValidForClass(classOrObject: KtClassOrObject): Boolean {
|
||||
if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false
|
||||
|
||||
-3
@@ -321,9 +321,6 @@ private fun generateFunction(
|
||||
}
|
||||
}
|
||||
|
||||
private fun BodyType.effectiveBodyType(canBeEmpty: Boolean): BodyType =
|
||||
if (!canBeEmpty && this == EMPTY_OR_TEMPLATE) FROM_TEMPLATE else this
|
||||
|
||||
fun generateUnsupportedOrSuperCall(
|
||||
project: Project,
|
||||
descriptor: CallableMemberDescriptor,
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
|
||||
import com.intellij.codeInsight.generation.ClassMember
|
||||
import com.intellij.codeInsight.generation.MemberChooserObject
|
||||
import com.intellij.codeInsight.generation.MemberChooserObjectBase
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiDocCommentOwner
|
||||
import org.jetbrains.kotlin.idea.core.TemplateKind
|
||||
import org.jetbrains.kotlin.idea.core.getFunctionBodyTextFromTemplate
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtDeclarationRendererOptions
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.RendererModifier
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtConstructorSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtPossibleExtensionSymbol
|
||||
import org.jetbrains.kotlin.idea.j2k.IdeaDocCommentConverter
|
||||
import org.jetbrains.kotlin.idea.kdoc.KDocElementFactory
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.findDocComment.findDocComment
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasExpectModifier
|
||||
import org.jetbrains.kotlin.renderer.render
|
||||
import javax.swing.Icon
|
||||
|
||||
internal data class KtClassMemberInfo(
|
||||
// TODO: use a `KtSymbolPointer` instead to avoid storing `KtSymbol` in an object after KT-46249 is fixed.
|
||||
val symbol: KtCallableSymbol,
|
||||
val memberText: String,
|
||||
val memberIcon: Icon?,
|
||||
val containingSymbolText: String?,
|
||||
val containingSymbolIcon: Icon?,
|
||||
) {
|
||||
val isProperty: Boolean get() = symbol is KtPropertySymbol
|
||||
}
|
||||
|
||||
internal class KtClassMember(
|
||||
private val memberInfo: KtClassMemberInfo,
|
||||
val bodyType: BodyType,
|
||||
val preferConstructorParameter: Boolean
|
||||
) : MemberChooserObjectBase(
|
||||
memberInfo.memberText,
|
||||
memberInfo.memberIcon,
|
||||
), ClassMember {
|
||||
val symbol = memberInfo.symbol
|
||||
override fun getParentNodeDelegate(): MemberChooserObject? =
|
||||
memberInfo.containingSymbolText?.let {
|
||||
KtClassOrObjectSymbolChooserObject(
|
||||
memberInfo.containingSymbolText,
|
||||
memberInfo.containingSymbolIcon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private data class KtClassOrObjectSymbolChooserObject(
|
||||
val symbolText: String?,
|
||||
val symbolIcon: Icon?
|
||||
) :
|
||||
MemberChooserObjectBase(symbolText, symbolIcon)
|
||||
|
||||
internal fun createKtClassMember(
|
||||
memberInfo: KtClassMemberInfo,
|
||||
bodyType: BodyType,
|
||||
preferConstructorParameter: Boolean
|
||||
): KtClassMember {
|
||||
return KtClassMember(memberInfo, bodyType, preferConstructorParameter)
|
||||
}
|
||||
|
||||
internal fun KtAnalysisSession.generateMember(
|
||||
project: Project,
|
||||
ktClassMember: KtClassMember,
|
||||
targetClass: KtClassOrObject?,
|
||||
copyDoc: Boolean,
|
||||
mode: MemberGenerateMode = MemberGenerateMode.OVERRIDE
|
||||
): KtCallableDeclaration = with(ktClassMember) {
|
||||
val bodyType = when {
|
||||
targetClass?.hasExpectModifier() == true -> BodyType.NO_BODY
|
||||
(symbol as? KtPossibleExtensionSymbol)?.isExtension == true && mode == MemberGenerateMode.OVERRIDE -> BodyType.FROM_TEMPLATE
|
||||
else -> bodyType
|
||||
}
|
||||
|
||||
val renderOptions = when (mode) {
|
||||
MemberGenerateMode.OVERRIDE -> RenderOptions.overrideRenderOptions
|
||||
MemberGenerateMode.ACTUAL -> RenderOptions.actualRenderOptions
|
||||
MemberGenerateMode.EXPECT -> RenderOptions.expectRenderOptions
|
||||
}
|
||||
if (preferConstructorParameter && symbol is KtPropertySymbol) {
|
||||
return generateConstructorParameter(project, symbol, renderOptions, mode == MemberGenerateMode.OVERRIDE)
|
||||
}
|
||||
|
||||
|
||||
val newMember: KtCallableDeclaration = when (symbol) {
|
||||
is KtFunctionSymbol -> generateFunction(project, symbol, renderOptions, bodyType, mode == MemberGenerateMode.OVERRIDE)
|
||||
is KtPropertySymbol -> generateProperty(project, symbol, renderOptions, bodyType, mode == MemberGenerateMode.OVERRIDE)
|
||||
else -> error("Unknown member to override: $symbol")
|
||||
}
|
||||
|
||||
when (mode) {
|
||||
MemberGenerateMode.ACTUAL -> newMember.addModifier(KtTokens.ACTUAL_KEYWORD)
|
||||
MemberGenerateMode.EXPECT -> if (targetClass == null) {
|
||||
newMember.addModifier(KtTokens.EXPECT_KEYWORD)
|
||||
}
|
||||
MemberGenerateMode.OVERRIDE -> {
|
||||
// TODO: add `actual` keyword to the generated member if the target class has `actual` and the generated member corresponds to
|
||||
// an `expect` member.
|
||||
}
|
||||
}
|
||||
|
||||
if (copyDoc) {
|
||||
val kDoc = when (val originalOverriddenPsi = symbol.originalOverriddenSymbol?.psi) {
|
||||
is KtDeclaration ->
|
||||
findDocComment(originalOverriddenPsi)
|
||||
is PsiDocCommentOwner -> {
|
||||
val kDocText = originalOverriddenPsi.docComment?.let { IdeaDocCommentConverter.convertDocComment(it) }
|
||||
if (kDocText.isNullOrEmpty()) null else KDocElementFactory(project).createKDocFromText(kDocText)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
if (kDoc != null) {
|
||||
newMember.addAfter(kDoc, null)
|
||||
}
|
||||
}
|
||||
|
||||
return newMember
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.generateConstructorParameter(
|
||||
project: Project,
|
||||
symbol: KtCallableSymbol,
|
||||
renderOptions: KtDeclarationRendererOptions,
|
||||
isOverride: Boolean,
|
||||
): KtCallableDeclaration {
|
||||
return KtPsiFactory(project).createParameter(symbol.render(renderOptions.copy(forceRenderingOverrideModifier = isOverride)))
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.generateFunction(
|
||||
project: Project,
|
||||
symbol: KtFunctionSymbol,
|
||||
renderOptions: KtDeclarationRendererOptions,
|
||||
bodyType: BodyType,
|
||||
isOverride: Boolean,
|
||||
): KtCallableDeclaration {
|
||||
val returnType = symbol.annotatedType.type
|
||||
val returnsUnit = returnType.isUnit
|
||||
|
||||
val body = if (bodyType != BodyType.NO_BODY) {
|
||||
val delegation = generateUnsupportedOrSuperCall(project, symbol, bodyType, returnsUnit)
|
||||
val returnPrefix = if (!returnsUnit && bodyType.requiresReturn) "return " else ""
|
||||
"{$returnPrefix$delegation\n}"
|
||||
} else ""
|
||||
|
||||
val factory = KtPsiFactory(project)
|
||||
val functionText = symbol.render(renderOptions.copy(forceRenderingOverrideModifier = isOverride)) + body
|
||||
return when (symbol) {
|
||||
is KtConstructorSymbol -> {
|
||||
if (symbol.isPrimary) {
|
||||
factory.createPrimaryConstructor(functionText)
|
||||
} else {
|
||||
factory.createSecondaryConstructor(functionText)
|
||||
}
|
||||
}
|
||||
else -> factory.createFunction(functionText)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.generateProperty(
|
||||
project: Project,
|
||||
symbol: KtPropertySymbol,
|
||||
renderOptions: KtDeclarationRendererOptions,
|
||||
bodyType: BodyType,
|
||||
isOverride: Boolean,
|
||||
): KtCallableDeclaration {
|
||||
val returnType = symbol.annotatedType.type
|
||||
val returnsNotUnit = !returnType.isUnit
|
||||
|
||||
val body =
|
||||
if (bodyType != BodyType.NO_BODY) {
|
||||
buildString {
|
||||
append("\nget()")
|
||||
append(" = ")
|
||||
append(generateUnsupportedOrSuperCall(project, symbol, bodyType, !returnsNotUnit))
|
||||
if (!symbol.isVal) {
|
||||
append("\nset(value) {}")
|
||||
}
|
||||
}
|
||||
} else ""
|
||||
return KtPsiFactory(project).createProperty(symbol.render(renderOptions.copy(forceRenderingOverrideModifier = isOverride)) + body)
|
||||
}
|
||||
|
||||
private fun <T> KtAnalysisSession.generateUnsupportedOrSuperCall(
|
||||
project: Project,
|
||||
symbol: T,
|
||||
bodyType: BodyType,
|
||||
canBeEmpty: Boolean = true
|
||||
): String where T : KtNamedSymbol, T : KtCallableSymbol {
|
||||
when (bodyType.effectiveBodyType(canBeEmpty)) {
|
||||
BodyType.EMPTY_OR_TEMPLATE -> return ""
|
||||
BodyType.FROM_TEMPLATE -> {
|
||||
val templateKind = when (symbol) {
|
||||
is KtFunctionSymbol -> TemplateKind.FUNCTION
|
||||
is KtPropertySymbol -> TemplateKind.PROPERTY_INITIALIZER
|
||||
else -> throw IllegalArgumentException("$symbol must be either a function or a property")
|
||||
}
|
||||
return getFunctionBodyTextFromTemplate(
|
||||
project,
|
||||
templateKind,
|
||||
symbol.name.asString(),
|
||||
symbol.annotatedType.type.render(),
|
||||
null
|
||||
)
|
||||
}
|
||||
else -> return buildString {
|
||||
if (bodyType is BodyType.Delegate) {
|
||||
append(bodyType.receiverName)
|
||||
} else {
|
||||
append("super")
|
||||
if (bodyType == BodyType.QUALIFIED_SUPER) {
|
||||
val superClassFqName = symbol.originalContainingClassForOverride?.name?.render()
|
||||
superClassFqName?.let {
|
||||
append("<").append(superClassFqName).append(">")
|
||||
}
|
||||
}
|
||||
}
|
||||
append(".").append(symbol.name.render())
|
||||
|
||||
if (symbol is KtFunctionSymbol) {
|
||||
val paramTexts = symbol.valueParameters.map {
|
||||
val renderedName = it.name.render()
|
||||
if (it.isVararg) "*$renderedName" else renderedName
|
||||
}
|
||||
paramTexts.joinTo(this, prefix = "(", postfix = ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object RenderOptions {
|
||||
// TODO: Currently rendering has the following problems:
|
||||
// - flexible types are not rendered correctly, specifically there are problems with the following
|
||||
// - flexible null type is rendered with `!`, which is not valid Kotlin code
|
||||
// - Array<(out) ...> (example idea/testData/codeInsight/overrideImplement/javaParameters/foo/Impl.kt.fir.after)
|
||||
// - incorrect type parameter (example idea/testData/codeInsight/overrideImplement/jdk8/overrideCollectionStream.kt.fir.after)
|
||||
// - some type annotations should be filtered, for example
|
||||
// - javax.annotation.Nonnull
|
||||
// - androidx.annotation.RecentlyNonNull
|
||||
// - org.jetbrains.annotations.NotNull
|
||||
val overrideRenderOptions = KtDeclarationRendererOptions(
|
||||
modifiers = setOf(RendererModifier.OVERRIDE, RendererModifier.ANNOTATIONS),
|
||||
approximateTypes = true,
|
||||
renderDefaultParameterValue = false,
|
||||
)
|
||||
|
||||
val actualRenderOptions = overrideRenderOptions.copy(
|
||||
modifiers = setOf(RendererModifier.VISIBILITY, RendererModifier.MODALITY, RendererModifier.OVERRIDE, RendererModifier.INNER)
|
||||
|
||||
)
|
||||
val expectRenderOptions = actualRenderOptions.copy(
|
||||
modifiers = actualRenderOptions.modifiers + RendererModifier.ACTUAL
|
||||
)
|
||||
}
|
||||
+324
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.SmartPsiElementPointer
|
||||
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||
import org.jetbrains.kotlin.idea.core.insertMembersAfter
|
||||
import org.jetbrains.kotlin.idea.core.moveCaretIntoGeneratedElement
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtDeclarationRendererOptions
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtTypeRendererOptions
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.RendererModifier
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.HackToForceAllowRunningAnalyzeOnEDT
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtClassBody
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.endOffset
|
||||
import org.jetbrains.kotlin.psi.psiUtil.prevSiblingOfSameType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
|
||||
|
||||
internal abstract class KtGenerateMembersHandler : AbstractGenerateMembersHandler<KtClassMember>() {
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
override fun generateMembers(
|
||||
editor: Editor,
|
||||
classOrObject: KtClassOrObject,
|
||||
selectedElements: Collection<KtClassMember>,
|
||||
copyDoc: Boolean
|
||||
) {
|
||||
// Using hackyAllowRunningOnEdt here because we don't want to pre-populate all possible textual overrides before user selection.
|
||||
val (commands, insertedBlocks) = hackyAllowRunningOnEdt {
|
||||
val insertedBlocks = analyse(classOrObject) {
|
||||
this.generateMembers(editor, classOrObject, selectedElements, copyDoc)
|
||||
}
|
||||
// Reference shortening is done in a separate analysis session because the session need to be aware of the newly generated
|
||||
// members.
|
||||
val commands = analyse(classOrObject) {
|
||||
insertedBlocks.mapNotNull { block ->
|
||||
val declarations = block.declarations.mapNotNull { it.element }
|
||||
val first = declarations.firstOrNull() ?: return@mapNotNull null
|
||||
val last = declarations.last()
|
||||
collectPossibleReferenceShortenings(first.containingKtFile, TextRange(first.startOffset, last.endOffset))
|
||||
}
|
||||
}
|
||||
commands to insertedBlocks
|
||||
}
|
||||
runWriteAction {
|
||||
commands.forEach { it.invokeShortening() }
|
||||
val project = classOrObject.project
|
||||
val codeStyleManager = CodeStyleManager.getInstance(project)
|
||||
insertedBlocks.forEach { block ->
|
||||
block.declarations.forEach { declaration ->
|
||||
declaration.element?.let { element ->
|
||||
codeStyleManager.reformat(
|
||||
element
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
insertedBlocks.firstOrNull()?.declarations?.firstNotNullResult { it.element }?.let {
|
||||
moveCaretIntoGeneratedElement(editor, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun KtAnalysisSession.generateMembers(
|
||||
editor: Editor,
|
||||
currentClass: KtClassOrObject,
|
||||
selectedElements: Collection<KtClassMember>,
|
||||
copyDoc: Boolean
|
||||
): List<InsertedBlock> {
|
||||
if (selectedElements.isEmpty()) return emptyList()
|
||||
val selectedMemberSymbolsAndGeneratedPsi: Map<KtCallableSymbol, KtCallableDeclaration> = selectedElements.associate {
|
||||
it.symbol to generateMember(currentClass.project, it, currentClass, copyDoc)
|
||||
}
|
||||
|
||||
val classBody = currentClass.body
|
||||
val offset = editor.caretModel.offset
|
||||
|
||||
// Insert members at the cursor position if the cursor is within the class body. Or, if there is no body, generate the body and put
|
||||
// stuff in it.
|
||||
if (classBody == null || isCursorInsideClassBodyExcludingBraces(classBody, offset)) {
|
||||
return runWriteAction {
|
||||
listOf(InsertedBlock(insertMembersAfter(editor, currentClass, selectedMemberSymbolsAndGeneratedPsi.values)))
|
||||
}
|
||||
}
|
||||
|
||||
// Insert members at positions such that the result aligns with ordering of members in super types.
|
||||
val orderedMembers = getMembersOrderedByRelativePositionsInSuperTypes(currentClass, selectedMemberSymbolsAndGeneratedPsi)
|
||||
return insertMembersAccordingToPreferredOrder(orderedMembers, classBody.lBrace, editor, currentClass)
|
||||
}
|
||||
|
||||
private fun isCursorInsideClassBodyExcludingBraces(classBody: KtClassBody, offset: Int): Boolean {
|
||||
return classBody.textRange.contains(offset)
|
||||
&& classBody.lBrace?.textRange?.contains(offset) == false
|
||||
&& classBody.rBrace?.textRange?.contains(offset) == false
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a class and some stub implementation of overridden members, output all the callable members in the desired order. For example,
|
||||
* consider the following code
|
||||
*
|
||||
* ```
|
||||
* interface Super {
|
||||
* fun a()
|
||||
* fun b()
|
||||
* fun c()
|
||||
* }
|
||||
*
|
||||
* class Sub: Super {
|
||||
* override fun b() {}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Now this method is invoked with `Sub` as [currentClass] and `Super.a` and `Super.c` as [newMemberSymbolsAndGeneratedPsi]. This
|
||||
* method outputs `[NewEntry(Sub.a), ExistingEntry(Sub.b), NewEntry(Sub.c)]`.
|
||||
*
|
||||
* How does this work?
|
||||
*
|
||||
* Initially we put all existing members in [currentClass] into a doubly linked list in the order they appear in the source code. Then,
|
||||
* for each new member, the algorithm finds a target node nearby which this new member should be inserted. If the algorithm fails to
|
||||
* find a desired target node, then the new member is inserted at the end.
|
||||
*
|
||||
* With the above code as an example, initially the doubly linked list contains `[ExistingEntry(Sub.b)]`. Then for `a`, the algorithm
|
||||
* somehow (explained below) finds `ExistingEntry(Sub.b)` as the target node before which the new member `a` should be inserted. So now
|
||||
* the doubly linked list contains `[NewEntry(Sub.a), ExistingEntry(Sub.b)]`. Similar steps are done for `c`.
|
||||
*
|
||||
* How does the algorithm find the target node and how does it decide whether to insert the new member before or after the target node?
|
||||
*
|
||||
* Throughout the algorithm, we maintain a map that tracks super member declarations for each member in the doubly linked list. For
|
||||
* example, initially, the map contains `{ Super.b -> ExistingEntry(Sub.b) }`
|
||||
*
|
||||
* Given a new member, the algorithm first finds the PSI that declares this member in the super class. Then it traverses all the
|
||||
* sibling members before this PSI element. With the above example, there is nothing before `Super.a`. Next it traverses all the
|
||||
* sibling members after this PSI element. With the above example, it finds `Super.b`, which exists in the map. So the algorithm now
|
||||
* knows `Sub.a` should be inserted before `Sub.b`.
|
||||
*
|
||||
* @param currentClass the class where the generated member code will be placed in
|
||||
* @param newMemberSymbolsAndGeneratedPsi the generated members to insert into the class. For each entry in the map, the key is a
|
||||
* callable symbol for an overridable member that the user has picked to override (or implement), and the value is the stub
|
||||
* implementation for the chosen symbol.
|
||||
*/
|
||||
private fun KtAnalysisSession.getMembersOrderedByRelativePositionsInSuperTypes(
|
||||
currentClass: KtClassOrObject,
|
||||
newMemberSymbolsAndGeneratedPsi: Map<KtCallableSymbol, KtCallableDeclaration>
|
||||
): List<MemberEntry> {
|
||||
|
||||
// This doubly linked list tracks the preferred ordering of members.
|
||||
val sentinelHeadNode = DoublyLinkedNode<MemberEntry>()
|
||||
val sentinelTailNode = DoublyLinkedNode<MemberEntry>()
|
||||
sentinelHeadNode.append(sentinelTailNode)
|
||||
|
||||
// Traverse existing members in the current class and populate
|
||||
// - a doubly linked list tracking the order
|
||||
// - a map that tracks a member (as a doubly linked list node) in the current class and its overridden members in super classes (as
|
||||
// a PSI element). This map is to allow fast look up from a super PSI element to a member entry in the current class
|
||||
val existingDeclarations = currentClass.declarations.filterIsInstance<KtCallableDeclaration>()
|
||||
val superPsiToMemberEntry = mutableMapOf<PsiElement, DoublyLinkedNode<MemberEntry>>().apply {
|
||||
for (existingDeclaration in existingDeclarations) {
|
||||
val node: DoublyLinkedNode<MemberEntry> = DoublyLinkedNode(MemberEntry.ExistingEntry(existingDeclaration))
|
||||
sentinelTailNode.prepend(node)
|
||||
val callableSymbol = existingDeclaration.getSymbol() as? KtCallableSymbol ?: continue
|
||||
for (overriddenSymbol in callableSymbol.getAllOverriddenSymbols()) {
|
||||
put(overriddenSymbol.psi ?: continue, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note on implementation: here we need the original ordering defined in the source code, so we stick to PSI rather than using
|
||||
// `KtMemberScope` because the latter does not guarantee members are traversed in the original order. For example the
|
||||
// FIR implementation groups overloaded functions together.
|
||||
outer@ for ((selectedSymbol, generatedPsi) in newMemberSymbolsAndGeneratedPsi) {
|
||||
val superSymbol = selectedSymbol.originalOverriddenSymbol
|
||||
val superPsi = superSymbol?.psi
|
||||
if (superPsi == null) {
|
||||
// This normally should not happen, but we just try to play safe here.
|
||||
sentinelTailNode.prepend(DoublyLinkedNode(MemberEntry.NewEntry(generatedPsi)))
|
||||
continue
|
||||
}
|
||||
var currentPsi = superSymbol.psi?.prevSibling
|
||||
while (currentPsi != null) {
|
||||
val matchedNode = superPsiToMemberEntry[currentPsi]
|
||||
if (matchedNode != null) {
|
||||
val newNode = DoublyLinkedNode<MemberEntry>(MemberEntry.NewEntry(generatedPsi))
|
||||
matchedNode.append(newNode)
|
||||
superPsiToMemberEntry[superPsi] = newNode
|
||||
continue@outer
|
||||
}
|
||||
currentPsi = currentPsi.prevSibling
|
||||
}
|
||||
currentPsi = superSymbol.psi?.nextSibling
|
||||
while (currentPsi != null) {
|
||||
val matchedNode = superPsiToMemberEntry[currentPsi]
|
||||
if (matchedNode != null) {
|
||||
val newNode = DoublyLinkedNode<MemberEntry>(MemberEntry.NewEntry(generatedPsi))
|
||||
matchedNode.prepend(newNode)
|
||||
superPsiToMemberEntry[superPsi] = newNode
|
||||
continue@outer
|
||||
}
|
||||
currentPsi = currentPsi.nextSibling
|
||||
}
|
||||
val newNode = DoublyLinkedNode<MemberEntry>(MemberEntry.NewEntry(generatedPsi))
|
||||
superPsiToMemberEntry[superPsi] = newNode
|
||||
sentinelTailNode.prepend(newNode)
|
||||
}
|
||||
return sentinelHeadNode.toListSkippingNulls()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun insertMembersAccordingToPreferredOrder(
|
||||
symbolsInPreferredOrder: List<MemberEntry>,
|
||||
classLeftBrace: PsiElement?,
|
||||
editor: Editor,
|
||||
currentClass: KtClassOrObject
|
||||
): List<InsertedBlock> {
|
||||
require(symbolsInPreferredOrder.isNotEmpty()) { "symbolsInPreferredOrder must not be empty" }
|
||||
var firstAnchor: PsiElement? = null
|
||||
if (symbolsInPreferredOrder.first() is MemberEntry.NewEntry) {
|
||||
val firstExistingEntry = symbolsInPreferredOrder.firstIsInstanceOrNull<MemberEntry.ExistingEntry>()
|
||||
if (firstExistingEntry != null) {
|
||||
firstAnchor = firstExistingEntry.psi.prevSiblingOfSameType() ?: classLeftBrace
|
||||
}
|
||||
}
|
||||
|
||||
val insertionBlocks = mutableListOf<InsertionBlock>()
|
||||
var currentAnchor = firstAnchor
|
||||
val currentBatch = mutableListOf<KtCallableDeclaration>()
|
||||
fun updateBatch() {
|
||||
if (currentBatch.isNotEmpty()) {
|
||||
insertionBlocks += InsertionBlock(currentBatch.toList(), currentAnchor)
|
||||
currentBatch.clear()
|
||||
}
|
||||
}
|
||||
for (entry in symbolsInPreferredOrder) {
|
||||
when (entry) {
|
||||
is MemberEntry.ExistingEntry -> {
|
||||
updateBatch()
|
||||
currentAnchor = entry.psi
|
||||
}
|
||||
is MemberEntry.NewEntry -> {
|
||||
currentBatch += entry.psi
|
||||
}
|
||||
}
|
||||
}
|
||||
updateBatch()
|
||||
|
||||
return runWriteAction {
|
||||
insertionBlocks.map { (newDeclarations, anchor) ->
|
||||
InsertedBlock(insertMembersAfter(editor, currentClass, newDeclarations, anchor = anchor))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DoublyLinkedNode<T>(val t: T? = null) {
|
||||
private var prev: DoublyLinkedNode<T>? = null
|
||||
private var next: DoublyLinkedNode<T>? = null
|
||||
|
||||
fun append(node: DoublyLinkedNode<T>) {
|
||||
val next = this.next
|
||||
this.next = node
|
||||
node.prev = this
|
||||
node.next = next
|
||||
next?.prev = node
|
||||
}
|
||||
|
||||
fun prepend(node: DoublyLinkedNode<T>) {
|
||||
val prev = this.prev
|
||||
this.prev = node
|
||||
node.next = this
|
||||
node.prev = prev
|
||||
prev?.next = node
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
fun toListSkippingNulls(): List<T> {
|
||||
var current: DoublyLinkedNode<T>? = this
|
||||
return buildList {
|
||||
while (current != null) {
|
||||
current?.let {
|
||||
if (it.t != null) add(it.t)
|
||||
current = it.next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class MemberEntry {
|
||||
data class ExistingEntry(val psi: KtCallableDeclaration) : MemberEntry()
|
||||
data class NewEntry(val psi: KtCallableDeclaration) : MemberEntry()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val renderOption = KtDeclarationRendererOptions(
|
||||
modifiers = setOf(RendererModifier.OVERRIDE, RendererModifier.ANNOTATIONS),
|
||||
renderDeclarationHeader = false,
|
||||
renderUnitReturnType = true,
|
||||
typeRendererOptions = KtTypeRendererOptions(
|
||||
shortQualifiedNames = true,
|
||||
renderFunctionType = true,
|
||||
renderAnnotations = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** A block of code (represented as a list of Kotlin declarations) that should be inserted at a given anchor. */
|
||||
private data class InsertionBlock(val declarations: List<KtDeclaration>, val anchor: PsiElement?)
|
||||
|
||||
/** A block of generated code. The code is represented as a list of Kotlin declarations that are defined one after another. */
|
||||
private data class InsertedBlock(val declarations: List<SmartPsiElementPointer<KtDeclaration>>)
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler.Companion.getUnimplementedMembers
|
||||
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
|
||||
import org.jetbrains.kotlin.idea.fir.api.fixes.diagnosticFixFactory
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics.KtFirDiagnostic
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtIconProvider.getIcon
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithModality
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.HackToForceAllowRunningAnalyzeOnEDT
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtEnumEntry
|
||||
import org.jetbrains.kotlin.util.ImplementationStatus
|
||||
|
||||
internal open class KtImplementMembersHandler : KtGenerateMembersHandler() {
|
||||
override fun getChooserTitle() = KotlinIdeaCoreBundle.message("implement.members.handler.title")
|
||||
|
||||
override fun getNoMembersFoundHint() = KotlinIdeaCoreBundle.message("implement.members.handler.no.members.hint")
|
||||
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
override fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<KtClassMember> {
|
||||
return hackyAllowRunningOnEdt {
|
||||
analyse(classOrObject) {
|
||||
getUnimplementedMembers(classOrObject).map { createKtClassMember(it, BodyType.FROM_TEMPLATE, false) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun KtAnalysisSession.getUnimplementedMembers(classWithUnimplementedMembers: KtClassOrObject): List<KtClassMemberInfo> {
|
||||
return getUnimplementedMemberSymbols(classWithUnimplementedMembers.getClassOrObjectSymbol()).map { unimplementedMemberSymbol ->
|
||||
val containingSymbol = unimplementedMemberSymbol.originalContainingClassForOverride
|
||||
KtClassMemberInfo(
|
||||
symbol = unimplementedMemberSymbol,
|
||||
memberText = unimplementedMemberSymbol.render(renderOption),
|
||||
memberIcon = getIcon(unimplementedMemberSymbol),
|
||||
containingSymbolText = containingSymbol?.classIdIfNonLocal?.asSingleFqName()?.toString()
|
||||
?: containingSymbol?.name?.asString(),
|
||||
containingSymbolIcon = containingSymbol?.let { symbol -> getIcon(symbol) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun KtAnalysisSession.getUnimplementedMemberSymbols(classWithUnimplementedMembers: KtClassOrObjectSymbol): List<KtCallableSymbol> {
|
||||
return buildList {
|
||||
classWithUnimplementedMembers.getMemberScope().getCallableSymbols().forEach { symbol ->
|
||||
if (!symbol.isVisibleInClass(classWithUnimplementedMembers)) return@forEach
|
||||
when (symbol.getImplementationStatus(classWithUnimplementedMembers)) {
|
||||
ImplementationStatus.NOT_IMPLEMENTED -> add(symbol)
|
||||
ImplementationStatus.AMBIGUOUSLY_INHERITED,
|
||||
ImplementationStatus.INHERITED_OR_SYNTHESIZED -> {
|
||||
// This case is to show user abstract members that don't need to be implemented because another super class has provide
|
||||
// an implementation. For example, given the following
|
||||
//
|
||||
// interface A { fun foo() }
|
||||
// interface B { fun foo() {} }
|
||||
// class Foo : A, B {}
|
||||
//
|
||||
// `Foo` does not need to implement `foo` since it inherits the implementation from `B`. But in the dialog, we should
|
||||
// allow user to choose `foo` to implement.
|
||||
symbol.getIntersectionOverriddenSymbols()
|
||||
.filter { (it as? KtSymbolWithModality)?.modality == Modality.ABSTRACT }
|
||||
.forEach { add(it) }
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class KtImplementMembersQuickfix(private val members: Collection<KtClassMemberInfo>) : KtImplementMembersHandler(),
|
||||
IntentionAction {
|
||||
override fun getText() = familyName
|
||||
override fun getFamilyName() = KotlinIdeaCoreBundle.message("implement.members.handler.family")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
|
||||
|
||||
override fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<KtClassMember> {
|
||||
return members.map { createKtClassMember(it, BodyType.FROM_TEMPLATE, false) }
|
||||
}
|
||||
}
|
||||
|
||||
internal class KtImplementAsConstructorParameterQuickfix(private val members: Collection<KtClassMemberInfo>) : KtImplementMembersHandler(),
|
||||
IntentionAction {
|
||||
override fun getText() = KotlinIdeaCoreBundle.message("action.text.implement.as.constructor.parameters")
|
||||
|
||||
override fun getFamilyName() = KotlinIdeaCoreBundle.message("implement.members.handler.family")
|
||||
|
||||
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
|
||||
|
||||
override fun isValidForClass(classOrObject: KtClassOrObject): Boolean {
|
||||
if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false
|
||||
// TODO: when MPP support is ready, return false if this class is `actual` and any expect classes have primary constructor.
|
||||
return members.any { it.isProperty }
|
||||
}
|
||||
|
||||
override fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<KtClassMember> {
|
||||
return members.filter { it.isProperty }.map { createKtClassMember(it, BodyType.FROM_TEMPLATE, true) }
|
||||
}
|
||||
}
|
||||
|
||||
object MemberNotImplementedQuickfixFactories {
|
||||
|
||||
val abstractMemberNotImplemented = diagnosticFixFactory<KtFirDiagnostic.AbstractMemberNotImplemented> { diagnostic ->
|
||||
getUnimplementedMemberFixes(diagnostic.psi)
|
||||
}
|
||||
|
||||
val abstractClassMemberNotImplemented = diagnosticFixFactory<KtFirDiagnostic.AbstractClassMemberNotImplemented> { diagnostic ->
|
||||
getUnimplementedMemberFixes(diagnostic.psi)
|
||||
}
|
||||
|
||||
val manyInterfacesMemberNotImplemented = diagnosticFixFactory<KtFirDiagnostic.ManyInterfacesMemberNotImplemented> { diagnostic ->
|
||||
getUnimplementedMemberFixes(diagnostic.psi)
|
||||
}
|
||||
|
||||
val manyImplMemberNotImplemented = diagnosticFixFactory<KtFirDiagnostic.ManyImplMemberNotImplemented> { diagnostic ->
|
||||
getUnimplementedMemberFixes(diagnostic.psi, false)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun KtAnalysisSession.getUnimplementedMemberFixes(
|
||||
classWithUnimplementedMembers: KtClassOrObject,
|
||||
includeImplementAsConstructorParameterQuickfix: Boolean = true
|
||||
): List<IntentionAction> {
|
||||
val unimplementedMembers = getUnimplementedMembers(classWithUnimplementedMembers)
|
||||
|
||||
return buildList {
|
||||
add(KtImplementMembersQuickfix(unimplementedMembers))
|
||||
if (includeImplementAsConstructorParameterQuickfix) {
|
||||
add(KtImplementAsConstructorParameterQuickfix(unimplementedMembers))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.core.overrideImplement
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.analyse
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassKind
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtIconProvider.getIcon
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtPropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithModality
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.HackToForceAllowRunningAnalyzeOnEDT
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.hackyAllowRunningOnEdt
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
|
||||
internal class KtOverrideMembersHandler : KtGenerateMembersHandler() {
|
||||
@OptIn(HackToForceAllowRunningAnalyzeOnEDT::class)
|
||||
override fun collectMembersToGenerate(classOrObject: KtClassOrObject): Collection<KtClassMember> {
|
||||
return hackyAllowRunningOnEdt {
|
||||
analyse(classOrObject) {
|
||||
collectMembers(classOrObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun KtAnalysisSession.collectMembers(classOrObject: KtClassOrObject): List<KtClassMember> {
|
||||
val classOrObjectSymbol = classOrObject.getClassOrObjectSymbol()
|
||||
return getOverridableMembers(classOrObjectSymbol).map { (symbol, bodyType, containingSymbol) ->
|
||||
KtClassMember(
|
||||
KtClassMemberInfo(
|
||||
symbol,
|
||||
symbol.render(renderOption),
|
||||
getIcon(symbol),
|
||||
containingSymbol?.classIdIfNonLocal?.asSingleFqName()?.toString() ?: containingSymbol?.name?.asString(),
|
||||
containingSymbol?.let { getIcon(it) }
|
||||
),
|
||||
bodyType,
|
||||
preferConstructorParameter = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalStdlibApi::class)
|
||||
private fun KtAnalysisSession.getOverridableMembers(classOrObjectSymbol: KtClassOrObjectSymbol): List<OverrideMember> {
|
||||
return buildList {
|
||||
classOrObjectSymbol.getMemberScope().getCallableSymbols().forEach { symbol ->
|
||||
if (!symbol.isVisibleInClass(classOrObjectSymbol)) return@forEach
|
||||
val implementationStatus = symbol.getImplementationStatus(classOrObjectSymbol) ?: return@forEach
|
||||
if (!implementationStatus.isOverridable) return@forEach
|
||||
|
||||
val intersectionSymbols = symbol.getIntersectionOverriddenSymbols()
|
||||
val symbolsToProcess = if (intersectionSymbols.size <= 1) {
|
||||
listOf(symbol)
|
||||
} else {
|
||||
val nonAbstractMembers = intersectionSymbols.filter { (it as? KtSymbolWithModality)?.modality != Modality.ABSTRACT }
|
||||
// If there are non-abstract members, we only want to show override for these non-abstract members. Otherwise, show any
|
||||
// abstract member to override.
|
||||
nonAbstractMembers.ifEmpty {
|
||||
listOf(intersectionSymbols.first())
|
||||
}
|
||||
}
|
||||
|
||||
val hasNoSuperTypesExceptAny = classOrObjectSymbol.superTypes.singleOrNull()?.type?.isAny == true
|
||||
for (symbolToProcess in symbolsToProcess) {
|
||||
val originalOverriddenSymbol = symbolToProcess.originalOverriddenSymbol
|
||||
val containingSymbol = originalOverriddenSymbol?.originalContainingClassForOverride
|
||||
|
||||
val bodyType = when {
|
||||
classOrObjectSymbol.classKind == KtClassKind.INTERFACE && containingSymbol?.classIdIfNonLocal == StandardClassIds.Any -> {
|
||||
if (hasNoSuperTypesExceptAny) {
|
||||
// If an interface does not extends any other interfaces, FE1.0 simply skips members of `Any`. So we mimic
|
||||
// the same behavior. See idea/testData/codeInsight/overrideImplement/noAnyMembersInInterface.kt
|
||||
continue
|
||||
} else {
|
||||
BodyType.NO_BODY
|
||||
}
|
||||
}
|
||||
(originalOverriddenSymbol as? KtSymbolWithModality)?.modality == Modality.ABSTRACT ->
|
||||
BodyType.FROM_TEMPLATE
|
||||
symbolsToProcess.size > 1 ->
|
||||
BodyType.QUALIFIED_SUPER
|
||||
else ->
|
||||
BodyType.SUPER
|
||||
}
|
||||
// Ideally, we should simply create `KtClassMember` here and remove the intermediate `OverrideMember` data class. But
|
||||
// that doesn't work because this callback function is holding a read lock and `symbol.render(renderOption)` requires
|
||||
// the write lock.
|
||||
// Hence, we store the data in an intermediate `OverrideMember` data class and do the rendering later in the `map` call.
|
||||
add(OverrideMember(symbolToProcess, bodyType, containingSymbol))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private data class OverrideMember(val symbol: KtCallableSymbol, val bodyType: BodyType, val containingSymbol: KtClassOrObjectSymbol?)
|
||||
|
||||
override fun getChooserTitle() = KotlinIdeaCoreBundle.message("override.members.handler.title")
|
||||
|
||||
override fun getNoMembersFoundHint() = KotlinIdeaCoreBundle.message("override.members.handler.no.members.hint")
|
||||
}
|
||||
+2
-4
@@ -18,8 +18,6 @@ import org.jetbrains.kotlin.idea.frontend.api.analyseInModalWindow
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtFunctionSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtNamedSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||
import org.jetbrains.kotlin.idea.refactoring.CHECK_SUPER_METHODS_YES_NO_DIALOG
|
||||
@@ -70,12 +68,12 @@ class KotlinFindUsagesSupportFirImpl : KotlinFindUsagesSupport {
|
||||
|
||||
val analyzeResult = analyseInModalWindow(declaration, KotlinBundle.message("find.usages.progress.text.declaration.superMethods")) {
|
||||
(declaration.getSymbol() as? KtCallableSymbol)?.let { callableSymbol ->
|
||||
((callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol)?.let { containingClass ->
|
||||
callableSymbol.originalContainingClassForOverride?.let { containingClass ->
|
||||
val overriddenSymbols = callableSymbol.getAllOverriddenSymbols()
|
||||
|
||||
val renderToPsi = overriddenSymbols.mapNotNull {
|
||||
it.psi?.let { psi ->
|
||||
psi to getClassDescription(psi, (it as? KtSymbolWithKind)?.getContainingSymbol())
|
||||
psi to getClassDescription(psi, it.originalContainingClassForOverride)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.MemberNotImplementedQuickfixFactories
|
||||
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixRegistrar
|
||||
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesList
|
||||
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
|
||||
@@ -78,6 +79,10 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
||||
registerApplicator(ChangeTypeQuickFix.changeFunctionReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFix.changePropertyReturnTypeOnOverride)
|
||||
registerApplicator(ChangeTypeQuickFix.changeVariableReturnTypeOnOverride)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.abstractMemberNotImplemented)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.abstractClassMemberNotImplemented)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.manyInterfacesMemberNotImplemented)
|
||||
registerApplicator(MemberNotImplementedQuickfixFactories.manyImplMemberNotImplemented)
|
||||
}
|
||||
|
||||
private val mutability = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||
|
||||
+6
-1
@@ -41,7 +41,9 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
|
||||
KtReferenceResolveMixIn,
|
||||
KtReferenceShortenerMixIn,
|
||||
KtSymbolDeclarationRendererMixIn,
|
||||
KtVisibilityCheckerMixIn {
|
||||
KtVisibilityCheckerMixIn,
|
||||
KtMemberSymbolProviderMixin
|
||||
{
|
||||
|
||||
override val analysisSession: KtAnalysisSession get() = this
|
||||
|
||||
@@ -94,4 +96,7 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
|
||||
|
||||
internal val visibilityChecker: KtVisibilityChecker get() = visibilityCheckerImpl
|
||||
protected abstract val visibilityCheckerImpl: KtVisibilityChecker
|
||||
|
||||
internal val overrideInfoProvider: KtOverrideInfoProvider get() = overrideInfoProviderImpl
|
||||
protected abstract val overrideInfoProviderImpl: KtOverrideInfoProvider
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.components
|
||||
|
||||
import org.jetbrains.kotlin.util.ImplementationStatus
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
|
||||
abstract class KtOverrideInfoProvider : KtAnalysisSessionComponent() {
|
||||
abstract fun isVisible(memberSymbol: KtCallableSymbol, classSymbol: KtClassOrObjectSymbol): Boolean
|
||||
abstract fun getImplementationStatus(memberSymbol: KtCallableSymbol, parentClassSymbol: KtClassOrObjectSymbol): ImplementationStatus?
|
||||
abstract fun getOriginalOverriddenSymbol(symbol: KtCallableSymbol): KtCallableSymbol?
|
||||
abstract fun getOriginalContainingClassForOverride(symbol: KtCallableSymbol): KtClassOrObjectSymbol?
|
||||
}
|
||||
|
||||
interface KtMemberSymbolProviderMixin : KtAnalysisSessionMixIn {
|
||||
|
||||
/** Checks if the given symbol (possibly a symbol inherited from a super class) is visible in the given class. */
|
||||
fun KtCallableSymbol.isVisibleInClass(classSymbol: KtClassOrObjectSymbol): Boolean =
|
||||
analysisSession.overrideInfoProvider.isVisible(this, classSymbol)
|
||||
|
||||
/**
|
||||
* Gets the [ImplementationStatus] of the [this] member symbol in the given [parentClassSymbol]. Or null if this symbol is not a
|
||||
* member.
|
||||
*/
|
||||
fun KtCallableSymbol.getImplementationStatus(parentClassSymbol: KtClassOrObjectSymbol): ImplementationStatus? =
|
||||
analysisSession.overrideInfoProvider.getImplementationStatus(this, parentClassSymbol)
|
||||
|
||||
/**
|
||||
* Gets the original symbol for the given callable symbol. In a class scope, a symbol may be derived from symbols declared in super
|
||||
* classes. For example, consider
|
||||
*
|
||||
* ```
|
||||
* interface A<T> {
|
||||
* fun foo(t:T)
|
||||
* }
|
||||
*
|
||||
* interface B: A<String> {
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In the class scope of `B`, there is a callable symbol `foo` that takes a `String`. This symbol is derived from the original symbol
|
||||
* in `A` that takes the type parameter `T`. Given such a derived symbol, [originalOverriddenSymbol] recovers the original declared
|
||||
* symbol.
|
||||
*
|
||||
* Such situation can also happens for intersection symbols (in case of multiple super types containing symbols with identical signature
|
||||
* after specialization) and delegation.
|
||||
*/
|
||||
val KtCallableSymbol.originalOverriddenSymbol: KtCallableSymbol?
|
||||
get() = analysisSession.overrideInfoProvider.getOriginalOverriddenSymbol(this)
|
||||
|
||||
/**
|
||||
* Gets the class symbol where the given callable symbol is declared. See [originalOverriddenSymbol] for more details.
|
||||
*/
|
||||
val KtCallableSymbol.originalContainingClassForOverride: KtClassOrObjectSymbol?
|
||||
get() = analysisSession.overrideInfoProvider.getOriginalContainingClassForOverride(this)
|
||||
}
|
||||
+18
-1
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.frontend.api.components
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
import javax.swing.Icon
|
||||
|
||||
/**
|
||||
* KtType to string renderer options
|
||||
@@ -24,6 +25,8 @@ data class KtTypeRendererOptions(
|
||||
* @sample Function0<Int> returns () -> Int
|
||||
*/
|
||||
val renderFunctionType: Boolean = true,
|
||||
|
||||
val renderAnnotations: Boolean = true,
|
||||
) {
|
||||
companion object {
|
||||
val DEFAULT = KtTypeRendererOptions()
|
||||
@@ -63,6 +66,19 @@ data class KtDeclarationRendererOptions(
|
||||
* Approximate Kotlin not-denotable types into denotable for declarations return type
|
||||
*/
|
||||
val approximateTypes: Boolean = false,
|
||||
|
||||
/**
|
||||
* Declaration header is something like `abstract class`, `fun`, or `private interface` in a declaration.
|
||||
*/
|
||||
val renderDeclarationHeader: Boolean = true,
|
||||
|
||||
/**
|
||||
* Whether to forcefully add `override` modifier when rendering functions or properties. Note that the [modifiers] option still
|
||||
* controls whether `override` is rendered. That is, if [modifiers] don't contain `override`, then this flag does not have any effect.
|
||||
*/
|
||||
val forceRenderingOverrideModifier: Boolean = false,
|
||||
|
||||
val renderDefaultParameterValue: Boolean = true,
|
||||
) {
|
||||
companion object {
|
||||
val DEFAULT = KtDeclarationRendererOptions()
|
||||
@@ -82,7 +98,8 @@ enum class RendererModifier(val includeByDefault: Boolean) {
|
||||
CONST(true),
|
||||
LATEINIT(true),
|
||||
FUN(true),
|
||||
VALUE(true)
|
||||
VALUE(true),
|
||||
OPERATOR(true)
|
||||
;
|
||||
|
||||
companion object {
|
||||
|
||||
+2
@@ -38,6 +38,7 @@ interface KtTypeInfoProviderMixIn : KtAnalysisSessionMixIn {
|
||||
val KtType.isChar: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR)
|
||||
val KtType.isBoolean: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.BOOLEAN)
|
||||
val KtType.isString: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.STRING)
|
||||
val KtType.isAny: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.ANY)
|
||||
|
||||
val KtType.isUInt: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uInt)
|
||||
val KtType.isULong: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uLong)
|
||||
@@ -84,5 +85,6 @@ object DefaultTypeClassIds {
|
||||
val CHAR = ClassId.topLevel(StandardNames.FqNames._char.toSafe())
|
||||
val BOOLEAN = ClassId.topLevel(StandardNames.FqNames._boolean.toSafe())
|
||||
val STRING = ClassId.topLevel(StandardNames.FqNames.string.toSafe())
|
||||
val ANY = ClassId.topLevel(StandardNames.FqNames.any.toSafe())
|
||||
val PRIMITIVES = setOf(INT, LONG, SHORT, BYTE, FLOAT, DOUBLE, CHAR, BOOLEAN)
|
||||
}
|
||||
|
||||
+2
-1
@@ -5,11 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.symbols
|
||||
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtTypedSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
|
||||
abstract class KtCallableSymbol : KtSymbol, KtTypedSymbol {
|
||||
abstract class KtCallableSymbol : KtSymbol, KtTypedSymbol, KtSymbolWithKind {
|
||||
abstract val callableIdIfNonLocal: CallableId?
|
||||
abstract override fun createPointer(): KtSymbolPointer<KtCallableSymbol>
|
||||
}
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.symbols
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.util.Iconable
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.ui.RowIcon
|
||||
import com.intellij.util.PlatformIcons
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.idea.KotlinIcons
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtPossibleMemberSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithModality
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithVisibility
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.isExtension
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import javax.swing.Icon
|
||||
|
||||
object KtIconProvider {
|
||||
private val LOG = Logger.getInstance(KtIconProvider::class.java)
|
||||
|
||||
// KtAnalysisSession is unused, but we want to keep it here to force all access to this method has a KtAnalysisSession
|
||||
@Suppress("unused")
|
||||
fun KtAnalysisSession.getIcon(ktSymbol: KtSymbol): Icon? {
|
||||
// logic copied from org.jetbrains.kotlin.idea.KotlinDescriptorIconProvider
|
||||
val declaration = ktSymbol.psi
|
||||
return if (declaration?.isValid == true) {
|
||||
val isClass = declaration is PsiClass || declaration is KtClass
|
||||
val flags = if (isClass) 0 else Iconable.ICON_FLAG_VISIBILITY
|
||||
if (declaration is KtDeclaration) {
|
||||
// kotlin declaration
|
||||
// visibility and abstraction better detect by a descriptor
|
||||
getIcon(ktSymbol, flags) ?: declaration.getIcon(flags)
|
||||
} else {
|
||||
// Use Java icons if it's not Kotlin declaration
|
||||
declaration.getIcon(flags)
|
||||
}
|
||||
} else {
|
||||
getIcon(ktSymbol, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIcon(symbol: KtSymbol, flags: Int): Icon? {
|
||||
var result: Icon = getBaseIcon(symbol) ?: return null
|
||||
|
||||
if (flags and Iconable.ICON_FLAG_VISIBILITY > 0) {
|
||||
val rowIcon = RowIcon(2)
|
||||
rowIcon.setIcon(result, 0)
|
||||
rowIcon.setIcon(getVisibilityIcon(symbol), 1)
|
||||
result = rowIcon
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getBaseIcon(symbol: KtSymbol): Icon? {
|
||||
val isAbstract = (symbol as? KtSymbolWithModality)?.modality == Modality.ABSTRACT
|
||||
return when (symbol) {
|
||||
is KtPackageSymbol -> PlatformIcons.PACKAGE_ICON
|
||||
is KtFunctionLikeSymbol -> {
|
||||
val isExtension = symbol.isExtension
|
||||
val isMember = (symbol as? KtPossibleMemberSymbol)?.dispatchType != null
|
||||
when {
|
||||
isExtension && isAbstract -> KotlinIcons.ABSTRACT_EXTENSION_FUNCTION
|
||||
isExtension && !isAbstract -> KotlinIcons.EXTENSION_FUNCTION
|
||||
isMember && isAbstract -> PlatformIcons.ABSTRACT_METHOD_ICON
|
||||
isMember && !isAbstract -> PlatformIcons.METHOD_ICON
|
||||
else -> KotlinIcons.FUNCTION
|
||||
}
|
||||
}
|
||||
is KtClassOrObjectSymbol -> {
|
||||
when (symbol.classKind) {
|
||||
KtClassKind.CLASS -> when {
|
||||
isAbstract -> KotlinIcons.ABSTRACT_CLASS
|
||||
else -> KotlinIcons.CLASS
|
||||
}
|
||||
KtClassKind.ENUM_CLASS, KtClassKind.ENUM_ENTRY -> KotlinIcons.ENUM
|
||||
KtClassKind.ANNOTATION_CLASS -> KotlinIcons.ANNOTATION
|
||||
KtClassKind.INTERFACE -> KotlinIcons.INTERFACE
|
||||
KtClassKind.ANONYMOUS_OBJECT, KtClassKind.OBJECT, KtClassKind.COMPANION_OBJECT -> KotlinIcons.OBJECT
|
||||
}
|
||||
}
|
||||
is KtValueParameterSymbol -> KotlinIcons.PARAMETER
|
||||
is KtLocalVariableSymbol -> when {
|
||||
symbol.isVal -> KotlinIcons.VAL
|
||||
else -> KotlinIcons.VAR
|
||||
}
|
||||
is KtPropertySymbol -> when {
|
||||
symbol.isVal -> KotlinIcons.FIELD_VAL
|
||||
else -> KotlinIcons.FIELD_VAR
|
||||
}
|
||||
is KtTypeParameterSymbol -> PlatformIcons.CLASS_ICON
|
||||
is KtTypeAliasSymbol -> KotlinIcons.TYPE_ALIAS
|
||||
|
||||
else -> {
|
||||
LOG.warn("No icon for symbol: $symbol")
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVisibilityIcon(symbol: KtSymbol): Icon? {
|
||||
return when ((symbol as? KtSymbolWithVisibility)?.visibility?.normalize()) {
|
||||
Visibilities.Public -> PlatformIcons.PUBLIC_ICON
|
||||
Visibilities.Protected -> PlatformIcons.PROTECTED_ICON
|
||||
Visibilities.Private, Visibilities.PrivateToThis -> PlatformIcons.PRIVATE_ICON
|
||||
Visibilities.Internal -> PlatformIcons.PACKAGE_LOCAL_ICON
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -16,13 +16,14 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.getFirFile
|
||||
import org.jetbrains.kotlin.idea.frontend.api.InvalidWayOfUsingAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationRendererProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirOverrideInfoProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbolProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.EnclosingDeclarationContext
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.recordCompletionContext
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.threadLocal
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
@@ -62,6 +63,8 @@ private constructor(
|
||||
|
||||
override val expressionInfoProviderImpl = KtFirExpressionInfoProvider(this, token)
|
||||
|
||||
override val overrideInfoProviderImpl = KtFirOverrideInfoProvider(this, token)
|
||||
|
||||
override val visibilityCheckerImpl: KtVisibilityChecker = KtFirVisibilityChecker(this, token)
|
||||
|
||||
override val typeProviderImpl = KtFirTypeProvider(this, token)
|
||||
|
||||
+8
-1
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtObjectLiteralExpression
|
||||
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
|
||||
@@ -28,6 +27,14 @@ internal class KtFirSymbolContainingDeclarationProvider(
|
||||
override fun getContainingDeclaration(symbol: KtSymbolWithKind): KtSymbolWithKind? {
|
||||
if (symbol is KtPackageSymbol) return null
|
||||
if (symbol.symbolKind == KtSymbolKind.TOP_LEVEL) return null
|
||||
if (symbol is KtCallableSymbol) {
|
||||
val classId = symbol.callableIdIfNonLocal?.classId
|
||||
if (classId != null) {
|
||||
with(analysisSession) {
|
||||
return classId.getCorrespondingToplevelClassOrObjectSymbol()
|
||||
}
|
||||
}
|
||||
}
|
||||
return when (symbol.origin) {
|
||||
KtSymbolOrigin.SOURCE, KtSymbolOrigin.SOURCE_MEMBER_GENERATED ->
|
||||
getContainingDeclarationForKotlinInSourceSymbol(symbol)
|
||||
|
||||
+5
-4
@@ -13,13 +13,14 @@ import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverridePropertySymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtSymbolDeclarationOverridesProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirNamedClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbolOrigin
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
|
||||
internal class KtFirSymbolDeclarationOverridesProvider(
|
||||
override val analysisSession: KtFirAnalysisSession,
|
||||
@@ -90,7 +91,7 @@ internal class KtFirSymbolDeclarationOverridesProvider(
|
||||
) {
|
||||
require(callableSymbol is KtFirSymbol<*>)
|
||||
val containingDeclaration = with(analysisSession) {
|
||||
(callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol
|
||||
(callableSymbol as? KtCallableSymbol)?.originalContainingClassForOverride
|
||||
} ?: return
|
||||
check(containingDeclaration is KtFirNamedClassOrObjectSymbol)
|
||||
|
||||
|
||||
+3
-3
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.idea.frontend.api.fir.renderer.ConeTypeIdeRenderer
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.renderer.FirIdeRenderer
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.types.KtFirType
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.*
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||
|
||||
@@ -39,7 +39,7 @@ internal class KtFirSymbolDeclarationRendererProvider(
|
||||
val phaseNeeded =
|
||||
if (options.renderContainingDeclarations) FirResolvePhase.BODY_RESOLVE else FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
|
||||
|
||||
return (containingSymbol ?: symbol).firRef.withFir(phaseNeeded) { fir ->
|
||||
return symbol.firRef.withFir(phaseNeeded) { fir ->
|
||||
val containingFir = containingSymbol?.firRef?.withFirUnsafe { it }
|
||||
FirIdeRenderer.render(fir, containingFir, options, fir.declarationSiteSession)
|
||||
}
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ internal class ConeTypeIdeRenderer(
|
||||
private var filterExtensionFunctionType: Boolean = false
|
||||
|
||||
private fun StringBuilder.renderAnnotationList(annotations: List<FirAnnotationCall>?) {
|
||||
if (annotations != null) {
|
||||
if (annotations != null && options.renderAnnotations) {
|
||||
val filteredExtensionIfNeeded = annotations.applyIf(filterExtensionFunctionType) {
|
||||
annotations.filterNot { it.toAnnotationClassId() == StandardClassIds.extensionFunctionType }
|
||||
}
|
||||
|
||||
+84
-54
@@ -50,7 +50,12 @@ internal class FirIdeRenderer private constructor(
|
||||
val approximatedIfNeeded = approximate.ifTrue {
|
||||
PublicTypeApproximator.approximateTypeToPublicDenotable(firRef.coneType, session)
|
||||
} ?: firRef.coneType
|
||||
return renderType(approximatedIfNeeded, firRef.annotations)
|
||||
val annotations = if (RendererModifier.ANNOTATIONS in options.modifiers) {
|
||||
firRef.annotations
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return renderType(approximatedIfNeeded, annotations)
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderName(declaration: FirDeclaration) {
|
||||
@@ -133,7 +138,7 @@ internal class FirIdeRenderer private constructor(
|
||||
|
||||
private fun StringBuilder.renderOverride(callableMember: FirCallableMemberDeclaration<*>) {
|
||||
if (RendererModifier.OVERRIDE !in options.modifiers) return
|
||||
renderModifier(callableMember.isOverride, "override")
|
||||
renderModifier(callableMember.isOverride || options.forceRenderingOverrideModifier, "override")
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderModifier(value: Boolean, modifier: String) {
|
||||
@@ -159,7 +164,7 @@ internal class FirIdeRenderer private constructor(
|
||||
renderSuspendModifier(firMember)
|
||||
renderModifier(firMember.isInline, "inline")
|
||||
renderModifier(isInfix, "infix")
|
||||
renderModifier(isOperator, "operator")
|
||||
renderModifier(RendererModifier.OPERATOR in options.modifiers && isOperator, "operator")
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderSuspendModifier(functionDescriptor: FirMemberDeclaration) {
|
||||
@@ -178,15 +183,17 @@ internal class FirIdeRenderer private constructor(
|
||||
override fun visitProperty(property: FirProperty, data: StringBuilder) = with(data) {
|
||||
appendLine()
|
||||
appendTabs()
|
||||
renderAnnotations(property)
|
||||
renderVisibility(property.visibility)
|
||||
renderModifier(RendererModifier.CONST in options.modifiers && property.isConst, "const")
|
||||
renderMemberModifiers(property)
|
||||
renderModalityForCallable(property, containingDeclaration)
|
||||
renderOverride(property)
|
||||
renderModifier(RendererModifier.LATEINIT in options.modifiers && property.isLateInit, "lateinit")
|
||||
renderValVarPrefix(property)
|
||||
renderTypeParameters(property.typeParameters, true)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(property)
|
||||
renderVisibility(property.visibility)
|
||||
renderModifier(RendererModifier.CONST in options.modifiers && property.isConst, "const")
|
||||
renderMemberModifiers(property)
|
||||
renderModalityForCallable(property, containingDeclaration)
|
||||
renderOverride(property)
|
||||
renderModifier(RendererModifier.LATEINIT in options.modifiers && property.isLateInit, "lateinit")
|
||||
renderValVarPrefix(property)
|
||||
renderTypeParameters(property.typeParameters, true)
|
||||
}
|
||||
renderReceiver(property)
|
||||
|
||||
renderName(property)
|
||||
@@ -221,11 +228,13 @@ internal class FirIdeRenderer private constructor(
|
||||
with(data) {
|
||||
appendLine()
|
||||
appendTabs()
|
||||
renderAnnotations(propertyAccessor)
|
||||
renderVisibility(propertyAccessor.visibility)
|
||||
renderModalityForCallable(propertyAccessor, containingDeclaration)
|
||||
renderMemberModifiers(propertyAccessor)
|
||||
renderAdditionalModifiers(propertyAccessor)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(propertyAccessor)
|
||||
renderVisibility(propertyAccessor.visibility)
|
||||
renderModalityForCallable(propertyAccessor, containingDeclaration)
|
||||
renderMemberModifiers(propertyAccessor)
|
||||
renderAdditionalModifiers(propertyAccessor)
|
||||
}
|
||||
append(if (propertyAccessor.isGetter) "get" else "set")
|
||||
if (propertyAccessor.isSetter) {
|
||||
append("(value: ")
|
||||
@@ -248,15 +257,17 @@ internal class FirIdeRenderer private constructor(
|
||||
with(data) {
|
||||
appendLine()
|
||||
appendTabs()
|
||||
renderAnnotations(simpleFunction)
|
||||
renderVisibility(simpleFunction.visibility)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(simpleFunction)
|
||||
renderVisibility(simpleFunction.visibility)
|
||||
|
||||
renderModalityForCallable(simpleFunction, containingDeclaration)
|
||||
renderMemberModifiers(simpleFunction)
|
||||
renderOverride(simpleFunction)
|
||||
renderAdditionalModifiers(simpleFunction)
|
||||
append("fun ")
|
||||
renderTypeParameters(simpleFunction.typeParameters, true)
|
||||
renderModalityForCallable(simpleFunction, containingDeclaration)
|
||||
renderMemberModifiers(simpleFunction)
|
||||
renderOverride(simpleFunction)
|
||||
renderAdditionalModifiers(simpleFunction)
|
||||
append("fun ")
|
||||
renderTypeParameters(simpleFunction.typeParameters, true)
|
||||
}
|
||||
renderReceiver(simpleFunction)
|
||||
renderName(simpleFunction)
|
||||
renderValueParameters(simpleFunction.valueParameters)
|
||||
@@ -282,7 +293,9 @@ internal class FirIdeRenderer private constructor(
|
||||
appendLine()
|
||||
appendTabs()
|
||||
|
||||
renderAnnotations(anonymousObject)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(anonymousObject)
|
||||
}
|
||||
append(getClassifierKindPrefix(anonymousObject))
|
||||
renderSuperTypes(anonymousObject)
|
||||
}
|
||||
@@ -304,7 +317,9 @@ internal class FirIdeRenderer private constructor(
|
||||
check(containingClass is FirDeclaration && (containingClass is FirClass<*> || containingClass is FirEnumEntry)) {
|
||||
"Invalid renderer containing declaration for constructor"
|
||||
}
|
||||
renderAnnotations(constructor)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(constructor)
|
||||
}
|
||||
append("constructor")
|
||||
renderValueParameters(constructor.valueParameters)
|
||||
}
|
||||
@@ -325,25 +340,27 @@ internal class FirIdeRenderer private constructor(
|
||||
appendLine()
|
||||
appendTabs()
|
||||
|
||||
renderAnnotations(regularClass)
|
||||
if (regularClass.classKind != ClassKind.ENUM_ENTRY) {
|
||||
renderVisibility(regularClass.visibility)
|
||||
}
|
||||
|
||||
val haveNotModality = regularClass.classKind == ClassKind.INTERFACE && regularClass.modality == Modality.ABSTRACT ||
|
||||
regularClass.classKind.isSingleton && regularClass.modality == Modality.FINAL
|
||||
if (!haveNotModality) {
|
||||
regularClass.modality?.let {
|
||||
renderModality(it, regularClass.implicitModalityWithoutExtensions(containingDeclaration))
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(regularClass)
|
||||
if (regularClass.classKind != ClassKind.ENUM_ENTRY) {
|
||||
renderVisibility(regularClass.visibility)
|
||||
}
|
||||
|
||||
val haveNotModality = regularClass.classKind == ClassKind.INTERFACE && regularClass.modality == Modality.ABSTRACT ||
|
||||
regularClass.classKind.isSingleton && regularClass.modality == Modality.FINAL
|
||||
if (!haveNotModality) {
|
||||
regularClass.modality?.let {
|
||||
renderModality(it, regularClass.implicitModalityWithoutExtensions(containingDeclaration))
|
||||
}
|
||||
}
|
||||
renderMemberModifiers(regularClass)
|
||||
renderModifier(RendererModifier.INNER in options.modifiers && regularClass.isInner, "inner")
|
||||
renderModifier(RendererModifier.DATA in options.modifiers && regularClass.isData, "data")
|
||||
renderModifier(RendererModifier.INLINE in options.modifiers && regularClass.isInline, "inline")
|
||||
//TODO renderModifier(data, RendererModifier.VALUE in modifiers && regularClass.isValue, "value")
|
||||
renderModifier(RendererModifier.FUN in options.modifiers && regularClass.isFun, "fun")
|
||||
append(getClassifierKindPrefix(regularClass))
|
||||
}
|
||||
renderMemberModifiers(regularClass)
|
||||
renderModifier(RendererModifier.INNER in options.modifiers && regularClass.isInner, "inner")
|
||||
renderModifier(RendererModifier.DATA in options.modifiers && regularClass.isData, "data")
|
||||
renderModifier(RendererModifier.INLINE in options.modifiers && regularClass.isInline, "inline")
|
||||
//TODO renderModifier(data, RendererModifier.VALUE in modifiers && regularClass.isValue, "value")
|
||||
renderModifier(RendererModifier.FUN in options.modifiers && regularClass.isFun, "fun")
|
||||
append(getClassifierKindPrefix(regularClass))
|
||||
|
||||
if (!regularClass.isCompanion) {
|
||||
tabRightBySpace()
|
||||
@@ -459,10 +476,12 @@ internal class FirIdeRenderer private constructor(
|
||||
}
|
||||
|
||||
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: StringBuilder) = with(data) {
|
||||
renderAnnotations(typeAlias)
|
||||
renderVisibility(typeAlias.visibility)
|
||||
renderMemberModifiers(typeAlias)
|
||||
append("typealias").append(" ")
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(typeAlias)
|
||||
renderVisibility(typeAlias.visibility)
|
||||
renderMemberModifiers(typeAlias)
|
||||
append("typealias").append(" ")
|
||||
}
|
||||
renderName(typeAlias)
|
||||
renderTypeParameters(typeAlias.typeParameters, false)
|
||||
append(" = ").append(renderType(typeAlias.expandedTypeRef))
|
||||
@@ -561,7 +580,9 @@ internal class FirIdeRenderer private constructor(
|
||||
private fun StringBuilder.renderReceiver(firCallableDeclaration: FirCallableDeclaration<*>) {
|
||||
val receiverType = firCallableDeclaration.receiverTypeRef
|
||||
if (receiverType != null) {
|
||||
renderAnnotations(firCallableDeclaration)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(firCallableDeclaration)
|
||||
}
|
||||
|
||||
val needBrackets =
|
||||
typeIdeRenderer.shouldRenderAsPrettyFunctionType(receiverType.coneType) && receiverType.isMarkedNullable == true
|
||||
@@ -601,14 +622,18 @@ internal class FirIdeRenderer private constructor(
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderValueParameter(valueParameter: FirValueParameter) {
|
||||
renderAnnotations(valueParameter)
|
||||
if (options.renderDeclarationHeader) {
|
||||
renderAnnotations(valueParameter)
|
||||
}
|
||||
renderModifier(valueParameter.isCrossinline, "crossinline")
|
||||
renderModifier(valueParameter.isNoinline, "noinline")
|
||||
renderVariable(valueParameter)
|
||||
|
||||
val withDefaultValue = valueParameter.defaultValue != null //TODO check if default value is inherited
|
||||
if (withDefaultValue) {
|
||||
append(" = ...")
|
||||
if (options.renderDefaultParameterValue) {
|
||||
val withDefaultValue = valueParameter.defaultValue != null //TODO check if default value is inherited
|
||||
if (withDefaultValue) {
|
||||
append(" = ...")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +649,12 @@ internal class FirIdeRenderer private constructor(
|
||||
renderModifier(isVarArg, "vararg")
|
||||
renderName(variable)
|
||||
append(": ")
|
||||
append(renderType(typeToRender))
|
||||
val parameterType = typeToRender.coneType
|
||||
if (isVarArg) {
|
||||
append(renderType(parameterType.arrayElementType() ?: parameterType, typeToRender.annotations))
|
||||
} else {
|
||||
append(renderType(typeToRender))
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.renderSuperTypes(klass: FirClass<*>) {
|
||||
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.frontend.api.fir.symbols
|
||||
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getImplementationStatus
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isVisibleInClass
|
||||
import org.jetbrains.kotlin.fir.containingClass
|
||||
import org.jetbrains.kotlin.fir.declarations.FirCallableMemberDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.FirClass
|
||||
import org.jetbrains.kotlin.fir.originalForIntersectionOverrideAttr
|
||||
import org.jetbrains.kotlin.fir.originalForSubstitutionOverride
|
||||
import org.jetbrains.kotlin.fir.resolve.ScopeSession
|
||||
import org.jetbrains.kotlin.fir.resolve.SessionHolderImpl
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData
|
||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtOverrideInfoProvider
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
||||
import org.jetbrains.kotlin.idea.frontend.api.fir.buildSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtCallableSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtClassOrObjectSymbol
|
||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||
import org.jetbrains.kotlin.util.ImplementationStatus
|
||||
|
||||
class KtFirOverrideInfoProvider(
|
||||
override val analysisSession: KtAnalysisSession,
|
||||
override val token: ValidityToken,
|
||||
) : KtOverrideInfoProvider() {
|
||||
|
||||
private val firAnalysisSession = analysisSession as KtFirAnalysisSession
|
||||
|
||||
override fun isVisible(memberSymbol: KtCallableSymbol, classSymbol: KtClassOrObjectSymbol): Boolean {
|
||||
require(memberSymbol is KtFirSymbol<*>)
|
||||
require(classSymbol is KtFirSymbol<*>)
|
||||
return memberSymbol.firRef.withFir { memberFir ->
|
||||
if (memberFir !is FirCallableMemberDeclaration<*>) return@withFir false
|
||||
classSymbol.firRef.withFir inner@{ parentClassFir ->
|
||||
if (parentClassFir !is FirClass<*>) return@inner false
|
||||
memberFir.isVisibleInClass(parentClassFir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getImplementationStatus(memberSymbol: KtCallableSymbol, parentClassSymbol: KtClassOrObjectSymbol): ImplementationStatus? {
|
||||
require(memberSymbol is KtFirSymbol<*>)
|
||||
require(parentClassSymbol is KtFirSymbol<*>)
|
||||
return memberSymbol.firRef.withFir { memberFir ->
|
||||
if (memberFir !is FirCallableMemberDeclaration<*>) return@withFir null
|
||||
parentClassSymbol.firRef.withFir inner@{ parentClassFir ->
|
||||
if (parentClassFir !is FirClass<*>) return@inner null
|
||||
memberFir.getImplementationStatus(SessionHolderImpl(firAnalysisSession.rootModuleSession, ScopeSession()), parentClassFir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOriginalContainingClassForOverride(symbol: KtCallableSymbol): KtClassOrObjectSymbol? {
|
||||
require(symbol is KtFirSymbol<*>)
|
||||
return symbol.firRef.withFir { firDeclaration ->
|
||||
if (firDeclaration !is FirCallableMemberDeclaration<*>) return@withFir null
|
||||
with(analysisSession) {
|
||||
getOriginalOverriddenSymbol(firDeclaration)?.containingClass()?.classId?.getCorrespondingToplevelClassOrObjectSymbol()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOriginalOverriddenSymbol(symbol: KtCallableSymbol): KtCallableSymbol? {
|
||||
require(symbol is KtFirSymbol<*>)
|
||||
return symbol.firRef.withFir { firDeclaration ->
|
||||
if (firDeclaration !is FirCallableMemberDeclaration<*>) return@withFir null
|
||||
with(analysisSession) {
|
||||
getOriginalOverriddenSymbol(firDeclaration)
|
||||
?.buildSymbol((analysisSession as KtFirAnalysisSession).firSymbolBuilder) as KtCallableSymbol?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOriginalOverriddenSymbol(member: FirCallableMemberDeclaration<*>): FirCallableMemberDeclaration<*>? {
|
||||
val originalForSubstitutionOverride = member.originalForSubstitutionOverride
|
||||
if (originalForSubstitutionOverride != null) return getOriginalOverriddenSymbol(originalForSubstitutionOverride)
|
||||
|
||||
val originalForIntersectionOverrideAttr = member.originalForIntersectionOverrideAttr
|
||||
if (originalForIntersectionOverrideAttr != null) return getOriginalOverriddenSymbol(originalForIntersectionOverrideAttr)
|
||||
|
||||
val delegatedWrapperData = member.delegatedWrapperData
|
||||
if (delegatedWrapperData != null) return getOriginalOverriddenSymbol(delegatedWrapperData.wrapped)
|
||||
|
||||
return member
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE) annotation class base
|
||||
|
||||
fun foo1(vararg ints: Int) {}
|
||||
fun foo2(@base vararg ints: @base Int) {}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
@Target(allowedTargets = NOT_CONST_EXPRESSION) annotation class base
|
||||
fun foo1(vararg ints: Int)
|
||||
fun foo2(@base vararg ints: @base Int)
|
||||
+5
@@ -154,6 +154,11 @@ public class RendererTestGenerated extends AbstractRendererTest {
|
||||
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/typeParameters.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("vararg.kt")
|
||||
public void testVararg() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/vararg.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("where.kt")
|
||||
public void testWhere() throws Exception {
|
||||
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt");
|
||||
|
||||
+2
-13
@@ -1,17 +1,6 @@
|
||||
/*
|
||||
* 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.
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea;
|
||||
+2
-3
@@ -28,7 +28,7 @@ abstract class AbstractGenerateMembersHandler<T : ClassMember> : LanguageCodeIns
|
||||
|
||||
protected abstract fun getNoMembersFoundHint(): String
|
||||
|
||||
protected abstract fun generateMembers(
|
||||
abstract fun generateMembers(
|
||||
editor: Editor,
|
||||
classOrObject: KtClassOrObject,
|
||||
selectedElements: Collection<T>,
|
||||
@@ -74,8 +74,7 @@ abstract class AbstractGenerateMembersHandler<T : ClassMember> : LanguageCodeIns
|
||||
}
|
||||
|
||||
val copyDoc: Boolean
|
||||
val selectedElements: Collection<T
|
||||
>
|
||||
val selectedElements: Collection<T>
|
||||
if (implementAll) {
|
||||
selectedElements = members
|
||||
copyDoc = false
|
||||
|
||||
+4
-1
@@ -13,4 +13,7 @@ sealed class BodyType(val requiresReturn: Boolean = true) {
|
||||
object QUALIFIED_SUPER : BodyType()
|
||||
|
||||
class Delegate(val receiverName: String) : BodyType()
|
||||
}
|
||||
|
||||
fun effectiveBodyType(canBeEmpty: Boolean): BodyType = if (!canBeEmpty && this == EMPTY_OR_TEMPLATE) FROM_TEMPLATE else this
|
||||
}
|
||||
|
||||
|
||||
@@ -144,6 +144,8 @@ The Kotlin FIR plugin provides language support in IntelliJ IDEA and Android Stu
|
||||
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinOutOfBlockPsiTreeChangePreprocessor"/>
|
||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.fir.api.impl.ResolveAllowanceServiceFirImpl"
|
||||
serviceInterface="org.jetbrains.kotlin.idea.api.ResolveAllowanceService"/>
|
||||
<codeInsight.implementMethod language="kotlin" implementationClass="org.jetbrains.kotlin.idea.core.overrideImplement.KtImplementMembersHandler"/>
|
||||
<codeInsight.overrideMethod language="kotlin" implementationClass="org.jetbrains.kotlin.idea.core.overrideImplement.KtOverrideMembersHandler"/>
|
||||
</extensions>
|
||||
|
||||
<!-- scripts -->
|
||||
|
||||
Reference in New Issue
Block a user