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:
Tianyu Geng
2021-04-13 21:42:56 -07:00
committed by TeamCityServer
parent 34387e228f
commit 00031c8eb1
32 changed files with 1454 additions and 231 deletions
@@ -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()
@@ -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
}
@@ -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
@@ -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
)
}
@@ -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>>)
}
@@ -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))
}
}
}
}
@@ -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")
}
@@ -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 {
@@ -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
}
@@ -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)
}
@@ -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 {
@@ -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)
}
@@ -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>
}
@@ -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
}
}
}
@@ -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)
@@ -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)
@@ -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)
@@ -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)
}
@@ -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 }
}
@@ -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<*>) {
@@ -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) {}
@@ -0,0 +1,3 @@
@Target(allowedTargets = NOT_CONST_EXPRESSION) annotation class base
fun foo1(vararg ints: Int)
fun foo2(@base vararg ints: @base Int)
@@ -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");
@@ -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;
@@ -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
@@ -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
}
+2
View File
@@ -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 -->