Kotlin Scripting Compiler extension providing code completion and static analysis for IDE integration
—
Advanced call context analysis for determining the type of call being made and providing appropriate completion suggestions. This system analyzes the syntactic context around the cursor to provide context-aware completions.
Sealed class hierarchy representing different types of calls that can occur in Kotlin code.
/**
* Represents the type of call being made in Kotlin code
* Used for context-aware completion and analysis
*/
sealed class CallType<TReceiver : KtElement?>
/**
* Unknown or unrecognized call context
*/
object UNKNOWN : CallType<Nothing?>
/**
* Default call context (no specific receiver)
*/
object DEFAULT : CallType<Nothing?>
/**
* Dot call (receiver.member)
*/
object DOT : CallType<KtExpression>
/**
* Safe call (receiver?.member)
*/
object SAFE : CallType<KtExpression>
/**
* Super member access (super.member)
*/
object SUPER_MEMBERS : CallType<KtSuperExpression>
/**
* Infix function call (receiver infix function)
*/
object INFIX : CallType<KtExpression>
/**
* Operator call (receiver + other)
*/
object OPERATOR : CallType<KtExpression>
/**
* Callable reference (::member or receiver::member)
*/
object CALLABLE_REFERENCE : CallType<KtExpression?>
/**
* Import directive context (import package.member)
*/
object IMPORT_DIRECTIVE : CallType<Nothing?>
/**
* Package directive context (package name.subpackage)
*/
object PACKAGE_DIRECTIVE : CallType<Nothing?>
/**
* Type context (for type annotations, generic parameters)
*/
object TYPE : CallType<Nothing?>
/**
* Delegate context (by delegate)
*/
object DELEGATE : CallType<Nothing?>
/**
* Annotation context (@Annotation)
*/
object ANNOTATION : CallType<Nothing?>Combines call type information with receiver details for comprehensive context analysis.
/**
* Combines call type with receiver information for completion context
* Provides both the type of call and the receiver element
*/
sealed class CallTypeAndReceiver<TReceiver : KtElement?, out TCallType : CallType<TReceiver>> {
abstract val callType: TCallType
abstract val receiver: TReceiver
/**
* Detects the call type and receiver for a given expression
* @param expression - The simple name expression to analyze
* @returns CallTypeAndReceiver instance with detected context
*/
companion object {
fun detect(expression: KtSimpleNameExpression): CallTypeAndReceiver<*, *>
}
}
/**
* Dot call with receiver (receiver.member)
*/
data class DOT<TReceiver : KtExpression>(
override val receiver: TReceiver
) : CallTypeAndReceiver<TReceiver, CallType.DOT>() {
override val callType: CallType.DOT get() = CallType.DOT
}
/**
* Safe call with receiver (receiver?.member)
*/
data class SAFE<TReceiver : KtExpression>(
override val receiver: TReceiver
) : CallTypeAndReceiver<TReceiver, CallType.SAFE>() {
override val callType: CallType.SAFE get() = CallType.SAFE
}
/**
* Super member access (super.member)
*/
data class SUPER_MEMBERS<TReceiver : KtSuperExpression>(
override val receiver: TReceiver
) : CallTypeAndReceiver<TReceiver, CallType.SUPER_MEMBERS>() {
override val callType: CallType.SUPER_MEMBERS get() = CallType.SUPER_MEMBERS
}
/**
* Infix call with receiver (receiver infix function)
*/
data class INFIX<TReceiver : KtExpression>(
override val receiver: TReceiver
) : CallTypeAndReceiver<TReceiver, CallType.INFIX>() {
override val callType: CallType.INFIX get() = CallType.INFIX
}
/**
* Operator call with receiver (receiver + other)
*/
data class OPERATOR<TReceiver : KtExpression>(
override val receiver: TReceiver
) : CallTypeAndReceiver<TReceiver, CallType.OPERATOR>() {
override val callType: CallType.OPERATOR get() = CallType.OPERATOR
}
/**
* Callable reference (::member or receiver::member)
*/
data class CALLABLE_REFERENCE<TReceiver : KtExpression?>(
override val receiver: TReceiver
) : CallTypeAndReceiver<TReceiver, CallType.CALLABLE_REFERENCE>() {
override val callType: CallType.CALLABLE_REFERENCE get() = CallType.CALLABLE_REFERENCE
}
/**
* Default context (no specific receiver)
*/
object DEFAULT : CallTypeAndReceiver<Nothing?, CallType.DEFAULT>() {
override val callType: CallType.DEFAULT get() = CallType.DEFAULT
override val receiver: Nothing? get() = null
}
/**
* Unknown context
*/
object UNKNOWN : CallTypeAndReceiver<Nothing?, CallType.UNKNOWN>() {
override val callType: CallType.UNKNOWN get() = CallType.UNKNOWN
override val receiver: Nothing? get() = null
}Usage Examples:
// Detect call type from expression
val expression: KtSimpleNameExpression = // PSI element at cursor
val callTypeAndReceiver = CallTypeAndReceiver.detect(expression)
when (callTypeAndReceiver) {
is CallTypeAndReceiver.DOT -> {
println("Dot call on receiver: ${callTypeAndReceiver.receiver.text}")
// Provide member completions for the receiver type
}
is CallTypeAndReceiver.SAFE -> {
println("Safe call on receiver: ${callTypeAndReceiver.receiver.text}")
// Provide nullable member completions
}
is CallTypeAndReceiver.DEFAULT -> {
println("Default context - no specific receiver")
// Provide local variables, functions, and imports
}
is CallTypeAndReceiver.SUPER_MEMBERS -> {
println("Super member access")
// Provide super class member completions
}
else -> {
println("Other call type: ${callTypeAndReceiver.callType}")
}
}Data class representing receiver type information for completion context.
/**
* Represents receiver type information for completion
* Contains the Kotlin type and additional metadata about the receiver
*/
data class ReceiverType(
/** The Kotlin type of the receiver */
val type: KotlinType,
/** Index of the receiver in implicit receiver chain */
val receiverIndex: Int,
/** Optional implicit receiver value */
val implicitValue: ReceiverValue?
) {
/**
* Extracts DSL markers from the receiver type
* @returns Collection of annotation class IDs representing DSL markers
*/
fun extractDslMarkers(): Collection<ClassId>
}Functions for resolving receiver types from call context:
/**
* Gets the receiver types for a call type and receiver
* @param bindingContext - Binding context from analysis
* @param contextElement - PSI element providing context
* @param moduleDescriptor - Module descriptor for resolution
* @param resolutionFacade - Resolution facade for type resolution
* @param stableSmartCastsOnly - Whether to only consider stable smart casts
* @param withImplicitReceiversWhenExplicitPresent - Include implicit receivers even with explicit receiver
* @returns List of Kotlin types for the receivers
*/
fun CallTypeAndReceiver<*, *>.receiverTypes(
bindingContext: BindingContext,
contextElement: PsiElement,
moduleDescriptor: ModuleDescriptor,
resolutionFacade: ResolutionFacade,
stableSmartCastsOnly: Boolean,
withImplicitReceiversWhenExplicitPresent: Boolean = false
): List<KotlinType>?
/**
* Gets receiver types with index information
* @param bindingContext - Binding context from analysis
* @param contextElement - PSI element providing context
* @param moduleDescriptor - Module descriptor for resolution
* @param resolutionFacade - Resolution facade for type resolution
* @param stableSmartCastsOnly - Whether to only consider stable smart casts
* @param withImplicitReceiversWhenExplicitPresent - Include implicit receivers even with explicit receiver
* @returns List of ReceiverType objects with detailed information
*/
fun CallTypeAndReceiver<*, *>.receiverTypesWithIndex(
bindingContext: BindingContext,
contextElement: PsiElement,
moduleDescriptor: ModuleDescriptor,
resolutionFacade: ResolutionFacade,
stableSmartCastsOnly: Boolean,
withImplicitReceiversWhenExplicitPresent: Boolean = false
): List<ReceiverType>?Usage Examples:
// Get receiver types for completion
val callTypeAndReceiver = CallTypeAndReceiver.detect(expression)
val receiverTypes = callTypeAndReceiver.receiverTypes(
bindingContext = bindingContext,
contextElement = expression,
moduleDescriptor = moduleDescriptor,
resolutionFacade = resolutionFacade,
stableSmartCastsOnly = true
)
receiverTypes?.forEach { receiverType ->
println("Receiver type: ${receiverType}")
// Get members for this receiver type
val members = receiverType.memberScope.getContributedDescriptors()
// Process members for completion
}
// Get detailed receiver information
val receiverTypesWithIndex = callTypeAndReceiver.receiverTypesWithIndex(
bindingContext = bindingContext,
contextElement = expression,
moduleDescriptor = moduleDescriptor,
resolutionFacade = resolutionFacade,
stableSmartCastsOnly = true
)
receiverTypesWithIndex?.forEach { receiverType ->
println("Receiver ${receiverType.receiverIndex}: ${receiverType.type}")
receiverType.implicitValue?.let { implicit ->
println(" Implicit value: $implicit")
}
// Extract DSL markers for context-aware completion
val dslMarkers = receiverType.extractDslMarkers()
if (dslMarkers.isNotEmpty()) {
println(" DSL markers: ${dslMarkers.joinToString()}")
}
}The call type detection system is automatically used by the completion engine:
// Internal usage in completion system
private val getDescriptorsQualified = ResultGetter { element, options ->
val expression = element.thisOrParent<KtQualifiedExpression>() ?: return@ResultGetter null
val receiverExpression = expression.receiverExpression
val expressionType = bindingContext.get(BindingContext.EXPRESSION_TYPE_INFO, receiverExpression)?.type
DescriptorsResult(targetElement = expression).apply {
if (expressionType != null) {
sortNeeded = false
descriptors.addAll(
getVariantsHelper { true }
.getReferenceVariants(
receiverExpression,
CallTypeAndReceiver.DOT(receiverExpression), // Using call type detection
DescriptorKindFilter.ALL,
ALL_NAME_FILTER,
filterOutShadowed = options.filterOutShadowedDescriptors,
)
)
}
}
}Different call types enable different completion strategies:
val list = listOf(1, 2, 3)
list. // DOT call type
// Provides: map, filter, size, isEmpty, etc.val nullable: String? = "hello"
nullable?. // SAFE call type
// Provides: length, uppercase, lowercase, etc. (nullable context)class Child : Parent() {
override fun method() {
super. // SUPER_MEMBERS call type
// Provides: parent class members only
}
}import kotlin.collections. // IMPORT_DIRECTIVE call type
// Provides: package members and subpackagesfun method(): List< // TYPE call type
// Provides: type names, generic parametersInstall with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-scripting-ide-services