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.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||||
import org.jetbrains.kotlin.descriptors.Visibility
|
import org.jetbrains.kotlin.descriptors.Visibility
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.FirSymbolOwner
|
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.modalityModifier
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.modalityModifier
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.overrideModifier
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.overrideModifier
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.visibilityModifier
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.visibilityModifier
|
||||||
import org.jetbrains.kotlin.fir.analysis.getChild
|
import org.jetbrains.kotlin.fir.analysis.getChild
|
||||||
import org.jetbrains.kotlin.fir.containingClass
|
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirComponentCall
|
import org.jetbrains.kotlin.fir.expressions.FirComponentCall
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
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.FirQualifiedAccessExpression
|
||||||
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
|
import org.jetbrains.kotlin.fir.expressions.impl.FirEmptyExpressionBlock
|
||||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
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.fullyExpandedType
|
||||||
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
import org.jetbrains.kotlin.fir.resolve.inference.isBuiltinFunctionalType
|
||||||
import org.jetbrains.kotlin.fir.resolve.symbolProvider
|
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.ProcessorAction
|
||||||
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
|
import org.jetbrains.kotlin.fir.scopes.processOverriddenFunctions
|
||||||
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
import org.jetbrains.kotlin.fir.scopes.unsubstitutedScope
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
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.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
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.Variance
|
||||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||||
import org.jetbrains.kotlin.types.model.TypeCheckerProviderContext
|
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
|
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||||
|
|
||||||
private val INLINE_ONLY_ANNOTATION_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.InlineOnly"))
|
private val INLINE_ONLY_ANNOTATION_CLASS_ID = ClassId.topLevel(FqName("kotlin.internal.InlineOnly"))
|
||||||
@@ -428,3 +425,107 @@ fun isSubtypeOfForFunctionalTypeReturningUnit(context: ConeInferenceContext, sub
|
|||||||
}
|
}
|
||||||
return false
|
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.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
|
||||||
import org.jetbrains.kotlin.fir.*
|
import org.jetbrains.kotlin.fir.analysis.checkers.*
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
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.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED
|
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
|
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.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.FirErrors.OVERRIDING_FINAL_MEMBER_BY_DELEGATION
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
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.declarations.*
|
||||||
|
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverridePropertySymbol
|
import org.jetbrains.kotlin.util.ImplementationStatus
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
|
||||||
import org.jetbrains.kotlin.fir.types.isNullableAny
|
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
||||||
|
|
||||||
object FirNotImplementedOverrideChecker : FirClassChecker() {
|
object FirNotImplementedOverrideChecker : FirClassChecker() {
|
||||||
|
|
||||||
@@ -51,115 +47,24 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
|||||||
val notImplementedSymbols = mutableListOf<FirCallableSymbol<*>>()
|
val notImplementedSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||||
val notImplementedIntersectionSymbols = mutableListOf<FirCallableSymbol<*>>()
|
val notImplementedIntersectionSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||||
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
|
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||||
val classPackage = declaration.symbol.classId.packageFqName
|
|
||||||
|
|
||||||
fun FirCallableMemberDeclaration<*>.isInvisible(): Boolean {
|
fun collectSymbol(symbol: FirCallableSymbol<*>) {
|
||||||
if (visibility == Visibilities.Private ||
|
val fir = symbol.fir as? FirCallableMemberDeclaration<*> ?: return
|
||||||
!visibility.visibleFromPackage(classPackage, symbol.callableId.packageName)
|
when (fir.getImplementationStatus(context.sessionHolder, declaration)) {
|
||||||
) return true
|
ImplementationStatus.AMBIGUOUSLY_INHERITED -> notImplementedIntersectionSymbols.add(symbol)
|
||||||
if (visibility == Visibilities.Internal &&
|
ImplementationStatus.NOT_IMPLEMENTED -> when {
|
||||||
declarationSiteSession !== declaration.declarationSiteSession
|
fir.isVisibleInClass(declaration) -> notImplementedSymbols.add(symbol)
|
||||||
) return true
|
else -> invisibleSymbols.add(symbol)
|
||||||
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++
|
|
||||||
}
|
}
|
||||||
if (nonAbstractCountInClass + nonAbstractCountInInterface > 1) {
|
else -> {
|
||||||
return true
|
// nothing to do
|
||||||
}
|
|
||||||
if (nonAbstractCountInInterface > 0 && abstractCountInInterface > 0) {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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()) {
|
for (name in classScope.getCallableNames()) {
|
||||||
classScope.processFunctionsByName(name) { namedFunctionSymbol ->
|
classScope.processFunctionsByName(name, ::collectSymbol)
|
||||||
val simpleFunction = namedFunctionSymbol.fir
|
classScope.processPropertiesByName(name, ::collectSymbol)
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notImplementedSymbols.isNotEmpty()) {
|
if (notImplementedSymbols.isNotEmpty()) {
|
||||||
@@ -237,19 +142,4 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
|||||||
|
|
||||||
private fun FirCallableDeclaration<*>.isFromInterfaceOrEnum(context: CheckerContext): Boolean =
|
private fun FirCallableDeclaration<*>.isFromInterfaceOrEnum(context: CheckerContext): Boolean =
|
||||||
(getContainingClass(context) as? FirRegularClass)?.let { it.isInterface || it.isEnumClass } == true
|
(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.modality get() = status.modality
|
||||||
inline val FirMemberDeclaration.isAbstract get() = status.modality == Modality.ABSTRACT
|
inline val FirMemberDeclaration.isAbstract get() = status.modality == Modality.ABSTRACT
|
||||||
inline val FirMemberDeclaration.isOpen get() = status.modality == Modality.OPEN
|
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.visibility: Visibility get() = status.visibility
|
||||||
inline val FirMemberDeclaration.effectiveVisibility: EffectiveVisibility
|
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 com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
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.caches.resolve.resolveToDescriptorIfAny
|
||||||
|
import org.jetbrains.kotlin.idea.core.util.KotlinIdeaCoreBundle
|
||||||
import org.jetbrains.kotlin.idea.util.expectedDescriptors
|
import org.jetbrains.kotlin.idea.util.expectedDescriptors
|
||||||
import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor
|
import org.jetbrains.kotlin.js.descriptorUtils.hasPrimaryConstructor
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
import org.jetbrains.kotlin.psi.KtClass
|
||||||
@@ -37,18 +37,18 @@ open class ImplementMembersHandler : GenerateMembersHandler(), IntentionAction {
|
|||||||
.map { OverrideMemberChooserObject.create(project, it, it, BodyType.FROM_TEMPLATE) }
|
.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 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)
|
override fun isAvailable(project: Project, editor: Editor, file: PsiFile) = isValidFor(editor, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImplementAsConstructorParameter : ImplementMembersHandler() {
|
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 {
|
override fun isValidForClass(classOrObject: KtClassOrObject): Boolean {
|
||||||
if (classOrObject !is KtClass || classOrObject is KtEnumEntry || classOrObject.isInterface()) return false
|
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(
|
fun generateUnsupportedOrSuperCall(
|
||||||
project: Project,
|
project: Project,
|
||||||
descriptor: CallableMemberDescriptor,
|
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.idea.frontend.api.symbols.KtCallableSymbol
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.KtNamedSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
||||||
import org.jetbrains.kotlin.idea.refactoring.CHECK_SUPER_METHODS_YES_NO_DIALOG
|
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")) {
|
val analyzeResult = analyseInModalWindow(declaration, KotlinBundle.message("find.usages.progress.text.declaration.superMethods")) {
|
||||||
(declaration.getSymbol() as? KtCallableSymbol)?.let { callableSymbol ->
|
(declaration.getSymbol() as? KtCallableSymbol)?.let { callableSymbol ->
|
||||||
((callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol)?.let { containingClass ->
|
callableSymbol.originalContainingClassForOverride?.let { containingClass ->
|
||||||
val overriddenSymbols = callableSymbol.getAllOverriddenSymbols()
|
val overriddenSymbols = callableSymbol.getAllOverriddenSymbols()
|
||||||
|
|
||||||
val renderToPsi = overriddenSymbols.mapNotNull {
|
val renderToPsi = overriddenSymbols.mapNotNull {
|
||||||
it.psi?.let { psi ->
|
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
|
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.KtQuickFixRegistrar
|
||||||
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesList
|
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesList
|
||||||
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
|
import org.jetbrains.kotlin.idea.fir.api.fixes.KtQuickFixesListBuilder
|
||||||
@@ -78,6 +79,10 @@ class MainKtQuickFixRegistrar : KtQuickFixRegistrar() {
|
|||||||
registerApplicator(ChangeTypeQuickFix.changeFunctionReturnTypeOnOverride)
|
registerApplicator(ChangeTypeQuickFix.changeFunctionReturnTypeOnOverride)
|
||||||
registerApplicator(ChangeTypeQuickFix.changePropertyReturnTypeOnOverride)
|
registerApplicator(ChangeTypeQuickFix.changePropertyReturnTypeOnOverride)
|
||||||
registerApplicator(ChangeTypeQuickFix.changeVariableReturnTypeOnOverride)
|
registerApplicator(ChangeTypeQuickFix.changeVariableReturnTypeOnOverride)
|
||||||
|
registerApplicator(MemberNotImplementedQuickfixFactories.abstractMemberNotImplemented)
|
||||||
|
registerApplicator(MemberNotImplementedQuickfixFactories.abstractClassMemberNotImplemented)
|
||||||
|
registerApplicator(MemberNotImplementedQuickfixFactories.manyInterfacesMemberNotImplemented)
|
||||||
|
registerApplicator(MemberNotImplementedQuickfixFactories.manyImplMemberNotImplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val mutability = KtQuickFixesListBuilder.registerPsiQuickFix {
|
private val mutability = KtQuickFixesListBuilder.registerPsiQuickFix {
|
||||||
|
|||||||
+6
-1
@@ -41,7 +41,9 @@ abstract class KtAnalysisSession(final override val token: ValidityToken) : Vali
|
|||||||
KtReferenceResolveMixIn,
|
KtReferenceResolveMixIn,
|
||||||
KtReferenceShortenerMixIn,
|
KtReferenceShortenerMixIn,
|
||||||
KtSymbolDeclarationRendererMixIn,
|
KtSymbolDeclarationRendererMixIn,
|
||||||
KtVisibilityCheckerMixIn {
|
KtVisibilityCheckerMixIn,
|
||||||
|
KtMemberSymbolProviderMixin
|
||||||
|
{
|
||||||
|
|
||||||
override val analysisSession: KtAnalysisSession get() = this
|
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
|
internal val visibilityChecker: KtVisibilityChecker get() = visibilityCheckerImpl
|
||||||
protected abstract val visibilityCheckerImpl: KtVisibilityChecker
|
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.symbols.KtSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
|
import javax.swing.Icon
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KtType to string renderer options
|
* KtType to string renderer options
|
||||||
@@ -24,6 +25,8 @@ data class KtTypeRendererOptions(
|
|||||||
* @sample Function0<Int> returns () -> Int
|
* @sample Function0<Int> returns () -> Int
|
||||||
*/
|
*/
|
||||||
val renderFunctionType: Boolean = true,
|
val renderFunctionType: Boolean = true,
|
||||||
|
|
||||||
|
val renderAnnotations: Boolean = true,
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
val DEFAULT = KtTypeRendererOptions()
|
val DEFAULT = KtTypeRendererOptions()
|
||||||
@@ -63,6 +66,19 @@ data class KtDeclarationRendererOptions(
|
|||||||
* Approximate Kotlin not-denotable types into denotable for declarations return type
|
* Approximate Kotlin not-denotable types into denotable for declarations return type
|
||||||
*/
|
*/
|
||||||
val approximateTypes: Boolean = false,
|
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 {
|
companion object {
|
||||||
val DEFAULT = KtDeclarationRendererOptions()
|
val DEFAULT = KtDeclarationRendererOptions()
|
||||||
@@ -82,7 +98,8 @@ enum class RendererModifier(val includeByDefault: Boolean) {
|
|||||||
CONST(true),
|
CONST(true),
|
||||||
LATEINIT(true),
|
LATEINIT(true),
|
||||||
FUN(true),
|
FUN(true),
|
||||||
VALUE(true)
|
VALUE(true),
|
||||||
|
OPERATOR(true)
|
||||||
;
|
;
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
+2
@@ -38,6 +38,7 @@ interface KtTypeInfoProviderMixIn : KtAnalysisSessionMixIn {
|
|||||||
val KtType.isChar: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR)
|
val KtType.isChar: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.CHAR)
|
||||||
val KtType.isBoolean: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.BOOLEAN)
|
val KtType.isBoolean: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.BOOLEAN)
|
||||||
val KtType.isString: Boolean get() = isClassTypeWithClassId(DefaultTypeClassIds.STRING)
|
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.isUInt: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uInt)
|
||||||
val KtType.isULong: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uLong)
|
val KtType.isULong: Boolean get() = isClassTypeWithClassId(StandardNames.FqNames.uLong)
|
||||||
@@ -84,5 +85,6 @@ object DefaultTypeClassIds {
|
|||||||
val CHAR = ClassId.topLevel(StandardNames.FqNames._char.toSafe())
|
val CHAR = ClassId.topLevel(StandardNames.FqNames._char.toSafe())
|
||||||
val BOOLEAN = ClassId.topLevel(StandardNames.FqNames._boolean.toSafe())
|
val BOOLEAN = ClassId.topLevel(StandardNames.FqNames._boolean.toSafe())
|
||||||
val STRING = ClassId.topLevel(StandardNames.FqNames.string.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)
|
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
|
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.markers.KtTypedSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.pointers.KtSymbolPointer
|
||||||
import org.jetbrains.kotlin.name.CallableId
|
import org.jetbrains.kotlin.name.CallableId
|
||||||
|
|
||||||
abstract class KtCallableSymbol : KtSymbol, KtTypedSymbol {
|
abstract class KtCallableSymbol : KtSymbol, KtTypedSymbol, KtSymbolWithKind {
|
||||||
abstract val callableIdIfNonLocal: CallableId?
|
abstract val callableIdIfNonLocal: CallableId?
|
||||||
abstract override fun createPointer(): KtSymbolPointer<KtCallableSymbol>
|
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.InvalidWayOfUsingAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
import org.jetbrains.kotlin.idea.frontend.api.KtAnalysisSession
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.components.KtVisibilityChecker
|
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.components.KtSymbolDeclarationRendererProvider
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.components.*
|
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.symbols.KtFirSymbolProvider
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.EnclosingDeclarationContext
|
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.recordCompletionContext
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.utils.threadLocal
|
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.KtElement
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
|
||||||
@@ -62,6 +63,8 @@ private constructor(
|
|||||||
|
|
||||||
override val expressionInfoProviderImpl = KtFirExpressionInfoProvider(this, token)
|
override val expressionInfoProviderImpl = KtFirExpressionInfoProvider(this, token)
|
||||||
|
|
||||||
|
override val overrideInfoProviderImpl = KtFirOverrideInfoProvider(this, token)
|
||||||
|
|
||||||
override val visibilityCheckerImpl: KtVisibilityChecker = KtFirVisibilityChecker(this, token)
|
override val visibilityCheckerImpl: KtVisibilityChecker = KtFirVisibilityChecker(this, token)
|
||||||
|
|
||||||
override val typeProviderImpl = KtFirTypeProvider(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.*
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind
|
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolKind
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
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.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtObjectLiteralExpression
|
import org.jetbrains.kotlin.psi.KtObjectLiteralExpression
|
||||||
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
|
import org.jetbrains.kotlin.psi.KtPrimaryConstructor
|
||||||
@@ -28,6 +27,14 @@ internal class KtFirSymbolContainingDeclarationProvider(
|
|||||||
override fun getContainingDeclaration(symbol: KtSymbolWithKind): KtSymbolWithKind? {
|
override fun getContainingDeclaration(symbol: KtSymbolWithKind): KtSymbolWithKind? {
|
||||||
if (symbol is KtPackageSymbol) return null
|
if (symbol is KtPackageSymbol) return null
|
||||||
if (symbol.symbolKind == KtSymbolKind.TOP_LEVEL) 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) {
|
return when (symbol.origin) {
|
||||||
KtSymbolOrigin.SOURCE, KtSymbolOrigin.SOURCE_MEMBER_GENERATED ->
|
KtSymbolOrigin.SOURCE, KtSymbolOrigin.SOURCE_MEMBER_GENERATED ->
|
||||||
getContainingDeclarationForKotlinInSourceSymbol(symbol)
|
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.FirCallableSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverridePropertySymbol
|
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.components.KtSymbolDeclarationOverridesProvider
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.KtFirAnalysisSession
|
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.KtFirNamedClassOrObjectSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
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.KtCallableSymbol
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.markers.KtSymbolWithKind
|
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(
|
internal class KtFirSymbolDeclarationOverridesProvider(
|
||||||
override val analysisSession: KtFirAnalysisSession,
|
override val analysisSession: KtFirAnalysisSession,
|
||||||
@@ -90,7 +91,7 @@ internal class KtFirSymbolDeclarationOverridesProvider(
|
|||||||
) {
|
) {
|
||||||
require(callableSymbol is KtFirSymbol<*>)
|
require(callableSymbol is KtFirSymbol<*>)
|
||||||
val containingDeclaration = with(analysisSession) {
|
val containingDeclaration = with(analysisSession) {
|
||||||
(callableSymbol as? KtSymbolWithKind)?.getContainingSymbol() as? KtClassOrObjectSymbol
|
(callableSymbol as? KtCallableSymbol)?.originalContainingClassForOverride
|
||||||
} ?: return
|
} ?: return
|
||||||
check(containingDeclaration is KtFirNamedClassOrObjectSymbol)
|
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.renderer.FirIdeRenderer
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.fir.symbols.KtFirSymbol
|
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.fir.types.KtFirType
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.symbols.KtSymbol
|
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.markers.*
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
import org.jetbrains.kotlin.idea.frontend.api.tokens.ValidityToken
|
||||||
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
import org.jetbrains.kotlin.idea.frontend.api.types.KtType
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ internal class KtFirSymbolDeclarationRendererProvider(
|
|||||||
val phaseNeeded =
|
val phaseNeeded =
|
||||||
if (options.renderContainingDeclarations) FirResolvePhase.BODY_RESOLVE else FirResolvePhase.IMPLICIT_TYPES_BODY_RESOLVE
|
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 }
|
val containingFir = containingSymbol?.firRef?.withFirUnsafe { it }
|
||||||
FirIdeRenderer.render(fir, containingFir, options, fir.declarationSiteSession)
|
FirIdeRenderer.render(fir, containingFir, options, fir.declarationSiteSession)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -36,7 +36,7 @@ internal class ConeTypeIdeRenderer(
|
|||||||
private var filterExtensionFunctionType: Boolean = false
|
private var filterExtensionFunctionType: Boolean = false
|
||||||
|
|
||||||
private fun StringBuilder.renderAnnotationList(annotations: List<FirAnnotationCall>?) {
|
private fun StringBuilder.renderAnnotationList(annotations: List<FirAnnotationCall>?) {
|
||||||
if (annotations != null) {
|
if (annotations != null && options.renderAnnotations) {
|
||||||
val filteredExtensionIfNeeded = annotations.applyIf(filterExtensionFunctionType) {
|
val filteredExtensionIfNeeded = annotations.applyIf(filterExtensionFunctionType) {
|
||||||
annotations.filterNot { it.toAnnotationClassId() == StandardClassIds.extensionFunctionType }
|
annotations.filterNot { it.toAnnotationClassId() == StandardClassIds.extensionFunctionType }
|
||||||
}
|
}
|
||||||
|
|||||||
+84
-54
@@ -50,7 +50,12 @@ internal class FirIdeRenderer private constructor(
|
|||||||
val approximatedIfNeeded = approximate.ifTrue {
|
val approximatedIfNeeded = approximate.ifTrue {
|
||||||
PublicTypeApproximator.approximateTypeToPublicDenotable(firRef.coneType, session)
|
PublicTypeApproximator.approximateTypeToPublicDenotable(firRef.coneType, session)
|
||||||
} ?: firRef.coneType
|
} ?: 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) {
|
private fun StringBuilder.renderName(declaration: FirDeclaration) {
|
||||||
@@ -133,7 +138,7 @@ internal class FirIdeRenderer private constructor(
|
|||||||
|
|
||||||
private fun StringBuilder.renderOverride(callableMember: FirCallableMemberDeclaration<*>) {
|
private fun StringBuilder.renderOverride(callableMember: FirCallableMemberDeclaration<*>) {
|
||||||
if (RendererModifier.OVERRIDE !in options.modifiers) return
|
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) {
|
private fun StringBuilder.renderModifier(value: Boolean, modifier: String) {
|
||||||
@@ -159,7 +164,7 @@ internal class FirIdeRenderer private constructor(
|
|||||||
renderSuspendModifier(firMember)
|
renderSuspendModifier(firMember)
|
||||||
renderModifier(firMember.isInline, "inline")
|
renderModifier(firMember.isInline, "inline")
|
||||||
renderModifier(isInfix, "infix")
|
renderModifier(isInfix, "infix")
|
||||||
renderModifier(isOperator, "operator")
|
renderModifier(RendererModifier.OPERATOR in options.modifiers && isOperator, "operator")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun StringBuilder.renderSuspendModifier(functionDescriptor: FirMemberDeclaration) {
|
private fun StringBuilder.renderSuspendModifier(functionDescriptor: FirMemberDeclaration) {
|
||||||
@@ -178,15 +183,17 @@ internal class FirIdeRenderer private constructor(
|
|||||||
override fun visitProperty(property: FirProperty, data: StringBuilder) = with(data) {
|
override fun visitProperty(property: FirProperty, data: StringBuilder) = with(data) {
|
||||||
appendLine()
|
appendLine()
|
||||||
appendTabs()
|
appendTabs()
|
||||||
renderAnnotations(property)
|
if (options.renderDeclarationHeader) {
|
||||||
renderVisibility(property.visibility)
|
renderAnnotations(property)
|
||||||
renderModifier(RendererModifier.CONST in options.modifiers && property.isConst, "const")
|
renderVisibility(property.visibility)
|
||||||
renderMemberModifiers(property)
|
renderModifier(RendererModifier.CONST in options.modifiers && property.isConst, "const")
|
||||||
renderModalityForCallable(property, containingDeclaration)
|
renderMemberModifiers(property)
|
||||||
renderOverride(property)
|
renderModalityForCallable(property, containingDeclaration)
|
||||||
renderModifier(RendererModifier.LATEINIT in options.modifiers && property.isLateInit, "lateinit")
|
renderOverride(property)
|
||||||
renderValVarPrefix(property)
|
renderModifier(RendererModifier.LATEINIT in options.modifiers && property.isLateInit, "lateinit")
|
||||||
renderTypeParameters(property.typeParameters, true)
|
renderValVarPrefix(property)
|
||||||
|
renderTypeParameters(property.typeParameters, true)
|
||||||
|
}
|
||||||
renderReceiver(property)
|
renderReceiver(property)
|
||||||
|
|
||||||
renderName(property)
|
renderName(property)
|
||||||
@@ -221,11 +228,13 @@ internal class FirIdeRenderer private constructor(
|
|||||||
with(data) {
|
with(data) {
|
||||||
appendLine()
|
appendLine()
|
||||||
appendTabs()
|
appendTabs()
|
||||||
renderAnnotations(propertyAccessor)
|
if (options.renderDeclarationHeader) {
|
||||||
renderVisibility(propertyAccessor.visibility)
|
renderAnnotations(propertyAccessor)
|
||||||
renderModalityForCallable(propertyAccessor, containingDeclaration)
|
renderVisibility(propertyAccessor.visibility)
|
||||||
renderMemberModifiers(propertyAccessor)
|
renderModalityForCallable(propertyAccessor, containingDeclaration)
|
||||||
renderAdditionalModifiers(propertyAccessor)
|
renderMemberModifiers(propertyAccessor)
|
||||||
|
renderAdditionalModifiers(propertyAccessor)
|
||||||
|
}
|
||||||
append(if (propertyAccessor.isGetter) "get" else "set")
|
append(if (propertyAccessor.isGetter) "get" else "set")
|
||||||
if (propertyAccessor.isSetter) {
|
if (propertyAccessor.isSetter) {
|
||||||
append("(value: ")
|
append("(value: ")
|
||||||
@@ -248,15 +257,17 @@ internal class FirIdeRenderer private constructor(
|
|||||||
with(data) {
|
with(data) {
|
||||||
appendLine()
|
appendLine()
|
||||||
appendTabs()
|
appendTabs()
|
||||||
renderAnnotations(simpleFunction)
|
if (options.renderDeclarationHeader) {
|
||||||
renderVisibility(simpleFunction.visibility)
|
renderAnnotations(simpleFunction)
|
||||||
|
renderVisibility(simpleFunction.visibility)
|
||||||
|
|
||||||
renderModalityForCallable(simpleFunction, containingDeclaration)
|
renderModalityForCallable(simpleFunction, containingDeclaration)
|
||||||
renderMemberModifiers(simpleFunction)
|
renderMemberModifiers(simpleFunction)
|
||||||
renderOverride(simpleFunction)
|
renderOverride(simpleFunction)
|
||||||
renderAdditionalModifiers(simpleFunction)
|
renderAdditionalModifiers(simpleFunction)
|
||||||
append("fun ")
|
append("fun ")
|
||||||
renderTypeParameters(simpleFunction.typeParameters, true)
|
renderTypeParameters(simpleFunction.typeParameters, true)
|
||||||
|
}
|
||||||
renderReceiver(simpleFunction)
|
renderReceiver(simpleFunction)
|
||||||
renderName(simpleFunction)
|
renderName(simpleFunction)
|
||||||
renderValueParameters(simpleFunction.valueParameters)
|
renderValueParameters(simpleFunction.valueParameters)
|
||||||
@@ -282,7 +293,9 @@ internal class FirIdeRenderer private constructor(
|
|||||||
appendLine()
|
appendLine()
|
||||||
appendTabs()
|
appendTabs()
|
||||||
|
|
||||||
renderAnnotations(anonymousObject)
|
if (options.renderDeclarationHeader) {
|
||||||
|
renderAnnotations(anonymousObject)
|
||||||
|
}
|
||||||
append(getClassifierKindPrefix(anonymousObject))
|
append(getClassifierKindPrefix(anonymousObject))
|
||||||
renderSuperTypes(anonymousObject)
|
renderSuperTypes(anonymousObject)
|
||||||
}
|
}
|
||||||
@@ -304,7 +317,9 @@ internal class FirIdeRenderer private constructor(
|
|||||||
check(containingClass is FirDeclaration && (containingClass is FirClass<*> || containingClass is FirEnumEntry)) {
|
check(containingClass is FirDeclaration && (containingClass is FirClass<*> || containingClass is FirEnumEntry)) {
|
||||||
"Invalid renderer containing declaration for constructor"
|
"Invalid renderer containing declaration for constructor"
|
||||||
}
|
}
|
||||||
renderAnnotations(constructor)
|
if (options.renderDeclarationHeader) {
|
||||||
|
renderAnnotations(constructor)
|
||||||
|
}
|
||||||
append("constructor")
|
append("constructor")
|
||||||
renderValueParameters(constructor.valueParameters)
|
renderValueParameters(constructor.valueParameters)
|
||||||
}
|
}
|
||||||
@@ -325,25 +340,27 @@ internal class FirIdeRenderer private constructor(
|
|||||||
appendLine()
|
appendLine()
|
||||||
appendTabs()
|
appendTabs()
|
||||||
|
|
||||||
renderAnnotations(regularClass)
|
if (options.renderDeclarationHeader) {
|
||||||
if (regularClass.classKind != ClassKind.ENUM_ENTRY) {
|
renderAnnotations(regularClass)
|
||||||
renderVisibility(regularClass.visibility)
|
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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (!regularClass.isCompanion) {
|
||||||
tabRightBySpace()
|
tabRightBySpace()
|
||||||
@@ -459,10 +476,12 @@ internal class FirIdeRenderer private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: StringBuilder) = with(data) {
|
override fun visitTypeAlias(typeAlias: FirTypeAlias, data: StringBuilder) = with(data) {
|
||||||
renderAnnotations(typeAlias)
|
if (options.renderDeclarationHeader) {
|
||||||
renderVisibility(typeAlias.visibility)
|
renderAnnotations(typeAlias)
|
||||||
renderMemberModifiers(typeAlias)
|
renderVisibility(typeAlias.visibility)
|
||||||
append("typealias").append(" ")
|
renderMemberModifiers(typeAlias)
|
||||||
|
append("typealias").append(" ")
|
||||||
|
}
|
||||||
renderName(typeAlias)
|
renderName(typeAlias)
|
||||||
renderTypeParameters(typeAlias.typeParameters, false)
|
renderTypeParameters(typeAlias.typeParameters, false)
|
||||||
append(" = ").append(renderType(typeAlias.expandedTypeRef))
|
append(" = ").append(renderType(typeAlias.expandedTypeRef))
|
||||||
@@ -561,7 +580,9 @@ internal class FirIdeRenderer private constructor(
|
|||||||
private fun StringBuilder.renderReceiver(firCallableDeclaration: FirCallableDeclaration<*>) {
|
private fun StringBuilder.renderReceiver(firCallableDeclaration: FirCallableDeclaration<*>) {
|
||||||
val receiverType = firCallableDeclaration.receiverTypeRef
|
val receiverType = firCallableDeclaration.receiverTypeRef
|
||||||
if (receiverType != null) {
|
if (receiverType != null) {
|
||||||
renderAnnotations(firCallableDeclaration)
|
if (options.renderDeclarationHeader) {
|
||||||
|
renderAnnotations(firCallableDeclaration)
|
||||||
|
}
|
||||||
|
|
||||||
val needBrackets =
|
val needBrackets =
|
||||||
typeIdeRenderer.shouldRenderAsPrettyFunctionType(receiverType.coneType) && receiverType.isMarkedNullable == true
|
typeIdeRenderer.shouldRenderAsPrettyFunctionType(receiverType.coneType) && receiverType.isMarkedNullable == true
|
||||||
@@ -601,14 +622,18 @@ internal class FirIdeRenderer private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun StringBuilder.renderValueParameter(valueParameter: FirValueParameter) {
|
private fun StringBuilder.renderValueParameter(valueParameter: FirValueParameter) {
|
||||||
renderAnnotations(valueParameter)
|
if (options.renderDeclarationHeader) {
|
||||||
|
renderAnnotations(valueParameter)
|
||||||
|
}
|
||||||
renderModifier(valueParameter.isCrossinline, "crossinline")
|
renderModifier(valueParameter.isCrossinline, "crossinline")
|
||||||
renderModifier(valueParameter.isNoinline, "noinline")
|
renderModifier(valueParameter.isNoinline, "noinline")
|
||||||
renderVariable(valueParameter)
|
renderVariable(valueParameter)
|
||||||
|
|
||||||
val withDefaultValue = valueParameter.defaultValue != null //TODO check if default value is inherited
|
if (options.renderDefaultParameterValue) {
|
||||||
if (withDefaultValue) {
|
val withDefaultValue = valueParameter.defaultValue != null //TODO check if default value is inherited
|
||||||
append(" = ...")
|
if (withDefaultValue) {
|
||||||
|
append(" = ...")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -624,7 +649,12 @@ internal class FirIdeRenderer private constructor(
|
|||||||
renderModifier(isVarArg, "vararg")
|
renderModifier(isVarArg, "vararg")
|
||||||
renderName(variable)
|
renderName(variable)
|
||||||
append(": ")
|
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<*>) {
|
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");
|
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")
|
@TestMetadata("where.kt")
|
||||||
public void testWhere() throws Exception {
|
public void testWhere() throws Exception {
|
||||||
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt");
|
runTest("idea/idea-frontend-fir/testData/components/declarationRenderer/where.kt");
|
||||||
|
|||||||
+2
-13
@@ -1,17 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2015 JetBrains s.r.o.
|
* 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.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.idea;
|
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 getNoMembersFoundHint(): String
|
||||||
|
|
||||||
protected abstract fun generateMembers(
|
abstract fun generateMembers(
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
classOrObject: KtClassOrObject,
|
classOrObject: KtClassOrObject,
|
||||||
selectedElements: Collection<T>,
|
selectedElements: Collection<T>,
|
||||||
@@ -74,8 +74,7 @@ abstract class AbstractGenerateMembersHandler<T : ClassMember> : LanguageCodeIns
|
|||||||
}
|
}
|
||||||
|
|
||||||
val copyDoc: Boolean
|
val copyDoc: Boolean
|
||||||
val selectedElements: Collection<T
|
val selectedElements: Collection<T>
|
||||||
>
|
|
||||||
if (implementAll) {
|
if (implementAll) {
|
||||||
selectedElements = members
|
selectedElements = members
|
||||||
copyDoc = false
|
copyDoc = false
|
||||||
|
|||||||
+3
@@ -13,4 +13,7 @@ sealed class BodyType(val requiresReturn: Boolean = true) {
|
|||||||
object QUALIFIED_SUPER : BodyType()
|
object QUALIFIED_SUPER : BodyType()
|
||||||
|
|
||||||
class Delegate(val receiverName: String) : 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"/>
|
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.idea.fir.low.level.api.trackers.KotlinOutOfBlockPsiTreeChangePreprocessor"/>
|
||||||
<projectService serviceImplementation="org.jetbrains.kotlin.idea.fir.api.impl.ResolveAllowanceServiceFirImpl"
|
<projectService serviceImplementation="org.jetbrains.kotlin.idea.fir.api.impl.ResolveAllowanceServiceFirImpl"
|
||||||
serviceInterface="org.jetbrains.kotlin.idea.api.ResolveAllowanceService"/>
|
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>
|
</extensions>
|
||||||
|
|
||||||
<!-- scripts -->
|
<!-- scripts -->
|
||||||
|
|||||||
Reference in New Issue
Block a user