FIR checker: check if callable reference targets are allowed members
This commit is contained in:
committed by
Mikhail Glukhikh
parent
6769ce0e2b
commit
8d8ed4cc18
+2
-2
@@ -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() {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+15
@@ -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>()
|
||||
|
||||
+13
@@ -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
|
||||
}
|
||||
|
||||
+63
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-8
@@ -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
|
||||
}
|
||||
|
||||
+15
@@ -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")
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ object CommonExpressionCheckers : ExpressionCheckers() {
|
||||
)
|
||||
|
||||
override val qualifiedAccessCheckers: Set<FirQualifiedAccessChecker> = setOf(
|
||||
FirCallableReferenceChecker,
|
||||
FirSuperNotAvailableChecker,
|
||||
FirNotASupertypeChecker,
|
||||
FirSuperclassNotAccessibleFromInterfaceChecker,
|
||||
|
||||
Vendored
+2
-2
@@ -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<!>
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -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
|
||||
|
||||
Vendored
+4
-4
@@ -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<!>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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<!>
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||
|
||||
val i: Int = 10
|
||||
get() {
|
||||
<!UNSUPPORTED!>::field<!>
|
||||
return field
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// !DIAGNOSTICS: -UNUSED_EXPRESSION
|
||||
|
||||
val i: Int = 10
|
||||
get() {
|
||||
::<!UNSUPPORTED!>field<!>
|
||||
return field
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package
|
||||
|
||||
public val i: kotlin.Int = 10
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
|
||||
fun a() {
|
||||
val x = 10
|
||||
foo(::x)
|
||||
foo(<!UNSUPPORTED!>::x<!>)
|
||||
}
|
||||
|
||||
fun foo(a: Any) {}
|
||||
fun foo(a: Any) {}
|
||||
|
||||
+1
-1
@@ -5,4 +5,4 @@ fun a() {
|
||||
foo(::<!UNSUPPORTED!>x<!>)
|
||||
}
|
||||
|
||||
fun foo(a: Any) {}
|
||||
fun foo(a: Any) {}
|
||||
|
||||
+4
-4
@@ -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<!>)
|
||||
}
|
||||
|
||||
+2
-2
@@ -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<!>)
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -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
|
||||
|
||||
Generated
+6
@@ -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 {
|
||||
|
||||
+17
-5
@@ -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<*>
|
||||
|
||||
+27
@@ -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,
|
||||
|
||||
+6
-6
@@ -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))
|
||||
}
|
||||
|
||||
+27
@@ -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<*>,
|
||||
|
||||
+21
@@ -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
|
||||
}
|
||||
|
||||
+33
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user