FIR checker: check if callable reference targets are allowed members

This commit is contained in:
Jinseong Jeon
2021-03-17 08:41:45 -07:00
committed by Mikhail Glukhikh
parent 6769ce0e2b
commit 8d8ed4cc18
29 changed files with 299 additions and 42 deletions
@@ -36,8 +36,8 @@ class NoGenericTest {
fun B.extensionFun(): A = A()
fun test_1() {
val extensionValRef = B::extensionVal
val extensionFunRef = B::extensionFun
val extensionValRef = <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>B::extensionVal<!>
val extensionFunRef = <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>B::extensionFun<!>
}
fun test_2() {
@@ -2968,6 +2968,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference/property"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("backingField.kt")
public void testBackingField() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/property/backingField.kt");
}
@Test
@TestMetadata("classFromClass.kt")
public void testClassFromClass() throws Exception {
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir.checkers.generator.diagnostics
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiTypeElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -33,6 +35,15 @@ import kotlin.properties.ReadOnlyProperty
@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@OptIn(PrivateForInline::class)
object DIAGNOSTICS_LIST : DiagnosticList() {
val MetaErrors by object : DiagnosticGroup("Meta-errors") {
val UNSUPPORTED by error<FirSourceElement, PsiElement> {
parameter<String>("unsupported")
}
val UNSUPPORTED_FEATURE by error<FirSourceElement, PsiElement> {
parameter<Pair<LanguageFeature, LanguageVersionSettings>>("unsupportedFeature")
}
}
val Miscellaneous by object : DiagnosticGroup("Miscellaneous") {
val SYNTAX by error<FirSourceElement, PsiElement>()
val OTHER_ERROR by error<FirSourceElement, PsiElement>()
@@ -264,7 +275,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
}
val REFLECTION by object : DiagnosticGroup("Reflection") {
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error<FirSourceElement, KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<FirCallableDeclaration<*>>("referencedDeclaration")
}
val CALLABLE_REFERENCE_LHS_NOT_A_CLASS by error<FirSourceElement, KtExpression>()
val CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR by error<FirSourceElement, KtExpression>(PositioningStrategy.REFERENCE_BY_QUALIFIED)
val CLASS_LITERAL_LHS_NOT_A_CLASS by error<FirSourceElement, KtExpression>()
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error<FirSourceElement, KtExpression>()
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiTypeElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -54,6 +56,10 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
*/
object FirErrors {
// Meta-errors
val UNSUPPORTED by error1<FirSourceElement, PsiElement, String>()
val UNSUPPORTED_FEATURE by error1<FirSourceElement, PsiElement, Pair<LanguageFeature, LanguageVersionSettings>>()
// Miscellaneous
val SYNTAX by error0<FirSourceElement, PsiElement>()
val OTHER_ERROR by error0<FirSourceElement, PsiElement>()
@@ -201,7 +207,9 @@ object FirErrors {
val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
// Reflection
val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1<FirSourceElement, KtExpression, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val CALLABLE_REFERENCE_LHS_NOT_A_CLASS by error0<FirSourceElement, KtExpression>()
val CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR by error0<FirSourceElement, KtExpression>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val CLASS_LITERAL_LHS_NOT_A_CLASS by error0<FirSourceElement, KtExpression>()
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error0<FirSourceElement, KtExpression>()
val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error1<FirSourceElement, PsiElement, ConeKotlinType>()
@@ -186,3 +186,16 @@ internal fun FirRegularClass.isInlineOrValueClass(): Boolean {
return isInline || hasModifier(KtTokens.VALUE_KEYWORD)
}
internal val FirMemberDeclaration.isLocalMember: Boolean
get() = when (this) {
is FirProperty -> this.isLocal
is FirRegularClass -> this.isLocal
is FirSimpleFunction -> this.isLocal
else -> false
}
internal val FirCallableMemberDeclaration<*>.isExtensionMember: Boolean
get() {
return receiverTypeRef != null && dispatchReceiverType != null
}
@@ -0,0 +1,63 @@
/*
* 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.expression
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isExtensionMember
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.expressions.FirCallableReferenceAccess
import org.jetbrains.kotlin.fir.expressions.FirGetClassCall
import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.containingClass
import org.jetbrains.kotlin.fir.symbols.impl.FirBackingFieldSymbol
object FirCallableReferenceChecker : FirQualifiedAccessChecker() {
override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) {
if (expression !is FirCallableReferenceAccess) return
// [FirGetClassCallChecker] will check [FirGetClassCall].
if (expression is FirGetClassCall) return
checkReferenceIsToAllowedMember(expression, context, reporter)
}
// See FE 1.0 [DoubleColonExpressionResolver#checkReferenceIsToAllowedMember]
private fun checkReferenceIsToAllowedMember(
callableReferenceAccess: FirCallableReferenceAccess,
context: CheckerContext,
reporter: DiagnosticReporter
) {
// UNRESOLVED_REFERENCE will be reported separately.
val reference = callableReferenceAccess.calleeReference as? FirResolvedNamedReference ?: return
val source = reference.source ?: return
if (source.kind is FirFakeSourceElementKind) return
val referredDeclaration = reference.resolvedSymbol.fir
if (referredDeclaration is FirConstructor && referredDeclaration.containingClass?.classKind == ClassKind.ANNOTATION_CLASS) {
reporter.reportOn(source, FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR, context)
}
if ((referredDeclaration as? FirCallableMemberDeclaration<*>)?.isExtensionMember == true &&
(referredDeclaration as? FirMemberDeclaration)?.isLocalMember == false
) {
reporter.reportOn(source, FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED, referredDeclaration, context)
}
// The counterpart in FE 1.0 checks if the given descriptor is VariableDescriptor yet not PropertyDescriptor.
// Here, we explicitly check if the referred declaration/symbol is value parameter, local variable, or backing field.
if (referredDeclaration is FirValueParameter ||
(referredDeclaration is FirProperty && (referredDeclaration.isLocal || reference.resolvedSymbol is FirBackingFieldSymbol))
) {
// TODO: we can't set positioning strategy to meta error. Should report on reference expression, not entire reference access.
reporter.reportOn(source, FirErrors.UNSUPPORTED, "References to variables aren't supported yet", context)
}
}
}
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
@@ -110,14 +111,6 @@ object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
return null
}
private val FirMemberDeclaration.isLocalMember: Boolean
get() = when (this) {
is FirProperty -> this.isLocal
is FirRegularClass -> this.isLocal
is FirSimpleFunction -> this.isLocal
else -> false
}
private val CheckerContext.containingPropertyVisibility
get() = (this.containingDeclarations.last() as? FirProperty)?.visibility
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.LanguageFeatureMessageRenderer
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.AMBIGUOUS_CALLS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR
@@ -44,6 +45,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ASSIGN_OPERATOR_A
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BACKING_FIELD_IN_INTERFACE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.BREAK_OR_CONTINUE_OUTSIDE_A_LOOP
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_LHS_NOT_A_CLASS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CANNOT_CHANGE_ACCESS_PRIVILEGE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CANNOT_WEAKEN_ACCESS_PRIVILEGE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CAN_BE_REPLACED_WITH_OPERATOR_ASSIGNMENT
@@ -87,6 +89,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_SUPER_INT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_TYPEALIAS_EXPANDED_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_TYPE_PARAMETER_BOUND
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERTY_MUST_HAVE_ACCESSORS_OR_BE_ABSTRACT
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXTENSION_PROPERTY_WITH_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE
@@ -207,6 +210,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_IMPLICIT_INVOKE_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_INFIX_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_OPERATOR_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSUPPORTED_FEATURE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNUSED_VARIABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
@@ -245,6 +250,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
// + - Better message required
// # - The new diagnostic differs from the old FE's one
val MAP = FirDiagnosticFactoryToRendererMap("FIR").also { map ->
// Meta-errors
map.put(UNSUPPORTED, "Unsupported [{0}]", TO_STRING)
map.put(UNSUPPORTED_FEATURE, "{0}", LanguageFeatureMessageRenderer(LanguageFeatureMessageRenderer.Type.UNSUPPORTED))
// Miscellaneous
map.put(SYNTAX, "Syntax error")
map.put(OTHER_ERROR, "Unknown (other) error")
@@ -422,7 +431,13 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
map.put(TYPE_PARAMETER_AS_REIFIED, "Cannot use ''{0}'' as reified type parameter. Use a class instead", SYMBOL)
// Reflection
map.put(
EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
"''{0}'' is a member and an extension at the same time. References to such elements are not allowed",
NAME
)
map.put(CALLABLE_REFERENCE_LHS_NOT_A_CLASS, "Left-hand side of a callable reference cannot be a type parameter")
map.put(CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR, "Annotation class cannot be instantiated")
map.put(CLASS_LITERAL_LHS_NOT_A_CLASS, "Only classes are allowed on the left hand side of a class literal")
map.put(NULLABLE_TYPE_IN_CLASS_LITERAL_LHS, "Type in a class literal must not be nullable")
@@ -14,6 +14,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
)
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = setOf(
FirCallableReferenceChecker,
FirSuperNotAvailableChecker,
FirNotASupertypeChecker,
FirSuperclassNotAccessibleFromInterfaceChecker,
@@ -7,6 +7,6 @@ enum class D
fun main() {
<!UNRESOLVED_REFERENCE!>::A<!>
::B
::C // KT-3465
<!CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR!>::C<!> // KT-3465
<!UNRESOLVED_REFERENCE!>::D<!>
}
}
@@ -1,5 +1,5 @@
annotation class Ann(val prop: String)
val annCtorRef = ::Ann
val annCtorRef = <!CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR!>::Ann<!>
val annClassRef = Ann::class
val annPropRef = Ann::prop
@@ -4,11 +4,11 @@ class A {
fun A.extA(x: String) = x
fun main() {
Int::extInt
A::extA
<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>Int::extInt<!>
<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>A::extA<!>
eat(Int::extInt)
eat(A::extA)
eat(<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>Int::extInt<!>)
eat(<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>A::extA<!>)
}
}
@@ -14,10 +14,10 @@ class A {
}
fun test() {
String::ext
<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>String::ext<!>
<!UNRESOLVED_REFERENCE!>Obj::ext<!>
String::ext2
<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>String::ext2<!>
<!UNRESOLVED_REFERENCE!>A.Companion::ext2<!>
<!UNRESOLVED_REFERENCE!>A::ext2<!>
@@ -0,0 +1,7 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
val i: Int = 10
get() {
<!UNSUPPORTED!>::field<!>
return field
}
@@ -0,0 +1,7 @@
// !DIAGNOSTICS: -UNUSED_EXPRESSION
val i: Int = 10
get() {
::<!UNSUPPORTED!>field<!>
return field
}
@@ -0,0 +1,3 @@
package
public val i: kotlin.Int = 10
@@ -2,7 +2,7 @@
fun a() {
val x = 10
foo(::x)
foo(<!UNSUPPORTED!>::x<!>)
}
fun foo(a: Any) {}
fun foo(a: Any) {}
@@ -5,4 +5,4 @@ fun a() {
foo(::<!UNSUPPORTED!>x<!>)
}
fun foo(a: Any) {}
fun foo(a: Any) {}
@@ -3,13 +3,13 @@
fun eat(value: Any) {}
fun test(param: String) {
val a = ::param
val a = <!UNSUPPORTED!>::param<!>
val local = "local"
val b = ::local
val b = <!UNSUPPORTED!>::local<!>
val lambda = { -> }
val g = ::lambda
val g = <!UNSUPPORTED!>::lambda<!>
eat(::param)
eat(<!UNSUPPORTED!>::param<!>)
}
@@ -9,6 +9,6 @@ class Foo {
fun main() {
val f = Foo()
val a: Int
<!VARIABLE_EXPECTED!>get()<!> = f.getValue(null, ::a) // no exception after fix
<!VARIABLE_EXPECTED!>get()<!> = f.getValue(null, <!UNSUPPORTED!>::a<!>) // no exception after fix
<!UNRESOLVED_REFERENCE!>print<!>(<!UNINITIALIZED_VARIABLE!>a<!>)
}
}
@@ -9,6 +9,6 @@ class Foo {
fun main(x: Int) {
val f = Foo()
val a: Int
<!VARIABLE_EXPECTED!>get()<!> = f.getValue(null, ::x) // no exception after fix
<!VARIABLE_EXPECTED!>get()<!> = f.getValue(null, <!UNSUPPORTED!>::x<!>) // no exception after fix
<!UNRESOLVED_REFERENCE!>print<!>(<!UNINITIALIZED_VARIABLE!>a<!>)
}
}
@@ -11,7 +11,7 @@ interface TypeConstructor
class Refiner {
val memoizedFunctionLambda = createMemoizedFunction { it.foo() } // error type infered, no diagnostic, BAD, backend fails
val memoizedFunctionReference = createMemoizedFunction(TypeConstructor::foo) // EXTENSION_IN_CLASS_REFERENCE_IS_NOT_ALLOWED, fine
val memoizedFunctionReference = createMemoizedFunction(<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>TypeConstructor::foo<!>) // EXTENSION_IN_CLASS_REFERENCE_IS_NOT_ALLOWED, fine
val memoizedFunctionTypes = createMemoizedFunction<TypeConstructor, Boolean> { it.foo() } // works fine
private fun TypeConstructor.foo(): Boolean = true
@@ -2974,6 +2974,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/callableReference/property"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true);
}
@Test
@TestMetadata("backingField.kt")
public void testBackingField() throws Exception {
runTest("compiler/testData/diagnostics/tests/callableReference/property/backingField.kt");
}
@Test
@TestMetadata("classFromClass.kt")
public void testClassFromClass() throws Exception {
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.generator
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -47,7 +49,7 @@ object HLDiagnosticConverter {
)
private fun convertParameter(index: Int, diagnosticParameter: DiagnosticParameter): HLDiagnosticParameter {
val conversion = FirToKtConversionCreator.creatConversion(diagnosticParameter.type)
val conversion = FirToKtConversionCreator.createConversion(diagnosticParameter.type)
val convertedType = conversion.convertType(diagnosticParameter.type)
return HLDiagnosticParameter(
name = diagnosticParameter.name,
@@ -73,12 +75,12 @@ object HLDiagnosticConverter {
private object FirToKtConversionCreator {
fun creatConversion(type: KType): HLParameterConversion {
fun createConversion(type: KType): HLParameterConversion {
val kClass = type.classifier as KClass<*>
return tryMapAllowedType(kClass)
?: tryMapPsiElementType(type, kClass)
?: tryMapFirTypeToKtType(kClass)
?: tryMapCollectionType(type, kClass)
?: tryMapPlatformType(type, kClass)
?: error("Unsupported type $type, consider add corresponding mapping")
}
@@ -91,12 +93,20 @@ private object FirToKtConversionCreator {
return null
}
private fun tryMapCollectionType(type: KType, kClass: KClass<*>): HLParameterConversion? {
private fun tryMapPlatformType(type: KType, kClass: KClass<*>): HLParameterConversion? {
if (kClass.isSubclassOf(Collection::class)) {
val elementType = type.arguments.single().type ?: return HLIdParameterConversion
return HLMapParameterConversion(
parameterName = elementType.kClass.simpleName!!.decapitalize(),
mappingConversion = creatConversion(elementType)
mappingConversion = createConversion(elementType)
)
}
if (kClass.isSubclassOf(Pair::class)) {
val first = type.arguments.getOrNull(0)?.type ?: return HLIdParameterConversion
val second = type.arguments.getOrNull(1)?.type ?: return HLIdParameterConversion
return HLPairParameterConversion(
mappingConversionFirst = createConversion(first),
mappingConversionSecond = createConversion(second)
)
}
return null
@@ -175,6 +185,8 @@ private object FirToKtConversionCreator {
Visibility::class,
WhenMissingCase::class,
ForbiddenNamedArgumentsTarget::class,
LanguageFeature::class,
LanguageVersionSettings::class,
)
private val KType.kClass: KClass<*>
@@ -47,6 +47,33 @@ class HLMapParameterConversion(
override val importsToAdd get() = mappingConversion.importsToAdd
}
class HLPairParameterConversion(
private val mappingConversionFirst: HLParameterConversion,
private val mappingConversionSecond: HLParameterConversion,
) : HLParameterConversion() {
override fun convertExpression(expression: String, context: ConversionContext): String = expression
override fun convertType(type: KType): KType {
val first = type.arguments.getOrNull(0)?.type ?: return type
val second = type.arguments.getOrNull(1)?.type ?: return type
return Pair::class.createType(
arguments = listOf(
KTypeProjection(
variance = KVariance.INVARIANT,
type = mappingConversionFirst.convertType(first)
),
KTypeProjection(
variance = KVariance.INVARIANT,
type = mappingConversionSecond.convertType(second)
)
)
)
}
override val importsToAdd
get() = mappingConversionFirst.importsToAdd + mappingConversionSecond.importsToAdd
}
class HLFunctionCallConversion(
private val callTemplate: String,
private val callType: KType,
@@ -10,15 +10,15 @@ import kotlin.reflect.KClass
import kotlin.reflect.KType
internal fun SmartPrinter.printTypeWithShortNames(type: KType) {
print((type.classifier as KClass<*>).simpleName!!)
if (type.arguments.isNotEmpty()) {
print("<")
type.arguments.map {
fun typeConversion(type: KType): String {
val simpleName = (type.classifier as KClass<*>).simpleName!!
return if (type.arguments.isEmpty()) simpleName
else simpleName + type.arguments.joinToString(separator = ", ", prefix = "<", postfix = ">") {
when (val typeArgument = it.type) {
null -> "*"
else -> printTypeWithShortNames(typeArgument)
else -> typeConversion(typeArgument)
}
}
print(">")
}
print(typeConversion(type))
}
@@ -44,6 +44,20 @@ import org.jetbrains.kotlin.psi.KtWhenExpression
*/
internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConverter {
add(FirErrors.UNSUPPORTED) { firDiagnostic ->
UnsupportedImpl(
firDiagnostic.a,
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.UNSUPPORTED_FEATURE) { firDiagnostic ->
UnsupportedFeatureImpl(
firDiagnostic.a,
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.SYNTAX) { firDiagnostic ->
SyntaxImpl(
firDiagnostic as FirPsiDiagnostic<*>,
@@ -830,12 +844,25 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED) { firDiagnostic ->
ExtensionInClassReferenceNotAllowedImpl(
firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration),
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.CALLABLE_REFERENCE_LHS_NOT_A_CLASS) { firDiagnostic ->
CallableReferenceLhsNotAClassImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR) { firDiagnostic ->
CallableReferenceToAnnotationConstructorImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS) { firDiagnostic ->
ClassLiteralLhsNotAClassImpl(
firDiagnostic as FirPsiDiagnostic<*>,
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiTypeElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -49,6 +51,16 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
*/
sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract class Unsupported : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = Unsupported::class
abstract val unsupported: String
}
abstract class UnsupportedFeature : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = UnsupportedFeature::class
abstract val unsupportedFeature: Pair<LanguageFeature, LanguageVersionSettings>
}
abstract class Syntax : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = Syntax::class
}
@@ -589,10 +601,19 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val typeParameter: KtTypeParameterSymbol
}
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
abstract val referencedDeclaration: KtCallableSymbol
}
abstract class CallableReferenceLhsNotAClass : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = CallableReferenceLhsNotAClass::class
}
abstract class CallableReferenceToAnnotationConstructor : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = CallableReferenceToAnnotationConstructor::class
}
abstract class ClassLiteralLhsNotAClass : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ClassLiteralLhsNotAClass::class
}
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.idea.frontend.api.fir.diagnostics
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiTypeElement
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -50,6 +52,22 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
* DO NOT MODIFY IT MANUALLY
*/
internal class UnsupportedImpl(
override val unsupported: String,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.Unsupported(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class UnsupportedFeatureImpl(
override val unsupportedFeature: Pair<LanguageFeature, LanguageVersionSettings>,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.UnsupportedFeature(), KtAbstractFirDiagnostic<PsiElement> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class SyntaxImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
@@ -950,6 +968,14 @@ internal class TypeParameterAsReifiedImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class ExtensionInClassReferenceNotAllowedImpl(
override val referencedDeclaration: KtCallableSymbol,
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.ExtensionInClassReferenceNotAllowed(), KtAbstractFirDiagnostic<KtExpression> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class CallableReferenceLhsNotAClassImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
@@ -957,6 +983,13 @@ internal class CallableReferenceLhsNotAClassImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class CallableReferenceToAnnotationConstructorImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,
) : KtFirDiagnostic.CallableReferenceToAnnotationConstructor(), KtAbstractFirDiagnostic<KtExpression> {
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic)
}
internal class ClassLiteralLhsNotAClassImpl(
firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken,