All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirDestructuringDeclarationChecker.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.analysis.checkers.declaration
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.KtNodeTypes
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.diagnostics.ConeSimpleDiagnostic
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.references.FirErrorNamedReference
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeAmbiguityError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeConstraintSystemHasContradiction
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeInapplicableCandidateError
import org.jetbrains.kotlin.fir.resolve.diagnostics.ConeUnresolvedNameError
import org.jetbrains.kotlin.fir.resolvedSymbol
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
object FirDestructuringDeclarationChecker : FirPropertyChecker() {
override fun check(declaration: FirProperty, context: CheckerContext, reporter: DiagnosticReporter) {
val source = declaration.source ?: return
// val (...) = `destructuring_declaration`
if (source.elementType == KtNodeTypes.DESTRUCTURING_DECLARATION) {
checkInitializer(source, declaration.initializer, reporter, context)
return
}
// val (`destructuring_declaration_entry`, ...) = ...
if (source.elementType != KtNodeTypes.DESTRUCTURING_DECLARATION_ENTRY) return
val componentCall = declaration.initializer as? FirComponentCall ?: return
val originalExpression = componentCall.explicitReceiverOfQualifiedAccess ?: return
val originalDestructuringDeclaration = originalExpression.resolvedVariable ?: return
val originalDestructuringDeclarationOrInitializer =
when (originalDestructuringDeclaration) {
is FirProperty -> {
if (originalDestructuringDeclaration.initializer?.source?.elementType == KtNodeTypes.FOR) {
// for ((entry, ...) = `destructuring_declaration`) { ... }
// It will be wrapped as `next()` call whose explicit receiver is `iterator()` on the actual source.
val iterator = originalDestructuringDeclaration.initializer?.explicitReceiverOfQualifiedAccess
(iterator?.resolvedVariable as? FirProperty)?.initializer?.explicitReceiverOfQualifiedAccess
} else {
// val (entry, ...) = `destructuring_declaration`
originalDestructuringDeclaration.initializer
}
}
is FirValueParameter -> {
// ... = { `(entry, ...)` -> ... } // value parameter itself is a destructuring declaration
originalDestructuringDeclaration
}
else -> null
} ?: return
val originalDestructuringDeclarationOrInitializerSource = originalDestructuringDeclarationOrInitializer.source ?: return
val originalDestructuringDeclarationType =
when (originalDestructuringDeclarationOrInitializer) {
is FirVariable -> originalDestructuringDeclarationOrInitializer.returnTypeRef.coneType
is FirExpression -> originalDestructuringDeclarationOrInitializer.typeRef.coneType
else -> null
} ?: return
when (val reference = componentCall.calleeReference) {
is FirErrorNamedReference ->
checkComponentCall(
originalDestructuringDeclarationOrInitializerSource,
originalDestructuringDeclarationType,
reference,
declaration,
componentCall,
originalDestructuringDeclaration,
reporter,
context
)
}
}
private fun checkInitializer(
source: KtSourceElement,
initializer: FirExpression?,
reporter: DiagnosticReporter,
context: CheckerContext
) {
val needToReport =
when (initializer) {
null -> true
is FirErrorExpression -> (initializer.diagnostic as? ConeSimpleDiagnostic)?.kind == DiagnosticKind.Syntax
else -> false
}
if (needToReport) {
reporter.reportOn(source, FirErrors.INITIALIZER_REQUIRED_FOR_DESTRUCTURING_DECLARATION, context)
}
}
private fun checkComponentCall(
source: KtSourceElement,
destructuringDeclarationType: ConeKotlinType,
reference: FirErrorNamedReference,
property: FirProperty,
componentCall: FirComponentCall,
destructuringDeclaration: FirVariable,
reporter: DiagnosticReporter,
context: CheckerContext
) {
when (val diagnostic = reference.diagnostic) {
is ConeUnresolvedNameError -> {
reporter.reportOn(
source,
FirErrors.COMPONENT_FUNCTION_MISSING,
diagnostic.name,
destructuringDeclarationType,
context
)
}
is ConeAmbiguityError -> {
reporter.reportOn(
source,
FirErrors.COMPONENT_FUNCTION_AMBIGUITY,
diagnostic.name,
diagnostic.candidates.map { it.symbol },
context
)
}
is ConeInapplicableCandidateError -> {
if (destructuringDeclarationType.isNullable) {
reporter.reportOn(
source,
FirErrors.COMPONENT_FUNCTION_ON_NULLABLE,
(diagnostic.candidate.symbol as FirNamedFunctionSymbol).callableId.callableName,
context
)
}
}
is ConeConstraintSystemHasContradiction -> {
val componentType = componentCall.typeRef.coneType
if (componentType is ConeErrorType) {
// There will be other errors on this error type.
return
}
val expectedType = property.returnTypeRef.coneType
if (!AbstractTypeChecker.isSubtypeOf(context.session.typeContext, componentType, expectedType)) {
val typeMismatchSource =
// ... = { `(entry, ...)` -> ... } // Report on specific `entry`
if (destructuringDeclaration is FirValueParameter)
property.source
// val (entry, ...) = `destructuring_declaration` // Report on a destructuring declaration
else
source
reporter.reportOn(
typeMismatchSource,
FirErrors.COMPONENT_FUNCTION_RETURN_TYPE_MISMATCH,
diagnostic.candidate.callInfo.name,
componentType,
expectedType,
context
)
}
}
}
}
private val FirExpression.explicitReceiverOfQualifiedAccess: FirQualifiedAccessExpression?
get() = (this as? FirQualifiedAccess)?.explicitReceiver?.unwrapped as? FirQualifiedAccessExpression
private val FirExpression.unwrapped: FirExpression
get() =
when (this) {
is FirExpressionWithSmartcast -> this.originalExpression
is FirWrappedExpression -> this.expression
else -> this
}
private val FirQualifiedAccessExpression.resolvedVariable: FirVariable?
get() {
val symbol = calleeReference.resolvedSymbol as? FirVariableSymbol<*> ?: return null
symbol.ensureResolved(FirResolvePhase.BODY_RESOLVE)
@OptIn(SymbolInternals::class)
return symbol.fir
}
}