Kotlin Scripting Compiler extension providing code completion and static analysis for IDE integration
—
Various utility classes for filtering, type handling, and descriptor management in IDE contexts. These utilities support advanced completion scenarios and provide essential functionality for the scripting IDE services.
Filters out shadowed declarations in completion to provide cleaner, more relevant results.
/**
* Filters out shadowed declarations in completion contexts
* Removes declarations that are hidden by more specific declarations in the same scope
*/
class ShadowedDeclarationsFilter(
private val bindingContext: BindingContext,
private val resolutionFacade: ResolutionFacade,
private val context: PsiElement,
private val explicitReceiverValue: ReceiverValue?
) {
/**
* Filters a collection of declarations, removing shadowed ones
* @param declarations - Collection of declaration descriptors to filter
* @returns Filtered collection with shadowed declarations removed
*/
fun <TDescriptor : DeclarationDescriptor> filter(
declarations: Collection<TDescriptor>
): Collection<TDescriptor>
companion object {
/**
* Creates a ShadowedDeclarationsFilter for the given context
* @param bindingContext - Binding context from analysis
* @param resolutionFacade - Resolution facade for type resolution
* @param context - PSI element providing context
* @param callTypeAndReceiver - Call type and receiver information
* @returns ShadowedDeclarationsFilter instance or null if not applicable
*/
fun create(
bindingContext: BindingContext,
resolutionFacade: ResolutionFacade,
context: PsiElement,
callTypeAndReceiver: CallTypeAndReceiver<*, *>
): ShadowedDeclarationsFilter?
}
}Usage Examples:
// Create filter for completion context
val filter = ShadowedDeclarationsFilter.create(
bindingContext = bindingContext,
resolutionFacade = resolutionFacade,
context = expression,
callTypeAndReceiver = callTypeAndReceiver
)
// Apply filtering to completion candidates
val allDescriptors: Collection<DeclarationDescriptor> = // from reference variants
val filteredDescriptors = filter?.filter(allDescriptors) ?: allDescriptors
// Example of shadowing:
// class Parent { fun method() {} }
// class Child : Parent() { fun method() {} } // shadows parent method
// In completion, only Child.method() would be shownRepresents types with free parameters for advanced completion scenarios, particularly useful for generic type inference.
/**
* Represents a type with free parameters for completion and type inference
* Useful for handling generic types and type parameters in completion
*/
class FuzzyType(
/** The underlying Kotlin type */
val type: KotlinType,
/** Collection of free type parameters */
val freeParameters: Collection<TypeParameterDescriptor>
) {
/**
* Checks if this fuzzy type represents a function type
*/
fun isFunctionType(): Boolean
/**
* Gets the nullability of the fuzzy type
*/
fun nullability(): TypeNullability
}Extension functions for working with fuzzy types:
/**
* Gets the fuzzy extension receiver type for a callable descriptor
* @returns FuzzyType representing the extension receiver or null
*/
fun CallableDescriptor.fuzzyExtensionReceiverType(): FuzzyType?
/**
* Converts a Kotlin type to a fuzzy type with specified free parameters
* @param freeParameters - Collection of type parameters to treat as free
* @returns FuzzyType instance
*/
fun KotlinType.toFuzzyType(freeParameters: Collection<TypeParameterDescriptor>): FuzzyType
/**
* Approximates flexible types for better IDE integration
* @param builtIns - Built-in types for the module
* @param languageVersionSettings - Language version settings
* @returns Approximated Kotlin type
*/
fun KotlinType.approximateFlexibleTypes(
builtIns: KotlinBuiltIns,
languageVersionSettings: LanguageVersionSettings
): KotlinTypeUsage Examples:
// Convert type to fuzzy type for generic inference
val genericFunction: FunctionDescriptor = // function with type parameters
val freeParams = genericFunction.typeParameters
val returnType = genericFunction.returnType
val fuzzyReturnType = returnType?.toFuzzyType(freeParams)
if (fuzzyReturnType?.isFunctionType() == true) {
// Handle function type completion
println("Return type is a function with free parameters: ${freeParams.map { it.name }}")
}
// Get extension receiver as fuzzy type
val extensionFunction: FunctionDescriptor = // extension function
val fuzzyReceiver = extensionFunction.fuzzyExtensionReceiverType()
fuzzyReceiver?.let { receiver ->
println("Extension receiver: ${receiver.type} with free params: ${receiver.freeParameters.size}")
}Object providing descriptor renderers optimized for IDE scenarios in scripting contexts.
/**
* Provides descriptor renderers for IDE scenarios in scripting contexts
* Contains pre-configured renderers for different presentation needs
*/
object IdeDescriptorRenderersScripting {
/** Renderer for source code presentation */
val SOURCE_CODE: DescriptorRenderer
/** Type normalizer for approximating flexible types */
val APPROXIMATE_FLEXIBLE_TYPES: (KotlinType) -> KotlinType
/** Renderer for short names in types */
val SHORT_NAMES_IN_TYPES: DescriptorRenderer
/** Renderer for function parameters */
val FUNCTION_PARAMETERS: DescriptorRenderer
}Usage Examples:
// Render descriptors for completion display
val function: FunctionDescriptor = // function to render
val sourceCodePresentation = IdeDescriptorRenderersScripting.SOURCE_CODE.render(function)
// Render function parameters
val parametersPresentation = IdeDescriptorRenderersScripting.FUNCTION_PARAMETERS
.renderFunctionParameters(function)
// Render type with short names
val type: KotlinType = function.returnType!!
val shortTypePresentation = IdeDescriptorRenderersScripting.SHORT_NAMES_IN_TYPES.renderType(type)
println("Function: $sourceCodePresentation")
println("Parameters: $parametersPresentation")
println("Return type: $shortTypePresentation")Extension functions for handling imports and references:
/**
* Gets the importable FqName for a declaration descriptor
*/
val DeclarationDescriptor.importableFqName: FqName?
/**
* Checks if a declaration can be referenced via import
*/
fun DeclarationDescriptor.canBeReferencedViaImport(): Boolean
/**
* Checks if a declaration can be added to imports
*/
fun DeclarationDescriptor.canBeAddedToImport(): Boolean
/**
* Checks if a type can be referenced via import
*/
fun KotlinType.canBeReferencedViaImport(): Boolean
/**
* Gets importable targets for a reference expression
* @param bindingContext - Binding context from analysis
* @returns Collection of declaration descriptors that can be imported
*/
fun KtReferenceExpression.getImportableTargets(bindingContext: BindingContext): Collection<DeclarationDescriptor>Usage Examples:
// Check if descriptor can be imported
val descriptor: DeclarationDescriptor = // some descriptor
if (descriptor.canBeReferencedViaImport()) {
val fqName = descriptor.importableFqName
println("Can import: $fqName")
}
// Get importable targets for unresolved reference
val unresolvedRef: KtReferenceExpression = // unresolved reference
val importableTargets = unresolvedRef.getImportableTargets(bindingContext)
importableTargets.forEach { target ->
println("Possible import: ${target.importableFqName}")
}Functions for working with implicit receivers in completion contexts:
/**
* Interface for creating receiver expressions
*/
interface ReceiverExpressionFactory {
/** Checks if the factory creates immediate receivers for the scope */
fun isImmediate(scope: LexicalScope): Boolean
/** Creates a receiver expression for the scope */
fun createReceiverExpression(scope: LexicalScope): KtExpression
}
/**
* Gets implicit receivers with instance for a lexical scope
* @param excludeShadowedByDslMarkers - Whether to exclude receivers shadowed by DSL markers
* @returns Collection of receiver parameter descriptors
*/
fun LexicalScope.getImplicitReceiversWithInstance(
excludeShadowedByDslMarkers: Boolean = false
): Collection<ReceiverParameterDescriptor>
/**
* Gets a factory for implicit receivers with subtype of the specified type
* @param receiverType - Required receiver type
* @returns ReceiverExpressionFactory or null if no suitable receiver
*/
fun LexicalScope.getFactoryForImplicitReceiverWithSubtypeOf(
receiverType: KotlinType
): ReceiverExpressionFactory?
/**
* Gets implicit receivers mapped to their expressions
* @param excludeShadowedByDslMarkers - Whether to exclude shadowed receivers
* @returns Collection of Kotlin expressions representing implicit receivers
*/
fun LexicalScope.getImplicitReceiversWithInstanceToExpression(
excludeShadowedByDslMarkers: Boolean = false
): Collection<KtExpression>Utility functions for working with descriptors:
/**
* Checks if two descriptors are equal with substitution
* @param descriptor1 - First descriptor
* @param descriptor2 - Second descriptor
* @param allowDifferentTypeParameters - Whether to allow different type parameters
* @returns true if descriptors are equivalent
*/
fun descriptorsEqualWithSubstitution(
descriptor1: DeclarationDescriptor?,
descriptor2: DeclarationDescriptor?,
allowDifferentTypeParameters: Boolean = false
): Boolean
/**
* Gets supertypes including Any for a type constructor
* @returns Collection of supertypes with Any included
*/
fun TypeConstructor.supertypesWithAny(): Collection<KotlinType>Extension properties for descriptor information:
/**
* Gets constructors for a classifier descriptor with type parameters
*/
val ClassifierDescriptorWithTypeParameters.constructors: Collection<ConstructorDescriptor>
/**
* Gets the class kind for a classifier descriptor
*/
val ClassifierDescriptorWithTypeParameters.kind: ClassKind?
/**
* Checks if a declaration descriptor is from Java
*/
val DeclarationDescriptor.isJavaDescriptor: BooleanExtensions for function descriptor handling:
/**
* Checks if a function should not be converted to property
* @param notProperties - Set of FqNames that should not be treated as properties
* @returns true if function should remain a function
*/
fun FunctionDescriptor.shouldNotConvertToProperty(notProperties: Set<FqNameUnsafe>): Boolean
/**
* Checks if a synthetic Java property is suppressed by the not-property list
* @param set - Set of FqNames for suppressed properties
* @returns true if property is suppressed
*/
fun SyntheticJavaPropertyDescriptor.suppressedByNotPropertyList(set: Set<FqNameUnsafe>): BooleanUtilities for working with smart casts:
/**
* Gets smart cast variants with less specific types excluded
* @param receiver - Receiver value for smart cast analysis
* @param bindingContext - Binding context from analysis
* @param containingDeclarationOrModule - Context declaration or module
* @param dataFlowInfo - Data flow information
* @param languageVersionSettings - Language version settings
* @param dataFlowValueFactory - Factory for data flow values
* @returns List of smart cast types
*/
fun SmartCastManager.getSmartCastVariantsWithLessSpecificExcluded(
receiver: ReceiverValue,
bindingContext: BindingContext,
containingDeclarationOrModule: DeclarationDescriptor,
dataFlowInfo: DataFlowInfo,
languageVersionSettings: LanguageVersionSettings,
dataFlowValueFactory: DataFlowValueFactory
): List<KotlinType>These utilities are used throughout the completion system:
// Example integration in completion engine
class CompletionEngine {
private fun getFilteredCompletions(
rawCompletions: Collection<DeclarationDescriptor>,
context: CompletionContext
): Collection<DeclarationDescriptor> {
// Apply shadowed declaration filtering
val shadowedFilter = ShadowedDeclarationsFilter.create(
bindingContext = context.bindingContext,
resolutionFacade = context.resolutionFacade,
context = context.position,
callTypeAndReceiver = context.callTypeAndReceiver
)
var filtered = shadowedFilter?.filter(rawCompletions) ?: rawCompletions
// Filter out non-importable items for import contexts
if (context.callTypeAndReceiver.callType == CallType.IMPORT_DIRECTIVE) {
filtered = filtered.filter { it.canBeReferencedViaImport() }
}
return filtered
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-jetbrains-kotlin--kotlin-scripting-ide-services