Reformat 'frontend' module according to new codestyle

This commit is contained in:
Dmitry Savvinov
2018-01-16 10:57:56 +03:00
parent b567817d1f
commit e5f0ffb0c2
204 changed files with 3869 additions and 3395 deletions
@@ -22,9 +22,9 @@ import org.jetbrains.kotlin.types.ErrorUtils
import java.io.File import java.io.File
open class AnalysisResult protected constructor( open class AnalysisResult protected constructor(
val bindingContext: BindingContext, val bindingContext: BindingContext,
val moduleDescriptor: ModuleDescriptor, val moduleDescriptor: ModuleDescriptor,
val shouldGenerateCode: Boolean = true val shouldGenerateCode: Boolean = true
) { ) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
@@ -63,33 +63,37 @@ open class AnalysisResult protected constructor(
private class CompilationError(bindingContext: BindingContext) : AnalysisResult(bindingContext, ErrorUtils.getErrorModule()) private class CompilationError(bindingContext: BindingContext) : AnalysisResult(bindingContext, ErrorUtils.getErrorModule())
private class InternalError( private class InternalError(
bindingContext: BindingContext, bindingContext: BindingContext,
val exception: Throwable val exception: Throwable
) : AnalysisResult(bindingContext, ErrorUtils.getErrorModule()) ) : AnalysisResult(bindingContext, ErrorUtils.getErrorModule())
class RetryWithAdditionalJavaRoots( class RetryWithAdditionalJavaRoots(
bindingContext: BindingContext, bindingContext: BindingContext,
moduleDescriptor: ModuleDescriptor, moduleDescriptor: ModuleDescriptor,
val additionalJavaRoots: List<File>, val additionalJavaRoots: List<File>,
val addToEnvironment: Boolean = true val addToEnvironment: Boolean = true
) : AnalysisResult(bindingContext, moduleDescriptor) ) : AnalysisResult(bindingContext, moduleDescriptor)
companion object { companion object {
val EMPTY: AnalysisResult = success(BindingContext.EMPTY, ErrorUtils.getErrorModule()) val EMPTY: AnalysisResult = success(BindingContext.EMPTY, ErrorUtils.getErrorModule())
@JvmStatic fun success(bindingContext: BindingContext, module: ModuleDescriptor): AnalysisResult { @JvmStatic
fun success(bindingContext: BindingContext, module: ModuleDescriptor): AnalysisResult {
return AnalysisResult(bindingContext, module, true) return AnalysisResult(bindingContext, module, true)
} }
@JvmStatic fun success(bindingContext: BindingContext, module: ModuleDescriptor, shouldGenerateCode: Boolean): AnalysisResult { @JvmStatic
fun success(bindingContext: BindingContext, module: ModuleDescriptor, shouldGenerateCode: Boolean): AnalysisResult {
return AnalysisResult(bindingContext, module, shouldGenerateCode) return AnalysisResult(bindingContext, module, shouldGenerateCode)
} }
@JvmStatic fun internalError(bindingContext: BindingContext, error: Throwable): AnalysisResult { @JvmStatic
fun internalError(bindingContext: BindingContext, error: Throwable): AnalysisResult {
return InternalError(bindingContext, error) return InternalError(bindingContext, error)
} }
@JvmStatic fun compilationError(bindingContext: BindingContext): AnalysisResult { @JvmStatic
fun compilationError(bindingContext: BindingContext): AnalysisResult {
return CompilationError(bindingContext) return CompilationError(bindingContext)
} }
} }
@@ -38,19 +38,19 @@ interface LeakingThisDescriptor {
data class PropertyIsNull(val property: PropertyDescriptor, override val classOrObject: KtClassOrObject) : LeakingThisDescriptor data class PropertyIsNull(val property: PropertyDescriptor, override val classOrObject: KtClassOrObject) : LeakingThisDescriptor
data class NonFinalClass(val klass: ClassDescriptor, override val classOrObject: KtClassOrObject): LeakingThisDescriptor data class NonFinalClass(val klass: ClassDescriptor, override val classOrObject: KtClassOrObject) : LeakingThisDescriptor
data class NonFinalProperty(val property: PropertyDescriptor, override val classOrObject: KtClassOrObject): LeakingThisDescriptor data class NonFinalProperty(val property: PropertyDescriptor, override val classOrObject: KtClassOrObject) : LeakingThisDescriptor
data class NonFinalFunction(val function: FunctionDescriptor, override val classOrObject: KtClassOrObject): LeakingThisDescriptor data class NonFinalFunction(val function: FunctionDescriptor, override val classOrObject: KtClassOrObject) : LeakingThisDescriptor
} }
class ConstructorConsistencyChecker private constructor( class ConstructorConsistencyChecker private constructor(
private val classOrObject: KtClassOrObject, private val classOrObject: KtClassOrObject,
private val classDescriptor: ClassDescriptor, private val classDescriptor: ClassDescriptor,
private val trace: BindingTrace, private val trace: BindingTrace,
private val pseudocode: Pseudocode, private val pseudocode: Pseudocode,
private val variablesData: PseudocodeVariablesData private val variablesData: PseudocodeVariablesData
) { ) {
private val finalClass = classDescriptor.isFinalClass private val finalClass = classDescriptor.isFinalClass
@@ -110,77 +110,79 @@ class ConstructorConsistencyChecker private constructor(
fun check() { fun check() {
// List of properties to initialize // List of properties to initialize
val propertyDescriptors = variablesData.getDeclaredVariables(pseudocode, false) val propertyDescriptors = variablesData.getDeclaredVariables(pseudocode, false)
.filterIsInstance<PropertyDescriptor>() .filterIsInstance<PropertyDescriptor>()
.filter { trace.get(BindingContext.BACKING_FIELD_REQUIRED, it) == true } .filter { trace.get(BindingContext.BACKING_FIELD_REQUIRED, it) == true }
pseudocode.traverse( pseudocode.traverse(
TraversalOrder.FORWARD, variablesData.variableInitializers, { instruction, enterData, _ -> TraversalOrder.FORWARD, variablesData.variableInitializers, { instruction, enterData, _ ->
fun firstUninitializedNotNullProperty() = propertyDescriptors.firstOrNull { fun firstUninitializedNotNullProperty() = propertyDescriptors.firstOrNull {
!it.type.isMarkedNullable && !KotlinBuiltIns.isPrimitiveType(it.type) && !it.type.isMarkedNullable && !KotlinBuiltIns.isPrimitiveType(it.type) &&
!it.isLateInit && !(enterData.getOrNull(it)?.definitelyInitialized() ?: false) !it.isLateInit && !(enterData.getOrNull(it)?.definitelyInitialized() ?: false)
}
fun handleLeakingThis(expression: KtExpression) {
if (!finalClass) {
trace.record(BindingContext.LEAKING_THIS, target(expression),
LeakingThisDescriptor.NonFinalClass(classDescriptor, classOrObject))
} }
else {
val uninitializedProperty = firstUninitializedNotNullProperty() fun handleLeakingThis(expression: KtExpression) {
if (uninitializedProperty != null) { if (!finalClass) {
trace.record(BindingContext.LEAKING_THIS, target(expression), trace.record(
LeakingThisDescriptor.PropertyIsNull(uninitializedProperty, classOrObject)) BindingContext.LEAKING_THIS, target(expression),
LeakingThisDescriptor.NonFinalClass(classDescriptor, classOrObject)
)
} else {
val uninitializedProperty = firstUninitializedNotNullProperty()
if (uninitializedProperty != null) {
trace.record(
BindingContext.LEAKING_THIS, target(expression),
LeakingThisDescriptor.PropertyIsNull(uninitializedProperty, classOrObject)
)
}
} }
} }
}
if (instruction.owner != pseudocode) { if (instruction.owner != pseudocode) {
return@traverse return@traverse
}
if (instruction is KtElementInstruction) {
val element = instruction.element
when (instruction) {
is ReadValueInstruction ->
if (element is KtThisExpression) {
if (!safeThisUsage(element)) {
handleLeakingThis(element)
}
}
is MagicInstruction ->
if (instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
if (element is KtCallExpression) {
if (!safeCallUsage(element)) {
handleLeakingThis(element)
}
}
else if (element is KtReferenceExpression) {
if (!safeReferenceUsage(element)) {
handleLeakingThis(element)
}
}
}
} }
}
}) if (instruction is KtElementInstruction) {
val element = instruction.element
when (instruction) {
is ReadValueInstruction ->
if (element is KtThisExpression) {
if (!safeThisUsage(element)) {
handleLeakingThis(element)
}
}
is MagicInstruction ->
if (instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
if (element is KtCallExpression) {
if (!safeCallUsage(element)) {
handleLeakingThis(element)
}
} else if (element is KtReferenceExpression) {
if (!safeReferenceUsage(element)) {
handleLeakingThis(element)
}
}
}
}
}
})
} }
companion object { companion object {
@JvmStatic @JvmStatic
fun check( fun check(
constructor: KtSecondaryConstructor, constructor: KtSecondaryConstructor,
trace: BindingTrace, trace: BindingTrace,
pseudocode: Pseudocode, pseudocode: Pseudocode,
pseudocodeVariablesData: PseudocodeVariablesData pseudocodeVariablesData: PseudocodeVariablesData
) = check(constructor.getContainingClassOrObject(), trace, pseudocode, pseudocodeVariablesData) ) = check(constructor.getContainingClassOrObject(), trace, pseudocode, pseudocodeVariablesData)
@JvmStatic @JvmStatic
fun check( fun check(
classOrObject: KtClassOrObject, classOrObject: KtClassOrObject,
trace: BindingTrace, trace: BindingTrace,
pseudocode: Pseudocode, pseudocode: Pseudocode,
pseudocodeVariablesData: PseudocodeVariablesData pseudocodeVariablesData: PseudocodeVariablesData
) { ) {
val classDescriptor = trace.get(BindingContext.CLASS, classOrObject) ?: return val classDescriptor = trace.get(BindingContext.CLASS, classOrObject) ?: return
ConstructorConsistencyChecker(classOrObject, classDescriptor, trace, pseudocode, pseudocodeVariablesData).check() ConstructorConsistencyChecker(classOrObject, classDescriptor, trace, pseudocode, pseudocodeVariablesData).check()
@@ -103,25 +103,29 @@ interface ControlFlowBuilder {
fun loadStringTemplate(expression: KtStringTemplateExpression, inputValues: List<PseudoValue>): InstructionWithValue fun loadStringTemplate(expression: KtStringTemplateExpression, inputValues: List<PseudoValue>): InstructionWithValue
fun magic( fun magic(
instructionElement: KtElement, instructionElement: KtElement,
valueElement: KtElement?, valueElement: KtElement?,
inputValues: List<PseudoValue>, inputValues: List<PseudoValue>,
kind: MagicKind): MagicInstruction kind: MagicKind
): MagicInstruction
fun merge( fun merge(
expression: KtExpression, expression: KtExpression,
inputValues: List<PseudoValue>): MergeInstruction inputValues: List<PseudoValue>
): MergeInstruction
fun readVariable( fun readVariable(
expression: KtExpression, expression: KtExpression,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue>): ReadValueInstruction receiverValues: Map<PseudoValue, ReceiverValue>
): ReadValueInstruction
fun call( fun call(
valueElement: KtElement, valueElement: KtElement,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue>, receiverValues: Map<PseudoValue, ReceiverValue>,
arguments: Map<PseudoValue, ValueParameterDescriptor>): CallInstruction arguments: Map<PseudoValue, ValueParameterDescriptor>
): CallInstruction
enum class PredefinedOperation { enum class PredefinedOperation {
AND, AND,
@@ -130,16 +134,18 @@ interface ControlFlowBuilder {
} }
fun predefinedOperation( fun predefinedOperation(
expression: KtExpression, expression: KtExpression,
operation: PredefinedOperation, operation: PredefinedOperation,
inputValues: List<PseudoValue>): OperationInstruction inputValues: List<PseudoValue>
): OperationInstruction
fun read(element: KtElement, target: AccessTarget, receiverValues: Map<PseudoValue, ReceiverValue>): ReadValueInstruction fun read(element: KtElement, target: AccessTarget, receiverValues: Map<PseudoValue, ReceiverValue>): ReadValueInstruction
fun write( fun write(
assignment: KtElement, assignment: KtElement,
lValue: KtElement, lValue: KtElement,
rValue: PseudoValue, rValue: PseudoValue,
target: AccessTarget, target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>) receiverValues: Map<PseudoValue, ReceiverValue>
)
} }
@@ -35,42 +35,46 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
} }
override fun loadConstant(expression: KtExpression, constant: CompileTimeConstant<*>?): InstructionWithValue = override fun loadConstant(expression: KtExpression, constant: CompileTimeConstant<*>?): InstructionWithValue =
delegateBuilder.loadConstant(expression, constant) delegateBuilder.loadConstant(expression, constant)
override fun createAnonymousObject(expression: KtObjectLiteralExpression): InstructionWithValue = override fun createAnonymousObject(expression: KtObjectLiteralExpression): InstructionWithValue =
delegateBuilder.createAnonymousObject(expression) delegateBuilder.createAnonymousObject(expression)
override fun createLambda(expression: KtFunction): InstructionWithValue = delegateBuilder.createLambda(expression) override fun createLambda(expression: KtFunction): InstructionWithValue = delegateBuilder.createLambda(expression)
override fun loadStringTemplate(expression: KtStringTemplateExpression, inputValues: List<PseudoValue>): InstructionWithValue = override fun loadStringTemplate(expression: KtStringTemplateExpression, inputValues: List<PseudoValue>): InstructionWithValue =
delegateBuilder.loadStringTemplate(expression, inputValues) delegateBuilder.loadStringTemplate(expression, inputValues)
override fun magic( override fun magic(
instructionElement: KtElement, instructionElement: KtElement,
valueElement: KtElement?, valueElement: KtElement?,
inputValues: List<PseudoValue>, inputValues: List<PseudoValue>,
kind: MagicKind): MagicInstruction = delegateBuilder.magic(instructionElement, valueElement, inputValues, kind) kind: MagicKind
): MagicInstruction = delegateBuilder.magic(instructionElement, valueElement, inputValues, kind)
override fun merge(expression: KtExpression, inputValues: List<PseudoValue>): MergeInstruction = override fun merge(expression: KtExpression, inputValues: List<PseudoValue>): MergeInstruction =
delegateBuilder.merge(expression, inputValues) delegateBuilder.merge(expression, inputValues)
override fun readVariable( override fun readVariable(
expression: KtExpression, expression: KtExpression,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue>): ReadValueInstruction = receiverValues: Map<PseudoValue, ReceiverValue>
delegateBuilder.readVariable(expression, resolvedCall, receiverValues) ): ReadValueInstruction =
delegateBuilder.readVariable(expression, resolvedCall, receiverValues)
override fun call( override fun call(
valueElement: KtElement, valueElement: KtElement,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue>, receiverValues: Map<PseudoValue, ReceiverValue>,
arguments: Map<PseudoValue, ValueParameterDescriptor>): CallInstruction = arguments: Map<PseudoValue, ValueParameterDescriptor>
delegateBuilder.call(valueElement, resolvedCall, receiverValues, arguments) ): CallInstruction =
delegateBuilder.call(valueElement, resolvedCall, receiverValues, arguments)
override fun predefinedOperation( override fun predefinedOperation(
expression: KtExpression, expression: KtExpression,
operation: ControlFlowBuilder.PredefinedOperation, operation: ControlFlowBuilder.PredefinedOperation,
inputValues: List<PseudoValue>): OperationInstruction = delegateBuilder.predefinedOperation(expression, operation, inputValues) inputValues: List<PseudoValue>
): OperationInstruction = delegateBuilder.predefinedOperation(expression, operation, inputValues)
override fun createUnboundLabel(): Label = delegateBuilder.createUnboundLabel() override fun createUnboundLabel(): Label = delegateBuilder.createUnboundLabel()
@@ -140,7 +144,7 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
} }
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode = override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode =
delegateBuilder.exitSubroutine(subroutine, invocationKind) delegateBuilder.exitSubroutine(subroutine, invocationKind)
override val currentSubroutine: KtElement override val currentSubroutine: KtElement
get() = delegateBuilder.currentSubroutine get() = delegateBuilder.currentSubroutine
@@ -157,14 +161,15 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
} }
override fun read(element: KtElement, target: AccessTarget, receiverValues: Map<PseudoValue, ReceiverValue>) = override fun read(element: KtElement, target: AccessTarget, receiverValues: Map<PseudoValue, ReceiverValue>) =
delegateBuilder.read(element, target, receiverValues) delegateBuilder.read(element, target, receiverValues)
override fun write( override fun write(
assignment: KtElement, assignment: KtElement,
lValue: KtElement, lValue: KtElement,
rValue: PseudoValue, rValue: PseudoValue,
target: AccessTarget, target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>) { receiverValues: Map<PseudoValue, ReceiverValue>
) {
delegateBuilder.write(assignment, lValue, rValue, target, receiverValues) delegateBuilder.write(assignment, lValue, rValue, target, receiverValues)
} }
@@ -24,7 +24,7 @@ typealias ImmutableHashMap<K, V> = javaslang.collection.HashMap<K, V>
abstract class ControlFlowInfo<S : ControlFlowInfo<S, D>, D : Any> abstract class ControlFlowInfo<S : ControlFlowInfo<S, D>, D : Any>
internal constructor( internal constructor(
protected val map: ImmutableMap<VariableDescriptor, D> = ImmutableHashMap.empty() protected val map: ImmutableMap<VariableDescriptor, D> = ImmutableHashMap.empty()
) : ImmutableMap<VariableDescriptor, D> by map, ReadOnlyControlFlowInfo<D> { ) : ImmutableMap<VariableDescriptor, D> by map, ReadOnlyControlFlowInfo<D> {
abstract protected fun copy(newMap: ImmutableMap<VariableDescriptor, D>): S abstract protected fun copy(newMap: ImmutableMap<VariableDescriptor, D>): S
@@ -69,7 +69,7 @@ interface ReadOnlyInitControlFlowInfo : ReadOnlyControlFlowInfo<VariableControlF
typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo<VariableUseState> typealias ReadOnlyUseControlFlowInfo = ReadOnlyControlFlowInfo<VariableUseState>
class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlFlowState> = ImmutableHashMap.empty()) : class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlFlowState> = ImmutableHashMap.empty()) :
ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map), ReadOnlyInitControlFlowInfo { ControlFlowInfo<InitControlFlowInfo, VariableControlFlowState>(map), ReadOnlyInitControlFlowInfo {
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) = InitControlFlowInfo(newMap) override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableControlFlowState>) = InitControlFlowInfo(newMap)
// this = output of EXHAUSTIVE_WHEN_ELSE instruction // this = output of EXHAUSTIVE_WHEN_ELSE instruction
@@ -87,7 +87,7 @@ class InitControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableControlF
} }
class UseControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableUseState> = ImmutableHashMap.empty()) : class UseControlFlowInfo(map: ImmutableMap<VariableDescriptor, VariableUseState> = ImmutableHashMap.empty()) :
ControlFlowInfo<UseControlFlowInfo, VariableUseState>(map), ReadOnlyUseControlFlowInfo { ControlFlowInfo<UseControlFlowInfo, VariableUseState>(map), ReadOnlyUseControlFlowInfo {
override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableUseState>) = UseControlFlowInfo(newMap) override fun copy(newMap: ImmutableMap<VariableDescriptor, VariableUseState>) = UseControlFlowInfo(newMap)
} }
@@ -144,13 +144,13 @@ class VariableControlFlowState private constructor(val initState: InitState, val
} }
fun createInitializedExhaustively(isDeclared: Boolean): VariableControlFlowState = fun createInitializedExhaustively(isDeclared: Boolean): VariableControlFlowState =
create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared) create(InitState.INITIALIZED_EXHAUSTIVELY, isDeclared)
fun create(isInitialized: Boolean, isDeclared: Boolean = false): VariableControlFlowState = fun create(isInitialized: Boolean, isDeclared: Boolean = false): VariableControlFlowState =
create(if (isInitialized) InitState.INITIALIZED else InitState.NOT_INITIALIZED, isDeclared) create(if (isInitialized) InitState.INITIALIZED else InitState.NOT_INITIALIZED, isDeclared)
fun create(isDeclaredHere: Boolean, mergedEdgesData: VariableControlFlowState?): VariableControlFlowState = fun create(isDeclaredHere: Boolean, mergedEdgesData: VariableControlFlowState?): VariableControlFlowState =
create(true, isDeclaredHere || mergedEdgesData != null && mergedEdgesData.isDeclared) create(true, isDeclaredHere || mergedEdgesData != null && mergedEdgesData.isDeclared)
} }
} }
@@ -51,11 +51,11 @@ import org.jetbrains.kotlin.types.isFlexible
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
class ControlFlowInformationProvider private constructor( class ControlFlowInformationProvider private constructor(
private val subroutine: KtElement, private val subroutine: KtElement,
private val trace: BindingTrace, private val trace: BindingTrace,
private val pseudocode: Pseudocode, private val pseudocode: Pseudocode,
private val languageVersionSettings: LanguageVersionSettings, private val languageVersionSettings: LanguageVersionSettings,
private val diagnosticSuppressor: PlatformDiagnosticSuppressor private val diagnosticSuppressor: PlatformDiagnosticSuppressor
) { ) {
private val pseudocodeVariablesData by lazy { private val pseudocodeVariablesData by lazy {
@@ -63,16 +63,16 @@ class ControlFlowInformationProvider private constructor(
} }
constructor( constructor(
declaration: KtElement, declaration: KtElement,
trace: BindingTrace, trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings, languageVersionSettings: LanguageVersionSettings,
diagnosticSuppressor: PlatformDiagnosticSuppressor diagnosticSuppressor: PlatformDiagnosticSuppressor
) : this( ) : this(
declaration, declaration,
trace, trace,
ControlFlowProcessor(trace).generatePseudocode(declaration), ControlFlowProcessor(trace).generatePseudocode(declaration),
languageVersionSettings, languageVersionSettings,
diagnosticSuppressor diagnosticSuppressor
) )
fun checkForLocalClassOrObjectMode() { fun checkForLocalClassOrObjectMode() {
@@ -161,8 +161,7 @@ class ControlFlowInformationProvider private constructor(
override fun visitInstruction(instruction: Instruction) { override fun visitInstruction(instruction: Instruction) {
if (instruction is KtElementInstruction) { if (instruction is KtElementInstruction) {
returnedExpressions.add(instruction.element) returnedExpressions.add(instruction.element)
} } else {
else {
throw IllegalStateException("$instruction precedes the exit point") throw IllegalStateException("$instruction precedes the exit point")
} }
} }
@@ -179,7 +178,7 @@ class ControlFlowInformationProvider private constructor(
val expectedType = functionDescriptor?.returnType val expectedType = functionDescriptor?.returnType
val providerForLocalDeclaration = ControlFlowInformationProvider( val providerForLocalDeclaration = ControlFlowInformationProvider(
element, trace, localDeclarationInstruction.body, languageVersionSettings, diagnosticSuppressor element, trace, localDeclarationInstruction.body, languageVersionSettings, diagnosticSuppressor
) )
providerForLocalDeclaration.checkFunction(expectedType) providerForLocalDeclaration.checkFunction(expectedType)
@@ -189,7 +188,7 @@ class ControlFlowInformationProvider private constructor(
private fun checkDefiniteReturn(expectedReturnType: KotlinType, unreachableCode: UnreachableCode) { private fun checkDefiniteReturn(expectedReturnType: KotlinType, unreachableCode: UnreachableCode) {
val function = subroutine as? KtDeclarationWithBody val function = subroutine as? KtDeclarationWithBody
?: throw AssertionError("checkDefiniteReturn is called for ${subroutine.text} which is not KtDeclarationWithBody") ?: throw AssertionError("checkDefiniteReturn is called for ${subroutine.text} which is not KtDeclarationWithBody")
if (!function.hasBody()) return if (!function.hasBody()) return
@@ -244,16 +243,15 @@ class ControlFlowInformationProvider private constructor(
if (instruction is JumpInstruction) { if (instruction is JumpInstruction) {
val isJumpElement = element is KtBreakExpression val isJumpElement = element is KtBreakExpression
|| element is KtContinueExpression || element is KtContinueExpression
|| element is KtReturnExpression || element is KtReturnExpression
|| element is KtThrowExpression || element is KtThrowExpression
if (!isJumpElement) continue if (!isJumpElement) continue
} }
if (instruction.dead) { if (instruction.dead) {
unreachableElements.add(element) unreachableElements.add(element)
} } else {
else {
reachableElements.add(element) reachableElements.add(element)
} }
} }
@@ -274,10 +272,9 @@ class ControlFlowInformationProvider private constructor(
val reportedDiagnosticMap = hashMapOf<Instruction, DiagnosticFactory<*>>() val reportedDiagnosticMap = hashMapOf<Instruction, DiagnosticFactory<*>>()
pseudocode.traverse(TraversalOrder.FORWARD, initializers) { pseudocode.traverse(TraversalOrder.FORWARD, initializers) { instruction: Instruction,
instruction: Instruction, enterData: ReadOnlyInitControlFlowInfo,
enterData: ReadOnlyInitControlFlowInfo, exitData: ReadOnlyInitControlFlowInfo ->
exitData: ReadOnlyInitControlFlowInfo ->
val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo) val ctxt = VariableInitContext(instruction, reportedDiagnosticMap, enterData, exitData, blockScopeVariableInfo)
if (ctxt.variableDescriptor == null) return@traverse if (ctxt.variableDescriptor == null) return@traverse
@@ -291,8 +288,10 @@ class ControlFlowInformationProvider private constructor(
} }
if (instruction !is WriteValueInstruction) return@traverse if (instruction !is WriteValueInstruction) return@traverse
val element = instruction.lValue as? KtExpression ?: return@traverse val element = instruction.lValue as? KtExpression ?: return@traverse
var error = checkValReassignment(ctxt, element, instruction, var error = checkValReassignment(
varWithValReassignErrorGenerated) ctxt, element, instruction,
varWithValReassignErrorGenerated
)
if (!error && processClassOrObject) { if (!error && processClassOrObject) {
error = checkAssignmentBeforeDeclaration(ctxt, element) error = checkAssignmentBeforeDeclaration(ctxt, element)
} }
@@ -319,9 +318,9 @@ class ControlFlowInformationProvider private constructor(
} }
private fun checkIsInitialized( private fun checkIsInitialized(
ctxt: VariableInitContext, ctxt: VariableInitContext,
element: KtElement, element: KtElement,
varWithUninitializedErrorGenerated: MutableCollection<VariableDescriptor> varWithUninitializedErrorGenerated: MutableCollection<VariableDescriptor>
) { ) {
if (element !is KtSimpleNameExpression) return if (element !is KtSimpleNameExpression) return
@@ -333,8 +332,7 @@ class ControlFlowInformationProvider private constructor(
if (!isDefinitelyInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) { if (!isDefinitelyInitialized && !varWithUninitializedErrorGenerated.contains(variableDescriptor)) {
if (variableDescriptor !is PropertyDescriptor) { if (variableDescriptor !is PropertyDescriptor) {
variableDescriptor?.let { varWithUninitializedErrorGenerated.add(it) } variableDescriptor?.let { varWithUninitializedErrorGenerated.add(it) }
} } else if (variableDescriptor.isLateInit) {
else if (variableDescriptor.isLateInit) {
trace.record(MUST_BE_LATEINIT, variableDescriptor) trace.record(MUST_BE_LATEINIT, variableDescriptor)
return return
} }
@@ -352,7 +350,8 @@ class ControlFlowInformationProvider private constructor(
report(Errors.UNINITIALIZED_ENUM_COMPANION.on(element, container), ctxt) report(Errors.UNINITIALIZED_ENUM_COMPANION.on(element, container), ctxt)
} }
} }
else -> {} else -> {
}
} }
} }
is VariableDescriptor -> is VariableDescriptor ->
@@ -365,8 +364,8 @@ class ControlFlowInformationProvider private constructor(
} }
private fun isCapturedWrite( private fun isCapturedWrite(
variableDescriptor: VariableDescriptor, variableDescriptor: VariableDescriptor,
writeValueInstruction: WriteValueInstruction writeValueInstruction: WriteValueInstruction
): Boolean { ): Boolean {
val containingDeclarationDescriptor = variableDescriptor.containingDeclaration val containingDeclarationDescriptor = variableDescriptor.containingDeclaration
// Do not consider top-level properties // Do not consider top-level properties
@@ -405,10 +404,10 @@ class ControlFlowInformationProvider private constructor(
} }
private fun checkValReassignment( private fun checkValReassignment(
ctxt: VariableInitContext, ctxt: VariableInitContext,
expression: KtExpression, expression: KtExpression,
writeValueInstruction: WriteValueInstruction, writeValueInstruction: WriteValueInstruction,
varWithValReassignErrorGenerated: MutableCollection<VariableDescriptor> varWithValReassignErrorGenerated: MutableCollection<VariableDescriptor>
): Boolean { ): Boolean {
val variableDescriptor = ctxt.variableDescriptor val variableDescriptor = ctxt.variableDescriptor
val mayBeInitializedNotHere = ctxt.enterInitState?.mayBeInitialized() ?: false val mayBeInitializedNotHere = ctxt.enterInitState?.mayBeInitialized() ?: false
@@ -424,8 +423,12 @@ class ControlFlowInformationProvider private constructor(
if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor) if (Visibilities.isVisible(receiverValue, variableDescriptor, descriptor)
&& setterDescriptor != null && setterDescriptor != null
&& !Visibilities.isVisible(receiverValue, setterDescriptor, descriptor)) { && !Visibilities.isVisible(receiverValue, setterDescriptor, descriptor)) {
report(Errors.INVISIBLE_SETTER.on(expression, variableDescriptor, setterDescriptor.visibility, report(
setterDescriptor), ctxt) Errors.INVISIBLE_SETTER.on(
expression, variableDescriptor, setterDescriptor.visibility,
setterDescriptor
), ctxt
)
return true return true
} }
} }
@@ -436,11 +439,11 @@ class ControlFlowInformationProvider private constructor(
var hasReassignMethodReturningUnit = false var hasReassignMethodReturningUnit = false
val parent = expression.parent val parent = expression.parent
val operationReference = val operationReference =
when (parent) { when (parent) {
is KtBinaryExpression -> parent.operationReference is KtBinaryExpression -> parent.operationReference
is KtUnaryExpression -> parent.operationReference is KtUnaryExpression -> parent.operationReference
else -> null else -> null
} }
if (operationReference != null) { if (operationReference != null) {
val descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference) val descriptor = trace.get(BindingContext.REFERENCE_TARGET, operationReference)
if (descriptor is FunctionDescriptor) { if (descriptor is FunctionDescriptor) {
@@ -462,16 +465,13 @@ class ControlFlowInformationProvider private constructor(
if (captured && !mayBeInitializedNotHere && hasBackingField && isThisOrNoDispatchReceiver) { if (captured && !mayBeInitializedNotHere && hasBackingField && isThisOrNoDispatchReceiver) {
if (variableDescriptor.containingDeclaration is ClassDescriptor) { if (variableDescriptor.containingDeclaration is ClassDescriptor) {
report(Errors.CAPTURED_MEMBER_VAL_INITIALIZATION.on(expression, variableDescriptor), ctxt) report(Errors.CAPTURED_MEMBER_VAL_INITIALIZATION.on(expression, variableDescriptor), ctxt)
} } else {
else {
report(Errors.CAPTURED_VAL_INITIALIZATION.on(expression, variableDescriptor), ctxt) report(Errors.CAPTURED_VAL_INITIALIZATION.on(expression, variableDescriptor), ctxt)
} }
} } else {
else {
if (KtPsiUtil.isBackingFieldReference(variableDescriptor)) { if (KtPsiUtil.isBackingFieldReference(variableDescriptor)) {
reportValReassigned(expression, variableDescriptor, ctxt) reportValReassigned(expression, variableDescriptor, ctxt)
} } else {
else {
report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt) report(Errors.VAL_REASSIGNMENT.on(expression, variableDescriptor), ctxt)
} }
} }
@@ -497,21 +497,20 @@ class ControlFlowInformationProvider private constructor(
} }
private fun checkAssignmentBeforeDeclaration(ctxt: VariableInitContext, expression: KtExpression) = private fun checkAssignmentBeforeDeclaration(ctxt: VariableInitContext, expression: KtExpression) =
if (ctxt.isInitializationBeforeDeclaration()) { if (ctxt.isInitializationBeforeDeclaration()) {
if (ctxt.variableDescriptor != null) { if (ctxt.variableDescriptor != null) {
report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt) report(Errors.INITIALIZATION_BEFORE_DECLARATION.on(expression, ctxt.variableDescriptor), ctxt)
}
true
}
else {
false
} }
true
} else {
false
}
private fun VariableInitContext.isInitializationBeforeDeclaration(): Boolean = private fun VariableInitContext.isInitializationBeforeDeclaration(): Boolean =
// is not declared // is not declared
enterInitState?.isDeclared != true && exitInitState?.isDeclared != true && enterInitState?.isDeclared != true && exitInitState?.isDeclared != true &&
// wasn't initialized before current instruction // wasn't initialized before current instruction
enterInitState?.mayBeInitialized() != true enterInitState?.mayBeInitialized() != true
private fun checkInitializationForCustomSetter(ctxt: VariableInitContext, expression: KtExpression): Boolean { private fun checkInitializationForCustomSetter(ctxt: VariableInitContext, expression: KtExpression): Boolean {
val variableDescriptor = ctxt.variableDescriptor val variableDescriptor = ctxt.variableDescriptor
@@ -524,17 +523,16 @@ class ControlFlowInformationProvider private constructor(
} }
val property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor) as? KtProperty val property = DescriptorToSourceUtils.descriptorToDeclaration(variableDescriptor) as? KtProperty
?: throw AssertionError("$variableDescriptor is not related to KtProperty") ?: throw AssertionError("$variableDescriptor is not related to KtProperty")
val setter = property.setter val setter = property.setter
if (variableDescriptor.modality == Modality.FINAL && (setter == null || !setter.hasBody())) { if (variableDescriptor.modality == Modality.FINAL && (setter == null || !setter.hasBody())) {
return false return false
} }
val variable = if (expression is KtDotQualifiedExpression && val variable = if (expression is KtDotQualifiedExpression &&
expression.receiverExpression is KtThisExpression) { expression.receiverExpression is KtThisExpression) {
expression.selectorExpression expression.selectorExpression
} } else {
else {
expression expression
} }
if (variable is KtSimpleNameExpression) { if (variable is KtSimpleNameExpression) {
@@ -545,8 +543,8 @@ class ControlFlowInformationProvider private constructor(
} }
private fun recordInitializedVariables( private fun recordInitializedVariables(
pseudocode: Pseudocode, pseudocode: Pseudocode,
initializersMap: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>> initializersMap: Map<Instruction, Edges<ReadOnlyInitControlFlowInfo>>
) { ) {
val initializers = initializersMap[pseudocode.exitInstruction] ?: return val initializers = initializersMap[pseudocode.exitInstruction] ?: return
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false) val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(pseudocode, false)
@@ -566,15 +564,15 @@ class ControlFlowInformationProvider private constructor(
val reportedDiagnosticMap = hashMapOf<Instruction, DiagnosticFactory<*>>() val reportedDiagnosticMap = hashMapOf<Instruction, DiagnosticFactory<*>>()
val unusedValueExpressions = hashMapOf<KtExpression, Pair<VariableDescriptor, VariableUseContext>>() val unusedValueExpressions = hashMapOf<KtExpression, Pair<VariableDescriptor, VariableUseContext>>()
val usedValueExpressions = hashSetOf<KtExpression>() val usedValueExpressions = hashSetOf<KtExpression>()
pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) { pseudocode.traverse(TraversalOrder.BACKWARD, variableStatusData) { instruction: Instruction,
instruction: Instruction, enterData: ReadOnlyUseControlFlowInfo,
enterData: ReadOnlyUseControlFlowInfo, _: ReadOnlyUseControlFlowInfo ->
_: ReadOnlyUseControlFlowInfo ->
val ctxt = VariableUseContext(instruction, reportedDiagnosticMap) val ctxt = VariableUseContext(instruction, reportedDiagnosticMap)
val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false) val declaredVariables = pseudocodeVariablesData.getDeclaredVariables(instruction.owner, false)
val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny( val variableDescriptor = PseudocodeUtil.extractVariableDescriptorIfAny(
instruction, trace.bindingContext) instruction, trace.bindingContext
)
if (variableDescriptor == null if (variableDescriptor == null
|| !declaredVariables.contains(variableDescriptor) || !declaredVariables.contains(variableDescriptor)
|| !ExpressionTypingUtils.isLocal(variableDescriptor.containingDeclaration, variableDescriptor)) { || !ExpressionTypingUtils.isLocal(variableDescriptor.containingDeclaration, variableDescriptor)) {
@@ -587,8 +585,7 @@ class ControlFlowInformationProvider private constructor(
val expressionInQuestion = instruction.element as? KtExpression ?: return@traverse val expressionInQuestion = instruction.element as? KtExpression ?: return@traverse
if (variableUseState != READ) { if (variableUseState != READ) {
unusedValueExpressions.put(expressionInQuestion, variableDescriptor to ctxt) unusedValueExpressions.put(expressionInQuestion, variableDescriptor to ctxt)
} } else {
else {
usedValueExpressions.add(expressionInQuestion) usedValueExpressions.add(expressionInQuestion)
} }
} }
@@ -618,10 +615,10 @@ class ControlFlowInformationProvider private constructor(
} }
private fun processUnusedDeclaration( private fun processUnusedDeclaration(
element: KtNamedDeclaration, element: KtNamedDeclaration,
variableDescriptor: VariableDescriptor, variableDescriptor: VariableDescriptor,
ctxt: VariableUseContext, ctxt: VariableUseContext,
variableUseState: VariableUseState? variableUseState: VariableUseState?
) { ) {
element.nameIdentifier ?: return element.nameIdentifier ?: return
if (!VariableUseState.isUsed(variableUseState)) { if (!VariableUseState.isUsed(variableUseState)) {
@@ -637,11 +634,9 @@ class ControlFlowInformationProvider private constructor(
element is KtParameter -> element is KtParameter ->
processUnusedParameter(ctxt, element, variableDescriptor) processUnusedParameter(ctxt, element, variableDescriptor)
} }
} } else if (variableUseState === ONLY_WRITTEN_NEVER_READ && KtPsiUtil.isRemovableVariableDeclaration(element)) {
else if (variableUseState === ONLY_WRITTEN_NEVER_READ && KtPsiUtil.isRemovableVariableDeclaration(element)) {
report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on(element, variableDescriptor), ctxt) report(Errors.ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE.on(element, variableDescriptor), ctxt)
} } else if (variableUseState === WRITTEN_AFTER_READ && element is KtVariableDeclaration) {
else if (variableUseState === WRITTEN_AFTER_READ && element is KtVariableDeclaration) {
when (element) { when (element) {
is KtProperty -> is KtProperty ->
element.initializer?.let { element.initializer?.let {
@@ -675,8 +670,8 @@ class ControlFlowInformationProvider private constructor(
val mainFunctionDetector = MainFunctionDetector(trace.bindingContext) val mainFunctionDetector = MainFunctionDetector(trace.bindingContext)
val isMain = owner is KtNamedFunction && mainFunctionDetector.isMain(owner) val isMain = owner is KtNamedFunction && mainFunctionDetector.isMain(owner)
val functionDescriptor = val functionDescriptor =
trace.get(DECLARATION_TO_DESCRIPTOR, owner) as? FunctionDescriptor trace.get(DECLARATION_TO_DESCRIPTOR, owner) as? FunctionDescriptor
?: throw AssertionError(owner.text) ?: throw AssertionError(owner.text)
val functionName = functionDescriptor.name val functionName = functionDescriptor.name
if (isMain if (isMain
|| functionDescriptor.isOverridableOrOverrides || functionDescriptor.isOverridableOrOverrides
@@ -691,8 +686,7 @@ class ControlFlowInformationProvider private constructor(
} }
if (anonymous) { if (anonymous) {
report(UNUSED_ANONYMOUS_PARAMETER.on(element, variableDescriptor), ctxt) report(UNUSED_ANONYMOUS_PARAMETER.on(element, variableDescriptor), ctxt)
} } else {
else {
report(UNUSED_PARAMETER.on(element, variableDescriptor), ctxt) report(UNUSED_PARAMETER.on(element, variableDescriptor), ctxt)
} }
} }
@@ -711,10 +705,12 @@ class ControlFlowInformationProvider private constructor(
if (element.isUsedAsStatement(trace.bindingContext) && instruction.sideEffectFree) { if (element.isUsedAsStatement(trace.bindingContext) && instruction.sideEffectFree) {
val context = VariableContext(instruction, reportedDiagnosticMap) val context = VariableContext(instruction, reportedDiagnosticMap)
report(when (element) { report(
is KtLambdaExpression -> Errors.UNUSED_LAMBDA_EXPRESSION.on(element) when (element) {
else -> Errors.UNUSED_EXPRESSION.on(element) is KtLambdaExpression -> Errors.UNUSED_LAMBDA_EXPRESSION.on(element)
}, context) else -> Errors.UNUSED_EXPRESSION.on(element)
}, context
)
} }
} }
} }
@@ -745,8 +741,7 @@ class ControlFlowInformationProvider private constructor(
if (thenExpression == null || elseExpression == null) { if (thenExpression == null || elseExpression == null) {
trace.report(INVALID_IF_AS_EXPRESSION.on(element.ifKeyword)) trace.report(INVALID_IF_AS_EXPRESSION.on(element.ifKeyword))
} } else {
else {
checkImplicitCastOnConditionalExpression(element) checkImplicitCastOnConditionalExpression(element)
} }
} }
@@ -811,8 +806,7 @@ class ControlFlowInformationProvider private constructor(
if (usedAsExpression && missingCases.isNotEmpty()) { if (usedAsExpression && missingCases.isNotEmpty()) {
if (elseEntry != null) continue if (elseEntry != null) continue
trace.report(NO_ELSE_IN_WHEN.on(element, missingCases)) trace.report(NO_ELSE_IN_WHEN.on(element, missingCases))
} } else if (subjectExpression != null) {
else if (subjectExpression != null) {
val subjectType = trace.getType(subjectExpression) val subjectType = trace.getType(subjectExpression)
if (elseEntry != null) { if (elseEntry != null) {
if (missingCases.isEmpty() && subjectType != null && !subjectType.isFlexible()) { if (missingCases.isEmpty() && subjectType != null && !subjectType.isFlexible()) {
@@ -923,17 +917,17 @@ class ControlFlowInformationProvider private constructor(
} }
private fun isInsideTry(element: KtElement) = private fun isInsideTry(element: KtElement) =
getParentOfType( getParentOfType(
element, element,
KtTryExpression::class.java, KtFunction::class.java, KtAnonymousInitializer::class.java KtTryExpression::class.java, KtFunction::class.java, KtAnonymousInitializer::class.java
) is KtTryExpression ) is KtTryExpression
private fun CallInstruction.isTailCall(subroutine: KtElement = this@ControlFlowInformationProvider.subroutine): Boolean { private fun CallInstruction.isTailCall(subroutine: KtElement = this@ControlFlowInformationProvider.subroutine): Boolean {
val tailInstructionDetector = TailInstructionDetector(subroutine) val tailInstructionDetector = TailInstructionDetector(subroutine)
return traverseFollowingInstructions( return traverseFollowingInstructions(
this, this,
hashSetOf(), hashSetOf(),
TraversalOrder.FORWARD TraversalOrder.FORWARD
) { ) {
if (it == this@isTailCall || it.accept(tailInstructionDetector)) if (it == this@isTailCall || it.accept(tailInstructionDetector))
TraverseInstructionResult.CONTINUE TraverseInstructionResult.CONTINUE
@@ -957,8 +951,8 @@ class ControlFlowInformationProvider private constructor(
* (depends on whether it should be reported for all or only for one of the copies) * (depends on whether it should be reported for all or only for one of the copies)
*/ */
private fun report( private fun report(
diagnostic: Diagnostic, diagnostic: Diagnostic,
ctxt: VariableContext ctxt: VariableContext
) { ) {
val instruction = ctxt.instruction val instruction = ctxt.instruction
if (instruction.copies.isEmpty()) { if (instruction.copies.isEmpty()) {
@@ -985,8 +979,7 @@ class ControlFlowInformationProvider private constructor(
if (sameErrorForAllCopies) { if (sameErrorForAllCopies) {
trace.report(diagnostic) trace.report(diagnostic)
} }
} } else {
else {
//only one reporting required //only one reporting required
if (!alreadyReported) { if (!alreadyReported) {
trace.report(diagnostic) trace.report(diagnostic)
@@ -996,26 +989,26 @@ class ControlFlowInformationProvider private constructor(
private open inner class VariableContext( private open inner class VariableContext(
internal val instruction: Instruction, internal val instruction: Instruction,
internal val reportedDiagnosticMap: MutableMap<Instruction, DiagnosticFactory<*>> internal val reportedDiagnosticMap: MutableMap<Instruction, DiagnosticFactory<*>>
) { ) {
internal val variableDescriptor = PseudocodeUtil.extractVariableDescriptorFromReference(instruction, trace.bindingContext) internal val variableDescriptor = PseudocodeUtil.extractVariableDescriptorFromReference(instruction, trace.bindingContext)
} }
private inner class VariableInitContext( private inner class VariableInitContext(
instruction: Instruction, instruction: Instruction,
map: MutableMap<Instruction, DiagnosticFactory<*>>, map: MutableMap<Instruction, DiagnosticFactory<*>>,
`in`: ReadOnlyInitControlFlowInfo, `in`: ReadOnlyInitControlFlowInfo,
out: ReadOnlyInitControlFlowInfo, out: ReadOnlyInitControlFlowInfo,
blockScopeVariableInfo: BlockScopeVariableInfo blockScopeVariableInfo: BlockScopeVariableInfo
) : VariableContext(instruction, map) { ) : VariableContext(instruction, map) {
internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`) internal val enterInitState = initialize(variableDescriptor, blockScopeVariableInfo, `in`)
internal val exitInitState = initialize(variableDescriptor, blockScopeVariableInfo, out) internal val exitInitState = initialize(variableDescriptor, blockScopeVariableInfo, out)
private fun initialize( private fun initialize(
variableDescriptor: VariableDescriptor?, variableDescriptor: VariableDescriptor?,
blockScopeVariableInfo: BlockScopeVariableInfo, blockScopeVariableInfo: BlockScopeVariableInfo,
map: ReadOnlyInitControlFlowInfo map: ReadOnlyInitControlFlowInfo
): VariableControlFlowState? { ): VariableControlFlowState? {
val state = map.getOrNull(variableDescriptor ?: return null) val state = map.getOrNull(variableDescriptor ?: return null)
if (state != null) return state if (state != null) return state
@@ -1024,26 +1017,27 @@ class ControlFlowInformationProvider private constructor(
} }
private inner class VariableUseContext( private inner class VariableUseContext(
instruction: Instruction, instruction: Instruction,
map: MutableMap<Instruction, DiagnosticFactory<*>> map: MutableMap<Instruction, DiagnosticFactory<*>>
) : VariableContext(instruction, map) ) : VariableContext(instruction, map)
companion object { companion object {
// Should return KtDeclarationWithBody, KtClassOrObject, or KtClassInitializer // Should return KtDeclarationWithBody, KtClassOrObject, or KtClassInitializer
fun getElementParentDeclaration(element: KtElement) = fun getElementParentDeclaration(element: KtElement) =
getParentOfType(element, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtClassInitializer::class.java) getParentOfType(element, KtDeclarationWithBody::class.java, KtClassOrObject::class.java, KtClassInitializer::class.java)
fun getDeclarationDescriptorIncludingConstructors(context: BindingContext, declaration: KtDeclaration?): DeclarationDescriptor? { fun getDeclarationDescriptorIncludingConstructors(context: BindingContext, declaration: KtDeclaration?): DeclarationDescriptor? {
val descriptor = context.get(DECLARATION_TO_DESCRIPTOR, val descriptor = context.get(
(declaration as? KtClassInitializer)?.containingDeclaration ?: declaration) DECLARATION_TO_DESCRIPTOR,
(declaration as? KtClassInitializer)?.containingDeclaration ?: declaration
)
return if (descriptor is ClassDescriptor && declaration is KtClassInitializer) { return if (descriptor is ClassDescriptor && declaration is KtClassInitializer) {
// For a class primary constructor, we cannot directly get ConstructorDescriptor by KtClassInitializer, // For a class primary constructor, we cannot directly get ConstructorDescriptor by KtClassInitializer,
// so we have to do additional conversion: KtClassInitializer -> KtClassOrObject -> ClassDescriptor -> ConstructorDescriptor // so we have to do additional conversion: KtClassInitializer -> KtClassOrObject -> ClassDescriptor -> ConstructorDescriptor
descriptor.unsubstitutedPrimaryConstructor descriptor.unsubstitutedPrimaryConstructor
?: (descriptor as? ClassDescriptorWithResolutionScopes)?.scopeForInitializerResolution?.ownerDescriptor ?: (descriptor as? ClassDescriptorWithResolutionScopes)?.scopeForInitializerResolution?.ownerDescriptor
} } else {
else {
descriptor descriptor
} }
} }
@@ -1069,8 +1063,8 @@ class ControlFlowInformationProvider private constructor(
} }
private fun collectResultingExpressionsOfConditionalExpressionRec( private fun collectResultingExpressionsOfConditionalExpressionRec(
expression: KtExpression?, expression: KtExpression?,
resultingExpressions: MutableList<KtExpression> resultingExpressions: MutableList<KtExpression>
) { ) {
when (expression) { when (expression) {
is KtIfExpression -> { is KtIfExpression -> {
@@ -1084,8 +1078,7 @@ class ControlFlowInformationProvider private constructor(
val resultingExpression = getResultingExpression(expression) val resultingExpression = getResultingExpression(expression)
if (resultingExpression is KtIfExpression || resultingExpression is KtWhenExpression) { if (resultingExpression is KtIfExpression || resultingExpression is KtWhenExpression) {
collectResultingExpressionsOfConditionalExpressionRec(resultingExpression, resultingExpressions) collectResultingExpressionsOfConditionalExpressionRec(resultingExpression, resultingExpressions)
} } else {
else {
resultingExpressions.add(resultingExpression) resultingExpressions.add(resultingExpression)
} }
} }
@@ -1106,8 +1099,7 @@ class ControlFlowInformationProvider private constructor(
private fun combineKinds(kind: TailRecursionKind, existingKind: TailRecursionKind?): TailRecursionKind { private fun combineKinds(kind: TailRecursionKind, existingKind: TailRecursionKind?): TailRecursionKind {
return if (existingKind == null || existingKind == kind) { return if (existingKind == null || existingKind == kind) {
kind kind
} } else {
else {
when { when {
check(kind, existingKind, IN_TRY, TAIL_CALL) -> IN_TRY check(kind, existingKind, IN_TRY, TAIL_CALL) -> IN_TRY
check(kind, existingKind, IN_TRY, NON_TAIL) -> IN_TRY check(kind, existingKind, IN_TRY, NON_TAIL) -> IN_TRY
@@ -1119,9 +1111,9 @@ class ControlFlowInformationProvider private constructor(
private fun check(a: Any, b: Any, x: Any, y: Any) = a === x && b === y || a === y && b === x private fun check(a: Any, b: Any, x: Any, y: Any) = a === x && b === y || a === y && b === x
private fun mustBeReportedOnAllCopies(diagnosticFactory: DiagnosticFactory<*>) = private fun mustBeReportedOnAllCopies(diagnosticFactory: DiagnosticFactory<*>) =
diagnosticFactory === UNUSED_VARIABLE diagnosticFactory === UNUSED_VARIABLE
|| diagnosticFactory === UNUSED_PARAMETER || diagnosticFactory === UNUSED_PARAMETER
|| diagnosticFactory === UNUSED_ANONYMOUS_PARAMETER || diagnosticFactory === UNUSED_ANONYMOUS_PARAMETER
|| diagnosticFactory === UNUSED_CHANGED_VALUE || diagnosticFactory === UNUSED_CHANGED_VALUE
} }
} }
@@ -88,8 +88,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
generateImplicitReturnValue(bodyExpression, subroutine) generateImplicitReturnValue(bodyExpression, subroutine)
} }
} }
} } else {
else {
cfpVisitor.generateInstructions(subroutine) cfpVisitor.generateInstructions(subroutine)
} }
return builder.exitSubroutine(subroutine, invocationKind) return builder.exitSubroutine(subroutine, invocationKind)
@@ -130,7 +129,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
private val conditionVisitor = object : KtVisitorVoid() { private val conditionVisitor = object : KtVisitorVoid() {
private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? = private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? =
condition.getStrictParentOfType<KtWhenExpression>()?.subjectExpression condition.getStrictParentOfType<KtWhenExpression>()?.subjectExpression
override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) { override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
if (!generateCall(condition.operationReference)) { if (!generateCall(condition.operationReference)) {
@@ -155,8 +154,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (subjectExpression != null) { if (subjectExpression != null) {
// todo: this can be replaced by equals() invocation (when corresponding resolved call is recorded) // todo: this can be replaced by equals() invocation (when corresponding resolved call is recorded)
createNonSyntheticValue(condition, MagicKind.EQUALS_IN_WHEN_CONDITION, subjectExpression, expression) createNonSyntheticValue(condition, MagicKind.EQUALS_IN_WHEN_CONDITION, subjectExpression, expression)
} } else {
else {
copyValue(expression, condition) copyValue(expression, condition)
} }
} }
@@ -193,13 +191,13 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun createSyntheticValue(instructionElement: KtElement, kind: MagicKind, vararg from: KtElement): PseudoValue = private fun createSyntheticValue(instructionElement: KtElement, kind: MagicKind, vararg from: KtElement): PseudoValue =
builder.magic(instructionElement, null, elementsToValues(from.asList()), kind).outputValue builder.magic(instructionElement, null, elementsToValues(from.asList()), kind).outputValue
private fun createNonSyntheticValue(to: KtElement, from: List<KtElement?>, kind: MagicKind): PseudoValue = private fun createNonSyntheticValue(to: KtElement, from: List<KtElement?>, kind: MagicKind): PseudoValue =
builder.magic(to, to, elementsToValues(from), kind).outputValue builder.magic(to, to, elementsToValues(from), kind).outputValue
private fun createNonSyntheticValue(to: KtElement, kind: MagicKind, vararg from: KtElement?): PseudoValue = private fun createNonSyntheticValue(to: KtElement, kind: MagicKind, vararg from: KtElement?): PseudoValue =
createNonSyntheticValue(to, from.asList(), kind) createNonSyntheticValue(to, from.asList(), kind)
private fun mergeValues(from: List<KtExpression>, to: KtExpression) { private fun mergeValues(from: List<KtExpression>, to: KtExpression) {
builder.merge(to, elementsToValues(from)) builder.merge(to, elementsToValues(from))
@@ -217,14 +215,14 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun elementsToValues(from: List<KtElement?>): List<PseudoValue> = private fun elementsToValues(from: List<KtElement?>): List<PseudoValue> =
from.mapNotNull { element -> getBoundOrUnreachableValue(element) } from.mapNotNull { element -> getBoundOrUnreachableValue(element) }
private fun generateInitializer(declaration: KtDeclaration, initValue: PseudoValue) { private fun generateInitializer(declaration: KtDeclaration, initValue: PseudoValue) {
builder.write(declaration, declaration, initValue, getDeclarationAccessTarget(declaration), emptyMap()) builder.write(declaration, declaration, initValue, getDeclarationAccessTarget(declaration), emptyMap())
} }
private fun getResolvedCallAccessTarget(element: KtElement?): AccessTarget = private fun getResolvedCallAccessTarget(element: KtElement?): AccessTarget =
element.getResolvedCall(trace.bindingContext)?.let { AccessTarget.Call(it) } element.getResolvedCall(trace.bindingContext)?.let { AccessTarget.Call(it) }
?: AccessTarget.BlackBox ?: AccessTarget.BlackBox
private fun getDeclarationAccessTarget(element: KtElement): AccessTarget { private fun getDeclarationAccessTarget(element: KtElement): AccessTarget {
@@ -276,8 +274,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext) val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall is VariableAsFunctionResolvedCall) { if (resolvedCall is VariableAsFunctionResolvedCall) {
generateCall(resolvedCall.variableCall) generateCall(resolvedCall.variableCall)
} } else {
else {
if (resolvedCall == null) { if (resolvedCall == null) {
val qualifier = trace.bindingContext[BindingContext.QUALIFIER, expression] val qualifier = trace.bindingContext[BindingContext.QUALIFIER, expression]
if (qualifier != null && generateQualifier(expression, qualifier)) return if (qualifier != null && generateQualifier(expression, qualifier)) return
@@ -305,11 +302,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val right = expression.right val right = expression.right
if (operationType === ANDAND || operationType === OROR) { if (operationType === ANDAND || operationType === OROR) {
generateBooleanOperation(expression) generateBooleanOperation(expression)
} } else if (operationType === EQ) {
else if (operationType === EQ) {
visitAssignment(left, getDeferredValue(right), expression) visitAssignment(left, getDeferredValue(right), expression)
} } else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext) val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall != null) { if (resolvedCall != null) {
val rhsValue = generateCall(resolvedCall).outputValue val rhsValue = generateCall(resolvedCall).outputValue
@@ -321,12 +316,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
*/ */
visitAssignment(left, getValueAsFunction(rhsValue), expression) visitAssignment(left, getValueAsFunction(rhsValue), expression)
} }
} } else {
else {
generateBothArgumentsAndMark(expression) generateBothArgumentsAndMark(expression)
} }
} } else if (operationType === ELVIS) {
else if (operationType === ELVIS) {
generateInstructions(left) generateInstructions(left)
mark(expression) mark(expression)
val afterElvis = builder.createUnboundLabel("after elvis operator") val afterElvis = builder.createUnboundLabel("after elvis operator")
@@ -334,8 +327,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
generateInstructions(right) generateInstructions(right)
builder.bindLabel(afterElvis) builder.bindLabel(afterElvis)
mergeValues(listOf(left, right).filterNotNull(), expression) mergeValues(listOf(left, right).filterNotNull(), expression)
} } else {
else {
if (!generateCall(expression)) { if (!generateCall(expression)) {
generateBothArgumentsAndMark(expression) generateBothArgumentsAndMark(expression)
} }
@@ -351,8 +343,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
generateInstructions(left) generateInstructions(left)
if (operationType === ANDAND) { if (operationType === ANDAND) {
builder.jumpOnFalse(resultLabel, expression, builder.getBoundValue(left)) builder.jumpOnFalse(resultLabel, expression, builder.getBoundValue(left))
} } else {
else {
builder.jumpOnTrue(resultLabel, expression, builder.getBoundValue(left)) builder.jumpOnTrue(resultLabel, expression, builder.getBoundValue(left))
} }
generateInstructions(right) generateInstructions(right)
@@ -382,9 +373,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun visitAssignment( private fun visitAssignment(
lhs: KtExpression?, lhs: KtExpression?,
rhsDeferredValue: () -> PseudoValue?, rhsDeferredValue: () -> PseudoValue?,
parentExpression: KtExpression parentExpression: KtExpression
) { ) {
val left = KtPsiUtil.deparenthesize(lhs) val left = KtPsiUtil.deparenthesize(lhs)
if (left == null) { if (left == null) {
@@ -405,8 +396,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (accessTarget is AccessTarget.Call) { if (accessTarget is AccessTarget.Call) {
receiverValues = getReceiverValues(accessTarget.resolvedCall) receiverValues = getReceiverValues(accessTarget.resolvedCall)
} }
} } else if (left is KtProperty) {
else if (left is KtProperty) {
accessTarget = getDeclarationAccessTarget(left) accessTarget = getDeclarationAccessTarget(left)
} }
@@ -421,9 +411,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun generateArrayAssignment( private fun generateArrayAssignment(
lhs: KtArrayAccessExpression, lhs: KtArrayAccessExpression,
rhsDeferredValue: () -> PseudoValue?, rhsDeferredValue: () -> PseudoValue?,
parentExpression: KtExpression parentExpression: KtExpression
) { ) {
val setResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_SET, lhs) val setResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_SET, lhs)
@@ -456,11 +446,11 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
* we first generate instructions for 1 and 2 whereas 3 is replaced by pseudo-value corresponding to "a.get(1) + 3" * we first generate instructions for 1 and 2 whereas 3 is replaced by pseudo-value corresponding to "a.get(1) + 3"
*/ */
private fun getArraySetterArguments( private fun getArraySetterArguments(
rhsDeferredValue: () -> PseudoValue?, rhsDeferredValue: () -> PseudoValue?,
setResolvedCall: ResolvedCall<FunctionDescriptor> setResolvedCall: ResolvedCall<FunctionDescriptor>
): SmartFMap<PseudoValue, ValueParameterDescriptor> { ): SmartFMap<PseudoValue, ValueParameterDescriptor> {
val valueArguments = setResolvedCall.resultingDescriptor.valueParameters.flatMapTo( val valueArguments = setResolvedCall.resultingDescriptor.valueParameters.flatMapTo(
ArrayList<ValueArgument>() ArrayList<ValueArgument>()
) { descriptor -> setResolvedCall.valueArguments[descriptor]?.arguments ?: emptyList() } ) { descriptor -> setResolvedCall.valueArguments[descriptor]?.arguments ?: emptyList() }
val rhsArgument = valueArguments.lastOrNull() val rhsArgument = valueArguments.lastOrNull()
@@ -470,8 +460,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val parameterDescriptor = argumentMapping.valueParameter val parameterDescriptor = argumentMapping.valueParameter
if (valueArgument !== rhsArgument) { if (valueArgument !== rhsArgument) {
argumentValues = generateValueArgument(valueArgument, parameterDescriptor, argumentValues) argumentValues = generateValueArgument(valueArgument, parameterDescriptor, argumentValues)
} } else {
else {
val rhsValue = rhsDeferredValue.invoke() val rhsValue = rhsDeferredValue.invoke()
if (rhsValue != null) { if (rhsValue != null) {
argumentValues = argumentValues.plus(rhsValue, parameterDescriptor) argumentValues = argumentValues.plus(rhsValue, parameterDescriptor)
@@ -525,8 +514,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val rhsValue: PseudoValue? = if (resolvedCall != null) { val rhsValue: PseudoValue? = if (resolvedCall != null) {
generateCall(resolvedCall).outputValue generateCall(resolvedCall).outputValue
} } else {
else {
generateInstructions(baseExpression) generateInstructions(baseExpression)
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression) createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression)
} }
@@ -540,7 +528,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun isIncrementOrDecrement(operationType: IElementType): Boolean = private fun isIncrementOrDecrement(operationType: IElementType): Boolean =
operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS
override fun visitIfExpression(expression: KtIfExpression) { override fun visitIfExpression(expression: KtIfExpression) {
mark(expression) mark(expression)
@@ -553,8 +541,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (thenBranch != null) { if (thenBranch != null) {
branches.add(thenBranch) branches.add(thenBranch)
generateInstructions(thenBranch) generateInstructions(thenBranch)
} } else {
else {
builder.loadUnit(expression) builder.loadUnit(expression)
} }
val resultLabel = builder.createUnboundLabel("'if' expression result") val resultLabel = builder.createUnboundLabel("'if' expression result")
@@ -564,8 +551,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (elseBranch != null) { if (elseBranch != null) {
branches.add(elseBranch) branches.add(elseBranch)
generateInstructions(elseBranch) generateInstructions(elseBranch)
} } else {
else {
builder.loadUnit(expression) builder.loadUnit(expression)
} }
builder.bindLabel(resultLabel) builder.bindLabel(resultLabel)
@@ -684,8 +670,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
builder.enterBlockScope(catchClause) builder.enterBlockScope(catchClause)
if (!isFirst) { if (!isFirst) {
builder.bindLabel(catchLabels.remove()) builder.bindLabel(catchLabels.remove())
} } else {
else {
isFirst = false isFirst = false
} }
val catchParameter = catchClause.catchParameter val catchParameter = catchClause.catchParameter
@@ -713,8 +698,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
mark(expression) mark(expression)
if (!CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace.bindingContext, true)) { if (!CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace.bindingContext, true)) {
builder.jumpOnFalse(loopInfo.exitPoint, expression, builder.getBoundValue(condition)) builder.jumpOnFalse(loopInfo.exitPoint, expression, builder.getBoundValue(condition))
} } else {
else {
assert(condition != null) { "Invalid while condition: " + expression.text } assert(condition != null) { "Invalid while condition: " + expression.text }
createSyntheticValue(condition!!, MagicKind.VALUE_CONSUMER, condition) createSyntheticValue(condition!!, MagicKind.VALUE_CONSUMER, condition)
} }
@@ -741,8 +725,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
builder.exitBlockScope(expression) builder.exitBlockScope(expression)
if (!CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace.bindingContext, true)) { if (!CompileTimeConstantUtils.canBeReducedToBooleanConstant(condition, trace.bindingContext, true)) {
builder.jumpOnTrue(loopInfo.entryPoint, expression, builder.getBoundValue(expression.condition)) builder.jumpOnTrue(loopInfo.entryPoint, expression, builder.getBoundValue(expression.condition))
} } else {
else {
assert(condition != null) { "Invalid do / while condition: " + expression.text } assert(condition != null) { "Invalid do / while condition: " + expression.text }
createSyntheticValue(condition!!, MagicKind.VALUE_CONSUMER, condition) createSyntheticValue(condition!!, MagicKind.VALUE_CONSUMER, condition)
builder.jump(loopInfo.entryPoint, expression) builder.jump(loopInfo.entryPoint, expression)
@@ -781,8 +764,8 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun generateLoopConventionCall( private fun generateLoopConventionCall(
loopRange: KtExpression?, loopRange: KtExpression?,
callSlice: ReadOnlySlice<KtExpression, ResolvedCall<FunctionDescriptor>> callSlice: ReadOnlySlice<KtExpression, ResolvedCall<FunctionDescriptor>>
) { ) {
if (loopRange == null) return if (loopRange == null) return
val resolvedCall = trace.bindingContext[callSlice, loopRange] ?: return val resolvedCall = trace.bindingContext[callSlice, loopRange] ?: return
@@ -795,8 +778,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val destructuringDeclaration = loopParameter.destructuringDeclaration val destructuringDeclaration = loopParameter.destructuringDeclaration
if (destructuringDeclaration != null) { if (destructuringDeclaration != null) {
visitDestructuringDeclaration(destructuringDeclaration, false) visitDestructuringDeclaration(destructuringDeclaration, false)
} } else {
else {
builder.declareParameter(loopParameter) builder.declareParameter(loopParameter)
} }
} }
@@ -807,10 +789,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val loopRange = expression.loopRange val loopRange = expression.loopRange
val value = builder.magic( val value = builder.magic(
loopRange ?: expression, loopRange ?: expression,
null, null,
ContainerUtil.createMaybeSingletonList(builder.getBoundValue(loopRange)), ContainerUtil.createMaybeSingletonList(builder.getBoundValue(loopRange)),
MagicKind.LOOP_RANGE_ITERATION MagicKind.LOOP_RANGE_ITERATION
).outputValue ).outputValue
if (loopParameter != null) { if (loopParameter != null) {
@@ -819,8 +801,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
for (entry in destructuringDeclaration.entries) { for (entry in destructuringDeclaration.entries) {
generateInitializer(entry, value) generateInitializer(entry, value)
} }
} } else {
else {
generateInitializer(loopParameter, value) generateInitializer(loopParameter, value)
} }
} }
@@ -857,8 +838,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val parentBody = parentLoop.body val parentBody = parentLoop.body
return if (parentBody != null && parentBody.textRange.contains(expression.textRange)) { return if (parentBody != null && parentBody.textRange.contains(expression.textRange)) {
parentLoop parentLoop
} } else {
else {
getNearestLoopExpression(parentLoop) getNearestLoopExpression(parentLoop)
} }
} }
@@ -871,20 +851,19 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val labeledElement = trace.get(BindingContext.LABEL_TARGET, targetLabel) val labeledElement = trace.get(BindingContext.LABEL_TARGET, targetLabel)
loop = if (labeledElement is KtLoopExpression) { loop = if (labeledElement is KtLoopExpression) {
labeledElement labeledElement
} } else {
else {
trace.report(NOT_A_LOOP_LABEL.on(expression, targetLabel.text)) trace.report(NOT_A_LOOP_LABEL.on(expression, targetLabel.text))
null null
} }
} } else {
else {
loop = getCorrespondingLoopWithoutLabel(expression) loop = getCorrespondingLoopWithoutLabel(expression)
if (loop == null) { if (loop == null) {
trace.report(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP.on(expression)) trace.report(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP.on(expression))
} } else {
else { val whenExpression = PsiTreeUtil.getParentOfType(
val whenExpression = PsiTreeUtil.getParentOfType(expression, KtWhenExpression::class.java, true, expression, KtWhenExpression::class.java, true,
KtLoopExpression::class.java) KtLoopExpression::class.java
)
if (whenExpression != null) { if (whenExpression != null) {
trace.report(BREAK_OR_CONTINUE_IN_WHEN.on(expression)) trace.report(BREAK_OR_CONTINUE_IN_WHEN.on(expression))
} }
@@ -931,8 +910,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression)) trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression))
} }
false false
} } else {
else {
true true
} }
} }
@@ -953,12 +931,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (labeledElement != null) { if (labeledElement != null) {
assert(labeledElement is KtElement) assert(labeledElement is KtElement)
labeledElement as KtElement? labeledElement as KtElement?
} } else {
else {
null null
} }
} } else {
else {
builder.returnSubroutine builder.returnSubroutine
// TODO : a context check // TODO : a context check
} }
@@ -967,12 +943,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val returnValue = if (returnedExpression != null) builder.getBoundValue(returnedExpression) else null val returnValue = if (returnedExpression != null) builder.getBoundValue(returnedExpression) else null
if (returnValue == null) { if (returnValue == null) {
builder.returnNoValue(expression, subroutine) builder.returnNoValue(expression, subroutine)
} } else {
else {
builder.returnValue(expression, returnValue, subroutine) builder.returnValue(expression, returnValue, subroutine)
} }
} } else {
else {
createNonSyntheticValue(expression, MagicKind.UNSUPPORTED_ELEMENT, returnedExpression) createNonSyntheticValue(expression, MagicKind.UNSUPPORTED_ELEMENT, returnedExpression)
} }
} }
@@ -1018,8 +992,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
if (statements.isEmpty()) { if (statements.isEmpty()) {
builder.loadUnit(expression) builder.loadUnit(expression)
} } else {
else {
copyValue(statements.lastOrNull(), expression) copyValue(statements.lastOrNull(), expression)
} }
if (declareBlockScope) { if (declareBlockScope) {
@@ -1035,8 +1008,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
private fun visitFunction(function: KtFunction, invocationKind: InvocationKind? = null) { private fun visitFunction(function: KtFunction, invocationKind: InvocationKind? = null) {
if (invocationKind == null) { if (invocationKind == null) {
processLocalDeclaration(function) processLocalDeclaration(function)
} } else {
else {
visitInlinedFunction(function, invocationKind) visitInlinedFunction(function, invocationKind)
} }
@@ -1093,16 +1065,14 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (selectorExpression is KtCallExpression || selectorExpression is KtSimpleNameExpression) { if (selectorExpression is KtCallExpression || selectorExpression is KtSimpleNameExpression) {
if (!safe) { if (!safe) {
generateInstructions(selectorExpression) generateInstructions(selectorExpression)
} } else {
else {
val resultLabel = builder.createUnboundLabel("result of call") val resultLabel = builder.createUnboundLabel("result of call")
builder.jumpOnFalse(resultLabel, expression, null) builder.jumpOnFalse(resultLabel, expression, null)
generateInstructions(selectorExpression) generateInstructions(selectorExpression)
builder.bindLabel(resultLabel) builder.bindLabel(resultLabel)
} }
copyValue(selectorExpression, expression) copyValue(selectorExpression, expression)
} } else {
else {
generateInstructions(receiverExpression) generateInstructions(receiverExpression)
createNonSyntheticValue(expression, MagicKind.UNSUPPORTED_ELEMENT, receiverExpression) createNonSyntheticValue(expression, MagicKind.UNSUPPORTED_ELEMENT, receiverExpression)
} }
@@ -1182,12 +1152,12 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val writtenValue: PseudoValue? val writtenValue: PseudoValue?
writtenValue = if (resolvedCall != null) { writtenValue = if (resolvedCall != null) {
builder.call( builder.call(
entry, entry,
resolvedCall, resolvedCall,
getReceiverValues(resolvedCall), getReceiverValues(resolvedCall),
emptyMap()).outputValue emptyMap()
} ).outputValue
else { } else {
initializer?.let { createSyntheticValue(entry, MagicKind.UNRESOLVED_CALL, it) } initializer?.let { createSyntheticValue(entry, MagicKind.UNRESOLVED_CALL, it) }
} }
@@ -1211,8 +1181,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (getBoundOrUnreachableValue(left) != null) { if (getBoundOrUnreachableValue(left) != null) {
createNonSyntheticValue(expression, MagicKind.CAST, left) createNonSyntheticValue(expression, MagicKind.CAST, left)
} }
} } else {
else {
visitKtElement(expression) visitKtElement(expression)
createNonSyntheticValue(expression, MagicKind.UNSUPPORTED_ELEMENT, left) createNonSyntheticValue(expression, MagicKind.UNSUPPORTED_ELEMENT, left)
} }
@@ -1376,8 +1345,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val classDescriptor = trace[BindingContext.DECLARATION_TO_DESCRIPTOR, entryOrObject] val classDescriptor = trace[BindingContext.DECLARATION_TO_DESCRIPTOR, entryOrObject]
if (classDescriptor is ClassDescriptor) { if (classDescriptor is ClassDescriptor) {
builder.declareEntryOrObject(entryOrObject) builder.declareEntryOrObject(entryOrObject)
builder.write(entryOrObject, entryOrObject, createSyntheticValue(entryOrObject, MagicKind.FAKE_INITIALIZER), builder.write(
AccessTarget.Declaration(FakeCallableDescriptorForObject(classDescriptor)), emptyMap()) entryOrObject, entryOrObject, createSyntheticValue(entryOrObject, MagicKind.FAKE_INITIALIZER),
AccessTarget.Declaration(FakeCallableDescriptorForObject(classDescriptor)), emptyMap()
)
generateInstructions(entryOrObject) generateInstructions(entryOrObject)
} }
} }
@@ -1431,7 +1402,8 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) { override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) {
val classOrObject = PsiTreeUtil.getParentOfType(constructor, KtClassOrObject::class.java) ?: error("Guaranteed by parsing contract") val classOrObject =
PsiTreeUtil.getParentOfType(constructor, KtClassOrObject::class.java) ?: error("Guaranteed by parsing contract")
processParameters(constructor.valueParameters) processParameters(constructor.valueParameters)
generateCallOrMarkUnresolved(constructor.getDelegationCall()) generateCallOrMarkUnresolved(constructor.getDelegationCall())
@@ -1493,8 +1465,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
trace.bindingContext.get(BindingContext.DOUBLE_COLON_LHS, receiverExpression) is DoubleColonLHS.Expression) { trace.bindingContext.get(BindingContext.DOUBLE_COLON_LHS, receiverExpression) is DoubleColonLHS.Expression) {
generateInstructions(receiverExpression) generateInstructions(receiverExpression)
createNonSyntheticValue(expression, MagicKind.BOUND_CALLABLE_REFERENCE, receiverExpression) createNonSyntheticValue(expression, MagicKind.BOUND_CALLABLE_REFERENCE, receiverExpression)
} } else {
else {
createNonSyntheticValue(expression, MagicKind.UNBOUND_CALLABLE_REFERENCE) createNonSyntheticValue(expression, MagicKind.UNBOUND_CALLABLE_REFERENCE)
} }
} }
@@ -1541,8 +1512,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
val argumentExpression = argument.getArgumentExpression() val argumentExpression = argument.getArgumentExpression()
if (argumentMapping is ArgumentMatch) { if (argumentMapping is ArgumentMatch) {
parameterValues = generateValueArgument(argument, argumentMapping.valueParameter, parameterValues) parameterValues = generateValueArgument(argument, argumentMapping.valueParameter, parameterValues)
} } else if (argumentExpression != null) {
else if (argumentExpression != null) {
generateInstructions(argumentExpression) generateInstructions(argumentExpression)
createSyntheticValue(argumentExpression, MagicKind.VALUE_CONSUMER, argumentExpression) createSyntheticValue(argumentExpression, MagicKind.VALUE_CONSUMER, argumentExpression)
} }
@@ -1551,7 +1521,8 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
if (resolvedCall.resultingDescriptor is VariableDescriptor) { if (resolvedCall.resultingDescriptor is VariableDescriptor) {
// If a callee of the call is just a variable (without 'invoke'), 'read variable' is generated. // If a callee of the call is just a variable (without 'invoke'), 'read variable' is generated.
// todo : process arguments for such a case (KT-5387) // todo : process arguments for such a case (KT-5387)
val callExpression = callElement as? KtExpression ?: error("Variable-based call without callee expression: " + callElement.text) val callExpression =
callElement as? KtExpression ?: error("Variable-based call without callee expression: " + callElement.text)
assert(parameterValues.isEmpty()) { "Variable-based call with non-empty argument list: " + callElement.text } assert(parameterValues.isEmpty()) { "Variable-based call with non-empty argument list: " + callElement.text }
return builder.readVariable(callExpression, resolvedCall, receivers) return builder.readVariable(callExpression, resolvedCall, receivers)
} }
@@ -1589,9 +1560,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun getReceiverValues( private fun getReceiverValues(
callElement: KtElement, callElement: KtElement,
receiver: ReceiverValue?, receiver: ReceiverValue?,
receiverValuesArg: SmartFMap<PseudoValue, ReceiverValue> receiverValuesArg: SmartFMap<PseudoValue, ReceiverValue>
): SmartFMap<PseudoValue, ReceiverValue> { ): SmartFMap<PseudoValue, ReceiverValue> {
var receiverValues = receiverValuesArg var receiverValues = receiverValuesArg
if (receiver == null || receiverValues.containsValue(receiver)) return receiverValues if (receiver == null || receiverValues.containsValue(receiver)) return receiverValues
@@ -1633,9 +1604,9 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
} }
private fun generateValueArgument( private fun generateValueArgument(
valueArgument: ValueArgument, valueArgument: ValueArgument,
parameterDescriptor: ValueParameterDescriptor, parameterDescriptor: ValueParameterDescriptor,
parameterValuesArg: SmartFMap<PseudoValue, ValueParameterDescriptor> parameterValuesArg: SmartFMap<PseudoValue, ValueParameterDescriptor>
): SmartFMap<PseudoValue, ValueParameterDescriptor> { ): SmartFMap<PseudoValue, ValueParameterDescriptor> {
var parameterValues = parameterValuesArg var parameterValues = parameterValuesArg
val expression = valueArgument.getArgumentExpression() val expression = valueArgument.getArgumentExpression()
@@ -19,12 +19,12 @@ package org.jetbrains.kotlin.cfg
import org.jetbrains.kotlin.psi.KtLoopExpression import org.jetbrains.kotlin.psi.KtLoopExpression
class LoopInfo( class LoopInfo(
override val element: KtLoopExpression, override val element: KtLoopExpression,
entryPoint: Label, entryPoint: Label,
exitPoint: Label, exitPoint: Label,
val bodyEntryPoint: Label, val bodyEntryPoint: Label,
val bodyExitPoint: Label, val bodyExitPoint: Label,
val conditionEntryPoint: Label val conditionEntryPoint: Label
) : BreakableBlockInfo(element, entryPoint, exitPoint) { ) : BreakableBlockInfo(element, entryPoint, exitPoint) {
init { init {
@@ -27,8 +27,8 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
import java.util.* import java.util.*
fun Pseudocode.traverse( fun Pseudocode.traverse(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
analyzeInstruction: (Instruction) -> Unit analyzeInstruction: (Instruction) -> Unit
) { ) {
val instructions = getInstructions(traversalOrder) val instructions = getInstructions(traversalOrder)
for (instruction in instructions) { for (instruction in instructions) {
@@ -40,9 +40,9 @@ fun Pseudocode.traverse(
} }
fun <D> Pseudocode.traverse( fun <D> Pseudocode.traverse(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
edgesMap: Map<Instruction, Edges<D>>, edgesMap: Map<Instruction, Edges<D>>,
analyzeInstruction: (Instruction, D, D) -> Unit analyzeInstruction: (Instruction, D, D) -> Unit
) { ) {
val instructions = getInstructions(traversalOrder) val instructions = getInstructions(traversalOrder)
for (instruction in instructions) { for (instruction in instructions) {
@@ -57,10 +57,10 @@ fun <D> Pseudocode.traverse(
} }
fun <I : ControlFlowInfo<*, *>> Pseudocode.collectData( fun <I : ControlFlowInfo<*, *>> Pseudocode.collectData(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
mergeEdges: (Instruction, Collection<I>) -> Edges<I>, mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
updateEdge: (Instruction, Instruction, I) -> I, updateEdge: (Instruction, Instruction, I) -> I,
initialInfo: I initialInfo: I
): Map<Instruction, Edges<I>> { ): Map<Instruction, Edges<I>> {
val edgesMap = LinkedHashMap<Instruction, Edges<I>>() val edgesMap = LinkedHashMap<Instruction, Edges<I>>()
edgesMap.put(getStartInstruction(traversalOrder), Edges(initialInfo, initialInfo)) edgesMap.put(getStartInstruction(traversalOrder), Edges(initialInfo, initialInfo))
@@ -68,21 +68,22 @@ fun <I : ControlFlowInfo<*, *>> Pseudocode.collectData(
val changed = mutableMapOf<Instruction, Boolean>() val changed = mutableMapOf<Instruction, Boolean>()
do { do {
collectDataFromSubgraph( collectDataFromSubgraph(
traversalOrder, edgesMap, traversalOrder, edgesMap,
mergeEdges, updateEdge, Collections.emptyList<Instruction>(), changed, false) mergeEdges, updateEdge, Collections.emptyList<Instruction>(), changed, false
)
} while (changed.any { it.value }) } while (changed.any { it.value })
return edgesMap return edgesMap
} }
private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph( private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
edgesMap: MutableMap<Instruction, Edges<I>>, edgesMap: MutableMap<Instruction, Edges<I>>,
mergeEdges: (Instruction, Collection<I>) -> Edges<I>, mergeEdges: (Instruction, Collection<I>) -> Edges<I>,
updateEdge: (Instruction, Instruction, I) -> I, updateEdge: (Instruction, Instruction, I) -> I,
previousSubGraphInstructions: Collection<Instruction>, previousSubGraphInstructions: Collection<Instruction>,
changed: MutableMap<Instruction, Boolean>, changed: MutableMap<Instruction, Boolean>,
isLocal: Boolean isLocal: Boolean
) { ) {
val instructions = getInstructions(traversalOrder) val instructions = getInstructions(traversalOrder)
val startInstruction = getStartInstruction(traversalOrder) val startInstruction = getStartInstruction(traversalOrder)
@@ -92,12 +93,13 @@ private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
if (!isLocal && isStart) if (!isLocal && isStart)
continue continue
val previousInstructions = getPreviousIncludingSubGraphInstructions(instruction, traversalOrder, startInstruction, previousSubGraphInstructions) val previousInstructions =
getPreviousIncludingSubGraphInstructions(instruction, traversalOrder, startInstruction, previousSubGraphInstructions)
if (instruction is LocalFunctionDeclarationInstruction) { if (instruction is LocalFunctionDeclarationInstruction) {
val subroutinePseudocode = instruction.body val subroutinePseudocode = instruction.body
subroutinePseudocode.collectDataFromSubgraph( subroutinePseudocode.collectDataFromSubgraph(
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true
) )
// Special case for inlined functions: take flow from EXIT instructions (it contains flow which exits declaration normally) // Special case for inlined functions: take flow from EXIT instructions (it contains flow which exits declaration normally)
val lastInstruction = if (instruction is InlinedLocalFunctionDeclarationInstruction && traversalOrder == FORWARD) val lastInstruction = if (instruction is InlinedLocalFunctionDeclarationInstruction && traversalOrder == FORWARD)
@@ -125,8 +127,11 @@ private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
for (previousInstruction in previousInstructions) { for (previousInstruction in previousInstructions) {
val previousData = edgesMap[previousInstruction] val previousData = edgesMap[previousInstruction]
if (previousData != null) { if (previousData != null) {
incomingEdgesData.add(updateEdge( incomingEdgesData.add(
previousInstruction, instruction, previousData.outgoing)) updateEdge(
previousInstruction, instruction, previousData.outgoing
)
)
} }
} }
val mergedData = mergeEdges(instruction, incomingEdgesData) val mergedData = mergeEdges(instruction, incomingEdgesData)
@@ -135,10 +140,10 @@ private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
} }
private fun getPreviousIncludingSubGraphInstructions( private fun getPreviousIncludingSubGraphInstructions(
instruction: Instruction, instruction: Instruction,
traversalOrder: TraversalOrder, traversalOrder: TraversalOrder,
startInstruction: Instruction, startInstruction: Instruction,
previousSubGraphInstructions: Collection<Instruction> previousSubGraphInstructions: Collection<Instruction>
): Collection<Instruction> { ): Collection<Instruction> {
val previous = instruction.getPreviousInstructions(traversalOrder) val previous = instruction.getPreviousInstructions(traversalOrder)
if (instruction != startInstruction || previousSubGraphInstructions.isEmpty()) { if (instruction != startInstruction || previousSubGraphInstructions.isEmpty()) {
@@ -150,17 +155,16 @@ private fun getPreviousIncludingSubGraphInstructions(
} }
private fun <I : ControlFlowInfo<*, *>> updateEdgeDataForInstruction( private fun <I : ControlFlowInfo<*, *>> updateEdgeDataForInstruction(
instruction: Instruction, instruction: Instruction,
previousValue: Edges<I>?, previousValue: Edges<I>?,
newValue: Edges<I>?, newValue: Edges<I>?,
edgesMap: MutableMap<Instruction, Edges<I>>, edgesMap: MutableMap<Instruction, Edges<I>>,
changed: MutableMap<Instruction, Boolean> changed: MutableMap<Instruction, Boolean>
) { ) {
if (previousValue != newValue && newValue != null) { if (previousValue != newValue && newValue != null) {
changed[instruction] = true changed[instruction] = true
edgesMap.put(instruction, newValue) edgesMap.put(instruction, newValue)
} } else {
else {
changed[instruction] = false changed[instruction] = false
} }
} }
@@ -175,11 +179,11 @@ enum class TraverseInstructionResult {
// returns false when interrupted by handler // returns false when interrupted by handler
fun traverseFollowingInstructions( fun traverseFollowingInstructions(
rootInstruction: Instruction, rootInstruction: Instruction,
visited: MutableSet<Instruction> = HashSet(), visited: MutableSet<Instruction> = HashSet(),
order: TraversalOrder = FORWARD, order: TraversalOrder = FORWARD,
// true to continue traversal // true to continue traversal
handler: ((Instruction) -> TraverseInstructionResult)? handler: ((Instruction) -> TraverseInstructionResult)?
): Boolean { ): Boolean {
val stack = ArrayDeque<Instruction>() val stack = ArrayDeque<Instruction>()
stack.push(rootInstruction) stack.push(rootInstruction)
@@ -189,7 +193,8 @@ fun traverseFollowingInstructions(
if (!visited.add(instruction)) continue if (!visited.add(instruction)) continue
when (handler?.let { it(instruction) } ?: TraverseInstructionResult.CONTINUE) { when (handler?.let { it(instruction) } ?: TraverseInstructionResult.CONTINUE) {
TraverseInstructionResult.CONTINUE -> instruction.getNextInstructions(order).forEach { stack.push(it) } TraverseInstructionResult.CONTINUE -> instruction.getNextInstructions(order).forEach { stack.push(it) }
TraverseInstructionResult.SKIP -> {} TraverseInstructionResult.SKIP -> {
}
TraverseInstructionResult.HALT -> return false TraverseInstructionResult.HALT -> return false
} }
} }
@@ -202,19 +207,19 @@ enum class TraversalOrder {
} }
fun Pseudocode.getStartInstruction(traversalOrder: TraversalOrder): Instruction = fun Pseudocode.getStartInstruction(traversalOrder: TraversalOrder): Instruction =
if (traversalOrder == FORWARD) enterInstruction else sinkInstruction if (traversalOrder == FORWARD) enterInstruction else sinkInstruction
fun Pseudocode.getLastInstruction(traversalOrder: TraversalOrder): Instruction = fun Pseudocode.getLastInstruction(traversalOrder: TraversalOrder): Instruction =
if (traversalOrder == FORWARD) sinkInstruction else enterInstruction if (traversalOrder == FORWARD) sinkInstruction else enterInstruction
fun Pseudocode.getInstructions(traversalOrder: TraversalOrder): List<Instruction> = fun Pseudocode.getInstructions(traversalOrder: TraversalOrder): List<Instruction> =
if (traversalOrder == FORWARD) instructions else reversedInstructions if (traversalOrder == FORWARD) instructions else reversedInstructions
fun Instruction.getNextInstructions(traversalOrder: TraversalOrder): Collection<Instruction> = fun Instruction.getNextInstructions(traversalOrder: TraversalOrder): Collection<Instruction> =
if (traversalOrder == FORWARD) nextInstructions else previousInstructions if (traversalOrder == FORWARD) nextInstructions else previousInstructions
fun Instruction.getPreviousInstructions(traversalOrder: TraversalOrder): Collection<Instruction> = fun Instruction.getPreviousInstructions(traversalOrder: TraversalOrder): Collection<Instruction> =
if (traversalOrder == FORWARD) previousInstructions else nextInstructions if (traversalOrder == FORWARD) previousInstructions else nextInstructions
fun Instruction.isStartInstruction(traversalOrder: TraversalOrder): Boolean = fun Instruction.isStartInstruction(traversalOrder: TraversalOrder): Boolean =
if (traversalOrder == FORWARD) this is SubroutineEnterInstruction else this is SubroutineSinkInstruction if (traversalOrder == FORWARD) this is SubroutineEnterInstruction else this is SubroutineSinkInstruction
@@ -20,14 +20,14 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
class PseudoValueImpl( class PseudoValueImpl(
override val debugName: String, override val debugName: String,
override val element: KtElement?, override val element: KtElement?,
override val createdAt: InstructionWithValue? override val createdAt: InstructionWithValue?
) : PseudoValue { ) : PseudoValue {
override fun toString(): String = debugName override fun toString(): String = debugName
} }
open class PseudoValueFactoryImpl: PseudoValueFactory { open class PseudoValueFactoryImpl : PseudoValueFactory {
private var lastIndex: Int = 0 private var lastIndex: Int = 0
override fun newValue(element: KtElement?, instruction: InstructionWithValue?): PseudoValue { override fun newValue(element: KtElement?, instruction: InstructionWithValue?): PseudoValue {
@@ -109,7 +109,12 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
override val reversedInstructions: List<Instruction> override val reversedInstructions: List<Instruction>
get() { get() {
val traversedInstructions = linkedSetOf<Instruction>() val traversedInstructions = linkedSetOf<Instruction>()
traverseFollowingInstructions(if (this.isInlined) instructions.last() else sinkInstruction, traversedInstructions, BACKWARD, null) traverseFollowingInstructions(
if (this.isInlined) instructions.last() else sinkInstruction,
traversedInstructions,
BACKWARD,
null
)
if (traversedInstructions.size < instructions.size) { if (traversedInstructions.size < instructions.size) {
val simplyReversedInstructions = instructions.reversed() val simplyReversedInstructions = instructions.reversed()
for (instruction in simplyReversedInstructions) { for (instruction in simplyReversedInstructions) {
@@ -194,8 +199,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
fun bindLabel(label: PseudocodeLabel) { fun bindLabel(label: PseudocodeLabel) {
assert(this == label.pseudocode) { assert(this == label.pseudocode) {
"Attempt to bind label $label to instruction from different pseudocode: " + "Attempt to bind label $label to instruction from different pseudocode: " +
"\nowner pseudocode = ${label.pseudocode.mutableInstructionList}, " + "\nowner pseudocode = ${label.pseudocode.mutableInstructionList}, " +
"\nbound pseudocode = ${this.mutableInstructionList}" "\nbound pseudocode = ${this.mutableInstructionList}"
} }
label.targetInstructionIndex = mutableInstructionList.size label.targetInstructionIndex = mutableInstructionList.size
} }
@@ -214,7 +219,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
private fun addValueUsage(value: PseudoValue, usage: Instruction) { private fun addValueUsage(value: PseudoValue, usage: Instruction) {
if (usage is MergeInstruction) return if (usage is MergeInstruction) return
valueUsages.getOrPut( valueUsages.getOrPut(
value value
) { arrayListOf() }.add(usage) ) { arrayListOf() }.add(usage)
} }
@@ -266,8 +271,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
if (instruction.onTrue) { if (instruction.onTrue) {
instruction.nextOnFalse = nextInstruction instruction.nextOnFalse = nextInstruction
instruction.nextOnTrue = jumpTarget instruction.nextOnTrue = jumpTarget
} } else {
else {
instruction.nextOnFalse = jumpTarget instruction.nextOnFalse = jumpTarget
instruction.nextOnTrue = nextInstruction instruction.nextOnTrue = nextInstruction
} }
@@ -286,7 +290,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
body.parent = this@PseudocodeImpl body.parent = this@PseudocodeImpl
body.postProcess() body.postProcess()
// Don't add edge to next instruction if flow can't reach exit of inlined declaration // Don't add edge to next instruction if flow can't reach exit of inlined declaration
instruction.next = if (body.instructions.contains(body.exitInstruction)) getNextPosition(currentPosition) else sinkInstruction instruction.next =
if (body.instructions.contains(body.exitInstruction)) getNextPosition(currentPosition) else sinkInstruction
} }
override fun visitSubroutineExit(instruction: SubroutineExitInstruction) { override fun visitSubroutineExit(instruction: SubroutineExitInstruction) {
@@ -305,7 +310,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
private fun collectReachableInstructions() { private fun collectReachableInstructions() {
val reachableFromThisPseudocode = hashSetOf<Instruction>() val reachableFromThisPseudocode = hashSetOf<Instruction>()
traverseFollowingInstructions(enterInstruction, reachableFromThisPseudocode, FORWARD traverseFollowingInstructions(
enterInstruction, reachableFromThisPseudocode, FORWARD
) { instruction -> ) { instruction ->
if (instruction is MagicInstruction && instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) { if (instruction is MagicInstruction && instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
return@traverseFollowingInstructions TraverseInstructionResult.SKIP return@traverseFollowingInstructions TraverseInstructionResult.SKIP
@@ -358,12 +364,13 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
} }
fun repeatPart(startLabel: Label, finishLabel: Label, labelCount: Int): Int = fun repeatPart(startLabel: Label, finishLabel: Label, labelCount: Int): Int =
repeatInternal(startLabel.pseudocode as PseudocodeImpl, startLabel, finishLabel, labelCount) repeatInternal(startLabel.pseudocode as PseudocodeImpl, startLabel, finishLabel, labelCount)
private fun repeatInternal( private fun repeatInternal(
originalPseudocode: PseudocodeImpl, originalPseudocode: PseudocodeImpl,
startLabel: Label?, finishLabel: Label?, startLabel: Label?, finishLabel: Label?,
labelCountArg: Int): Int { labelCountArg: Int
): Int {
var labelCount = labelCountArg var labelCount = labelCountArg
val startIndex = startLabel?.targetInstructionIndex ?: 0 val startIndex = startLabel?.targetInstructionIndex ?: 0
val finishIndex = finishLabel?.targetInstructionIndex ?: originalPseudocode.mutableInstructionList.size val finishIndex = finishLabel?.targetInstructionIndex ?: originalPseudocode.mutableInstructionList.size
@@ -401,17 +408,20 @@ class PseudocodeImpl(override val correspondingElement: KtElement, override val
} }
} }
if (finishIndex < originalPseudocode.mutableInstructionList.size) { if (finishIndex < originalPseudocode.mutableInstructionList.size) {
repeatLabelsBindingForInstruction(originalPseudocode.mutableInstructionList[finishIndex], repeatLabelsBindingForInstruction(
originalToCopy, originalPseudocode.mutableInstructionList[finishIndex],
originalLabelsForInstruction) originalToCopy,
originalLabelsForInstruction
)
} }
return labelCount return labelCount
} }
private fun repeatLabelsBindingForInstruction( private fun repeatLabelsBindingForInstruction(
originalInstruction: Instruction, originalInstruction: Instruction,
originalToCopy: Map<Label, PseudocodeLabel>, originalToCopy: Map<Label, PseudocodeLabel>,
originalLabelsForInstruction: Multimap<Instruction, Label>) { originalLabelsForInstruction: Multimap<Instruction, Label>
) {
for (originalLabel in originalLabelsForInstruction.get(originalInstruction)) { for (originalLabel in originalLabelsForInstruction.get(originalInstruction)) {
bindLabel(originalToCopy[originalLabel]!!) bindLabel(originalToCopy[originalLabel]!!)
} }
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
class PseudocodeLabel internal constructor( class PseudocodeLabel internal constructor(
override val pseudocode: PseudocodeImpl, override val name: String, private val comment: String? override val pseudocode: PseudocodeImpl, override val name: String, private val comment: String?
) : Label { ) : Label {
private val instructionList: List<Instruction> get() = pseudocode.mutableInstructionList private val instructionList: List<Instruction> get() = pseudocode.mutableInstructionList
@@ -36,16 +36,20 @@ class PseudocodeLabel internal constructor(
val index = targetInstructionIndex val index = targetInstructionIndex
when { when {
index < 0 -> index < 0 ->
error("resolveToInstruction: unbound label $name " + error(
"in subroutine ${correspondingElement.text} with instructions $instructionList") "resolveToInstruction: unbound label $name " +
"in subroutine ${correspondingElement.text} with instructions $instructionList"
)
index >= instructionList.size -> index >= instructionList.size ->
error("resolveToInstruction: incorrect index $index for label $name " + error(
"in subroutine ${correspondingElement.text} with instructions $instructionList") "resolveToInstruction: incorrect index $index for label $name " +
"in subroutine ${correspondingElement.text} with instructions $instructionList"
)
else -> else ->
return instructionList[index] return instructionList[index]
} }
} }
fun copy(newPseudocode: PseudocodeImpl, newLabelIndex: Int): PseudocodeLabel = fun copy(newPseudocode: PseudocodeImpl, newLabelIndex: Int): PseudocodeLabel =
PseudocodeLabel(newPseudocode, "L" + newLabelIndex, "copy of $name, $comment") PseudocodeLabel(newPseudocode, "L" + newLabelIndex, "copy of $name, $comment")
} }
@@ -21,7 +21,7 @@ import java.util.LinkedHashSet
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
abstract class InstructionImpl(override val blockScope: BlockScope): Instruction { abstract class InstructionImpl(override val blockScope: BlockScope) : Instruction {
private var _owner: Pseudocode? = null private var _owner: Pseudocode? = null
override var owner: Pseudocode override var owner: Pseudocode
@@ -28,10 +28,10 @@ abstract class InstructionVisitorWithResult<out R> {
open fun visitReadValue(instruction: ReadValueInstruction): R = visitAccessInstruction(instruction) open fun visitReadValue(instruction: ReadValueInstruction): R = visitAccessInstruction(instruction)
open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction): R = open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction): R =
visitInstructionWithNext(instruction) visitInstructionWithNext(instruction)
open fun visitInlinedFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction): R = open fun visitInlinedFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction): R =
visitLocalFunctionDeclarationInstruction(instruction) visitLocalFunctionDeclarationInstruction(instruction)
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction): R = visitInstructionWithNext(instruction) open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction): R = visitInstructionWithNext(instruction)
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.cfg.pseudocode.instructions
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
abstract class InstructionWithNext( abstract class InstructionWithNext(
element: KtElement, element: KtElement,
blockScope: BlockScope blockScope: BlockScope
) : KtElementInstructionImpl(element, blockScope) { ) : KtElementInstructionImpl(element, blockScope) {
var next: Instruction? = null var next: Instruction? = null
set(value) { set(value) {
@@ -20,9 +20,9 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
abstract class KtElementInstructionImpl( abstract class KtElementInstructionImpl(
override val element: KtElement, override val element: KtElement,
blockScope: BlockScope blockScope: BlockScope
) : InstructionImpl(blockScope), KtElementInstruction { ) : InstructionImpl(blockScope), KtElementInstruction {
protected fun render(element: PsiElement): String = protected fun render(element: PsiElement): String =
element.text?.replace("\\s+".toRegex(), " ") ?: "" element.text?.replace("\\s+".toRegex(), " ") ?: ""
} }
@@ -20,6 +20,6 @@ import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
interface InstructionWithReceivers: Instruction { interface InstructionWithReceivers : Instruction {
val receiverValues: Map<PseudoValue, ReceiverValue> val receiverValues: Map<PseudoValue, ReceiverValue>
} }
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
class LoadUnitValueInstruction( class LoadUnitValueInstruction(
expression: KtExpression, expression: KtExpression,
blockScope: BlockScope blockScope: BlockScope
) : InstructionWithNext(expression, blockScope) { ) : InstructionWithNext(expression, blockScope) {
override fun accept(visitor: InstructionVisitor) { override fun accept(visitor: InstructionVisitor) {
visitor.visitLoadUnitValue(this) visitor.visitLoadUnitValue(this)
@@ -36,8 +36,8 @@ class LoadUnitValueInstruction(
} }
override fun toString(): String = override fun toString(): String =
"read (Unit)" "read (Unit)"
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
LoadUnitValueInstruction(element as KtExpression, blockScope) LoadUnitValueInstruction(element as KtExpression, blockScope)
} }
@@ -27,17 +27,19 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
sealed class AccessTarget { sealed class AccessTarget {
class Declaration(val descriptor: VariableDescriptor): AccessTarget() { class Declaration(val descriptor: VariableDescriptor) : AccessTarget() {
override fun equals(other: Any?) = other is Declaration && descriptor == other.descriptor override fun equals(other: Any?) = other is Declaration && descriptor == other.descriptor
override fun hashCode() = descriptor.hashCode() override fun hashCode() = descriptor.hashCode()
} }
class Call(val resolvedCall: ResolvedCall<*>): AccessTarget() {
class Call(val resolvedCall: ResolvedCall<*>) : AccessTarget() {
override fun equals(other: Any?) = other is Call && resolvedCall == other.resolvedCall override fun equals(other: Any?) = other is Call && resolvedCall == other.resolvedCall
override fun hashCode() = resolvedCall.hashCode() override fun hashCode() = resolvedCall.hashCode()
} }
object BlackBox: AccessTarget()
object BlackBox : AccessTarget()
} }
val AccessTarget.accessedDescriptor: CallableDescriptor? val AccessTarget.accessedDescriptor: CallableDescriptor?
@@ -48,26 +50,26 @@ val AccessTarget.accessedDescriptor: CallableDescriptor?
} }
abstract class AccessValueInstruction protected constructor( abstract class AccessValueInstruction protected constructor(
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
val target: AccessTarget, val target: AccessTarget,
override val receiverValues: Map<PseudoValue, ReceiverValue> override val receiverValues: Map<PseudoValue, ReceiverValue>
) : InstructionWithNext(element, blockScope), InstructionWithReceivers ) : InstructionWithNext(element, blockScope), InstructionWithReceivers
class ReadValueInstruction private constructor( class ReadValueInstruction private constructor(
element: KtElement,
blockScope: BlockScope,
target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>,
private var _outputValue: PseudoValue?
) : AccessValueInstruction(element, blockScope, target, receiverValues), InstructionWithValue {
constructor(
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
target: AccessTarget, target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>, receiverValues: Map<PseudoValue, ReceiverValue>,
private var _outputValue: PseudoValue? factory: PseudoValueFactory
) : AccessValueInstruction(element, blockScope, target, receiverValues), InstructionWithValue { ) : this(element, blockScope, target, receiverValues, null) {
constructor(
element: KtElement,
blockScope: BlockScope,
target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>,
factory: PseudoValueFactory
): this(element, blockScope, target, receiverValues, null) {
_outputValue = factory.newValue(element, this) _outputValue = factory.newValue(element, this)
} }
@@ -97,16 +99,16 @@ class ReadValueInstruction private constructor(
} }
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
ReadValueInstruction(element, blockScope, target, receiverValues, outputValue) ReadValueInstruction(element, blockScope, target, receiverValues, outputValue)
} }
class WriteValueInstruction( class WriteValueInstruction(
assignment: KtElement, assignment: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
target: AccessTarget, target: AccessTarget,
receiverValues: Map<PseudoValue, ReceiverValue>, receiverValues: Map<PseudoValue, ReceiverValue>,
val lValue: KtElement, val lValue: KtElement,
private val rValue: PseudoValue private val rValue: PseudoValue
) : AccessValueInstruction(assignment, blockScope, target, receiverValues) { ) : AccessValueInstruction(assignment, blockScope, target, receiverValues) {
override val inputValues: List<PseudoValue> override val inputValues: List<PseudoValue>
get() = (receiverValues.keys as Collection<PseudoValue>) + rValue get() = (receiverValues.keys as Collection<PseudoValue>) + rValue
@@ -123,5 +125,5 @@ class WriteValueInstruction(
} }
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
WriteValueInstruction(element, blockScope, target, receiverValues, lValue, rValue) WriteValueInstruction(element, blockScope, target, receiverValues, lValue, rValue)
} }
@@ -28,9 +28,9 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
abstract class OperationInstruction protected constructor( abstract class OperationInstruction protected constructor(
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
override val inputValues: List<PseudoValue> override val inputValues: List<PseudoValue>
) : InstructionWithNext(element, blockScope), InstructionWithValue { ) : InstructionWithNext(element, blockScope), InstructionWithValue {
protected var resultValue: PseudoValue? = null protected var resultValue: PseudoValue? = null
@@ -38,9 +38,9 @@ abstract class OperationInstruction protected constructor(
get() = resultValue get() = resultValue
protected fun renderInstruction(name: String, desc: String): String = protected fun renderInstruction(name: String, desc: String): String =
"$name($desc" + "$name($desc" +
(if (inputValues.isNotEmpty()) "|${inputValues.joinToString(", ")})" else ")") + (if (inputValues.isNotEmpty()) "|${inputValues.joinToString(", ")})" else ")") +
(if (resultValue != null) " -> $resultValue" else "") (if (resultValue != null) " -> $resultValue" else "")
protected fun setResult(value: PseudoValue?): OperationInstruction { protected fun setResult(value: PseudoValue?): OperationInstruction {
this.resultValue = value this.resultValue = value
@@ -48,25 +48,25 @@ abstract class OperationInstruction protected constructor(
} }
protected fun setResult(factory: PseudoValueFactory?, valueElement: KtElement? = element): OperationInstruction = protected fun setResult(factory: PseudoValueFactory?, valueElement: KtElement? = element): OperationInstruction =
setResult(factory?.newValue(valueElement, this)) setResult(factory?.newValue(valueElement, this))
} }
class CallInstruction private constructor( class CallInstruction private constructor(
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
val resolvedCall: ResolvedCall<*>, val resolvedCall: ResolvedCall<*>,
override val receiverValues: Map<PseudoValue, ReceiverValue>, override val receiverValues: Map<PseudoValue, ReceiverValue>,
val arguments: Map<PseudoValue, ValueParameterDescriptor> val arguments: Map<PseudoValue, ValueParameterDescriptor>
) : OperationInstruction(element, blockScope, (receiverValues.keys as Collection<PseudoValue>) + arguments.keys), InstructionWithReceivers { ) : OperationInstruction(element, blockScope, (receiverValues.keys as Collection<PseudoValue>) + arguments.keys), InstructionWithReceivers {
constructor ( constructor (
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
receiverValues: Map<PseudoValue, ReceiverValue>, receiverValues: Map<PseudoValue, ReceiverValue>,
arguments: Map<PseudoValue, ValueParameterDescriptor>, arguments: Map<PseudoValue, ValueParameterDescriptor>,
factory: PseudoValueFactory? factory: PseudoValueFactory?
): this(element, blockScope, resolvedCall, receiverValues, arguments) { ) : this(element, blockScope, resolvedCall, receiverValues, arguments) {
setResult(factory) setResult(factory)
} }
@@ -77,10 +77,10 @@ class CallInstruction private constructor(
override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitCallInstruction(this) override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitCallInstruction(this)
override fun createCopy() = override fun createCopy() =
CallInstruction(element, blockScope, resolvedCall, receiverValues, arguments).setResult(resultValue) CallInstruction(element, blockScope, resolvedCall, receiverValues, arguments).setResult(resultValue)
override fun toString() = override fun toString() =
renderInstruction("call", "${render(element)}, ${resolvedCall.resultingDescriptor!!.name}") renderInstruction("call", "${render(element)}, ${resolvedCall.resultingDescriptor!!.name}")
} }
// Introduces black-box operation // Introduces black-box operation
@@ -89,19 +89,19 @@ class CallInstruction private constructor(
// denote value transformation which can't be expressed by other instructions (such as call or read) // denote value transformation which can't be expressed by other instructions (such as call or read)
// pass more than one value to instruction which formally requires only one (e.g. jump) // pass more than one value to instruction which formally requires only one (e.g. jump)
class MagicInstruction( class MagicInstruction(
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
inputValues: List<PseudoValue>, inputValues: List<PseudoValue>,
val kind: MagicKind val kind: MagicKind
) : OperationInstruction(element, blockScope, inputValues) { ) : OperationInstruction(element, blockScope, inputValues) {
constructor ( constructor (
element: KtElement, element: KtElement,
valueElement: KtElement?, valueElement: KtElement?,
blockScope: BlockScope, blockScope: BlockScope,
inputValues: List<PseudoValue>, inputValues: List<PseudoValue>,
kind: MagicKind, kind: MagicKind,
factory: PseudoValueFactory factory: PseudoValueFactory
): this(element, blockScope, inputValues, kind) { ) : this(element, blockScope, inputValues, kind) {
setResult(factory, valueElement) setResult(factory, valueElement)
} }
@@ -115,7 +115,7 @@ class MagicInstruction(
override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitMagic(this) override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitMagic(this)
override fun createCopy() = override fun createCopy() =
MagicInstruction(element, blockScope, inputValues, kind).setResult(resultValue) MagicInstruction(element, blockScope, inputValues, kind).setResult(resultValue)
override fun toString() = renderInstruction("magic[$kind]", render(element)) override fun toString() = renderInstruction("magic[$kind]", render(element))
} }
@@ -145,16 +145,16 @@ enum class MagicKind(val sideEffectFree: Boolean = false) {
// Merges values produced by alternative control-flow paths (such as 'if' branches) // Merges values produced by alternative control-flow paths (such as 'if' branches)
class MergeInstruction private constructor( class MergeInstruction private constructor(
element: KtElement,
blockScope: BlockScope,
inputValues: List<PseudoValue>
) : OperationInstruction(element, blockScope, inputValues) {
constructor (
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
inputValues: List<PseudoValue> inputValues: List<PseudoValue>,
): OperationInstruction(element, blockScope, inputValues) { factory: PseudoValueFactory
constructor ( ) : this(element, blockScope, inputValues) {
element: KtElement,
blockScope: BlockScope,
inputValues: List<PseudoValue>,
factory: PseudoValueFactory
): this(element, blockScope, inputValues) {
setResult(factory) setResult(factory)
} }
@@ -26,11 +26,12 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithRe
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
class ConditionalJumpInstruction( class ConditionalJumpInstruction(
element: KtElement, element: KtElement,
val onTrue: Boolean, val onTrue: Boolean,
blockScope: BlockScope, blockScope: BlockScope,
targetLabel: Label, targetLabel: Label,
private val conditionValue: PseudoValue?) : AbstractJumpInstruction(element, targetLabel, blockScope) { private val conditionValue: PseudoValue?
) : AbstractJumpInstruction(element, targetLabel, blockScope) {
private var _nextOnTrue: Instruction? = null private var _nextOnTrue: Instruction? = null
private var _nextOnFalse: Instruction? = null private var _nextOnFalse: Instruction? = null
@@ -67,5 +68,5 @@ class ConditionalJumpInstruction(
} }
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction = override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
ConditionalJumpInstruction(element, onTrue, blockScope, newLabel, conditionValue) ConditionalJumpInstruction(element, onTrue, blockScope, newLabel, conditionValue)
} }
@@ -23,10 +23,10 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
class ReturnNoValueInstruction( class ReturnNoValueInstruction(
element: KtElement, element: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
targetLabel: Label, targetLabel: Label,
val subroutine: KtElement val subroutine: KtElement
) : AbstractJumpInstruction(element, targetLabel, blockScope) { ) : AbstractJumpInstruction(element, targetLabel, blockScope) {
override fun accept(visitor: InstructionVisitor) { override fun accept(visitor: InstructionVisitor) {
visitor.visitReturnNoValue(this) visitor.visitReturnNoValue(this)
@@ -37,5 +37,5 @@ class ReturnNoValueInstruction(
override fun toString(): String = "ret $targetLabel" override fun toString(): String = "ret $targetLabel"
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction = override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
ReturnNoValueInstruction(element, blockScope, newLabel, subroutine) ReturnNoValueInstruction(element, blockScope, newLabel, subroutine)
} }
@@ -27,11 +27,11 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtReturnExpression import org.jetbrains.kotlin.psi.KtReturnExpression
class ReturnValueInstruction( class ReturnValueInstruction(
returnExpression: KtExpression, returnExpression: KtExpression,
blockScope: BlockScope, blockScope: BlockScope,
targetLabel: Label, targetLabel: Label,
val returnedValue: PseudoValue, val returnedValue: PseudoValue,
val subroutine: KtElement val subroutine: KtElement
) : AbstractJumpInstruction(returnExpression, targetLabel, blockScope) { ) : AbstractJumpInstruction(returnExpression, targetLabel, blockScope) {
override val inputValues: List<PseudoValue> get() = Collections.singletonList(returnedValue) override val inputValues: List<PseudoValue> get() = Collections.singletonList(returnedValue)
@@ -44,7 +44,7 @@ class ReturnValueInstruction(
override fun toString(): String = "ret(*|$returnedValue) $targetLabel" override fun toString(): String = "ret(*|$returnedValue) $targetLabel"
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction = override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
ReturnValueInstruction((element as KtExpression), blockScope, newLabel, returnedValue, subroutine) ReturnValueInstruction((element as KtExpression), blockScope, newLabel, returnedValue, subroutine)
val returnExpressionIfAny: KtReturnExpression? = element as? KtReturnExpression val returnExpressionIfAny: KtReturnExpression? = element as? KtReturnExpression
} }
@@ -25,10 +25,10 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
class ThrowExceptionInstruction( class ThrowExceptionInstruction(
expression: KtThrowExpression, expression: KtThrowExpression,
blockScope: BlockScope, blockScope: BlockScope,
errorLabel: Label, errorLabel: Label,
private val thrownValue: PseudoValue private val thrownValue: PseudoValue
) : AbstractJumpInstruction(expression, errorLabel, blockScope) { ) : AbstractJumpInstruction(expression, errorLabel, blockScope) {
override val inputValues: List<PseudoValue> get() = Collections.singletonList(thrownValue) override val inputValues: List<PseudoValue> get() = Collections.singletonList(thrownValue)
@@ -41,5 +41,5 @@ class ThrowExceptionInstruction(
override fun toString(): String = "throw (${element.text}|$thrownValue)" override fun toString(): String = "throw (${element.text}|$thrownValue)"
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction = override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
ThrowExceptionInstruction((element as KtThrowExpression), blockScope, newLabel, thrownValue) ThrowExceptionInstruction((element as KtThrowExpression), blockScope, newLabel, thrownValue)
} }
@@ -21,9 +21,9 @@ import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.cfg.Label import org.jetbrains.kotlin.cfg.Label
class UnconditionalJumpInstruction( class UnconditionalJumpInstruction(
element: KtElement, element: KtElement,
targetLabel: Label, targetLabel: Label,
blockScope: BlockScope blockScope: BlockScope
) : AbstractJumpInstruction(element, targetLabel, blockScope) { ) : AbstractJumpInstruction(element, targetLabel, blockScope) {
override fun accept(visitor: InstructionVisitor) { override fun accept(visitor: InstructionVisitor) {
visitor.visitUnconditionalJump(this) visitor.visitUnconditionalJump(this)
@@ -34,5 +34,5 @@ class UnconditionalJumpInstruction(
override fun toString(): String = "jmp(${targetLabel.name})" override fun toString(): String = "jmp(${targetLabel.name})"
override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction = override fun createCopy(newLabel: Label, blockScope: BlockScope): AbstractJumpInstruction =
UnconditionalJumpInstruction(element, newLabel, blockScope) UnconditionalJumpInstruction(element, newLabel, blockScope)
} }
@@ -25,10 +25,10 @@ import org.jetbrains.kotlin.contracts.description.InvocationKind
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
class InlinedLocalFunctionDeclarationInstruction( class InlinedLocalFunctionDeclarationInstruction(
element: KtElement, element: KtElement,
body: Pseudocode, body: Pseudocode,
blockScope: BlockScope, blockScope: BlockScope,
val kind: InvocationKind val kind: InvocationKind
) : LocalFunctionDeclarationInstruction(element, body, blockScope) { ) : LocalFunctionDeclarationInstruction(element, body, blockScope) {
override fun createCopy(): InstructionImpl = InlinedLocalFunctionDeclarationInstruction(element, body, blockScope, kind) override fun createCopy(): InstructionImpl = InlinedLocalFunctionDeclarationInstruction(element, body, blockScope, kind)
@@ -26,9 +26,9 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithRe
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
open class LocalFunctionDeclarationInstruction( open class LocalFunctionDeclarationInstruction(
element: KtElement, element: KtElement,
val body: Pseudocode, val body: Pseudocode,
blockScope: BlockScope blockScope: BlockScope
) : InstructionWithNext(element, blockScope) { ) : InstructionWithNext(element, blockScope) {
var sink: SubroutineSinkInstruction? = null var sink: SubroutineSinkInstruction? = null
set(value) { set(value) {
@@ -54,5 +54,5 @@ open class LocalFunctionDeclarationInstruction(
override fun toString(): String = "d(${render(element)})" override fun toString(): String = "d(${render(element)})"
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
LocalFunctionDeclarationInstruction(element, body.copy(), blockScope) LocalFunctionDeclarationInstruction(element, body.copy(), blockScope)
} }
@@ -23,8 +23,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
class MarkInstruction( class MarkInstruction(
element: KtElement, element: KtElement,
blockScope: BlockScope blockScope: BlockScope
) : InstructionWithNext(element, blockScope) { ) : InstructionWithNext(element, blockScope) {
override fun accept(visitor: InstructionVisitor) { override fun accept(visitor: InstructionVisitor) {
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithRe
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
class SubroutineEnterInstruction( class SubroutineEnterInstruction(
val subroutine: KtElement, val subroutine: KtElement,
blockScope: BlockScope blockScope: BlockScope
) : InstructionWithNext(subroutine, blockScope) { ) : InstructionWithNext(subroutine, blockScope) {
override fun accept(visitor: InstructionVisitor) { override fun accept(visitor: InstructionVisitor) {
visitor.visitSubroutineEnter(this) visitor.visitSubroutineEnter(this)
@@ -36,5 +36,5 @@ class SubroutineEnterInstruction(
override fun toString(): String = "<START>" override fun toString(): String = "<START>"
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
SubroutineEnterInstruction(subroutine, blockScope) SubroutineEnterInstruction(subroutine, blockScope)
} }
@@ -21,9 +21,9 @@ import org.jetbrains.kotlin.psi.KtElement
import java.util.* import java.util.*
class SubroutineExitInstruction( class SubroutineExitInstruction(
val subroutine: KtElement, val subroutine: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
val isError: Boolean val isError: Boolean
) : InstructionImpl(blockScope) { ) : InstructionImpl(blockScope) {
private var _sink: SubroutineSinkInstruction? = null private var _sink: SubroutineSinkInstruction? = null
@@ -45,5 +45,5 @@ class SubroutineExitInstruction(
override fun toString(): String = if (isError) "<ERROR>" else "<END>" override fun toString(): String = if (isError) "<ERROR>" else "<END>"
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
SubroutineExitInstruction(subroutine, blockScope, isError) SubroutineExitInstruction(subroutine, blockScope, isError)
} }
@@ -25,9 +25,10 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
class SubroutineSinkInstruction( class SubroutineSinkInstruction(
val subroutine: KtElement, val subroutine: KtElement,
blockScope: BlockScope, blockScope: BlockScope,
private val debugLabel: String) : InstructionImpl(blockScope) { private val debugLabel: String
) : InstructionImpl(blockScope) {
override val nextInstructions: Collection<Instruction> override val nextInstructions: Collection<Instruction>
get() = Collections.emptyList() get() = Collections.emptyList()
@@ -40,5 +41,5 @@ class SubroutineSinkInstruction(
override fun toString(): String = debugLabel override fun toString(): String = debugLabel
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
SubroutineSinkInstruction(subroutine, blockScope, debugLabel) SubroutineSinkInstruction(subroutine, blockScope, debugLabel)
} }
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
class VariableDeclarationInstruction( class VariableDeclarationInstruction(
element: KtDeclaration, element: KtDeclaration,
blockScope: BlockScope blockScope: BlockScope
) : InstructionWithNext(element, blockScope) { ) : InstructionWithNext(element, blockScope) {
init { init {
assert(element is KtVariableDeclaration || element is KtParameter || element is KtEnumEntry || element is KtObjectDeclaration) { assert(element is KtVariableDeclaration || element is KtParameter || element is KtEnumEntry || element is KtObjectDeclaration) {
@@ -45,5 +45,5 @@ class VariableDeclarationInstruction(
override fun toString(): String = "v(${render(element)})" override fun toString(): String = "v(${render(element)})"
override fun createCopy(): InstructionImpl = override fun createCopy(): InstructionImpl =
VariableDeclarationInstruction(variableDeclarationElement, blockScope) VariableDeclarationInstruction(variableDeclarationElement, blockScope)
} }
@@ -40,7 +40,7 @@ interface ModuleContext : ProjectContext {
val module: ModuleDescriptor val module: ModuleDescriptor
} }
interface MutableModuleContext: ModuleContext { interface MutableModuleContext : ModuleContext {
override val module: ModuleDescriptorImpl override val module: ModuleDescriptorImpl
fun setDependencies(vararg dependencies: ModuleDescriptorImpl) { fun setDependencies(vararg dependencies: ModuleDescriptorImpl) {
@@ -57,30 +57,30 @@ interface MutableModuleContext: ModuleContext {
} }
open class SimpleGlobalContext( open class SimpleGlobalContext(
override val storageManager: StorageManager, override val storageManager: StorageManager,
override val exceptionTracker: ExceptionTracker override val exceptionTracker: ExceptionTracker
) : GlobalContext ) : GlobalContext
open class GlobalContextImpl( open class GlobalContextImpl(
storageManager: LockBasedStorageManager, storageManager: LockBasedStorageManager,
exceptionTracker: ExceptionTracker exceptionTracker: ExceptionTracker
) : SimpleGlobalContext(storageManager, exceptionTracker) { ) : SimpleGlobalContext(storageManager, exceptionTracker) {
override val storageManager: LockBasedStorageManager = super.storageManager as LockBasedStorageManager override val storageManager: LockBasedStorageManager = super.storageManager as LockBasedStorageManager
} }
class ProjectContextImpl( class ProjectContextImpl(
override val project: Project, override val project: Project,
private val globalContext: GlobalContext private val globalContext: GlobalContext
) : ProjectContext, GlobalContext by globalContext ) : ProjectContext, GlobalContext by globalContext
class ModuleContextImpl( class ModuleContextImpl(
override val module: ModuleDescriptor, override val module: ModuleDescriptor,
projectContext: ProjectContext projectContext: ProjectContext
) : ModuleContext, ProjectContext by projectContext ) : ModuleContext, ProjectContext by projectContext
class MutableModuleContextImpl( class MutableModuleContextImpl(
override val module: ModuleDescriptorImpl, override val module: ModuleDescriptorImpl,
projectContext: ProjectContext projectContext: ProjectContext
) : MutableModuleContext, ProjectContext by projectContext ) : MutableModuleContext, ProjectContext by projectContext
fun GlobalContext(): GlobalContextImpl { fun GlobalContext(): GlobalContextImpl {
@@ -90,16 +90,16 @@ fun GlobalContext(): GlobalContextImpl {
fun ProjectContext(project: Project): ProjectContext = ProjectContextImpl(project, GlobalContext()) fun ProjectContext(project: Project): ProjectContext = ProjectContextImpl(project, GlobalContext())
fun ModuleContext(module: ModuleDescriptor, project: Project): ModuleContext = fun ModuleContext(module: ModuleDescriptor, project: Project): ModuleContext =
ModuleContextImpl(module, ProjectContext(project)) ModuleContextImpl(module, ProjectContext(project))
fun GlobalContext.withProject(project: Project): ProjectContext = ProjectContextImpl(project, this) fun GlobalContext.withProject(project: Project): ProjectContext = ProjectContextImpl(project, this)
fun ProjectContext.withModule(module: ModuleDescriptor): ModuleContext = ModuleContextImpl(module, this) fun ProjectContext.withModule(module: ModuleDescriptor): ModuleContext = ModuleContextImpl(module, this)
fun ContextForNewModule( fun ContextForNewModule(
projectContext: ProjectContext, projectContext: ProjectContext,
moduleName: Name, moduleName: Name,
builtIns: KotlinBuiltIns, builtIns: KotlinBuiltIns,
multiTargetPlatform: MultiTargetPlatform? multiTargetPlatform: MultiTargetPlatform?
): MutableModuleContext { ): MutableModuleContext {
val module = ModuleDescriptorImpl(moduleName, projectContext.storageManager, builtIns, multiTargetPlatform) val module = ModuleDescriptorImpl(moduleName, projectContext.storageManager, builtIns, multiTargetPlatform)
return MutableModuleContextImpl(module, projectContext) return MutableModuleContextImpl(module, projectContext)
@@ -31,10 +31,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
class ContractDeserializerImpl(private val configuration: DeserializationConfiguration) : ContractDeserializer { class ContractDeserializerImpl(private val configuration: DeserializationConfiguration) : ContractDeserializer {
override fun deserializeContractFromFunction( override fun deserializeContractFromFunction(
proto: ProtoBuf.Function, proto: ProtoBuf.Function,
ownerFunction: FunctionDescriptor, ownerFunction: FunctionDescriptor,
typeTable: TypeTable, typeTable: TypeTable,
typeDeserializer: TypeDeserializer typeDeserializer: TypeDeserializer
): Pair<FunctionDescriptor.UserDataKey<*>, LazyContractProvider>? { ): Pair<FunctionDescriptor.UserDataKey<*>, LazyContractProvider>? {
if (!proto.hasContract()) return null if (!proto.hasContract()) return null
@@ -46,9 +46,9 @@ class ContractDeserializerImpl(private val configuration: DeserializationConfigu
} }
private class ContractDeserializationWorker( private class ContractDeserializationWorker(
private val typeTable: TypeTable, private val typeTable: TypeTable,
private val typeDeserializer: TypeDeserializer, private val typeDeserializer: TypeDeserializer,
private val ownerFunction: FunctionDescriptor private val ownerFunction: FunctionDescriptor
) { ) {
fun deserializeContract(proto: ProtoBuf.Contract): ContractDescription? { fun deserializeContract(proto: ProtoBuf.Contract): ContractDescription? {
@@ -71,7 +71,9 @@ class ContractDeserializerImpl(private val configuration: DeserializationConfigu
return when (type!!) { return when (type!!) {
ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> { ProtoBuf.Effect.EffectType.RETURNS_CONSTANT -> {
val argument = proto.effectConstructorArgumentList.getOrNull(0) val argument = proto.effectConstructorArgumentList.getOrNull(0)
val returnValue = if (argument == null) ConstantReference.WILDCARD else deserializeExpression(argument) as? ConstantReference ?: return null val returnValue =
if (argument == null) ConstantReference.WILDCARD else deserializeExpression(argument) as? ConstantReference
?: return null
ReturnsEffectDeclaration(returnValue) ReturnsEffectDeclaration(returnValue)
} }
@@ -225,7 +227,7 @@ class ContractDeserializerImpl(private val configuration: DeserializationConfigu
private fun ProtoBuf.Expression.hasType(): Boolean = this.hasIsInstanceType() || this.hasIsInstanceTypeId() private fun ProtoBuf.Expression.hasType(): Boolean = this.hasIsInstanceType() || this.hasIsInstanceTypeId()
private fun ProtoBuf.Expression.hasFlag(flag: Flags.BooleanFlagField) = private fun ProtoBuf.Expression.hasFlag(flag: Flags.BooleanFlagField) =
this.hasFlags() && flag.get(this.flags) this.hasFlags() && flag.get(this.flags)
// Arguments of expressions with such types are never other expressions // Arguments of expressions with such types are never other expressions
private enum class PrimitiveExpressionType { private enum class PrimitiveExpressionType {
@@ -41,9 +41,9 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
class EffectSystem(val languageVersionSettings: LanguageVersionSettings) { class EffectSystem(val languageVersionSettings: LanguageVersionSettings) {
fun getDataFlowInfoForFinishedCall( fun getDataFlowInfoForFinishedCall(
resolvedCall: ResolvedCall<*>, resolvedCall: ResolvedCall<*>,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor moduleDescriptor: ModuleDescriptor
): DataFlowInfo { ): DataFlowInfo {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY
@@ -57,16 +57,18 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings) {
} }
fun getDataFlowInfoWhenEquals( fun getDataFlowInfoWhenEquals(
leftExpression: KtExpression?, leftExpression: KtExpression?,
rightExpression: KtExpression?, rightExpression: KtExpression?,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor moduleDescriptor: ModuleDescriptor
): ConditionalDataFlowInfo { ): ConditionalDataFlowInfo {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return ConditionalDataFlowInfo.EMPTY if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return ConditionalDataFlowInfo.EMPTY
if (leftExpression == null || rightExpression == null) return ConditionalDataFlowInfo.EMPTY if (leftExpression == null || rightExpression == null) return ConditionalDataFlowInfo.EMPTY
val leftComputation = getNonTrivialComputation(leftExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY val leftComputation =
val rightComputation = getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY getNonTrivialComputation(leftExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
val rightComputation =
getNonTrivialComputation(rightExpression, bindingTrace, moduleDescriptor) ?: return ConditionalDataFlowInfo.EMPTY
val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation) val effects = EqualsFunctor(false).invokeWithArguments(leftComputation, rightComputation)
@@ -74,8 +76,8 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings) {
val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects) val notEqualsContextInfo = InfoCollector(ESReturns(false.lift())).collectFromSchema(effects)
return ConditionalDataFlowInfo( return ConditionalDataFlowInfo(
equalsContextInfo.toDataFlowInfo(languageVersionSettings), equalsContextInfo.toDataFlowInfo(languageVersionSettings),
notEqualsContextInfo.toDataFlowInfo(languageVersionSettings) notEqualsContextInfo.toDataFlowInfo(languageVersionSettings)
) )
} }
@@ -95,23 +97,23 @@ class EffectSystem(val languageVersionSettings: LanguageVersionSettings) {
} }
fun extractDataFlowInfoFromCondition( fun extractDataFlowInfoFromCondition(
condition: KtExpression?, condition: KtExpression?,
value: Boolean, value: Boolean,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor moduleDescriptor: ModuleDescriptor
): DataFlowInfo { ): DataFlowInfo {
if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY if (!languageVersionSettings.supportsFeature(LanguageFeature.ReturnsEffect)) return DataFlowInfo.EMPTY
if (condition == null) return DataFlowInfo.EMPTY if (condition == null) return DataFlowInfo.EMPTY
return getContextInfoWhen(ESReturns(value.lift()), condition, bindingTrace, moduleDescriptor) return getContextInfoWhen(ESReturns(value.lift()), condition, bindingTrace, moduleDescriptor)
.toDataFlowInfo(languageVersionSettings) .toDataFlowInfo(languageVersionSettings)
} }
private fun getContextInfoWhen( private fun getContextInfoWhen(
observedEffect: ESEffect, observedEffect: ESEffect,
expression: KtExpression, expression: KtExpression,
bindingTrace: BindingTrace, bindingTrace: BindingTrace,
moduleDescriptor: ModuleDescriptor moduleDescriptor: ModuleDescriptor
): MutableContextInfo { ): MutableContextInfo {
val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY val computation = getNonTrivialComputation(expression, bindingTrace, moduleDescriptor) ?: return MutableContextInfo.EMPTY
return InfoCollector(observedEffect).collectFromSchema(computation.effects) return InfoCollector(observedEffect).collectFromSchema(computation.effects)
@@ -50,8 +50,8 @@ import org.jetbrains.kotlin.utils.addIfNotNull
* about effects of that call. * about effects of that call.
*/ */
class EffectsExtractingVisitor( class EffectsExtractingVisitor(
private val trace: BindingTrace, private val trace: BindingTrace,
private val moduleDescriptor: ModuleDescriptor private val moduleDescriptor: ModuleDescriptor
) : KtVisitor<Computation, Unit>() { ) : KtVisitor<Computation, Unit>() {
fun extractOrGetCached(element: KtElement): Computation { fun extractOrGetCached(element: KtElement): Computation {
trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it } trace[BindingContext.EXPRESSION_EFFECTS, element]?.let { return it }
@@ -66,9 +66,18 @@ class EffectsExtractingVisitor(
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
return when { return when {
descriptor.isEqualsDescriptor() -> CallComputation(DefaultBuiltIns.Instance.booleanType, EqualsFunctor(false).invokeWithArguments(arguments)) descriptor.isEqualsDescriptor() -> CallComputation(
descriptor is ValueDescriptor -> ESDataFlowValue(descriptor, (element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION) DefaultBuiltIns.Instance.booleanType,
descriptor is FunctionDescriptor -> CallComputation(descriptor.returnType, descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList()) EqualsFunctor(false).invokeWithArguments(arguments)
)
descriptor is ValueDescriptor -> ESDataFlowValue(
descriptor,
(element as KtExpression).createDataFlowValue() ?: return UNKNOWN_COMPUTATION
)
descriptor is FunctionDescriptor -> CallComputation(
descriptor.returnType,
descriptor.getFunctor()?.invokeWithArguments(arguments) ?: emptyList()
)
else -> UNKNOWN_COMPUTATION else -> UNKNOWN_COMPUTATION
} }
} }
@@ -78,15 +87,15 @@ class EffectsExtractingVisitor(
override fun visitLambdaExpression(expression: KtLambdaExpression, data: Unit?): Computation = UNKNOWN_COMPUTATION override fun visitLambdaExpression(expression: KtLambdaExpression, data: Unit?): Computation = UNKNOWN_COMPUTATION
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): Computation = override fun visitParenthesizedExpression(expression: KtParenthesizedExpression, data: Unit): Computation =
KtPsiUtil.deparenthesize(expression)?.accept(this, data) ?: UNKNOWN_COMPUTATION KtPsiUtil.deparenthesize(expression)?.accept(this, data) ?: UNKNOWN_COMPUTATION
override fun visitConstantExpression(expression: KtConstantExpression, data: Unit): Computation { override fun visitConstantExpression(expression: KtConstantExpression, data: Unit): Computation {
val bindingContext = trace.bindingContext val bindingContext = trace.bindingContext
val type: KotlinType = bindingContext.getType(expression) ?: return UNKNOWN_COMPUTATION val type: KotlinType = bindingContext.getType(expression) ?: return UNKNOWN_COMPUTATION
val compileTimeConstant: CompileTimeConstant<*> val compileTimeConstant: CompileTimeConstant<*> =
= bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return UNKNOWN_COMPUTATION bindingContext.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return UNKNOWN_COMPUTATION
val value: Any? = compileTimeConstant.getValue(type) val value: Any? = compileTimeConstant.getValue(type)
return when (value) { return when (value) {
@@ -99,7 +108,10 @@ class EffectsExtractingVisitor(
override fun visitIsExpression(expression: KtIsExpression, data: Unit): Computation { override fun visitIsExpression(expression: KtIsExpression, data: Unit): Computation {
val rightType: KotlinType = trace[BindingContext.TYPE, expression.typeReference] ?: return UNKNOWN_COMPUTATION val rightType: KotlinType = trace[BindingContext.TYPE, expression.typeReference] ?: return UNKNOWN_COMPUTATION
val arg = extractOrGetCached(expression.leftHandSide) val arg = extractOrGetCached(expression.leftHandSide)
return CallComputation(DefaultBuiltIns.Instance.booleanType, IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg))) return CallComputation(
DefaultBuiltIns.Instance.booleanType,
IsFunctor(rightType, expression.isNegated).invokeWithArguments(listOf(arg))
)
} }
override fun visitBinaryExpression(expression: KtBinaryExpression, data: Unit): Computation { override fun visitBinaryExpression(expression: KtBinaryExpression, data: Unit): Computation {
@@ -132,10 +144,10 @@ class EffectsExtractingVisitor(
private fun KtExpression.createDataFlowValue(): DataFlowValue? { private fun KtExpression.createDataFlowValue(): DataFlowValue? {
return DataFlowValueFactory.createDataFlowValue( return DataFlowValueFactory.createDataFlowValue(
expression = this, expression = this,
type = trace.getType(this) ?: return null, type = trace.getType(this) ?: return null,
bindingContext = trace.bindingContext, bindingContext = trace.bindingContext,
containingDeclarationOrModule = moduleDescriptor containingDeclarationOrModule = moduleDescriptor
) )
} }
@@ -148,9 +160,9 @@ class EffectsExtractingVisitor(
} }
private fun ResolvedCall<*>.isCallWithUnsupportedReceiver(): Boolean = private fun ResolvedCall<*>.isCallWithUnsupportedReceiver(): Boolean =
(extensionReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this || (extensionReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this ||
(dispatchReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this || (dispatchReceiver as? ExpressionReceiver)?.expression?.getResolvedCall(trace.bindingContext) == this ||
(explicitReceiverKind == ExplicitReceiverKind.BOTH_RECEIVERS) (explicitReceiverKind == ExplicitReceiverKind.BOTH_RECEIVERS)
private fun ResolvedCall<*>.getCallArgumentsAsComputations(): List<Computation>? { private fun ResolvedCall<*>.getCallArgumentsAsComputations(): List<Computation>? {
val arguments = mutableListOf<Computation>() val arguments = mutableListOf<Computation>()
@@ -32,8 +32,7 @@ internal class PsiConstantParser(val trace: BindingTrace) : KtVisitor<ConstantRe
override fun visitConstantExpression(expression: KtConstantExpression, data: Unit?): ConstantReference? { override fun visitConstantExpression(expression: KtConstantExpression, data: Unit?): ConstantReference? {
val type: KotlinType = trace.getType(expression) ?: return null val type: KotlinType = trace.getType(expression) ?: return null
val compileTimeConstant: CompileTimeConstant<*> val compileTimeConstant: CompileTimeConstant<*> = trace.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return null
= trace.get(BindingContext.COMPILE_TIME_VALUE, expression) ?: return null
val value: Any? = compileTimeConstant.getValue(type) val value: Any? = compileTimeConstant.getValue(type)
return when (value) { return when (value) {
@@ -46,10 +46,10 @@ internal class PsiContractParserDispatcher(val trace: BindingTrace, val contract
private val conditionParser = PsiConditionParser(trace, this) private val conditionParser = PsiConditionParser(trace, this)
private val constantParser = PsiConstantParser(trace) private val constantParser = PsiConstantParser(trace)
private val effectsParsers: Map<Name, PsiEffectParser> = mapOf( private val effectsParsers: Map<Name, PsiEffectParser> = mapOf(
RETURNS_EFFECT to PsiReturnsEffectParser(trace, this), RETURNS_EFFECT to PsiReturnsEffectParser(trace, this),
RETURNS_NOT_NULL_EFFECT to PsiReturnsEffectParser(trace, this), RETURNS_NOT_NULL_EFFECT to PsiReturnsEffectParser(trace, this),
CALLS_IN_PLACE_EFFECT to PsiCallsEffectParser(trace, this), CALLS_IN_PLACE_EFFECT to PsiCallsEffectParser(trace, this),
CONDITIONAL_EFFECT to PsiConditionalEffectParser(trace, this) CONDITIONAL_EFFECT to PsiConditionalEffectParser(trace, this)
) )
fun parseContract(expression: KtExpression?, ownerDescriptor: FunctionDescriptor): ContractDescription? { fun parseContract(expression: KtExpression?, ownerDescriptor: FunctionDescriptor): ContractDescription? {
@@ -89,12 +89,22 @@ internal class PsiContractParserDispatcher(val trace: BindingTrace, val contract
if (expression == null) return null if (expression == null) return null
val descriptor = expression.getResolvedCall(trace.bindingContext)?.resultingDescriptor ?: return null val descriptor = expression.getResolvedCall(trace.bindingContext)?.resultingDescriptor ?: return null
if (descriptor !is ParameterDescriptor) { if (descriptor !is ParameterDescriptor) {
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "only references to parameters are allowed in contract description")) trace.report(
Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(
expression,
"only references to parameters are allowed in contract description"
)
)
return null return null
} }
if (descriptor is ReceiverParameterDescriptor && descriptor.type.constructor.declarationDescriptor?.isFromContractDsl() == true) { if (descriptor is ReceiverParameterDescriptor && descriptor.type.constructor.declarationDescriptor?.isFromContractDsl() == true) {
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(expression, "only references to parameters are allowed. Did you miss label on <this>?")) trace.report(
Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(
expression,
"only references to parameters are allowed. Did you miss label on <this>?"
)
)
} }
return if (KotlinBuiltIns.isBoolean(descriptor.type)) return if (KotlinBuiltIns.isBoolean(descriptor.type))
@@ -83,10 +83,10 @@ fun DeclarationDescriptor.isCallsInPlaceEffectDescriptor(): Boolean = equalsDslD
fun DeclarationDescriptor.isInvocationKindEnum(): Boolean = equalsDslDescriptor(INVOCATION_KIND_ENUM) fun DeclarationDescriptor.isInvocationKindEnum(): Boolean = equalsDslDescriptor(INVOCATION_KIND_ENUM)
fun DeclarationDescriptor.isEqualsDescriptor(): Boolean = fun DeclarationDescriptor.isEqualsDescriptor(): Boolean =
this is FunctionDescriptor && this.name == Name.identifier("equals") && // fast checks this is FunctionDescriptor && this.name == Name.identifier("equals") && // fast checks
this.returnType?.isBoolean() == true && this.valueParameters.singleOrNull()?.type?.isNullableAny() == true // signature matches this.returnType?.isBoolean() == true && this.valueParameters.singleOrNull()?.type?.isNullableAny() == true // signature matches
internal fun ResolvedCall<*>.firstArgumentAsExpressionOrNull(): KtExpression? = internal fun ResolvedCall<*>.firstArgumentAsExpressionOrNull(): KtExpression? =
this.valueArgumentsByIndex?.firstOrNull()?.safeAs<ExpressionValueArgument>()?.valueArgument?.getArgumentExpression() this.valueArgumentsByIndex?.firstOrNull()?.safeAs<ExpressionValueArgument>()?.valueArgument?.getArgumentExpression()
private fun DeclarationDescriptor.equalsDslDescriptor(dslName: Name): Boolean = this.name == dslName && this.isFromContractDsl() private fun DeclarationDescriptor.equalsDslDescriptor(dslName: Name): Boolean = this.name == dslName && this.isFromContractDsl()
@@ -29,8 +29,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.parents import org.jetbrains.kotlin.resolve.descriptorUtil.parents
internal class PsiCallsEffectParser( internal class PsiCallsEffectParser(
trace: BindingTrace, trace: BindingTrace,
contractParserDispatcher: PsiContractParserDispatcher contractParserDispatcher: PsiContractParserDispatcher
) : AbstractPsiEffectParser(trace, contractParserDispatcher) { ) : AbstractPsiEffectParser(trace, contractParserDispatcher) {
override fun tryParseEffect(expression: KtExpression): EffectDeclaration? { override fun tryParseEffect(expression: KtExpression): EffectDeclaration? {
@@ -29,8 +29,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
internal class PsiConditionalEffectParser( internal class PsiConditionalEffectParser(
trace: BindingTrace, trace: BindingTrace,
dispatcher: PsiContractParserDispatcher dispatcher: PsiContractParserDispatcher
) : AbstractPsiEffectParser(trace, dispatcher) { ) : AbstractPsiEffectParser(trace, dispatcher) {
override fun tryParseEffect(expression: KtExpression): EffectDeclaration? { override fun tryParseEffect(expression: KtExpression): EffectDeclaration? {
val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null
@@ -38,9 +38,9 @@ internal class PsiConditionalEffectParser(
if (!resolvedCall.resultingDescriptor.isImpliesCallDescriptor()) return null if (!resolvedCall.resultingDescriptor.isImpliesCallDescriptor()) return null
val effect = contractParserDispatcher.parseEffect(resolvedCall.dispatchReceiver.safeAs<ExpressionReceiver>()?.expression) val effect = contractParserDispatcher.parseEffect(resolvedCall.dispatchReceiver.safeAs<ExpressionReceiver>()?.expression)
?: return null ?: return null
val condition = contractParserDispatcher.parseCondition(resolvedCall.firstArgumentAsExpressionOrNull()) val condition = contractParserDispatcher.parseCondition(resolvedCall.firstArgumentAsExpressionOrNull())
?: return null ?: return null
return ConditionalEffectDeclaration(effect, condition) return ConditionalEffectDeclaration(effect, condition)
} }
@@ -26,8 +26,8 @@ import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
internal class PsiReturnsEffectParser( internal class PsiReturnsEffectParser(
trace: BindingTrace, trace: BindingTrace,
contractParserDispatcher: PsiContractParserDispatcher contractParserDispatcher: PsiContractParserDispatcher
) : AbstractPsiEffectParser(trace, contractParserDispatcher) { ) : AbstractPsiEffectParser(trace, contractParserDispatcher) {
override fun tryParseEffect(expression: KtExpression): EffectDeclaration? { override fun tryParseEffect(expression: KtExpression): EffectDeclaration? {
val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null val resolvedCall = expression.getResolvedCall(trace.bindingContext) ?: return null
@@ -39,13 +39,18 @@ internal class PsiReturnsEffectParser(
if (!descriptor.isReturnsEffectDescriptor()) return null if (!descriptor.isReturnsEffectDescriptor()) return null
val argumentExpression = resolvedCall.firstArgumentAsExpressionOrNull() val argumentExpression = resolvedCall.firstArgumentAsExpressionOrNull()
val constantValue = if (argumentExpression == null) val constantValue = if (argumentExpression == null) {
ConstantReference.WILDCARD ConstantReference.WILDCARD
else { } else {
// Note that we distinguish absence of an argument and unparsed argument // Note that we distinguish absence of an argument and unparsed argument
val constant = contractParserDispatcher.parseConstant(argumentExpression) val constant = contractParserDispatcher.parseConstant(argumentExpression)
if (constant == null) { if (constant == null) {
trace.report(Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(argumentExpression, "only true/false/null constants in Returns-effect are currently supported")) trace.report(
Errors.ERROR_IN_CONTRACT_DESCRIPTION.on(
argumentExpression,
"only true/false/null constants in Returns-effect are currently supported"
)
)
return null return null
} }
constant constant
@@ -40,22 +40,24 @@ import org.jetbrains.kotlin.storage.getValue
*/ */
class AnnotationSplitter( class AnnotationSplitter(
val storageManager: StorageManager, val storageManager: StorageManager,
allAnnotations: Annotations, allAnnotations: Annotations,
applicableTargetsLazy: () -> Set<AnnotationUseSiteTarget> applicableTargetsLazy: () -> Set<AnnotationUseSiteTarget>
) { ) {
companion object { companion object {
private val TARGET_PRIORITIES = setOf(CONSTRUCTOR_PARAMETER, PROPERTY, FIELD) private val TARGET_PRIORITIES = setOf(CONSTRUCTOR_PARAMETER, PROPERTY, FIELD)
@JvmStatic fun create( @JvmStatic
storageManager: StorageManager, fun create(
annotations: Annotations, storageManager: StorageManager,
targets: Set<AnnotationUseSiteTarget> annotations: Annotations,
targets: Set<AnnotationUseSiteTarget>
): AnnotationSplitter { ): AnnotationSplitter {
return AnnotationSplitter(storageManager, annotations, { targets }) return AnnotationSplitter(storageManager, annotations, { targets })
} }
@JvmStatic fun getTargetSet(parameter: Boolean, context: BindingContext, wrapper: PropertyWrapper): Set<AnnotationUseSiteTarget> { @JvmStatic
fun getTargetSet(parameter: Boolean, context: BindingContext, wrapper: PropertyWrapper): Set<AnnotationUseSiteTarget> {
val descriptor = wrapper.descriptor val descriptor = wrapper.descriptor
assert(descriptor != null) assert(descriptor != null)
val hasBackingField = context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] ?: false val hasBackingField = context[BindingContext.BACKING_FIELD_REQUIRED, descriptor] ?: false
@@ -63,8 +65,9 @@ class AnnotationSplitter(
return getTargetSet(parameter, descriptor!!.isVar, hasBackingField, hasDelegate) return getTargetSet(parameter, descriptor!!.isVar, hasBackingField, hasDelegate)
} }
@JvmStatic fun getTargetSet( @JvmStatic
parameter: Boolean, isVar: Boolean, hasBackingField: Boolean, hasDelegate: Boolean fun getTargetSet(
parameter: Boolean, isVar: Boolean, hasBackingField: Boolean, hasDelegate: Boolean
): Set<AnnotationUseSiteTarget> = hashSetOf(PROPERTY, PROPERTY_GETTER).apply { ): Set<AnnotationUseSiteTarget> = hashSetOf(PROPERTY, PROPERTY_GETTER).apply {
if (parameter) add(CONSTRUCTOR_PARAMETER) if (parameter) add(CONSTRUCTOR_PARAMETER)
if (hasBackingField) add(FIELD) if (hasBackingField) add(FIELD)
@@ -23,31 +23,33 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
sealed class LocalVariableAccessorDescriptor( sealed class LocalVariableAccessorDescriptor(
final override val correspondingVariable: LocalVariableDescriptor, final override val correspondingVariable: LocalVariableDescriptor,
isGetter: Boolean isGetter: Boolean
) : SimpleFunctionDescriptorImpl( ) : SimpleFunctionDescriptorImpl(
correspondingVariable.containingDeclaration, correspondingVariable.containingDeclaration,
null, null,
Annotations.EMPTY, Annotations.EMPTY,
Name.special((if (isGetter) "<get-" else "<set-") + correspondingVariable.name + ">"), Name.special((if (isGetter) "<get-" else "<set-") + correspondingVariable.name + ">"),
CallableMemberDescriptor.Kind.SYNTHESIZED, CallableMemberDescriptor.Kind.SYNTHESIZED,
SourceElement.NO_SOURCE SourceElement.NO_SOURCE
), VariableAccessorDescriptor { ), VariableAccessorDescriptor {
class Getter(correspondingVariable: LocalVariableDescriptor) : LocalVariableAccessorDescriptor(correspondingVariable, true) class Getter(correspondingVariable: LocalVariableDescriptor) : LocalVariableAccessorDescriptor(correspondingVariable, true)
class Setter(correspondingVariable: LocalVariableDescriptor) : LocalVariableAccessorDescriptor(correspondingVariable, false) class Setter(correspondingVariable: LocalVariableDescriptor) : LocalVariableAccessorDescriptor(correspondingVariable, false)
init { init {
val valueParameters = val valueParameters =
if (isGetter) emptyList() else listOf(createValueParameter(Name.identifier("value"), correspondingVariable.type)) if (isGetter) emptyList() else listOf(createValueParameter(Name.identifier("value"), correspondingVariable.type))
val returnType = val returnType =
if (isGetter) correspondingVariable.type else correspondingVariable.builtIns.unitType if (isGetter) correspondingVariable.type else correspondingVariable.builtIns.unitType
@Suppress("LeakingThis") @Suppress("LeakingThis")
initialize(null, null, emptyList(), valueParameters, returnType, Modality.FINAL, Visibilities.LOCAL) initialize(null, null, emptyList(), valueParameters, returnType, Modality.FINAL, Visibilities.LOCAL)
} }
private fun createValueParameter(name: Name, type: KotlinType): ValueParameterDescriptorImpl { private fun createValueParameter(name: Name, type: KotlinType): ValueParameterDescriptorImpl {
return ValueParameterDescriptorImpl(this, null, 0, Annotations.EMPTY, name, type, return ValueParameterDescriptorImpl(
false, false, false, null, SourceElement.NO_SOURCE) this, null, 0, Annotations.EMPTY, name, type,
false, false, false, null, SourceElement.NO_SOURCE
)
} }
} }
@@ -25,15 +25,19 @@ import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.resolve.source.toSourceElement import org.jetbrains.kotlin.resolve.source.toSourceElement
class SyntheticFieldDescriptor private constructor( class SyntheticFieldDescriptor private constructor(
val propertyDescriptor: PropertyDescriptor, val propertyDescriptor: PropertyDescriptor,
accessorDescriptor: PropertyAccessorDescriptor,
property: KtProperty
) : LocalVariableDescriptor(
accessorDescriptor, Annotations.EMPTY, SyntheticFieldDescriptor.NAME,
propertyDescriptor.type, propertyDescriptor.isVar, false, false,
property.toSourceElement()
) {
constructor(
accessorDescriptor: PropertyAccessorDescriptor, accessorDescriptor: PropertyAccessorDescriptor,
property: KtProperty property: KtProperty
): LocalVariableDescriptor(accessorDescriptor, Annotations.EMPTY, SyntheticFieldDescriptor.NAME, ) : this(accessorDescriptor.correspondingProperty, accessorDescriptor, property)
propertyDescriptor.type, propertyDescriptor.isVar, false, false,
property.toSourceElement()) {
constructor(accessorDescriptor: PropertyAccessorDescriptor,
property: KtProperty): this(accessorDescriptor.correspondingProperty, accessorDescriptor, property)
override fun getDispatchReceiverParameter() = null override fun getDispatchReceiverParameter() = null
@@ -19,8 +19,8 @@ package org.jetbrains.kotlin.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer
class RenderedDiagnostic<D : Diagnostic>( class RenderedDiagnostic<D : Diagnostic>(
val diagnostic: D, val diagnostic: D,
val renderer: DiagnosticRenderer<D> val renderer: DiagnosticRenderer<D>
) { ) {
val text = renderer.render(diagnostic) val text = renderer.render(diagnostic)
@@ -44,42 +44,39 @@ import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isNullableNothing import org.jetbrains.kotlin.types.typeUtil.isNullableNothing
fun ResolutionContext<*>.reportTypeMismatchDueToTypeProjection( fun ResolutionContext<*>.reportTypeMismatchDueToTypeProjection(
expression: KtElement, expression: KtElement,
expectedType: KotlinType, expectedType: KotlinType,
expressionType: KotlinType? expressionType: KotlinType?
): Boolean { ): Boolean {
if (!TypeUtils.contains(expectedType) { it.isAnyOrNullableAny() || it.isNothing() || it.isNullableNothing() }) return false if (!TypeUtils.contains(expectedType) { it.isAnyOrNullableAny() || it.isNothing() || it.isNullableNothing() }) return false
val callPosition = this.callPosition val callPosition = this.callPosition
val (resolvedCall, correspondingNotApproximatedTypeByDescriptor: (CallableDescriptor) -> KotlinType?) = when (callPosition) { val (resolvedCall, correspondingNotApproximatedTypeByDescriptor: (CallableDescriptor) -> KotlinType?) = when (callPosition) {
is CallPosition.ValueArgumentPosition -> Pair( is CallPosition.ValueArgumentPosition -> Pair(
callPosition.resolvedCall, { callPosition.resolvedCall, { f: CallableDescriptor ->
f: CallableDescriptor -> getEffectiveExpectedType(f.valueParameters[callPosition.valueParameter.index], callPosition.valueArgument, this)
getEffectiveExpectedType(f.valueParameters[callPosition.valueParameter.index], callPosition.valueArgument, this) })
})
is CallPosition.ExtensionReceiverPosition -> Pair( is CallPosition.ExtensionReceiverPosition -> Pair(
callPosition.resolvedCall, { callPosition.resolvedCall, { f: CallableDescriptor ->
f: CallableDescriptor -> f.extensionReceiverParameter?.type
f.extensionReceiverParameter?.type })
})
is CallPosition.PropertyAssignment -> Pair( is CallPosition.PropertyAssignment -> Pair(
callPosition.leftPart.getResolvedCall(trace.bindingContext) ?: return false, { callPosition.leftPart.getResolvedCall(trace.bindingContext) ?: return false, { f: CallableDescriptor ->
f: CallableDescriptor -> (f as? PropertyDescriptor)?.setter?.valueParameters?.get(0)?.type
(f as? PropertyDescriptor)?.setter?.valueParameters?.get(0)?.type })
})
is CallPosition.Unknown -> return false is CallPosition.Unknown -> return false
} }
val receiverType = resolvedCall.smartCastDispatchReceiverType val receiverType = resolvedCall.smartCastDispatchReceiverType
?: (resolvedCall.dispatchReceiver ?: return false).type ?: (resolvedCall.dispatchReceiver ?: return false).type
val callableDescriptor = resolvedCall.resultingDescriptor.original val callableDescriptor = resolvedCall.resultingDescriptor.original
val substitutedDescriptor = val substitutedDescriptor =
TypeConstructorSubstitution TypeConstructorSubstitution
.create(receiverType) .create(receiverType)
.wrapWithCapturingSubstitution(needApproximation = false) .wrapWithCapturingSubstitution(needApproximation = false)
.buildSubstitutor().let { callableDescriptor.substitute(it) } ?: return false .buildSubstitutor().let { callableDescriptor.substitute(it) } ?: return false
val nonApproximatedExpectedType = correspondingNotApproximatedTypeByDescriptor(substitutedDescriptor) ?: return false val nonApproximatedExpectedType = correspondingNotApproximatedTypeByDescriptor(substitutedDescriptor) ?: return false
if (!TypeUtils.contains(nonApproximatedExpectedType) { it.isCaptured() }) return false if (!TypeUtils.contains(nonApproximatedExpectedType) { it.isCaptured() }) return false
@@ -87,25 +84,26 @@ fun ResolutionContext<*>.reportTypeMismatchDueToTypeProjection(
if (expectedType.isNothing()) { if (expectedType.isNothing()) {
if (callPosition is CallPosition.PropertyAssignment) { if (callPosition is CallPosition.PropertyAssignment) {
trace.report(Errors.SETTER_PROJECTED_OUT.on(callPosition.leftPart ?: return false, resolvedCall.resultingDescriptor)) trace.report(Errors.SETTER_PROJECTED_OUT.on(callPosition.leftPart ?: return false, resolvedCall.resultingDescriptor))
} } else {
else {
val call = resolvedCall.call val call = resolvedCall.call
val reportOn = val reportOn =
if (resolvedCall is VariableAsFunctionResolvedCall) if (resolvedCall is VariableAsFunctionResolvedCall)
resolvedCall.variableCall.call.calleeExpression resolvedCall.variableCall.call.calleeExpression
else else
call.calleeExpression call.calleeExpression
trace.reportDiagnosticOnce(Errors.MEMBER_PROJECTED_OUT.on(reportOn ?: call.callElement, callableDescriptor, receiverType)) trace.reportDiagnosticOnce(Errors.MEMBER_PROJECTED_OUT.on(reportOn ?: call.callElement, callableDescriptor, receiverType))
} }
} } else {
else {
// expressionType can be null when reporting CONSTANT_EXPECTED_TYPE_MISMATCH (see addAll.kt test) // expressionType can be null when reporting CONSTANT_EXPECTED_TYPE_MISMATCH (see addAll.kt test)
expressionType ?: return false expressionType ?: return false
trace.report( trace.report(
Errors.TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS.on( Errors.TYPE_MISMATCH_DUE_TO_TYPE_PROJECTIONS.on(
expression, TypeMismatchDueToTypeProjectionsData( expression, TypeMismatchDueToTypeProjectionsData(
expectedType, expressionType, receiverType, callableDescriptor))) expectedType, expressionType, receiverType, callableDescriptor
)
)
)
} }
@@ -119,16 +117,16 @@ fun BindingTrace.reportDiagnosticOnce(diagnostic: Diagnostic) {
} }
class TypeMismatchDueToTypeProjectionsData( class TypeMismatchDueToTypeProjectionsData(
val expectedType: KotlinType, val expectedType: KotlinType,
val expressionType: KotlinType, val expressionType: KotlinType,
val receiverType: KotlinType, val receiverType: KotlinType,
val callableDescriptor: CallableDescriptor val callableDescriptor: CallableDescriptor
) )
fun ResolutionContext<*>.reportTypeMismatchDueToScalaLikeNamedFunctionSyntax( fun ResolutionContext<*>.reportTypeMismatchDueToScalaLikeNamedFunctionSyntax(
expression: KtElement, expression: KtElement,
expectedType: KotlinType, expectedType: KotlinType,
expressionType: KotlinType? expressionType: KotlinType?
): Boolean { ): Boolean {
if (expressionType == null) return false if (expressionType == null) return false
@@ -141,21 +139,26 @@ fun ResolutionContext<*>.reportTypeMismatchDueToScalaLikeNamedFunctionSyntax(
} }
private fun isScalaLikeEqualsBlock(expression: KtElement): Boolean = private fun isScalaLikeEqualsBlock(expression: KtElement): Boolean =
expression is KtLambdaExpression && expression is KtLambdaExpression &&
expression.parent.let { it is KtNamedFunction && it.equalsToken != null } expression.parent.let { it is KtNamedFunction && it.equalsToken != null }
inline fun reportOnDeclaration(trace: BindingTrace, descriptor: DeclarationDescriptor, what: (PsiElement) -> Diagnostic) { inline fun reportOnDeclaration(trace: BindingTrace, descriptor: DeclarationDescriptor, what: (PsiElement) -> Diagnostic) {
DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.let { psiElement -> DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.let { psiElement ->
trace.report(what(psiElement)) trace.report(what(psiElement))
} }
} }
inline fun reportOnDeclarationOrFail(trace: BindingTrace, descriptor: DeclarationDescriptor, what: (PsiElement) -> Diagnostic) { inline fun reportOnDeclarationOrFail(trace: BindingTrace, descriptor: DeclarationDescriptor, what: (PsiElement) -> Diagnostic) {
DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.let { psiElement -> DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.let { psiElement ->
trace.report(what(psiElement)) trace.report(what(psiElement))
} ?: throw AssertionError("No declaration for $descriptor") } ?: throw AssertionError("No declaration for $descriptor")
} }
inline fun <reified T : KtDeclaration> reportOnDeclarationAs(trace: BindingTrace, descriptor: DeclarationDescriptor, what: (T) -> Diagnostic) { inline fun <reified T : KtDeclaration> reportOnDeclarationAs(
trace: BindingTrace,
descriptor: DeclarationDescriptor,
what: (T) -> Diagnostic
) {
DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.let { psiElement -> DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.let { psiElement ->
(psiElement as? T)?.let { (psiElement as? T)?.let {
trace.report(what(it)) trace.report(what(it))
@@ -166,7 +169,7 @@ inline fun <reified T : KtDeclaration> reportOnDeclarationAs(trace: BindingTrace
fun <D : Diagnostic> DiagnosticSink.reportFromPlugin(diagnostic: D, ext: DefaultErrorMessages.Extension) { fun <D : Diagnostic> DiagnosticSink.reportFromPlugin(diagnostic: D, ext: DefaultErrorMessages.Extension) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val renderer = ext.map[diagnostic.factory] as? DiagnosticRenderer<D> val renderer = ext.map[diagnostic.factory] as? DiagnosticRenderer<D>
?: error("Renderer not found for diagnostic ${diagnostic.factory.name}") ?: error("Renderer not found for diagnostic ${diagnostic.factory.name}")
val renderedDiagnostic = RenderedDiagnostic(diagnostic, renderer) val renderedDiagnostic = RenderedDiagnostic(diagnostic, renderer)
@@ -20,9 +20,9 @@ import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
class LanguageFeatureMessageRenderer @JvmOverloads constructor( class LanguageFeatureMessageRenderer @JvmOverloads constructor(
private val type: Type, private val type: Type,
private val useHtml: Boolean = false private val useHtml: Boolean = false
): DiagnosticParameterRenderer<Pair<LanguageFeature, LanguageVersionSettings>> { ) : DiagnosticParameterRenderer<Pair<LanguageFeature, LanguageVersionSettings>> {
enum class Type { enum class Type {
UNSUPPORTED, UNSUPPORTED,
@@ -58,8 +58,7 @@ class LanguageFeatureMessageRenderer @JvmOverloads constructor(
if (hintUrl != null) { if (hintUrl != null) {
if (useHtml) { if (useHtml) {
sb.append(" (").append("see more <a href=\"").append(hintUrl).append("\">here</a>)") sb.append(" (").append("see more <a href=\"").append(hintUrl).append("\">here</a>)")
} } else {
else {
sb.append(" (see: ").append(hintUrl).append(")") sb.append(" (see: ").append(hintUrl).append(")")
} }
} }
@@ -21,11 +21,11 @@ import org.jetbrains.kotlin.descriptors.MemberDescriptor
import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker.Compatibility.Incompatible import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker.Compatibility.Incompatible
class PlatformIncompatibilityDiagnosticRenderer( class PlatformIncompatibilityDiagnosticRenderer(
private val mode: MultiplatformDiagnosticRenderingMode private val mode: MultiplatformDiagnosticRenderingMode
) : DiagnosticParameterRenderer<Map<Incompatible, Collection<MemberDescriptor>>> { ) : DiagnosticParameterRenderer<Map<Incompatible, Collection<MemberDescriptor>>> {
override fun render( override fun render(
obj: Map<Incompatible, Collection<MemberDescriptor>>, obj: Map<Incompatible, Collection<MemberDescriptor>>,
renderingContext: RenderingContext renderingContext: RenderingContext
): String { ): String {
if (obj.isEmpty()) return "" if (obj.isEmpty()) return ""
@@ -42,11 +42,11 @@ class PlatformIncompatibilityDiagnosticRenderer(
} }
class IncompatibleExpectedActualClassScopesRenderer( class IncompatibleExpectedActualClassScopesRenderer(
private val mode: MultiplatformDiagnosticRenderingMode private val mode: MultiplatformDiagnosticRenderingMode
) : DiagnosticParameterRenderer<List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>> { ) : DiagnosticParameterRenderer<List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>> {
override fun render( override fun render(
obj: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>, obj: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>,
renderingContext: RenderingContext renderingContext: RenderingContext
): String { ): String {
if (obj.isEmpty()) return "" if (obj.isEmpty()) return ""
@@ -82,10 +82,10 @@ open class MultiplatformDiagnosticRenderingMode {
} }
private fun StringBuilder.renderIncompatibilityInformation( private fun StringBuilder.renderIncompatibilityInformation(
map: Map<Incompatible, Collection<MemberDescriptor>>, map: Map<Incompatible, Collection<MemberDescriptor>>,
indent: String, indent: String,
context: RenderingContext, context: RenderingContext,
mode: MultiplatformDiagnosticRenderingMode mode: MultiplatformDiagnosticRenderingMode
) { ) {
for ((incompatibility, descriptors) in map) { for ((incompatibility, descriptors) in map) {
append(indent) append(indent)
@@ -109,10 +109,10 @@ private fun StringBuilder.renderIncompatibilityInformation(
} }
private fun StringBuilder.renderIncompatibleClassScopes( private fun StringBuilder.renderIncompatibleClassScopes(
unfulfilled: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>, unfulfilled: List<Pair<MemberDescriptor, Map<Incompatible, Collection<MemberDescriptor>>>>,
indent: String, indent: String,
context: RenderingContext, context: RenderingContext,
mode: MultiplatformDiagnosticRenderingMode mode: MultiplatformDiagnosticRenderingMode
) { ) {
mode.renderList(this, unfulfilled.indices.map { index -> mode.renderList(this, unfulfilled.indices.map { index ->
{ {
@@ -33,8 +33,8 @@ abstract class AbstractDiagnosticWithParametersRenderer<D : Diagnostic> protecte
class DiagnosticWithParameters1Renderer<A : Any>( class DiagnosticWithParameters1Renderer<A : Any>(
message: String, message: String,
private val rendererForA: DiagnosticParameterRenderer<A>? private val rendererForA: DiagnosticParameterRenderer<A>?
) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters1<*, A>>(message) { ) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters1<*, A>>(message) {
override fun renderParameters(diagnostic: DiagnosticWithParameters1<*, A>): Array<out Any> { override fun renderParameters(diagnostic: DiagnosticWithParameters1<*, A>): Array<out Any> {
@@ -44,40 +44,40 @@ class DiagnosticWithParameters1Renderer<A : Any>(
} }
class DiagnosticWithParameters2Renderer<A : Any, B : Any>( class DiagnosticWithParameters2Renderer<A : Any, B : Any>(
message: String, message: String,
private val rendererForA: DiagnosticParameterRenderer<A>?, private val rendererForA: DiagnosticParameterRenderer<A>?,
private val rendererForB: DiagnosticParameterRenderer<B>? private val rendererForB: DiagnosticParameterRenderer<B>?
) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters2<*, A, B>>(message) { ) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters2<*, A, B>>(message) {
override fun renderParameters(diagnostic: DiagnosticWithParameters2<*, A, B>): Array<out Any> { override fun renderParameters(diagnostic: DiagnosticWithParameters2<*, A, B>): Array<out Any> {
val context = RenderingContext.of(diagnostic.a, diagnostic.b) val context = RenderingContext.of(diagnostic.a, diagnostic.b)
return arrayOf( return arrayOf(
renderParameter(diagnostic.a, rendererForA, context), renderParameter(diagnostic.a, rendererForA, context),
renderParameter(diagnostic.b, rendererForB, context) renderParameter(diagnostic.b, rendererForB, context)
) )
} }
} }
class DiagnosticWithParameters3Renderer<A : Any, B : Any, C : Any>( class DiagnosticWithParameters3Renderer<A : Any, B : Any, C : Any>(
message: String, message: String,
private val rendererForA: DiagnosticParameterRenderer<A>?, private val rendererForA: DiagnosticParameterRenderer<A>?,
private val rendererForB: DiagnosticParameterRenderer<B>?, private val rendererForB: DiagnosticParameterRenderer<B>?,
private val rendererForC: DiagnosticParameterRenderer<C>? private val rendererForC: DiagnosticParameterRenderer<C>?
) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters3<*, A, B, C>>(message) { ) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters3<*, A, B, C>>(message) {
override fun renderParameters(diagnostic: DiagnosticWithParameters3<*, A, B, C>): Array<out Any> { override fun renderParameters(diagnostic: DiagnosticWithParameters3<*, A, B, C>): Array<out Any> {
val context = RenderingContext.of(diagnostic.a, diagnostic.b, diagnostic.c) val context = RenderingContext.of(diagnostic.a, diagnostic.b, diagnostic.c)
return arrayOf( return arrayOf(
renderParameter(diagnostic.a, rendererForA, context), renderParameter(diagnostic.a, rendererForA, context),
renderParameter(diagnostic.b, rendererForB, context), renderParameter(diagnostic.b, rendererForB, context),
renderParameter(diagnostic.c, rendererForC, context) renderParameter(diagnostic.c, rendererForC, context)
) )
} }
} }
class DiagnosticWithParametersMultiRenderer<A>( class DiagnosticWithParametersMultiRenderer<A>(
message: String, message: String,
private val renderer: MultiRenderer<A> private val renderer: MultiRenderer<A>
) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters1<*, A>>(message) { ) : AbstractDiagnosticWithParametersRenderer<DiagnosticWithParameters1<*, A>>(message) {
override fun renderParameters(diagnostic: DiagnosticWithParameters1<*, A>): Array<out Any> { override fun renderParameters(diagnostic: DiagnosticWithParameters1<*, A>): Array<out Any> {
@@ -42,9 +42,9 @@ interface AnnotationBasedExtension {
} }
private fun AnnotationDescriptor.isASpecialAnnotation( private fun AnnotationDescriptor.isASpecialAnnotation(
specialAnnotations: List<String>, specialAnnotations: List<String>,
visitedAnnotations: MutableSet<String> = hashSetOf(), visitedAnnotations: MutableSet<String> = hashSetOf(),
allowMetaAnnotations: Boolean = true allowMetaAnnotations: Boolean = true
): Boolean { ): Boolean {
val annotationFqName = fqName?.asString() ?: return false val annotationFqName = fqName?.asString() ?: return false
if (annotationFqName in visitedAnnotations) return false // Prevent infinite recursion if (annotationFqName in visitedAnnotations) return false // Prevent infinite recursion
@@ -24,21 +24,21 @@ import org.jetbrains.kotlin.resolve.BindingContext
interface DeclarationAttributeAltererExtension { interface DeclarationAttributeAltererExtension {
companion object : ProjectExtensionDescriptor<DeclarationAttributeAltererExtension>( companion object : ProjectExtensionDescriptor<DeclarationAttributeAltererExtension>(
"org.jetbrains.kotlin.declarationAttributeAltererExtension", "org.jetbrains.kotlin.declarationAttributeAltererExtension",
DeclarationAttributeAltererExtension::class.java DeclarationAttributeAltererExtension::class.java
) )
/** /**
* Returns the new modality for the [declaration], or null if the [currentModality] is good enough. * Returns the new modality for the [declaration], or null if the [currentModality] is good enough.
*/ */
fun refineDeclarationModality( fun refineDeclarationModality(
modifierListOwner: KtModifierListOwner, modifierListOwner: KtModifierListOwner,
declaration: DeclarationDescriptor?, declaration: DeclarationDescriptor?,
containingDeclaration: DeclarationDescriptor?, containingDeclaration: DeclarationDescriptor?,
currentModality: Modality, currentModality: Modality,
bindingContext: BindingContext, bindingContext: BindingContext,
isImplicitModality: Boolean isImplicitModality: Boolean
): Modality? = null ): Modality? = null
fun shouldConvertFirstSAMParameterToReceiver(function: FunctionDescriptor) : Boolean = false fun shouldConvertFirstSAMParameterToReceiver(function: FunctionDescriptor): Boolean = false
} }
@@ -27,9 +27,10 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult
*/ */
interface PreprocessedVirtualFileFactoryExtension { interface PreprocessedVirtualFileFactoryExtension {
companion object : ProjectExtensionDescriptor<PreprocessedVirtualFileFactoryExtension>( companion object : ProjectExtensionDescriptor<PreprocessedVirtualFileFactoryExtension>(
"org.jetbrains.kotlin.preprocessedVirtualFileFactoryExtension", "org.jetbrains.kotlin.preprocessedVirtualFileFactoryExtension",
PreprocessedVirtualFileFactoryExtension::class.java PreprocessedVirtualFileFactoryExtension::class.java
) )
fun isPassThrough(): Boolean fun isPassThrough(): Boolean
fun createPreprocessedFile(file: VirtualFile?): VirtualFile? fun createPreprocessedFile(file: VirtualFile?): VirtualFile?
@@ -45,6 +46,7 @@ class PreprocessedFileCreator(val project: Project) {
fun create(file: VirtualFile): VirtualFile = validExts.firstNotNullResult { it.createPreprocessedFile(file) } ?: file fun create(file: VirtualFile): VirtualFile = validExts.firstNotNullResult { it.createPreprocessedFile(file) } ?: file
// unused now, but could be used in the IDE at some point // unused now, but could be used in the IDE at some point
fun createLight(file: LightVirtualFile): LightVirtualFile = validExts.firstNotNullResult { it.createPreprocessedLightFile(file) } ?: file fun createLight(file: LightVirtualFile): LightVirtualFile =
validExts.firstNotNullResult { it.createPreprocessedLightFile(file) } ?: file
} }
@@ -26,9 +26,9 @@ open class ProjectExtensionDescriptor<T>(name: String, private val extensionClas
fun registerExtensionPoint(project: Project) { fun registerExtensionPoint(project: Project) {
Extensions.getArea(project).registerExtensionPoint( Extensions.getArea(project).registerExtensionPoint(
extensionPointName.name, extensionPointName.name,
extensionClass.name, extensionClass.name,
ExtensionPoint.Kind.INTERFACE ExtensionPoint.Kind.INTERFACE
) )
} }
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.resolve.TargetPlatform
interface StorageComponentContainerContributor { interface StorageComponentContainerContributor {
companion object : ProjectExtensionDescriptor<StorageComponentContainerContributor>( companion object : ProjectExtensionDescriptor<StorageComponentContainerContributor>(
"org.jetbrains.kotlin.storageComponentContainerContributor", StorageComponentContainerContributor::class.java "org.jetbrains.kotlin.storageComponentContainerContributor", StorageComponentContainerContributor::class.java
) )
fun registerModuleComponents(container: StorageComponentContainer, platform: TargetPlatform, moduleDescriptor: ModuleDescriptor) {} fun registerModuleComponents(container: StorageComponentContainer, platform: TargetPlatform, moduleDescriptor: ModuleDescriptor) {}
@@ -41,8 +41,7 @@ enum class KDocKnownTag private constructor(val isReferenceRequired: Boolean, va
} }
try { try {
return valueOf(tagName.toString().toUpperCase()) return valueOf(tagName.toString().toUpperCase())
} } catch (ignored: IllegalArgumentException) {
catch (ignored: IllegalArgumentException) {
} }
return null return null
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
/** /**
* A single part of a qualified name in the tag subject or link. * A single part of a qualified name in the tag subject or link.
*/ */
class KDocName(node: ASTNode): KtElementImpl(node) { class KDocName(node: ASTNode) : KtElementImpl(node) {
fun getContainingDoc(): KDoc { fun getContainingDoc(): KDoc {
val kdoc = getStrictParentOfType<KDoc>() val kdoc = getStrictParentOfType<KDoc>()
return kdoc ?: throw IllegalStateException("KDocName must be inside a KDoc") return kdoc ?: throw IllegalStateException("KDocName must be inside a KDoc")
@@ -292,9 +292,11 @@ private object DebugTextBuildingVisitor : KtVisitor<String, Unit>() {
private fun StringBuilder.appendInn(target: Any?, prefix: String = "", suffix: String = "") { private fun StringBuilder.appendInn(target: Any?, prefix: String = "", suffix: String = "") {
if (target == null) return if (target == null) return
append(prefix) append(prefix)
append(when (target) { append(
is KtElement -> target.getDebugText() when (target) {
else -> target.toString() is KtElement -> target.getDebugText()
}) else -> target.toString()
}
)
append(suffix) append(suffix)
} }
@@ -24,28 +24,31 @@ import org.jetbrains.kotlin.psi.psiUtil.siblings
object EditCommaSeparatedListHelper { object EditCommaSeparatedListHelper {
@JvmOverloads @JvmOverloads
fun <TItem: KtElement> addItem(list: KtElement, allItems: List<TItem>, item: TItem, prefix: KtToken = KtTokens.LPAR): TItem { fun <TItem : KtElement> addItem(list: KtElement, allItems: List<TItem>, item: TItem, prefix: KtToken = KtTokens.LPAR): TItem {
return addItemBefore(list, allItems, item, null, prefix) return addItemBefore(list, allItems, item, null, prefix)
} }
@JvmOverloads @JvmOverloads
fun <TItem: KtElement> addItemAfter(list: KtElement, allItems: List<TItem>, item: TItem, anchor: TItem?, prefix: KtToken = KtTokens.LPAR): TItem { fun <TItem : KtElement> addItemAfter(
list: KtElement,
allItems: List<TItem>,
item: TItem,
anchor: TItem?,
prefix: KtToken = KtTokens.LPAR
): TItem {
assert(anchor == null || anchor.parent == list) assert(anchor == null || anchor.parent == list)
if (allItems.isEmpty()) { if (allItems.isEmpty()) {
return if (list.firstChild?.node?.elementType == prefix) { return if (list.firstChild?.node?.elementType == prefix) {
list.addAfter(item, list.firstChild) as TItem list.addAfter(item, list.firstChild) as TItem
} } else {
else {
list.add(item) as TItem list.add(item) as TItem
} }
} } else {
else {
var comma = KtPsiFactory(list).createComma() var comma = KtPsiFactory(list).createComma()
return if (anchor != null) { return if (anchor != null) {
comma = list.addAfter(comma, anchor) comma = list.addAfter(comma, anchor)
list.addAfter(item, comma) as TItem list.addAfter(item, comma) as TItem
} } else {
else {
comma = list.addBefore(comma, allItems.first()) comma = list.addBefore(comma, allItems.first())
list.addBefore(item, comma) as TItem list.addBefore(item, comma) as TItem
} }
@@ -53,26 +56,30 @@ object EditCommaSeparatedListHelper {
} }
@JvmOverloads @JvmOverloads
fun <TItem: KtElement> addItemBefore(list: KtElement, allItems: List<TItem>, item: TItem, anchor: TItem?, prefix: KtToken = KtTokens.LPAR): TItem { fun <TItem : KtElement> addItemBefore(
list: KtElement,
allItems: List<TItem>,
item: TItem,
anchor: TItem?,
prefix: KtToken = KtTokens.LPAR
): TItem {
val anchorAfter: TItem? val anchorAfter: TItem?
anchorAfter = if (allItems.isEmpty()) { anchorAfter = if (allItems.isEmpty()) {
assert(anchor == null) assert(anchor == null)
null null
} } else {
else {
if (anchor != null) { if (anchor != null) {
val index = allItems.indexOf(anchor) val index = allItems.indexOf(anchor)
assert(index >= 0) assert(index >= 0)
if (index > 0) allItems[index - 1] else null if (index > 0) allItems[index - 1] else null
} } else {
else {
allItems[allItems.size - 1] allItems[allItems.size - 1]
} }
} }
return addItemAfter(list, allItems, item, anchorAfter, prefix) return addItemAfter(list, allItems, item, anchorAfter, prefix)
} }
fun <TItem: KtElement> removeItem(item: TItem) { fun <TItem : KtElement> removeItem(item: TItem) {
var comma = item.siblings(withItself = false).firstOrNull { it !is PsiWhiteSpace && it !is PsiComment } var comma = item.siblings(withItself = false).firstOrNull { it !is PsiWhiteSpace && it !is PsiComment }
if (comma?.node?.elementType != KtTokens.COMMA) { if (comma?.node?.elementType != KtTokens.COMMA) {
comma = item.siblings(forward = false, withItself = false).firstOrNull { it !is PsiWhiteSpace && it !is PsiComment } comma = item.siblings(forward = false, withItself = false).firstOrNull { it !is PsiWhiteSpace && it !is PsiComment }
@@ -23,12 +23,12 @@ import org.jetbrains.kotlin.psi.psiUtil.allChildren
fun findDocComment(declaration: KtDeclaration): KDoc? { fun findDocComment(declaration: KtDeclaration): KDoc? {
return declaration.allChildren return declaration.allChildren
.flatMap { .flatMap {
if (it is KtDeclarationModifierList) { if (it is KtDeclarationModifierList) {
return@flatMap it.children.asSequence() return@flatMap it.children.asSequence()
}
sequenceOf(it)
} }
.dropWhile { it !is KDoc } sequenceOf(it)
.firstOrNull() as? KDoc }
.dropWhile { it !is KDoc }
.firstOrNull() as? KDoc
} }
@@ -21,13 +21,13 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
class KtBlockCodeFragment( class KtBlockCodeFragment(
project: Project, project: Project,
name: String, name: String,
text: CharSequence, text: CharSequence,
imports: String?, imports: String?,
context: PsiElement? context: PsiElement?
) : KtCodeFragment(project, name, text, imports, KtNodeTypes.BLOCK_CODE_FRAGMENT, context) { ) : KtCodeFragment(project, name, text, imports, KtNodeTypes.BLOCK_CODE_FRAGMENT, context) {
override fun getContentElement() = findChildByClass(KtBlockExpression::class.java) override fun getContentElement() = findChildByClass(KtBlockExpression::class.java)
?: throw IllegalStateException("Block expression should be parsed for BlockCodeFragment") ?: throw IllegalStateException("Block expression should be parsed for BlockCodeFragment")
} }
@@ -30,7 +30,8 @@ import org.jetbrains.kotlin.psi.stubs.KotlinClassOrObjectStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
abstract class KtClassOrObject : abstract class KtClassOrObject :
KtTypeParameterListOwnerStub<KotlinClassOrObjectStub<out KtClassOrObject>>, KtDeclarationContainer, KtNamedDeclaration, KtPureClassOrObject { KtTypeParameterListOwnerStub<KotlinClassOrObjectStub<out KtClassOrObject>>, KtDeclarationContainer, KtNamedDeclaration,
KtPureClassOrObject {
constructor(node: ASTNode) : super(node) constructor(node: ASTNode) : super(node)
constructor(stub: KotlinClassOrObjectStub<out KtClassOrObject>, nodeType: IStubElementType<*, *>) : super(stub, nodeType) constructor(stub: KotlinClassOrObjectStub<out KtClassOrObject>, nodeType: IStubElementType<*, *>) : super(stub, nodeType)
@@ -59,8 +60,7 @@ abstract class KtClassOrObject :
if (specifierList.entries.size > 1) { if (specifierList.entries.size > 1) {
EditCommaSeparatedListHelper.removeItem<KtElement>(superTypeListEntry) EditCommaSeparatedListHelper.removeItem<KtElement>(superTypeListEntry)
} } else {
else {
deleteChildRange(findChildByType<PsiElement>(KtTokens.COLON) ?: specifierList, specifierList) deleteChildRange(findChildByType<PsiElement>(KtTokens.COLON) ?: specifierList, specifierList)
} }
} }
@@ -69,18 +69,18 @@ abstract class KtClassOrObject :
fun getBody(): KtClassBody? = getStubOrPsiChild(KtStubElementTypes.CLASS_BODY) fun getBody(): KtClassBody? = getStubOrPsiChild(KtStubElementTypes.CLASS_BODY)
inline fun <reified T: KtDeclaration> addDeclaration(declaration: T): T { inline fun <reified T : KtDeclaration> addDeclaration(declaration: T): T {
val body = getOrCreateBody() val body = getOrCreateBody()
val anchor = PsiTreeUtil.skipSiblingsBackward(body.rBrace ?: body.lastChild!!, PsiWhiteSpace::class.java) val anchor = PsiTreeUtil.skipSiblingsBackward(body.rBrace ?: body.lastChild!!, PsiWhiteSpace::class.java)
return body.addAfter(declaration, anchor) as T return body.addAfter(declaration, anchor) as T
} }
inline fun <reified T: KtDeclaration> addDeclarationAfter(declaration: T, anchor: PsiElement?): T { inline fun <reified T : KtDeclaration> addDeclarationAfter(declaration: T, anchor: PsiElement?): T {
val anchorBefore = anchor ?: declarations.lastOrNull() ?: return addDeclaration(declaration) val anchorBefore = anchor ?: declarations.lastOrNull() ?: return addDeclaration(declaration)
return getOrCreateBody().addAfter(declaration, anchorBefore) as T return getOrCreateBody().addAfter(declaration, anchorBefore) as T
} }
inline fun <reified T: KtDeclaration> addDeclarationBefore(declaration: T, anchor: PsiElement?): T { inline fun <reified T : KtDeclaration> addDeclarationBefore(declaration: T, anchor: PsiElement?): T {
val anchorAfter = anchor ?: declarations.firstOrNull() ?: return addDeclaration(declaration) val anchorAfter = anchor ?: declarations.firstOrNull() ?: return addDeclaration(declaration)
return getOrCreateBody().addBefore(declaration, anchorAfter) as T return getOrCreateBody().addBefore(declaration, anchorAfter) as T
} }
@@ -112,9 +112,11 @@ abstract class KtClassOrObject :
fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD) fun isAnnotation(): Boolean = hasModifier(KtTokens.ANNOTATION_KEYWORD)
fun getDeclarationKeyword(): PsiElement? = fun getDeclarationKeyword(): PsiElement? =
findChildByType(TokenSet.create( findChildByType(
KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD, KtTokens.OBJECT_KEYWORD TokenSet.create(
)) KtTokens.CLASS_KEYWORD, KtTokens.INTERFACE_KEYWORD, KtTokens.OBJECT_KEYWORD
)
)
override fun delete() { override fun delete() {
CheckUtil.checkWritable(this) CheckUtil.checkWritable(this)
@@ -122,8 +124,7 @@ abstract class KtClassOrObject :
val file = containingKtFile val file = containingKtFile
if (!isTopLevel() || file.declarations.size > 1) { if (!isTopLevel() || file.declarations.size > 1) {
super.delete() super.delete()
} } else {
else {
file.delete() file.delete()
} }
} }
@@ -31,13 +31,21 @@ import org.jetbrains.kotlin.types.KotlinType
import java.util.* import java.util.*
abstract class KtCodeFragment( abstract class KtCodeFragment(
private val _project: Project, private val _project: Project,
name: String, name: String,
text: CharSequence, text: CharSequence,
imports: String?, // Should be separated by JetCodeFragment.IMPORT_SEPARATOR imports: String?, // Should be separated by JetCodeFragment.IMPORT_SEPARATOR
elementType: IElementType, elementType: IElementType,
private val context: PsiElement? private val context: PsiElement?
): KtFile((PsiManager.getInstance(_project) as PsiManagerEx).fileManager.createFileViewProvider(LightVirtualFile(name, KotlinFileType.INSTANCE, text), true), false), JavaCodeFragment { ) : KtFile(
(PsiManager.getInstance(_project) as PsiManagerEx).fileManager.createFileViewProvider(
LightVirtualFile(
name,
KotlinFileType.INSTANCE,
text
), true
), false
), JavaCodeFragment {
private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider private var viewProvider = super.getViewProvider() as SingleRootFileViewProvider
private var imports = LinkedHashSet<String>() private var imports = LinkedHashSet<String>()
@@ -93,7 +101,8 @@ abstract class KtCodeFragment(
clone.isPhysical = false clone.isPhysical = false
clone.originalFile = this clone.originalFile = this
clone.imports = imports clone.imports = imports
clone.viewProvider = SingleRootFileViewProvider(PsiManager.getInstance(_project), LightVirtualFile(name, KotlinFileType.INSTANCE, text), false) clone.viewProvider =
SingleRootFileViewProvider(PsiManager.getInstance(_project), LightVirtualFile(name, KotlinFileType.INSTANCE, text), false)
clone.viewProvider.forceCachedPsi(clone) clone.viewProvider.forceCachedPsi(clone)
return clone return clone
} }
@@ -147,7 +156,7 @@ abstract class KtCodeFragment(
override val importDirectives: List<KtImportDirective> override val importDirectives: List<KtImportDirective>
get() = importsAsImportList()?.imports ?: emptyList() get() = importsAsImportList()?.imports ?: emptyList()
override fun setVisibilityChecker(checker: JavaCodeFragment.VisibilityChecker?) { } override fun setVisibilityChecker(checker: JavaCodeFragment.VisibilityChecker?) {}
override fun getVisibilityChecker() = JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE override fun getVisibilityChecker() = JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE
@@ -86,8 +86,8 @@ abstract class KtConstructor<T : KtConstructor<T>> : KtDeclarationStub<KotlinPla
override fun getTextOffset(): Int { override fun getTextOffset(): Int {
return getConstructorKeyword()?.textOffset return getConstructorKeyword()?.textOffset
?: valueParameterList?.textOffset ?: valueParameterList?.textOffset
?: super.getTextOffset() ?: super.getTextOffset()
} }
override fun getUseScope(): SearchScope { override fun getUseScope(): SearchScope {
@@ -58,15 +58,18 @@ class KtDotQualifiedExpression : KtExpressionImplStub<KotlinPlaceHolderStub<KtDo
private fun getChildExpressionsByStub(stub: KotlinPlaceHolderStub<KtDotQualifiedExpression>): Array<KtExpression>? { private fun getChildExpressionsByStub(stub: KotlinPlaceHolderStub<KtDotQualifiedExpression>): Array<KtExpression>? {
if (stub.getParentStubOfType(KtImportDirective::class.java) == null && if (stub.getParentStubOfType(KtImportDirective::class.java) == null &&
stub.getParentStubOfType(KtPackageDirective::class.java) == null) { stub.getParentStubOfType(KtPackageDirective::class.java) == null) {
LOG.error("KtDotQualifiedExpression should only have stubs inside import or package directives.\n" + LOG.error(
"Stubs were created for:\n$text\nFile text:\n${containingFile.text}") "KtDotQualifiedExpression should only have stubs inside import or package directives.\n" +
"Stubs were created for:\n$text\nFile text:\n${containingFile.text}"
)
return null return null
} } else {
else {
val expressions = stub.getChildrenByType(INSIDE_DIRECTIVE_EXPRESSIONS, KtExpression.ARRAY_FACTORY) val expressions = stub.getChildrenByType(INSIDE_DIRECTIVE_EXPRESSIONS, KtExpression.ARRAY_FACTORY)
if (expressions.size !in 1..2) { if (expressions.size !in 1..2) {
LOG.error("Invalid stub structure. DOT_QUALIFIED_EXPRESSION must have one or two children. Was: ${expressions.size}\n" + LOG.error(
"File text:\n${containingFile.text}") "Invalid stub structure. DOT_QUALIFIED_EXPRESSION must have one or two children. Was: ${expressions.size}\n" +
"File text:\n${containingFile.text}"
)
return null return null
} }
return expressions return expressions
@@ -21,11 +21,11 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.KtNodeTypes
class KtExpressionCodeFragment( class KtExpressionCodeFragment(
project: Project, project: Project,
name: String, name: String,
text: CharSequence, text: CharSequence,
imports: String?, imports: String?,
context: PsiElement? context: PsiElement?
) : KtCodeFragment(project, name, text, imports, KtNodeTypes.EXPRESSION_CODE_FRAGMENT, context) { ) : KtCodeFragment(project, name, text, imports, KtNodeTypes.EXPRESSION_CODE_FRAGMENT, context) {
override fun getContentElement() = findChildByClass(KtExpression::class.java) override fun getContentElement() = findChildByClass(KtExpression::class.java)
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.Name
open class KtExpressionWithLabel(node: ASTNode) : KtExpressionImpl(node) { open class KtExpressionWithLabel(node: ASTNode) : KtExpressionImpl(node) {
fun getTargetLabel(): KtSimpleNameExpression? = fun getTargetLabel(): KtSimpleNameExpression? =
labelQualifier?.findChildByType(KtNodeTypes.LABEL) as? KtSimpleNameExpression labelQualifier?.findChildByType(KtNodeTypes.LABEL) as? KtSimpleNameExpression
val labelQualifier: KtContainerNode? val labelQualifier: KtContainerNode?
get() = findChildByType(KtNodeTypes.LABEL_QUALIFIER) get() = findChildByType(KtNodeTypes.LABEL_QUALIFIER)
@@ -36,13 +36,13 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtPlaceHolderStubElementType
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) : open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
PsiFileBase(viewProvider, KotlinLanguage.INSTANCE), PsiFileBase(viewProvider, KotlinLanguage.INSTANCE),
KtDeclarationContainer, KtDeclarationContainer,
KtAnnotated, KtAnnotated,
KtElement, KtElement,
PsiClassOwner, PsiClassOwner,
PsiNamedElement, PsiNamedElement,
PsiModifiableCodeBlock { PsiModifiableCodeBlock {
@Volatile @Volatile
private var isScript: Boolean? = null private var isScript: Boolean? = null
@@ -85,8 +85,7 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
val packageDirective = packageDirective val packageDirective = packageDirective
if (packageDirective != null) { if (packageDirective != null) {
packageDirective.fqName = value packageDirective.fqName = value
} } else {
else {
val newPackageDirective = KtPsiFactory(this).createPackageDirectiveIfNeeded(value) ?: return val newPackageDirective = KtPsiFactory(this).createPackageDirectiveIfNeeded(value) ?: return
addAfter(newPackageDirective, null) addAfter(newPackageDirective, null)
} }
@@ -126,8 +125,8 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
get() { get() {
val stub = stub val stub = stub
val danglingModifierLists = stub?.getChildrenByType( val danglingModifierLists = stub?.getChildrenByType(
KtStubElementTypes.MODIFIER_LIST, KtStubElementTypes.MODIFIER_LIST,
KtStubElementTypes.MODIFIER_LIST.arrayFactory KtStubElementTypes.MODIFIER_LIST.arrayFactory
) ?: findChildrenByClass(KtModifierList::class.java) ) ?: findChildrenByClass(KtModifierList::class.java)
return danglingModifierLists.flatMap { obj: KtModifierList -> obj.annotationEntries } return danglingModifierLists.flatMap { obj: KtModifierList -> obj.annotationEntries }
} }
@@ -139,12 +138,12 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
override fun getDeclarations(): List<KtDeclaration> { override fun getDeclarations(): List<KtDeclaration> {
val stub = stub val stub = stub
return stub?.getChildrenByType(KtStubElementTypes.DECLARATION_TYPES, KtDeclaration.ARRAY_FACTORY)?.toList() return stub?.getChildrenByType(KtStubElementTypes.DECLARATION_TYPES, KtDeclaration.ARRAY_FACTORY)?.toList()
?: PsiTreeUtil.getChildrenOfTypeAsList(this, KtDeclaration::class.java) ?: PsiTreeUtil.getChildrenOfTypeAsList(this, KtDeclaration::class.java)
} }
fun <T : KtElementImplStub<out StubElement<*>>> findChildByTypeOrClass( fun <T : KtElementImplStub<out StubElement<*>>> findChildByTypeOrClass(
elementType: KtPlaceHolderStubElementType<T>, elementType: KtPlaceHolderStubElementType<T>,
elementClass: Class<T> elementClass: Class<T>
): T? { ): T? {
val stub = stub val stub = stub
if (stub != null) { if (stub != null) {
@@ -155,7 +154,7 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
} }
fun findImportByAlias(name: String): KtImportDirective? = fun findImportByAlias(name: String): KtImportDirective? =
importDirectives.firstOrNull { name == it.aliasName } importDirectives.firstOrNull { name == it.aliasName }
@Deprecated("") // getPackageFqName should be used instead @Deprecated("") // getPackageFqName should be used instead
override fun getPackageName(): String { override fun getPackageName(): String {
@@ -188,9 +187,9 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
val result = declarations.any { val result = declarations.any {
(it is KtProperty || (it is KtProperty ||
it is KtNamedFunction || it is KtNamedFunction ||
it is KtScript || it is KtScript ||
it is KtTypeAlias) && !it.hasExpectModifier() it is KtTypeAlias) && !it.hasExpectModifier()
} }
hasTopLeveCallables = result hasTopLeveCallables = result
@@ -201,8 +200,7 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
if (visitor is KtVisitor<*, *>) { if (visitor is KtVisitor<*, *>) {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
accept(visitor as KtVisitor<Any, Any?>, null) accept(visitor as KtVisitor<Any, Any?>, null)
} } else {
else {
visitor.visitFile(this) visitor.visitFile(this)
} }
} }
@@ -218,10 +216,10 @@ open class KtFile(viewProvider: FileViewProvider, val isCompiled: Boolean) :
} }
override fun getAnnotations(): List<KtAnnotation> = override fun getAnnotations(): List<KtAnnotation> =
fileAnnotationList?.annotations ?: emptyList() fileAnnotationList?.annotations ?: emptyList()
override fun getAnnotationEntries(): List<KtAnnotationEntry> = override fun getAnnotationEntries(): List<KtAnnotationEntry> =
fileAnnotationList?.annotationEntries ?: emptyList() fileAnnotationList?.annotationEntries ?: emptyList()
@Throws(IncorrectOperationException::class) @Throws(IncorrectOperationException::class)
override fun setName(name: String): PsiElement { override fun setName(name: String): PsiElement {
@@ -19,5 +19,5 @@ package org.jetbrains.kotlin.psi
import com.intellij.lang.ASTNode import com.intellij.lang.ASTNode
class KtLabelReferenceExpression(node: ASTNode) : KtSimpleNameExpressionImpl(node) { class KtLabelReferenceExpression(node: ASTNode) : KtSimpleNameExpressionImpl(node) {
override fun getReferencedNameElement() = getIdentifier() ?: this override fun getReferencedNameElement() = getIdentifier() ?: this
} }
@@ -49,8 +49,7 @@ class KtObjectDeclaration : KtClassOrObject {
addAfter(psiFactory.createWhiteSpace(), getObjectKeyword()!!) addAfter(psiFactory.createWhiteSpace(), getObjectKeyword()!!)
result result
} } else {
else {
super.setName(name) super.setName(name)
} }
} }
@@ -58,7 +57,7 @@ class KtObjectDeclaration : KtClassOrObject {
fun isCompanion(): Boolean = _stub?.isCompanion() ?: hasModifier(KtTokens.COMPANION_KEYWORD) fun isCompanion(): Boolean = _stub?.isCompanion() ?: hasModifier(KtTokens.COMPANION_KEYWORD)
override fun getTextOffset(): Int = nameIdentifier?.textRange?.startOffset override fun getTextOffset(): Int = nameIdentifier?.textRange?.startOffset
?: getObjectKeyword()!!.textRange.startOffset ?: getObjectKeyword()!!.textRange.startOffset
override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D): R { override fun <R, D> accept(visitor: KtVisitor<R, D>, data: D): R {
return visitor.visitObjectDeclaration(this, data) return visitor.visitObjectDeclaration(this, data)
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
* Underlying token is [org.jetbrains.kotlin.KtNodeTypes.TYPE_REFERENCE] * Underlying token is [org.jetbrains.kotlin.KtNodeTypes.TYPE_REFERENCE]
*/ */
class KtTypeReference : KtModifierListOwnerStub<KotlinPlaceHolderStub<KtTypeReference>>, class KtTypeReference : KtModifierListOwnerStub<KotlinPlaceHolderStub<KtTypeReference>>,
KtAnnotated, KtAnnotationsContainer { KtAnnotated, KtAnnotationsContainer {
constructor(node: ASTNode) : super(node) constructor(node: ASTNode) : super(node)
@@ -27,8 +27,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
fun getTypeReference(declaration: KtCallableDeclaration): KtTypeReference? { fun getTypeReference(declaration: KtCallableDeclaration): KtTypeReference? {
return declaration.firstChild!!.siblings(forward = true) return declaration.firstChild!!.siblings(forward = true)
.dropWhile { it.node!!.elementType != KtTokens.COLON } .dropWhile { it.node!!.elementType != KtTokens.COLON }
.firstIsInstanceOrNull<KtTypeReference>() .firstIsInstanceOrNull<KtTypeReference>()
} }
fun setTypeReference(declaration: KtCallableDeclaration, addAfter: PsiElement?, typeRef: KtTypeReference?): KtTypeReference? { fun setTypeReference(declaration: KtCallableDeclaration, addAfter: PsiElement?, typeRef: KtTypeReference?): KtTypeReference? {
@@ -36,17 +36,15 @@ fun setTypeReference(declaration: KtCallableDeclaration, addAfter: PsiElement?,
if (typeRef != null) { if (typeRef != null) {
return if (oldTypeRef != null) { return if (oldTypeRef != null) {
oldTypeRef.replace(typeRef) as KtTypeReference oldTypeRef.replace(typeRef) as KtTypeReference
} } else {
else {
val anchor = addAfter val anchor = addAfter
?: declaration.nameIdentifier?.siblings(forward = true)?.firstOrNull { it is PsiErrorElement } ?: declaration.nameIdentifier?.siblings(forward = true)?.firstOrNull { it is PsiErrorElement }
?: (declaration as? KtParameter)?.destructuringDeclaration ?: (declaration as? KtParameter)?.destructuringDeclaration
val newTypeRef = declaration.addAfter(typeRef, anchor) as KtTypeReference val newTypeRef = declaration.addAfter(typeRef, anchor) as KtTypeReference
declaration.addAfter(KtPsiFactory(declaration.project).createColon(), anchor) declaration.addAfter(KtPsiFactory(declaration.project).createColon(), anchor)
newTypeRef newTypeRef
} }
} } else {
else {
if (oldTypeRef != null) { if (oldTypeRef != null) {
val colon = declaration.colon!! val colon = declaration.colon!!
val removeFrom = colon.prevSibling as? PsiWhiteSpace ?: colon val removeFrom = colon.prevSibling as? PsiWhiteSpace ?: colon
@@ -57,30 +55,28 @@ fun setTypeReference(declaration: KtCallableDeclaration, addAfter: PsiElement?,
} }
private inline fun <T : KtElement> T.doSetReceiverTypeReference( private inline fun <T : KtElement> T.doSetReceiverTypeReference(
typeRef: KtTypeReference?, typeRef: KtTypeReference?,
getReceiverTypeReference: T.() -> KtTypeReference?, getReceiverTypeReference: T.() -> KtTypeReference?,
addReceiverTypeReference: T.(typeRef: KtTypeReference) -> KtTypeReference addReceiverTypeReference: T.(typeRef: KtTypeReference) -> KtTypeReference
): KtTypeReference? { ): KtTypeReference? {
val needParentheses = typeRef != null && typeRef.typeElement is KtFunctionType && !typeRef.hasParentheses() val needParentheses = typeRef != null && typeRef.typeElement is KtFunctionType && !typeRef.hasParentheses()
val oldTypeRef = getReceiverTypeReference() val oldTypeRef = getReceiverTypeReference()
if (typeRef != null) { if (typeRef != null) {
val newTypeRef = val newTypeRef =
if (oldTypeRef != null) { if (oldTypeRef != null) {
oldTypeRef.replace(typeRef) as KtTypeReference oldTypeRef.replace(typeRef) as KtTypeReference
} } else {
else { val newTypeRef = addReceiverTypeReference(typeRef)
val newTypeRef = addReceiverTypeReference(typeRef) addAfter(KtPsiFactory(project).createDot(), newTypeRef.parentsWithSelf.first { it.parent == this })
addAfter(KtPsiFactory(project).createDot(), newTypeRef.parentsWithSelf.first { it.parent == this }) newTypeRef
newTypeRef }
}
if (needParentheses) { if (needParentheses) {
val argList = KtPsiFactory(project).createCallArguments("()") val argList = KtPsiFactory(project).createCallArguments("()")
newTypeRef.addBefore(argList.leftParenthesis!!, newTypeRef.firstChild) newTypeRef.addBefore(argList.leftParenthesis!!, newTypeRef.firstChild)
newTypeRef.add(argList.rightParenthesis!!) newTypeRef.add(argList.rightParenthesis!!)
} }
return newTypeRef return newTypeRef
} } else {
else {
if (oldTypeRef != null) { if (oldTypeRef != null) {
val dotSibling = oldTypeRef.parent as? KtFunctionTypeReceiver ?: oldTypeRef val dotSibling = oldTypeRef.parent as? KtFunctionTypeReceiver ?: oldTypeRef
val dot = dotSibling.siblings(forward = true).firstOrNull { it.node.elementType == KtTokens.DOT } val dot = dotSibling.siblings(forward = true).firstOrNull { it.node.elementType == KtTokens.DOT }
@@ -91,18 +87,20 @@ private inline fun <T : KtElement> T.doSetReceiverTypeReference(
} }
fun KtCallableDeclaration.setReceiverTypeReference(typeRef: KtTypeReference?) = fun KtCallableDeclaration.setReceiverTypeReference(typeRef: KtTypeReference?) =
doSetReceiverTypeReference( doSetReceiverTypeReference(
typeRef, typeRef,
{ receiverTypeReference }, { receiverTypeReference },
{ this.addBefore(it, nameIdentifier ?: valueParameterList) as KtTypeReference } { this.addBefore(it, nameIdentifier ?: valueParameterList) as KtTypeReference }
) )
fun KtFunctionType.setReceiverTypeReference(typeRef: KtTypeReference?) = fun KtFunctionType.setReceiverTypeReference(typeRef: KtTypeReference?) =
doSetReceiverTypeReference( doSetReceiverTypeReference(
typeRef, typeRef,
{ receiverTypeReference }, { receiverTypeReference },
{ {
(addBefore(KtPsiFactory(project).createFunctionTypeReceiver(it), (addBefore(
parameterList ?: firstChild) as KtFunctionTypeReceiver).typeReference KtPsiFactory(project).createFunctionTypeReceiver(it),
} parameterList ?: firstChild
) ) as KtFunctionTypeReceiver).typeReference
}
)
@@ -20,7 +20,8 @@ import com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
interface ValueArgument { interface ValueArgument {
@IfNotParsed fun getArgumentExpression(): KtExpression? @IfNotParsed
fun getArgumentExpression(): KtExpression?
fun getArgumentName(): ValueArgumentName? fun getArgumentName(): ValueArgumentName?
@@ -23,7 +23,11 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.KtStringTemplateExpression
class KtStringTemplateExpressionManipulator : AbstractElementManipulator<KtStringTemplateExpression>() { class KtStringTemplateExpressionManipulator : AbstractElementManipulator<KtStringTemplateExpression>() {
override fun handleContentChange(element: KtStringTemplateExpression, range: TextRange, newContent: String): KtStringTemplateExpression? { override fun handleContentChange(
element: KtStringTemplateExpression,
range: TextRange,
newContent: String
): KtStringTemplateExpression? {
val node = element.node val node = element.node
val content = if (element.isSingleQuoted()) StringUtil.escapeStringCharacters(newContent) else newContent val content = if (element.isSingleQuoted()) StringUtil.escapeStringCharacters(newContent) else newContent
val oldText = node.text val oldText = node.text
@@ -22,8 +22,7 @@ data class PsiChildRange(val first: PsiElement?, val last: PsiElement?) : Sequen
init { init {
if (first == null) { if (first == null) {
assert(last == null) assert(last == null)
} } else {
else {
assert(first.parent == last!!.parent) assert(first.parent == last!!.parent)
} }
} }
@@ -34,8 +33,7 @@ data class PsiChildRange(val first: PsiElement?, val last: PsiElement?) : Sequen
override fun iterator(): Iterator<PsiElement> { override fun iterator(): Iterator<PsiElement> {
val sequence = if (first == null) { val sequence = if (first == null) {
emptySequence<PsiElement>() emptySequence<PsiElement>()
} } else {
else {
val afterLast = last!!.nextSibling val afterLast = last!!.nextSibling
first.siblings().takeWhile { it != afterLast } first.siblings().takeWhile { it != afterLast }
} }
@@ -22,8 +22,12 @@ import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.KtPsiFactory
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
class StringTemplateExpressionManipulator: AbstractElementManipulator<KtStringTemplateExpression>() { class StringTemplateExpressionManipulator : AbstractElementManipulator<KtStringTemplateExpression>() {
override fun handleContentChange(element: KtStringTemplateExpression, range: TextRange, newContent: String): KtStringTemplateExpression? { override fun handleContentChange(
element: KtStringTemplateExpression,
range: TextRange,
newContent: String
): KtStringTemplateExpression? {
val node = element.node val node = element.node
val content = if (node.firstChildNode.textLength == 1) StringUtil.escapeStringCharacters(newContent) else newContent val content = if (node.firstChildNode.textLength == 1) StringUtil.escapeStringCharacters(newContent) else newContent
val oldText = node.text val oldText = node.text
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.stubs.KotlinScriptStub
import org.jetbrains.kotlin.psi.stubs.impl.KotlinScriptStubImpl import org.jetbrains.kotlin.psi.stubs.impl.KotlinScriptStubImpl
class KtScriptElementType(debugName: String) : KtStubElementType<KotlinScriptStub, KtScript>( class KtScriptElementType(debugName: String) : KtStubElementType<KotlinScriptStub, KtScript>(
debugName, KtScript::class.java, KotlinScriptStub::class.java debugName, KtScript::class.java, KotlinScriptStub::class.java
) { ) {
override fun createStub(psi: KtScript, parentStub: StubElement<PsiElement>): KotlinScriptStub { override fun createStub(psi: KtScript, parentStub: StubElement<PsiElement>): KotlinScriptStub {
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.psi.stubs.impl.KotlinTypeAliasStubImpl
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
class KtTypeAliasElementType(debugName: String) : class KtTypeAliasElementType(debugName: String) :
KtStubElementType<KotlinTypeAliasStub, KtTypeAlias>(debugName, KtTypeAlias::class.java, KotlinTypeAliasStub::class.java) { KtStubElementType<KotlinTypeAliasStub, KtTypeAlias>(debugName, KtTypeAlias::class.java, KotlinTypeAliasStub::class.java) {
override fun createStub(psi: KtTypeAlias, parentStub: StubElement<*>?): KotlinTypeAliasStub { override fun createStub(psi: KtTypeAlias, parentStub: StubElement<*>?): KotlinTypeAliasStub {
val name = StringRef.fromString(psi.name) val name = StringRef.fromString(psi.name)
@@ -71,7 +71,8 @@ open class StubIndexService protected constructor() {
} }
companion object { companion object {
@JvmStatic fun getInstance(): StubIndexService { @JvmStatic
fun getInstance(): StubIndexService {
return ServiceManager.getService(StubIndexService::class.java) ?: NO_INDEX return ServiceManager.getService(StubIndexService::class.java) ?: NO_INDEX
} }
@@ -26,15 +26,15 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtClassElementType
import java.util.* import java.util.*
class KotlinClassStubImpl( class KotlinClassStubImpl(
type: KtClassElementType, type: KtClassElementType,
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val qualifiedName: StringRef?, private val qualifiedName: StringRef?,
private val name: StringRef?, private val name: StringRef?,
private val superNames: Array<StringRef>, private val superNames: Array<StringRef>,
private val isInterface: Boolean, private val isInterface: Boolean,
private val isEnumEntry: Boolean, private val isEnumEntry: Boolean,
private val isLocal: Boolean, private val isLocal: Boolean,
private val isTopLevel: Boolean private val isTopLevel: Boolean
) : KotlinStubBaseImpl<KtClass>(parent, type), KotlinClassStub { ) : KotlinStubBaseImpl<KtClass>(parent, type), KotlinClassStub {
override fun getFqName(): FqName? { override fun getFqName(): FqName? {
@@ -22,8 +22,9 @@ import org.jetbrains.kotlin.psi.KtEnumEntrySuperclassReferenceExpression
import org.jetbrains.kotlin.psi.stubs.KotlinEnumEntrySuperclassReferenceExpressionStub import org.jetbrains.kotlin.psi.stubs.KotlinEnumEntrySuperclassReferenceExpressionStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinEnumEntrySuperclassReferenceExpressionStubImpl(parent: StubElement<*>, private val referencedName: StringRef) class KotlinEnumEntrySuperclassReferenceExpressionStubImpl(parent: StubElement<*>, private val referencedName: StringRef) :
: KotlinStubBaseImpl<KtEnumEntrySuperclassReferenceExpression>(parent, KtStubElementTypes.ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION), KotlinEnumEntrySuperclassReferenceExpressionStub { KotlinStubBaseImpl<KtEnumEntrySuperclassReferenceExpression>(parent, KtStubElementTypes.ENUM_ENTRY_SUPERCLASS_REFERENCE_EXPRESSION),
KotlinEnumEntrySuperclassReferenceExpressionStub {
override fun getReferencedName() = referencedName.string override fun getReferencedName() = referencedName.string
} }
@@ -32,13 +32,13 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes.IMPORT_LIST
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
open class KotlinFileStubImpl( open class KotlinFileStubImpl(
jetFile: KtFile?, jetFile: KtFile?,
private val packageName: StringRef, private val packageName: StringRef,
private val isScript: Boolean private val isScript: Boolean
) : PsiFileStubImpl<KtFile>(jetFile), KotlinFileStub, PsiClassHolderFileStub<KtFile> { ) : PsiFileStubImpl<KtFile>(jetFile), KotlinFileStub, PsiClassHolderFileStub<KtFile> {
constructor(jetFile: KtFile?, packageName: String, isScript: Boolean) constructor(jetFile: KtFile?, packageName: String, isScript: Boolean)
: this(jetFile, StringRef.fromString(packageName)!!, isScript) : this(jetFile, StringRef.fromString(packageName)!!, isScript)
override fun getPackageFqName(): FqName = FqName(StringRef.toString(packageName)!!) override fun getPackageFqName(): FqName = FqName(StringRef.toString(packageName)!!)
override fun isScript(): Boolean = isScript override fun isScript(): Boolean = isScript
@@ -24,8 +24,8 @@ import org.jetbrains.kotlin.psi.stubs.KotlinImportAliasStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinImportAliasStubImpl( class KotlinImportAliasStubImpl(
parent: StubElement<PsiElement>?, parent: StubElement<PsiElement>?,
private val name: StringRef? private val name: StringRef?
) : KotlinStubBaseImpl<KtImportAlias>(parent, KtStubElementTypes.IMPORT_ALIAS), KotlinImportAliasStub { ) : KotlinStubBaseImpl<KtImportAlias>(parent, KtStubElementTypes.IMPORT_ALIAS), KotlinImportAliasStub {
override fun getName(): String? = StringRef.toString(name) override fun getName(): String? = StringRef.toString(name)
} }
@@ -25,10 +25,10 @@ import org.jetbrains.kotlin.psi.stubs.KotlinImportDirectiveStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinImportDirectiveStubImpl( class KotlinImportDirectiveStubImpl(
parent: StubElement<PsiElement>, parent: StubElement<PsiElement>,
private val isAllUnder: Boolean, private val isAllUnder: Boolean,
private val importedFqName: StringRef?, private val importedFqName: StringRef?,
private val isValid: Boolean private val isValid: Boolean
) : KotlinStubBaseImpl<KtImportDirective>(parent, KtStubElementTypes.IMPORT_DIRECTIVE), KotlinImportDirectiveStub { ) : KotlinStubBaseImpl<KtImportDirective>(parent, KtStubElementTypes.IMPORT_DIRECTIVE), KotlinImportDirectiveStub {
override fun isAllUnder(): Boolean = isAllUnder override fun isAllUnder(): Boolean = isAllUnder
@@ -26,14 +26,14 @@ import org.jetbrains.kotlin.name.FqName
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
class KotlinObjectStubImpl( class KotlinObjectStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val name: StringRef?, private val name: StringRef?,
private val fqName: FqName?, private val fqName: FqName?,
private val superNames: Array<StringRef>, private val superNames: Array<StringRef>,
private val isTopLevel: Boolean, private val isTopLevel: Boolean,
private val isDefault: Boolean, private val isDefault: Boolean,
private val isLocal: Boolean, private val isLocal: Boolean,
private val isObjectLiteral: Boolean private val isObjectLiteral: Boolean
) : KotlinStubBaseImpl<KtObjectDeclaration>(parent, KtStubElementTypes.OBJECT_DECLARATION), KotlinObjectStub { ) : KotlinStubBaseImpl<KtObjectDeclaration>(parent, KtStubElementTypes.OBJECT_DECLARATION), KotlinObjectStub {
override fun getFqName() = fqName override fun getFqName() = fqName
override fun getName() = StringRef.toString(name) override fun getName() = StringRef.toString(name)
@@ -25,16 +25,16 @@ import org.jetbrains.kotlin.name.FqName
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
class KotlinPropertyStubImpl( class KotlinPropertyStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val name: StringRef?, private val name: StringRef?,
private val isVar: Boolean, private val isVar: Boolean,
private val isTopLevel: Boolean, private val isTopLevel: Boolean,
private val hasDelegate: Boolean, private val hasDelegate: Boolean,
private val hasDelegateExpression: Boolean, private val hasDelegateExpression: Boolean,
private val hasInitializer: Boolean, private val hasInitializer: Boolean,
private val isExtension: Boolean, private val isExtension: Boolean,
private val hasReturnTypeRef: Boolean, private val hasReturnTypeRef: Boolean,
private val fqName: FqName? private val fqName: FqName?
) : KotlinStubBaseImpl<KtProperty>(parent, KtStubElementTypes.PROPERTY), KotlinPropertyStub { ) : KotlinStubBaseImpl<KtProperty>(parent, KtStubElementTypes.PROPERTY), KotlinPropertyStub {
init { init {
@@ -25,8 +25,8 @@ import org.jetbrains.kotlin.psi.stubs.KotlinScriptStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinScriptStubImpl( class KotlinScriptStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val _fqName: StringRef? private val _fqName: StringRef?
) : KotlinStubBaseImpl<KtScript>(parent, KtStubElementTypes.SCRIPT), KotlinScriptStub { ) : KotlinStubBaseImpl<KtScript>(parent, KtStubElementTypes.SCRIPT), KotlinScriptStub {
override fun getName(): String = getFqName().shortName().asString() override fun getName(): String = getFqName().shortName().asString()
@@ -25,17 +25,17 @@ import org.jetbrains.kotlin.psi.stubs.KotlinTypeAliasStub
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
class KotlinTypeAliasStubImpl( class KotlinTypeAliasStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val name: StringRef?, private val name: StringRef?,
private val qualifiedName: StringRef?, private val qualifiedName: StringRef?,
private val isTopLevel: Boolean private val isTopLevel: Boolean
) : KotlinStubBaseImpl<KtTypeAlias>(parent, KtStubElementTypes.TYPEALIAS), KotlinTypeAliasStub { ) : KotlinStubBaseImpl<KtTypeAlias>(parent, KtStubElementTypes.TYPEALIAS), KotlinTypeAliasStub {
override fun getName(): String? = override fun getName(): String? =
StringRef.toString(name) StringRef.toString(name)
override fun getFqName(): FqName? = override fun getFqName(): FqName? =
StringRef.toString(qualifiedName)?.let(::FqName) StringRef.toString(qualifiedName)?.let(::FqName)
override fun isTopLevel(): Boolean = isTopLevel override fun isTopLevel(): Boolean = isTopLevel
} }
@@ -24,10 +24,10 @@ import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
class KotlinTypeParameterStubImpl( class KotlinTypeParameterStubImpl(
parent: StubElement<out PsiElement>?, parent: StubElement<out PsiElement>?,
private val name: StringRef?, private val name: StringRef?,
private val isInVariance: Boolean, private val isInVariance: Boolean,
private val isOutVariance: Boolean private val isOutVariance: Boolean
) : KotlinStubBaseImpl<KtTypeParameter>(parent, KtStubElementTypes.TYPE_PARAMETER), KotlinTypeParameterStub { ) : KotlinStubBaseImpl<KtTypeParameter>(parent, KtStubElementTypes.TYPE_PARAMETER), KotlinTypeParameterStub {
override fun isInVariance() = isInVariance override fun isInVariance() = isInVariance
override fun isOutVariance() = isOutVariance override fun isOutVariance() = isOutVariance
@@ -26,9 +26,11 @@ object ModifierMaskUtils {
assert(MODIFIER_KEYWORDS_ARRAY.size <= 32) { "Current implementation depends on the ability to represent modifier list as bit mask" } assert(MODIFIER_KEYWORDS_ARRAY.size <= 32) { "Current implementation depends on the ability to represent modifier list as bit mask" }
} }
@JvmStatic fun computeMaskFromModifierList(modifierList: KtModifierList): Int = computeMask { modifierList.hasModifier(it) } @JvmStatic
fun computeMaskFromModifierList(modifierList: KtModifierList): Int = computeMask { modifierList.hasModifier(it) }
@JvmStatic fun computeMask(hasModifier: (KtModifierKeywordToken) -> Boolean): Int { @JvmStatic
fun computeMask(hasModifier: (KtModifierKeywordToken) -> Boolean): Int {
var mask = 0 var mask = 0
for ((index, modifierKeywordToken) in MODIFIER_KEYWORDS_ARRAY.withIndex()) { for ((index, modifierKeywordToken) in MODIFIER_KEYWORDS_ARRAY.withIndex()) {
if (hasModifier(modifierKeywordToken)) { if (hasModifier(modifierKeywordToken)) {
@@ -38,13 +40,15 @@ object ModifierMaskUtils {
return mask return mask
} }
@JvmStatic fun maskHasModifier(mask: Int, modifierToken: KtModifierKeywordToken): Boolean { @JvmStatic
fun maskHasModifier(mask: Int, modifierToken: KtModifierKeywordToken): Boolean {
val index = MODIFIER_KEYWORDS_ARRAY.indexOf(modifierToken) val index = MODIFIER_KEYWORDS_ARRAY.indexOf(modifierToken)
assert(index >= 0) { "All JetModifierKeywordTokens should be present in MODIFIER_KEYWORDS_ARRAY" } assert(index >= 0) { "All JetModifierKeywordTokens should be present in MODIFIER_KEYWORDS_ARRAY" }
return (mask and (1 shl index)) != 0 return (mask and (1 shl index)) != 0
} }
@JvmStatic fun maskToString(mask: Int): String { @JvmStatic
fun maskToString(mask: Int): String {
val sb = StringBuilder() val sb = StringBuilder()
sb.append("[") sb.append("[")
var first = true var first = true
@@ -27,8 +27,8 @@ import org.jetbrains.kotlin.util.slicedMap.WritableSlice
* (like storing them in the local map, later re-committing into parent's, etc.) * (like storing them in the local map, later re-committing into parent's, etc.)
*/ */
abstract class AbstractFilteringTrace( abstract class AbstractFilteringTrace(
private val parentTrace: BindingTrace, private val parentTrace: BindingTrace,
name: String name: String
) : DelegatingBindingTrace(parentTrace.bindingContext, name, true, BindingTraceFilter.ACCEPT_ALL, false) { ) : DelegatingBindingTrace(parentTrace.bindingContext, name, true, BindingTraceFilter.ACCEPT_ALL, false) {
abstract protected fun <K, V> shouldBeHiddenFromParent(slice: WritableSlice<K, V>, key: K): Boolean abstract protected fun <K, V> shouldBeHiddenFromParent(slice: WritableSlice<K, V>, key: K): Boolean
@@ -41,8 +41,8 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
import org.jetbrains.kotlin.types.isError import org.jetbrains.kotlin.types.isError
class AnnotationChecker( class AnnotationChecker(
private val additionalCheckers: Iterable<AdditionalAnnotationChecker>, private val additionalCheckers: Iterable<AdditionalAnnotationChecker>,
private val languageVersionSettings: LanguageVersionSettings private val languageVersionSettings: LanguageVersionSettings
) { ) {
fun check(annotated: KtAnnotated, trace: BindingTrace, descriptor: DeclarationDescriptor? = null) { fun check(annotated: KtAnnotated, trace: BindingTrace, descriptor: DeclarationDescriptor? = null) {
@@ -86,25 +86,25 @@ class AnnotationChecker(
private fun KtAnnotated?.getImplicitUseSiteTargetList(): List<AnnotationUseSiteTarget> = when (this) { private fun KtAnnotated?.getImplicitUseSiteTargetList(): List<AnnotationUseSiteTarget> = when (this) {
is KtParameter -> is KtParameter ->
if (ownerFunction is KtPrimaryConstructor) UseSiteTargetsList.T_CONSTRUCTOR_PARAMETER else emptyList() if (ownerFunction is KtPrimaryConstructor) UseSiteTargetsList.T_CONSTRUCTOR_PARAMETER else emptyList()
is KtProperty -> is KtProperty ->
if (!isLocal) UseSiteTargetsList.T_PROPERTY else emptyList() if (!isLocal) UseSiteTargetsList.T_PROPERTY else emptyList()
is KtPropertyAccessor -> is KtPropertyAccessor ->
if (isGetter) listOf(AnnotationUseSiteTarget.PROPERTY_GETTER) else listOf(AnnotationUseSiteTarget.PROPERTY_SETTER) if (isGetter) listOf(AnnotationUseSiteTarget.PROPERTY_GETTER) else listOf(AnnotationUseSiteTarget.PROPERTY_SETTER)
else -> else ->
emptyList() emptyList()
} }
private fun KtAnnotated?.getDefaultUseSiteTarget(descriptor: AnnotationDescriptor) = private fun KtAnnotated?.getDefaultUseSiteTarget(descriptor: AnnotationDescriptor) =
getImplicitUseSiteTargetList().firstOrNull { getImplicitUseSiteTargetList().firstOrNull {
KotlinTarget.USE_SITE_MAPPING[it] in AnnotationChecker.applicableTargetSet(descriptor) KotlinTarget.USE_SITE_MAPPING[it] in AnnotationChecker.applicableTargetSet(descriptor)
} }
private fun checkEntries( private fun checkEntries(
entries: List<KtAnnotationEntry>, entries: List<KtAnnotationEntry>,
actualTargets: TargetList, actualTargets: TargetList,
trace: BindingTrace, trace: BindingTrace,
annotated: KtAnnotated? = null annotated: KtAnnotated? = null
) { ) {
val entryTypesWithAnnotations = hashMapOf<KotlinType, MutableList<AnnotationUseSiteTarget?>>() val entryTypesWithAnnotations = hashMapOf<KotlinType, MutableList<AnnotationUseSiteTarget?>>()
@@ -116,7 +116,7 @@ class AnnotationChecker(
val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() ?: annotated.getDefaultUseSiteTarget(descriptor) val useSiteTarget = entry.useSiteTarget?.getAnnotationUseSiteTarget() ?: annotated.getDefaultUseSiteTarget(descriptor)
val existingTargetsForAnnotation = entryTypesWithAnnotations.getOrPut(descriptor.type) { arrayListOf() } val existingTargetsForAnnotation = entryTypesWithAnnotations.getOrPut(descriptor.type) { arrayListOf() }
val duplicateAnnotation = useSiteTarget in existingTargetsForAnnotation val duplicateAnnotation = useSiteTarget in existingTargetsForAnnotation
|| (existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) }) || (existingTargetsForAnnotation.any { (it == null) != (useSiteTarget == null) })
if (duplicateAnnotation && !classDescriptor.isRepeatableAnnotation()) { if (duplicateAnnotation && !classDescriptor.isRepeatableAnnotation()) {
trace.report(Errors.REPEATED_ANNOTATION.on(entry)) trace.report(Errors.REPEATED_ANNOTATION.on(entry))
@@ -174,12 +174,17 @@ class AnnotationChecker(
} }
if (useSiteTarget != null) { if (useSiteTarget != null) {
trace.report(Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on( trace.report(
entry, actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target", useSiteTarget.renderName)) Errors.WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET.on(
} entry, actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target", useSiteTarget.renderName
else { )
trace.report(Errors.WRONG_ANNOTATION_TARGET.on( )
entry, actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target")) } else {
trace.report(
Errors.WRONG_ANNOTATION_TARGET.on(
entry, actualTargets.defaultTargets.firstOrNull()?.description ?: "unidentified target"
)
)
} }
} }
@@ -191,14 +196,15 @@ class AnnotationChecker(
return descriptor.annotationClass?.let(this::applicableTargetSet) ?: KotlinTarget.DEFAULT_TARGET_SET return descriptor.annotationClass?.let(this::applicableTargetSet) ?: KotlinTarget.DEFAULT_TARGET_SET
} }
@JvmStatic fun applicableTargetSet(descriptor: AnnotationDescriptor): Set<KotlinTarget> { @JvmStatic
fun applicableTargetSet(descriptor: AnnotationDescriptor): Set<KotlinTarget> {
val classDescriptor = descriptor.annotationClass ?: return emptySet() val classDescriptor = descriptor.annotationClass ?: return emptySet()
return applicableTargetSet(classDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET return applicableTargetSet(classDescriptor) ?: KotlinTarget.DEFAULT_TARGET_SET
} }
fun applicableTargetSet(classDescriptor: ClassDescriptor): Set<KotlinTarget>? { fun applicableTargetSet(classDescriptor: ClassDescriptor): Set<KotlinTarget>? {
val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target) val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target)
?: return null ?: return null
val valueArguments = targetEntryDescriptor.allValueArguments val valueArguments = targetEntryDescriptor.allValueArguments
val valueArgument = valueArguments.entries.firstOrNull()?.value as? ArrayValue ?: return null val valueArgument = valueArguments.entries.firstOrNull()?.value as? ArrayValue ?: return null
return valueArgument.value.filterIsInstance<EnumValue>().mapNotNull { return valueArgument.value.filterIsInstance<EnumValue>().mapNotNull {
@@ -211,8 +217,8 @@ class AnnotationChecker(
return getActualTargetList(annotated, descriptor, trace).defaultTargets return getActualTargetList(annotated, descriptor, trace).defaultTargets
} }
private fun DeclarationDescriptor?.hasBackingField(bindingTrace: BindingTrace) private fun DeclarationDescriptor?.hasBackingField(bindingTrace: BindingTrace) =
= (this as? PropertyDescriptor)?.let { bindingTrace.get(BindingContext.BACKING_FIELD_REQUIRED, it) } ?: false (this as? PropertyDescriptor)?.let { bindingTrace.get(BindingContext.BACKING_FIELD_REQUIRED, it) } ?: false
private fun getActualTargetList(annotated: KtElement, descriptor: DeclarationDescriptor?, trace: BindingTrace): TargetList { private fun getActualTargetList(annotated: KtElement, descriptor: DeclarationDescriptor?, trace: BindingTrace): TargetList {
return when (annotated) { return when (annotated) {
@@ -273,29 +279,32 @@ class AnnotationChecker(
if (backingField) extraTargets(FIELD) if (backingField) extraTargets(FIELD)
if (delegate) { if (delegate) {
onlyWithUseSiteTarget(VALUE_PARAMETER, PROPERTY_GETTER, PROPERTY_SETTER, FIELD) onlyWithUseSiteTarget(VALUE_PARAMETER, PROPERTY_GETTER, PROPERTY_SETTER, FIELD)
} } else {
else {
onlyWithUseSiteTarget(VALUE_PARAMETER, PROPERTY_GETTER, PROPERTY_SETTER) onlyWithUseSiteTarget(VALUE_PARAMETER, PROPERTY_GETTER, PROPERTY_SETTER)
} }
} }
fun T_MEMBER_PROPERTY(backingField: Boolean, delegate: Boolean) = fun T_MEMBER_PROPERTY(backingField: Boolean, delegate: Boolean) =
targetList(when { targetList(
when {
backingField -> MEMBER_PROPERTY_WITH_BACKING_FIELD backingField -> MEMBER_PROPERTY_WITH_BACKING_FIELD
delegate -> MEMBER_PROPERTY_WITH_DELEGATE delegate -> MEMBER_PROPERTY_WITH_DELEGATE
else -> MEMBER_PROPERTY_WITHOUT_FIELD_OR_DELEGATE else -> MEMBER_PROPERTY_WITHOUT_FIELD_OR_DELEGATE
}, MEMBER_PROPERTY, PROPERTY) { }, MEMBER_PROPERTY, PROPERTY
propertyTargets(backingField, delegate) ) {
} propertyTargets(backingField, delegate)
}
fun T_TOP_LEVEL_PROPERTY(backingField: Boolean, delegate: Boolean) = fun T_TOP_LEVEL_PROPERTY(backingField: Boolean, delegate: Boolean) =
targetList(when { targetList(
when {
backingField -> TOP_LEVEL_PROPERTY_WITH_BACKING_FIELD backingField -> TOP_LEVEL_PROPERTY_WITH_BACKING_FIELD
delegate -> TOP_LEVEL_PROPERTY_WITH_DELEGATE delegate -> TOP_LEVEL_PROPERTY_WITH_DELEGATE
else -> TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE else -> TOP_LEVEL_PROPERTY_WITHOUT_FIELD_OR_DELEGATE
}, TOP_LEVEL_PROPERTY, PROPERTY) { }, TOP_LEVEL_PROPERTY, PROPERTY
propertyTargets(backingField, delegate) ) {
} propertyTargets(backingField, delegate)
}
val T_PROPERTY_GETTER = targetList(PROPERTY_GETTER) val T_PROPERTY_GETTER = targetList(PROPERTY_GETTER)
val T_PROPERTY_SETTER = targetList(PROPERTY_SETTER) val T_PROPERTY_SETTER = targetList(PROPERTY_SETTER)
@@ -368,17 +377,22 @@ class AnnotationChecker(
} }
private class TargetList( private class TargetList(
val defaultTargets: List<KotlinTarget>, val defaultTargets: List<KotlinTarget>,
val canBeSubstituted: List<KotlinTarget> = emptyList(), val canBeSubstituted: List<KotlinTarget> = emptyList(),
val onlyWithUseSiteTarget: List<KotlinTarget> = emptyList()) val onlyWithUseSiteTarget: List<KotlinTarget> = emptyList()
)
private object UseSiteTargetsList { private object UseSiteTargetsList {
val T_CONSTRUCTOR_PARAMETER = listOf(AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER, val T_CONSTRUCTOR_PARAMETER = listOf(
AnnotationUseSiteTarget.PROPERTY, AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER,
AnnotationUseSiteTarget.FIELD) AnnotationUseSiteTarget.PROPERTY,
AnnotationUseSiteTarget.FIELD
)
val T_PROPERTY = listOf(AnnotationUseSiteTarget.PROPERTY, val T_PROPERTY = listOf(
AnnotationUseSiteTarget.FIELD) AnnotationUseSiteTarget.PROPERTY,
AnnotationUseSiteTarget.FIELD
)
} }
} }
} }
@@ -29,23 +29,23 @@ import org.jetbrains.kotlin.types.KotlinType
abstract class AnnotationResolver { abstract class AnnotationResolver {
fun resolveAnnotationsWithoutArguments( fun resolveAnnotationsWithoutArguments(
scope: LexicalScope, scope: LexicalScope,
modifierList: KtModifierList?, modifierList: KtModifierList?,
trace: BindingTrace trace: BindingTrace
): Annotations = resolveAnnotationsFromModifierList(scope, modifierList, trace, false) ): Annotations = resolveAnnotationsFromModifierList(scope, modifierList, trace, false)
fun resolveAnnotationsWithArguments( fun resolveAnnotationsWithArguments(
scope: LexicalScope, scope: LexicalScope,
modifierList: KtModifierList?, modifierList: KtModifierList?,
trace: BindingTrace trace: BindingTrace
): Annotations = resolveAnnotationsFromModifierList(scope, modifierList, trace, true) ): Annotations = resolveAnnotationsFromModifierList(scope, modifierList, trace, true)
private fun resolveAnnotationsFromModifierList( private fun resolveAnnotationsFromModifierList(
scope: LexicalScope, scope: LexicalScope,
modifierList: KtModifierList?, modifierList: KtModifierList?,
trace: BindingTrace, trace: BindingTrace,
shouldResolveArguments: Boolean shouldResolveArguments: Boolean
): Annotations { ): Annotations {
if (modifierList == null) { if (modifierList == null) {
return Annotations.EMPTY return Annotations.EMPTY
@@ -55,36 +55,36 @@ abstract class AnnotationResolver {
} }
fun resolveAnnotationsWithoutArguments( fun resolveAnnotationsWithoutArguments(
scope: LexicalScope, scope: LexicalScope,
annotationEntries: @JvmSuppressWildcards List<KtAnnotationEntry>, annotationEntries: @JvmSuppressWildcards List<KtAnnotationEntry>,
trace: BindingTrace trace: BindingTrace
): Annotations = resolveAnnotationEntries(scope, annotationEntries, trace, false) ): Annotations = resolveAnnotationEntries(scope, annotationEntries, trace, false)
fun resolveAnnotationsWithArguments( fun resolveAnnotationsWithArguments(
scope: LexicalScope, scope: LexicalScope,
annotationEntries: @JvmSuppressWildcards List<KtAnnotationEntry>, annotationEntries: @JvmSuppressWildcards List<KtAnnotationEntry>,
trace: BindingTrace trace: BindingTrace
): Annotations = resolveAnnotationEntries(scope, annotationEntries, trace, true) ): Annotations = resolveAnnotationEntries(scope, annotationEntries, trace, true)
protected abstract fun resolveAnnotationEntries( protected abstract fun resolveAnnotationEntries(
scope: LexicalScope, scope: LexicalScope,
annotationEntries: @JvmSuppressWildcards List<KtAnnotationEntry>, annotationEntries: @JvmSuppressWildcards List<KtAnnotationEntry>,
trace: BindingTrace, trace: BindingTrace,
shouldResolveArguments: Boolean shouldResolveArguments: Boolean
): Annotations ): Annotations
abstract fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType abstract fun resolveAnnotationType(scope: LexicalScope, entryElement: KtAnnotationEntry, trace: BindingTrace): KotlinType
abstract fun resolveAnnotationCall( abstract fun resolveAnnotationCall(
annotationEntry: KtAnnotationEntry, annotationEntry: KtAnnotationEntry,
scope: LexicalScope, scope: LexicalScope,
trace: BindingTrace trace: BindingTrace
): OverloadResolutionResults<FunctionDescriptor> ): OverloadResolutionResults<FunctionDescriptor>
abstract fun getAnnotationArgumentValue( abstract fun getAnnotationArgumentValue(
trace: BindingTrace, trace: BindingTrace,
valueParameter: ValueParameterDescriptor, valueParameter: ValueParameterDescriptor,
resolvedArgument: ResolvedValueArgument resolvedArgument: ResolvedValueArgument
): ConstantValue<*>? ): ConstantValue<*>?
} }
@@ -46,26 +46,26 @@ object AnnotationUseSiteTargetChecker {
when (target) { when (target) {
AnnotationUseSiteTarget.FIELD -> checkIfHasBackingField(annotated, descriptor, annotation) AnnotationUseSiteTarget.FIELD -> checkIfHasBackingField(annotated, descriptor, annotation)
AnnotationUseSiteTarget.PROPERTY, AnnotationUseSiteTarget.PROPERTY,
AnnotationUseSiteTarget.PROPERTY_GETTER -> {} AnnotationUseSiteTarget.PROPERTY_GETTER -> {
}
AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD -> checkIfDelegatedProperty(annotated, annotation) AnnotationUseSiteTarget.PROPERTY_DELEGATE_FIELD -> checkIfDelegatedProperty(annotated, annotation)
AnnotationUseSiteTarget.PROPERTY_SETTER -> checkIfMutableProperty(annotated, annotation) AnnotationUseSiteTarget.PROPERTY_SETTER -> checkIfMutableProperty(annotated, annotation)
AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> { AnnotationUseSiteTarget.CONSTRUCTOR_PARAMETER -> {
if (annotated !is KtParameter) { if (annotated !is KtParameter) {
report(INAPPLICABLE_PARAM_TARGET.on(annotation)) report(INAPPLICABLE_PARAM_TARGET.on(annotation))
} } else {
else {
val containingDeclaration = bindingContext[BindingContext.VALUE_PARAMETER, annotated]?.containingDeclaration val containingDeclaration = bindingContext[BindingContext.VALUE_PARAMETER, annotated]?.containingDeclaration
if (containingDeclaration !is ConstructorDescriptor || !containingDeclaration.isPrimary) { if (containingDeclaration !is ConstructorDescriptor || !containingDeclaration.isPrimary) {
report(INAPPLICABLE_PARAM_TARGET.on(annotation)) report(INAPPLICABLE_PARAM_TARGET.on(annotation))
} } else if (!annotated.hasValOrVar()) {
else if (!annotated.hasValOrVar()) {
report(REDUNDANT_ANNOTATION_TARGET.on(annotation, target.renderName)) report(REDUNDANT_ANNOTATION_TARGET.on(annotation, target.renderName))
} }
} }
} }
AnnotationUseSiteTarget.SETTER_PARAMETER -> checkIfMutableProperty(annotated, annotation) AnnotationUseSiteTarget.SETTER_PARAMETER -> checkIfMutableProperty(annotated, annotation)
AnnotationUseSiteTarget.FILE -> reportDiagnosticOnce(INAPPLICABLE_FILE_TARGET.on(useSiteTarget)) AnnotationUseSiteTarget.FILE -> reportDiagnosticOnce(INAPPLICABLE_FILE_TARGET.on(useSiteTarget))
AnnotationUseSiteTarget.RECEIVER -> {} AnnotationUseSiteTarget.RECEIVER -> {
}
} }
} }
} }
@@ -76,7 +76,11 @@ object AnnotationUseSiteTargetChecker {
} }
} }
private fun BindingTrace.checkIfHasBackingField(annotated: KtAnnotated, descriptor: DeclarationDescriptor, annotation: KtAnnotationEntry) { private fun BindingTrace.checkIfHasBackingField(
annotated: KtAnnotated,
descriptor: DeclarationDescriptor,
annotation: KtAnnotationEntry
) {
if (annotated is KtProperty && annotated.hasDelegate() && if (annotated is KtProperty && annotated.hasDelegate() &&
descriptor is PropertyDescriptor && get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) != true) { descriptor is PropertyDescriptor && get(BindingContext.BACKING_FIELD_REQUIRED, descriptor) != true) {
report(INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD.on(annotation)) report(INAPPLICABLE_TARGET_PROPERTY_HAS_NO_BACKING_FIELD.on(annotation))
@@ -84,7 +88,7 @@ object AnnotationUseSiteTargetChecker {
} }
private fun KtAnnotationEntry.useSiteDescription() = private fun KtAnnotationEntry.useSiteDescription() =
useSiteTarget?.getAnnotationUseSiteTarget()?.renderName ?: "unknown target" // should not happen useSiteTarget?.getAnnotationUseSiteTarget()?.renderName ?: "unknown target" // should not happen
private fun BindingTrace.checkIfMutableProperty(annotated: KtAnnotated, annotation: KtAnnotationEntry) { private fun BindingTrace.checkIfMutableProperty(annotated: KtAnnotated, annotation: KtAnnotationEntry) {
if (!checkIfProperty(annotated, annotation)) return if (!checkIfProperty(annotated, annotation)) return
@@ -36,12 +36,12 @@ private val JVM_SYNTHETIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSynthetic")
fun DeclarationDescriptor.hasJvmSyntheticAnnotation() = findJvmSyntheticAnnotation() != null fun DeclarationDescriptor.hasJvmSyntheticAnnotation() = findJvmSyntheticAnnotation() != null
fun DeclarationDescriptor.findJvmSyntheticAnnotation() = fun DeclarationDescriptor.findJvmSyntheticAnnotation() =
DescriptorUtils.getAnnotationByFqName(annotations, JVM_SYNTHETIC_ANNOTATION_FQ_NAME) DescriptorUtils.getAnnotationByFqName(annotations, JVM_SYNTHETIC_ANNOTATION_FQ_NAME)
private val STRICTFP_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Strictfp") private val STRICTFP_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Strictfp")
fun DeclarationDescriptor.findStrictfpAnnotation() = fun DeclarationDescriptor.findStrictfpAnnotation() =
DescriptorUtils.getAnnotationByFqName(annotations, STRICTFP_ANNOTATION_FQ_NAME) DescriptorUtils.getAnnotationByFqName(annotations, STRICTFP_ANNOTATION_FQ_NAME)
fun AnnotationDescriptor.argumentValue(parameterName: String): Any? { fun AnnotationDescriptor.argumentValue(parameterName: String): Any? {
return allValueArguments[Name.identifier(parameterName)].takeUnless { it is ErrorValue }?.value return allValueArguments[Name.identifier(parameterName)].takeUnless { it is ErrorValue }?.value
@@ -46,8 +46,8 @@ fun KtReturnExpression.getTargetFunctionDescriptor(context: BindingContext): Fun
if (containingFunctionDescriptor == null) return null if (containingFunctionDescriptor == null) return null
return generateSequence(containingFunctionDescriptor) { DescriptorUtils.getParentOfType(it, FunctionDescriptor::class.java) } return generateSequence(containingFunctionDescriptor) { DescriptorUtils.getParentOfType(it, FunctionDescriptor::class.java) }
.dropWhile { it is AnonymousFunctionDescriptor } .dropWhile { it is AnonymousFunctionDescriptor }
.firstOrNull() .firstOrNull()
} }
fun KtReturnExpression.getTargetFunction(context: BindingContext): KtCallableDeclaration? { fun KtReturnExpression.getTargetFunction(context: BindingContext): KtCallableDeclaration? {
@@ -65,8 +65,7 @@ fun <C : ResolutionContext<C>> ResolutionContext<C>.recordDataFlowInfo(expressio
val typeInfo = trace.get(BindingContext.EXPRESSION_TYPE_INFO, expression) val typeInfo = trace.get(BindingContext.EXPRESSION_TYPE_INFO, expression)
if (typeInfo != null) { if (typeInfo != null) {
trace.record(BindingContext.EXPRESSION_TYPE_INFO, expression, typeInfo.replaceDataFlowInfo(dataFlowInfo)) trace.record(BindingContext.EXPRESSION_TYPE_INFO, expression, typeInfo.replaceDataFlowInfo(dataFlowInfo))
} } else if (dataFlowInfo != DataFlowInfo.EMPTY) {
else if (dataFlowInfo != DataFlowInfo.EMPTY) {
// Don't store anything in BindingTrace if it's simply an empty DataFlowInfo // Don't store anything in BindingTrace if it's simply an empty DataFlowInfo
trace.record(BindingContext.EXPRESSION_TYPE_INFO, expression, noTypeInfo(dataFlowInfo)) trace.record(BindingContext.EXPRESSION_TYPE_INFO, expression, noTypeInfo(dataFlowInfo))
} }
@@ -93,8 +92,8 @@ fun BindingContext.getDataFlowInfoAfter(position: PsiElement): DataFlowInfo {
fun BindingContext.getDataFlowInfoBefore(position: PsiElement): DataFlowInfo { fun BindingContext.getDataFlowInfoBefore(position: PsiElement): DataFlowInfo {
for (element in position.parentsWithSelf) { for (element in position.parentsWithSelf) {
(element as? KtExpression) (element as? KtExpression)
?.let { this[BindingContext.DATA_FLOW_INFO_BEFORE, it] } ?.let { this[BindingContext.DATA_FLOW_INFO_BEFORE, it] }
?.let { return it } ?.let { return it }
} }
return DataFlowInfo.EMPTY return DataFlowInfo.EMPTY
} }
@@ -107,7 +106,7 @@ fun KtExpression.getReferenceTargets(context: BindingContext): Collection<Declar
} }
fun KtTypeReference.getAbbreviatedTypeOrType(context: BindingContext) = fun KtTypeReference.getAbbreviatedTypeOrType(context: BindingContext) =
context[BindingContext.ABBREVIATED_TYPE, this] ?: context[BindingContext.TYPE, this] context[BindingContext.ABBREVIATED_TYPE, this] ?: context[BindingContext.TYPE, this]
fun KtTypeElement.getAbbreviatedTypeOrType(context: BindingContext): KotlinType? { fun KtTypeElement.getAbbreviatedTypeOrType(context: BindingContext): KotlinType? {
val parent = parent val parent = parent
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.resolve package org.jetbrains.kotlin.resolve
class BindingTraceFilter(val ignoreDiagnostics: Boolean) { class BindingTraceFilter(val ignoreDiagnostics: Boolean) {
companion object { companion object {
val ACCEPT_ALL = BindingTraceFilter(false) val ACCEPT_ALL = BindingTraceFilter(false)
val NO_DIAGNOSTICS = BindingTraceFilter(true) val NO_DIAGNOSTICS = BindingTraceFilter(true)
} }
@@ -27,11 +27,11 @@ interface CodeAnalyzerInitializer {
companion object { companion object {
fun getInstance(project: Project): CodeAnalyzerInitializer = fun getInstance(project: Project): CodeAnalyzerInitializer =
ServiceManager.getService<CodeAnalyzerInitializer>(project, CodeAnalyzerInitializer::class.java)!! ServiceManager.getService<CodeAnalyzerInitializer>(project, CodeAnalyzerInitializer::class.java)!!
} }
} }
class DummyCodeAnalyzerInitializer: CodeAnalyzerInitializer { class DummyCodeAnalyzerInitializer : CodeAnalyzerInitializer {
override fun initialize(trace: BindingTrace, module: ModuleDescriptor, codeAnalyzer: KotlinCodeAnalyzer) { override fun initialize(trace: BindingTrace, module: ModuleDescriptor, codeAnalyzer: KotlinCodeAnalyzer) {
// Do nothing // Do nothing
} }
@@ -39,25 +39,29 @@ import org.jetbrains.kotlin.types.expressions.KotlinTypeInfo
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo import org.jetbrains.kotlin.types.expressions.typeInfoFactory.createTypeInfo
import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo import org.jetbrains.kotlin.types.expressions.typeInfoFactory.noTypeInfo
class CollectionLiteralResolver(val module: ModuleDescriptor, val callResolver: CallResolver, val languageVersionSettings: LanguageVersionSettings) { class CollectionLiteralResolver(
val module: ModuleDescriptor,
val callResolver: CallResolver,
val languageVersionSettings: LanguageVersionSettings
) {
companion object { companion object {
val PRIMITIVE_TYPE_TO_ARRAY: Map<PrimitiveType, Name> = hashMapOf( val PRIMITIVE_TYPE_TO_ARRAY: Map<PrimitiveType, Name> = hashMapOf(
PrimitiveType.BOOLEAN to Name.identifier("booleanArrayOf"), PrimitiveType.BOOLEAN to Name.identifier("booleanArrayOf"),
PrimitiveType.CHAR to Name.identifier("charArrayOf"), PrimitiveType.CHAR to Name.identifier("charArrayOf"),
PrimitiveType.INT to Name.identifier("intArrayOf"), PrimitiveType.INT to Name.identifier("intArrayOf"),
PrimitiveType.BYTE to Name.identifier("byteArrayOf"), PrimitiveType.BYTE to Name.identifier("byteArrayOf"),
PrimitiveType.SHORT to Name.identifier("shortArrayOf"), PrimitiveType.SHORT to Name.identifier("shortArrayOf"),
PrimitiveType.FLOAT to Name.identifier("floatArrayOf"), PrimitiveType.FLOAT to Name.identifier("floatArrayOf"),
PrimitiveType.LONG to Name.identifier("longArrayOf"), PrimitiveType.LONG to Name.identifier("longArrayOf"),
PrimitiveType.DOUBLE to Name.identifier("doubleArrayOf") PrimitiveType.DOUBLE to Name.identifier("doubleArrayOf")
) )
val ARRAY_OF_FUNCTION = Name.identifier("arrayOf") val ARRAY_OF_FUNCTION = Name.identifier("arrayOf")
} }
fun resolveCollectionLiteral( fun resolveCollectionLiteral(
collectionLiteralExpression: KtCollectionLiteralExpression, collectionLiteralExpression: KtCollectionLiteralExpression,
context: ExpressionTypingContext context: ExpressionTypingContext
): KotlinTypeInfo { ): KotlinTypeInfo {
if (!isInsideAnnotationEntryOrClass(collectionLiteralExpression)) { if (!isInsideAnnotationEntryOrClass(collectionLiteralExpression)) {
context.trace.report(UNSUPPORTED.on(collectionLiteralExpression, "Collection literals outside of annotations")) context.trace.report(UNSUPPORTED.on(collectionLiteralExpression, "Collection literals outside of annotations"))
@@ -69,15 +73,18 @@ class CollectionLiteralResolver(val module: ModuleDescriptor, val callResolver:
} }
private fun resolveCollectionLiteralSpecialMethod( private fun resolveCollectionLiteralSpecialMethod(
expression: KtCollectionLiteralExpression, expression: KtCollectionLiteralExpression,
context: ExpressionTypingContext context: ExpressionTypingContext
): KotlinTypeInfo { ): KotlinTypeInfo {
val call = CallMaker.makeCallForCollectionLiteral(expression) val call = CallMaker.makeCallForCollectionLiteral(expression)
val callName = getArrayFunctionCallName(context.expectedType) val callName = getArrayFunctionCallName(context.expectedType)
val functionDescriptors = getFunctionDescriptorForCollectionLiteral(expression, callName) val functionDescriptors = getFunctionDescriptorForCollectionLiteral(expression, callName)
if (functionDescriptors.isEmpty()) { if (functionDescriptors.isEmpty()) {
context.trace.report(MISSING_STDLIB.on( context.trace.report(
expression, "Collection literal call '$callName()' is unresolved")) MISSING_STDLIB.on(
expression, "Collection literal call '$callName()' is unresolved"
)
)
return noTypeInfo(context) return noTypeInfo(context)
} }
@@ -92,8 +99,8 @@ class CollectionLiteralResolver(val module: ModuleDescriptor, val callResolver:
} }
private fun getFunctionDescriptorForCollectionLiteral( private fun getFunctionDescriptorForCollectionLiteral(
expression: KtCollectionLiteralExpression, expression: KtCollectionLiteralExpression,
callName: Name callName: Name
): Collection<SimpleFunctionDescriptor> { ): Collection<SimpleFunctionDescriptor> {
val memberScopeOfKotlinPackage = module.getPackage(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME).memberScope val memberScopeOfKotlinPackage = module.getPackage(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME).memberScope
return memberScopeOfKotlinPackage.getContributedFunctions(callName, KotlinLookupLocation(expression)) return memberScopeOfKotlinPackage.getContributedFunctions(callName, KotlinLookupLocation(expression))
@@ -23,18 +23,18 @@ import org.jetbrains.kotlin.types.WrappedTypeFactory
interface DeclarationReturnTypeSanitizer { interface DeclarationReturnTypeSanitizer {
fun sanitizeReturnType( fun sanitizeReturnType(
inferred: UnwrappedType, inferred: UnwrappedType,
wrappedTypeFactory: WrappedTypeFactory, wrappedTypeFactory: WrappedTypeFactory,
trace: BindingTrace, trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
): UnwrappedType ): UnwrappedType
object Default : DeclarationReturnTypeSanitizer { object Default : DeclarationReturnTypeSanitizer {
override fun sanitizeReturnType( override fun sanitizeReturnType(
inferred: UnwrappedType, inferred: UnwrappedType,
wrappedTypeFactory: WrappedTypeFactory, wrappedTypeFactory: WrappedTypeFactory,
trace: BindingTrace, trace: BindingTrace,
languageVersionSettings: LanguageVersionSettings languageVersionSettings: LanguageVersionSettings
) = inferred ) = inferred
} }
} }

Some files were not shown because too many files have changed in this diff Show More