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,
|
||||
|
||||
Reference in New Issue
Block a user