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 B.extensionFun(): A = A()
fun test_1() { fun test_1() {
val extensionValRef = B::extensionVal val extensionValRef = <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>B::extensionVal<!>
val extensionFunRef = B::extensionFun val extensionFunRef = <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>B::extensionFun<!>
} }
fun test_2() { 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); 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 @Test
@TestMetadata("classFromClass.kt") @TestMetadata("classFromClass.kt")
public void testClassFromClass() throws Exception { 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.PsiElement
import com.intellij.psi.PsiTypeElement 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.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -33,6 +35,15 @@ import kotlin.properties.ReadOnlyProperty
@Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused") @Suppress("UNUSED_VARIABLE", "LocalVariableName", "ClassName", "unused")
@OptIn(PrivateForInline::class) @OptIn(PrivateForInline::class)
object DIAGNOSTICS_LIST : DiagnosticList() { 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 Miscellaneous by object : DiagnosticGroup("Miscellaneous") {
val SYNTAX by error<FirSourceElement, PsiElement>() val SYNTAX by error<FirSourceElement, PsiElement>()
val OTHER_ERROR 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 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_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 CLASS_LITERAL_LHS_NOT_A_CLASS by error<FirSourceElement, KtExpression>()
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS 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.PsiElement
import com.intellij.psi.PsiTypeElement 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.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -54,6 +56,10 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
*/ */
object FirErrors { object FirErrors {
// Meta-errors
val UNSUPPORTED by error1<FirSourceElement, PsiElement, String>()
val UNSUPPORTED_FEATURE by error1<FirSourceElement, PsiElement, Pair<LanguageFeature, LanguageVersionSettings>>()
// Miscellaneous // Miscellaneous
val SYNTAX by error0<FirSourceElement, PsiElement>() val SYNTAX by error0<FirSourceElement, PsiElement>()
val OTHER_ERROR 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>() val TYPE_PARAMETER_AS_REIFIED by error1<FirSourceElement, PsiElement, FirTypeParameterSymbol>()
// Reflection // 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_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 CLASS_LITERAL_LHS_NOT_A_CLASS by error0<FirSourceElement, KtExpression>()
val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS 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>() 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) 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.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext 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.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.isLocalMember
import org.jetbrains.kotlin.fir.analysis.diagnostics.* import org.jetbrains.kotlin.fir.analysis.diagnostics.*
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
@@ -110,14 +111,6 @@ object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
return null 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 private val CheckerContext.containingPropertyVisibility
get() = (this.containingDeclarations.last() as? FirProperty)?.visibility 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.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap 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.AMBIGUOUS_CALLS
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.DECLARATION_NAME
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers.FIR 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.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.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_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_CHANGE_ACCESS_PRIVILEGE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.CANNOT_WEAKEN_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 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_TYPEALIAS_EXPANDED_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.EXPOSED_TYPE_PARAMETER_BOUND 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.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_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.EXTENSION_PROPERTY_WITH_BACKING_FIELD
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.FORBIDDEN_VARARG_PARAMETER_TYPE 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_IMPLICIT_INVOKE_CALL
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UNSAFE_INFIX_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.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.UNUSED_VARIABLE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.UPPER_BOUND_VIOLATED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.USELESS_VARARG_ON_PARAMETER
@@ -245,6 +250,10 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension {
// + - Better message required // + - Better message required
// # - The new diagnostic differs from the old FE's one // # - The new diagnostic differs from the old FE's one
val MAP = FirDiagnosticFactoryToRendererMap("FIR").also { map -> 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 // Miscellaneous
map.put(SYNTAX, "Syntax error") map.put(SYNTAX, "Syntax error")
map.put(OTHER_ERROR, "Unknown (other) 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) map.put(TYPE_PARAMETER_AS_REIFIED, "Cannot use ''{0}'' as reified type parameter. Use a class instead", SYMBOL)
// Reflection // 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_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(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") 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( override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = setOf(
FirCallableReferenceChecker,
FirSuperNotAvailableChecker, FirSuperNotAvailableChecker,
FirNotASupertypeChecker, FirNotASupertypeChecker,
FirSuperclassNotAccessibleFromInterfaceChecker, FirSuperclassNotAccessibleFromInterfaceChecker,
@@ -7,6 +7,6 @@ enum class D
fun main() { fun main() {
<!UNRESOLVED_REFERENCE!>::A<!> <!UNRESOLVED_REFERENCE!>::A<!>
::B ::B
::C // KT-3465 <!CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR!>::C<!> // KT-3465
<!UNRESOLVED_REFERENCE!>::D<!> <!UNRESOLVED_REFERENCE!>::D<!>
} }
@@ -1,5 +1,5 @@
annotation class Ann(val prop: String) annotation class Ann(val prop: String)
val annCtorRef = ::Ann val annCtorRef = <!CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR!>::Ann<!>
val annClassRef = Ann::class val annClassRef = Ann::class
val annPropRef = Ann::prop val annPropRef = Ann::prop
@@ -4,11 +4,11 @@ class A {
fun A.extA(x: String) = x fun A.extA(x: String) = x
fun main() { fun main() {
Int::extInt <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>Int::extInt<!>
A::extA <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>A::extA<!>
eat(Int::extInt) eat(<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>Int::extInt<!>)
eat(A::extA) eat(<!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>A::extA<!>)
} }
} }
@@ -14,10 +14,10 @@ class A {
} }
fun test() { fun test() {
String::ext <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>String::ext<!>
<!UNRESOLVED_REFERENCE!>Obj::ext<!> <!UNRESOLVED_REFERENCE!>Obj::ext<!>
String::ext2 <!EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED!>String::ext2<!>
<!UNRESOLVED_REFERENCE!>A.Companion::ext2<!> <!UNRESOLVED_REFERENCE!>A.Companion::ext2<!>
<!UNRESOLVED_REFERENCE!>A::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() { fun a() {
val x = 10 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<!>) foo(::<!UNSUPPORTED!>x<!>)
} }
fun foo(a: Any) {} fun foo(a: Any) {}
@@ -3,13 +3,13 @@
fun eat(value: Any) {} fun eat(value: Any) {}
fun test(param: String) { fun test(param: String) {
val a = ::param val a = <!UNSUPPORTED!>::param<!>
val local = "local" val local = "local"
val b = ::local val b = <!UNSUPPORTED!>::local<!>
val lambda = { -> } val lambda = { -> }
val g = ::lambda val g = <!UNSUPPORTED!>::lambda<!>
eat(::param) eat(<!UNSUPPORTED!>::param<!>)
} }
@@ -9,6 +9,6 @@ class Foo {
fun main() { fun main() {
val f = Foo() val f = Foo()
val a: Int 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<!>) <!UNRESOLVED_REFERENCE!>print<!>(<!UNINITIALIZED_VARIABLE!>a<!>)
} }
@@ -9,6 +9,6 @@ class Foo {
fun main(x: Int) { fun main(x: Int) {
val f = Foo() val f = Foo()
val a: Int 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<!>) <!UNRESOLVED_REFERENCE!>print<!>(<!UNINITIALIZED_VARIABLE!>a<!>)
} }
@@ -11,7 +11,7 @@ interface TypeConstructor
class Refiner { class Refiner {
val memoizedFunctionLambda = createMemoizedFunction { it.foo() } // error type infered, no diagnostic, BAD, backend fails 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 val memoizedFunctionTypes = createMemoizedFunction<TypeConstructor, Boolean> { it.foo() } // works fine
private fun TypeConstructor.foo(): Boolean = true 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); 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 @Test
@TestMetadata("classFromClass.kt") @TestMetadata("classFromClass.kt")
public void testClassFromClass() throws Exception { public void testClassFromClass() throws Exception {
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.idea.frontend.api.fir.generator package org.jetbrains.kotlin.idea.frontend.api.fir.generator
import com.intellij.psi.PsiElement 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.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -47,7 +49,7 @@ object HLDiagnosticConverter {
) )
private fun convertParameter(index: Int, diagnosticParameter: DiagnosticParameter): HLDiagnosticParameter { 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) val convertedType = conversion.convertType(diagnosticParameter.type)
return HLDiagnosticParameter( return HLDiagnosticParameter(
name = diagnosticParameter.name, name = diagnosticParameter.name,
@@ -73,12 +75,12 @@ object HLDiagnosticConverter {
private object FirToKtConversionCreator { private object FirToKtConversionCreator {
fun creatConversion(type: KType): HLParameterConversion { fun createConversion(type: KType): HLParameterConversion {
val kClass = type.classifier as KClass<*> val kClass = type.classifier as KClass<*>
return tryMapAllowedType(kClass) return tryMapAllowedType(kClass)
?: tryMapPsiElementType(type, kClass) ?: tryMapPsiElementType(type, kClass)
?: tryMapFirTypeToKtType(kClass) ?: tryMapFirTypeToKtType(kClass)
?: tryMapCollectionType(type, kClass) ?: tryMapPlatformType(type, kClass)
?: error("Unsupported type $type, consider add corresponding mapping") ?: error("Unsupported type $type, consider add corresponding mapping")
} }
@@ -91,12 +93,20 @@ private object FirToKtConversionCreator {
return null return null
} }
private fun tryMapCollectionType(type: KType, kClass: KClass<*>): HLParameterConversion? { private fun tryMapPlatformType(type: KType, kClass: KClass<*>): HLParameterConversion? {
if (kClass.isSubclassOf(Collection::class)) { if (kClass.isSubclassOf(Collection::class)) {
val elementType = type.arguments.single().type ?: return HLIdParameterConversion val elementType = type.arguments.single().type ?: return HLIdParameterConversion
return HLMapParameterConversion( return HLMapParameterConversion(
parameterName = elementType.kClass.simpleName!!.decapitalize(), 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 return null
@@ -175,6 +185,8 @@ private object FirToKtConversionCreator {
Visibility::class, Visibility::class,
WhenMissingCase::class, WhenMissingCase::class,
ForbiddenNamedArgumentsTarget::class, ForbiddenNamedArgumentsTarget::class,
LanguageFeature::class,
LanguageVersionSettings::class,
) )
private val KType.kClass: KClass<*> private val KType.kClass: KClass<*>
@@ -47,6 +47,33 @@ class HLMapParameterConversion(
override val importsToAdd get() = mappingConversion.importsToAdd 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( class HLFunctionCallConversion(
private val callTemplate: String, private val callTemplate: String,
private val callType: KType, private val callType: KType,
@@ -10,15 +10,15 @@ import kotlin.reflect.KClass
import kotlin.reflect.KType import kotlin.reflect.KType
internal fun SmartPrinter.printTypeWithShortNames(type: KType) { internal fun SmartPrinter.printTypeWithShortNames(type: KType) {
print((type.classifier as KClass<*>).simpleName!!) fun typeConversion(type: KType): String {
if (type.arguments.isNotEmpty()) { val simpleName = (type.classifier as KClass<*>).simpleName!!
print("<") return if (type.arguments.isEmpty()) simpleName
type.arguments.map { else simpleName + type.arguments.joinToString(separator = ", ", prefix = "<", postfix = ">") {
when (val typeArgument = it.type) { when (val typeArgument = it.type) {
null -> "*" 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 { 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 -> add(FirErrors.SYNTAX) { firDiagnostic ->
SyntaxImpl( SyntaxImpl(
firDiagnostic as FirPsiDiagnostic<*>, firDiagnostic as FirPsiDiagnostic<*>,
@@ -830,12 +844,25 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token, 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 -> add(FirErrors.CALLABLE_REFERENCE_LHS_NOT_A_CLASS) { firDiagnostic ->
CallableReferenceLhsNotAClassImpl( CallableReferenceLhsNotAClassImpl(
firDiagnostic as FirPsiDiagnostic<*>, firDiagnostic as FirPsiDiagnostic<*>,
token, token,
) )
} }
add(FirErrors.CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR) { firDiagnostic ->
CallableReferenceToAnnotationConstructorImpl(
firDiagnostic as FirPsiDiagnostic<*>,
token,
)
}
add(FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS) { firDiagnostic -> add(FirErrors.CLASS_LITERAL_LHS_NOT_A_CLASS) { firDiagnostic ->
ClassLiteralLhsNotAClassImpl( ClassLiteralLhsNotAClassImpl(
firDiagnostic as FirPsiDiagnostic<*>, 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.PsiElement
import com.intellij.psi.PsiTypeElement 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.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -49,6 +51,16 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
*/ */
sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> { 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>() { abstract class Syntax : KtFirDiagnostic<PsiElement>() {
override val diagnosticClass get() = Syntax::class override val diagnosticClass get() = Syntax::class
} }
@@ -589,10 +601,19 @@ sealed class KtFirDiagnostic<PSI: PsiElement> : KtDiagnosticWithPsi<PSI> {
abstract val typeParameter: KtTypeParameterSymbol abstract val typeParameter: KtTypeParameterSymbol
} }
abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class
abstract val referencedDeclaration: KtCallableSymbol
}
abstract class CallableReferenceLhsNotAClass : KtFirDiagnostic<KtExpression>() { abstract class CallableReferenceLhsNotAClass : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = CallableReferenceLhsNotAClass::class override val diagnosticClass get() = CallableReferenceLhsNotAClass::class
} }
abstract class CallableReferenceToAnnotationConstructor : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = CallableReferenceToAnnotationConstructor::class
}
abstract class ClassLiteralLhsNotAClass : KtFirDiagnostic<KtExpression>() { abstract class ClassLiteralLhsNotAClass : KtFirDiagnostic<KtExpression>() {
override val diagnosticClass get() = ClassLiteralLhsNotAClass::class 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.PsiElement
import com.intellij.psi.PsiTypeElement 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.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.diagnostics.WhenMissingCase import org.jetbrains.kotlin.diagnostics.WhenMissingCase
@@ -50,6 +52,22 @@ import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
* DO NOT MODIFY IT MANUALLY * 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( internal class SyntaxImpl(
firDiagnostic: FirPsiDiagnostic<*>, firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken, override val token: ValidityToken,
@@ -950,6 +968,14 @@ internal class TypeParameterAsReifiedImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) 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( internal class CallableReferenceLhsNotAClassImpl(
firDiagnostic: FirPsiDiagnostic<*>, firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken, override val token: ValidityToken,
@@ -957,6 +983,13 @@ internal class CallableReferenceLhsNotAClassImpl(
override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) 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( internal class ClassLiteralLhsNotAClassImpl(
firDiagnostic: FirPsiDiagnostic<*>, firDiagnostic: FirPsiDiagnostic<*>,
override val token: ValidityToken, override val token: ValidityToken,