diff --git a/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.kt b/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.kt index 721dae462a6..d91187bb763 100644 --- a/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.kt +++ b/compiler/fir/analysis-tests/testData/resolve/references/referenceToExtension.kt @@ -36,8 +36,8 @@ class NoGenericTest { fun B.extensionFun(): A = A() fun test_1() { - val extensionValRef = B::extensionVal - val extensionFunRef = B::extensionFun + val extensionValRef = B::extensionVal + val extensionFunRef = B::extensionFun } fun test_2() { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index 0fcde25b63e..c9d03eaf8a3 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -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 { diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index 59236dcb20a..4e000c72cd5 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -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 { + parameter("unsupported") + } + val UNSUPPORTED_FEATURE by error { + parameter>("unsupportedFeature") + } + } + val Miscellaneous by object : DiagnosticGroup("Miscellaneous") { val SYNTAX by error() val OTHER_ERROR by error() @@ -264,7 +275,11 @@ object DIAGNOSTICS_LIST : DiagnosticList() { } val REFLECTION by object : DiagnosticGroup("Reflection") { + val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) { + parameter>("referencedDeclaration") + } val CALLABLE_REFERENCE_LHS_NOT_A_CLASS by error() + val CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR by error(PositioningStrategy.REFERENCE_BY_QUALIFIED) val CLASS_LITERAL_LHS_NOT_A_CLASS by error() val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error() diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index d90aff7a443..6dd6f5dc5a7 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -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() + val UNSUPPORTED_FEATURE by error1>() + // Miscellaneous val SYNTAX by error0() val OTHER_ERROR by error0() @@ -201,7 +207,9 @@ object FirErrors { val TYPE_PARAMETER_AS_REIFIED by error1() // Reflection + val EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED by error1>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val CALLABLE_REFERENCE_LHS_NOT_A_CLASS by error0() + val CALLABLE_REFERENCE_TO_ANNOTATION_CONSTRUCTOR by error0(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED) val CLASS_LITERAL_LHS_NOT_A_CLASS by error0() val NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error0() val EXPRESSION_OF_NULLABLE_TYPE_IN_CLASS_LITERAL_LHS by error1() diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt index 3ddcb9659d0..98c9f58f5b5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirDeclarationCheckerUtils.kt @@ -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 + } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt new file mode 100644 index 00000000000..050bb109ab6 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirCallableReferenceChecker.kt @@ -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) + } + } +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt index 8318c2101d6..95cb2acd70a 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/extended/RedundantVisibilityModifierChecker.kt @@ -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 } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index d9367f4d791..c4153f94f78 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -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") diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt index 143921bf468..f225692fe90 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CommonExpressionCheckers.kt @@ -14,6 +14,7 @@ object CommonExpressionCheckers : ExpressionCheckers() { ) override val qualifiedAccessCheckers: Set = setOf( + FirCallableReferenceChecker, FirSuperNotAvailableChecker, FirNotASupertypeChecker, FirSuperclassNotAccessibleFromInterfaceChecker, diff --git a/compiler/testData/diagnostics/tests/callableReference/function/abstractClassConstructors.fir.kt b/compiler/testData/diagnostics/tests/callableReference/function/abstractClassConstructors.fir.kt index c1e3021ba95..c86e4aabb4a 100644 --- a/compiler/testData/diagnostics/tests/callableReference/function/abstractClassConstructors.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/function/abstractClassConstructors.fir.kt @@ -7,6 +7,6 @@ enum class D fun main() { ::A ::B - ::C // KT-3465 + ::C // KT-3465 ::D -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/callableReference/function/annotationClassConstructor.fir.kt b/compiler/testData/diagnostics/tests/callableReference/function/annotationClassConstructor.fir.kt index 1fac9d054b5..e8a359fe020 100644 --- a/compiler/testData/diagnostics/tests/callableReference/function/annotationClassConstructor.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/function/annotationClassConstructor.fir.kt @@ -1,5 +1,5 @@ annotation class Ann(val prop: String) -val annCtorRef = ::Ann +val annCtorRef = ::Ann val annClassRef = Ann::class val annPropRef = Ann::prop diff --git a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.fir.kt b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.fir.kt index 33d81eee6f6..179edb9c2f0 100644 --- a/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/function/extensionInClassDisallowed.fir.kt @@ -4,11 +4,11 @@ class A { fun A.extA(x: String) = x fun main() { - Int::extInt - A::extA + Int::extInt + A::extA - eat(Int::extInt) - eat(A::extA) + eat(Int::extInt) + eat(A::extA) } } diff --git a/compiler/testData/diagnostics/tests/callableReference/memberExtensionsImportedFromObjectsUnsupported.fir.kt b/compiler/testData/diagnostics/tests/callableReference/memberExtensionsImportedFromObjectsUnsupported.fir.kt index 029f5682d26..77eb76a3ed4 100644 --- a/compiler/testData/diagnostics/tests/callableReference/memberExtensionsImportedFromObjectsUnsupported.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/memberExtensionsImportedFromObjectsUnsupported.fir.kt @@ -14,10 +14,10 @@ class A { } fun test() { - String::ext + String::ext Obj::ext - String::ext2 + String::ext2 A.Companion::ext2 A::ext2 diff --git a/compiler/testData/diagnostics/tests/callableReference/property/backingField.fir.kt b/compiler/testData/diagnostics/tests/callableReference/property/backingField.fir.kt new file mode 100644 index 00000000000..fb88458b47b --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/property/backingField.fir.kt @@ -0,0 +1,7 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION + +val i: Int = 10 + get() { + ::field + return field + } diff --git a/compiler/testData/diagnostics/tests/callableReference/property/backingField.kt b/compiler/testData/diagnostics/tests/callableReference/property/backingField.kt new file mode 100644 index 00000000000..c0667c8be31 --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/property/backingField.kt @@ -0,0 +1,7 @@ +// !DIAGNOSTICS: -UNUSED_EXPRESSION + +val i: Int = 10 + get() { + ::field + return field + } diff --git a/compiler/testData/diagnostics/tests/callableReference/property/backingField.txt b/compiler/testData/diagnostics/tests/callableReference/property/backingField.txt new file mode 100644 index 00000000000..822f5b341ac --- /dev/null +++ b/compiler/testData/diagnostics/tests/callableReference/property/backingField.txt @@ -0,0 +1,3 @@ +package + +public val i: kotlin.Int = 10 diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt index fced0fba80e..88f02aa36e6 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.fir.kt @@ -2,7 +2,7 @@ fun a() { val x = 10 - foo(::x) + foo(::x) } -fun foo(a: Any) {} \ No newline at end of file +fun foo(a: Any) {} diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt index 246b540fdbd..33a7235d2e5 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/callableReferenceToLocalVariable.kt @@ -5,4 +5,4 @@ fun a() { foo(::x) } -fun foo(a: Any) {} \ No newline at end of file +fun foo(a: Any) {} diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt index c170c78a97a..48b11167566 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariable.fir.kt @@ -3,13 +3,13 @@ fun eat(value: Any) {} fun test(param: String) { - val a = ::param + val a = ::param val local = "local" - val b = ::local + val b = ::local val lambda = { -> } - val g = ::lambda + val g = ::lambda - eat(::param) + eat(::param) } diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt index 507c335add4..d6ed6422aac 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/localVariableWithSubstitution.fir.kt @@ -9,6 +9,6 @@ class Foo { fun main() { val f = Foo() val a: Int - get() = f.getValue(null, ::a) // no exception after fix + get() = f.getValue(null, ::a) // no exception after fix print(a) -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt b/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt index bb4e8214385..b32d30193bd 100644 --- a/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt +++ b/compiler/testData/diagnostics/tests/callableReference/unsupported/parameterWithSubstitution.fir.kt @@ -9,6 +9,6 @@ class Foo { fun main(x: Int) { val f = Foo() val a: Int - get() = f.getValue(null, ::x) // no exception after fix + get() = f.getValue(null, ::x) // no exception after fix print(a) -} \ No newline at end of file +} diff --git a/compiler/testData/diagnostics/tests/regressions/kt31975.fir.kt b/compiler/testData/diagnostics/tests/regressions/kt31975.fir.kt index a003b8815d1..7d2a4307a45 100644 --- a/compiler/testData/diagnostics/tests/regressions/kt31975.fir.kt +++ b/compiler/testData/diagnostics/tests/regressions/kt31975.fir.kt @@ -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(TypeConstructor::foo) // EXTENSION_IN_CLASS_REFERENCE_IS_NOT_ALLOWED, fine val memoizedFunctionTypes = createMemoizedFunction { it.foo() } // works fine private fun TypeConstructor.foo(): Boolean = true diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 695f882c561..3e82a4d5cc9 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -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 { diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt index 32ebbd8b9f3..8857d981888 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLDiagnosticConverter.kt @@ -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<*> diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLParameterConversion.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLParameterConversion.kt index be04c99aa68..74f8beb8184 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLParameterConversion.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/HLParameterConversion.kt @@ -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, diff --git a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/generatorUtils.kt b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/generatorUtils.kt index 0c3bd1f74a0..aff7a760e49 100644 --- a/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/generatorUtils.kt +++ b/idea/idea-frontend-fir/idea-frontend-fir-generator/src/org/jetbrains/kotlin/idea/frontend/api/fir/generator/generatorUtils.kt @@ -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)) } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 2499245ccd2..3d655cc2751 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -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<*>, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index f862adf2b71..1f5ce915ad8 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -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 : KtDiagnosticWithPsi { + abstract class Unsupported : KtFirDiagnostic() { + override val diagnosticClass get() = Unsupported::class + abstract val unsupported: String + } + + abstract class UnsupportedFeature : KtFirDiagnostic() { + override val diagnosticClass get() = UnsupportedFeature::class + abstract val unsupportedFeature: Pair + } + abstract class Syntax : KtFirDiagnostic() { override val diagnosticClass get() = Syntax::class } @@ -589,10 +601,19 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val typeParameter: KtTypeParameterSymbol } + abstract class ExtensionInClassReferenceNotAllowed : KtFirDiagnostic() { + override val diagnosticClass get() = ExtensionInClassReferenceNotAllowed::class + abstract val referencedDeclaration: KtCallableSymbol + } + abstract class CallableReferenceLhsNotAClass : KtFirDiagnostic() { override val diagnosticClass get() = CallableReferenceLhsNotAClass::class } + abstract class CallableReferenceToAnnotationConstructor : KtFirDiagnostic() { + override val diagnosticClass get() = CallableReferenceToAnnotationConstructor::class + } + abstract class ClassLiteralLhsNotAClass : KtFirDiagnostic() { override val diagnosticClass get() = ClassLiteralLhsNotAClass::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 77ced007e2b..cc38e2508c5 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -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 { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + +internal class UnsupportedFeatureImpl( + override val unsupportedFeature: Pair, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.UnsupportedFeature(), KtAbstractFirDiagnostic { + 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 { + 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 { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class ClassLiteralLhsNotAClassImpl( firDiagnostic: FirPsiDiagnostic<*>, override val token: ValidityToken,