From fe8e3e99abaff3d84ed0adf4e6fd003a6c027e6f Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 24 Sep 2020 12:01:04 +0300 Subject: [PATCH] [FIR Java] Hide function in scope in case it's an accessor by fact #KT-42116 Fixed --- .../problems/inaccessibleJavaGetter.kt | 26 +++++++++++++++ .../problems/inaccessibleJavaGetter.txt | 32 ++++++++++++++++++ .../fir/FirDiagnosticsTestGenerated.java | 5 +++ ...DiagnosticsWithLightTreeTestGenerated.java | 5 +++ .../kotlin/fir/backend/ConversionUtils.kt | 2 +- .../scopes/JavaClassUseSiteMemberScope.kt | 33 +++++++++++++++++-- .../resolve/FirJavaSyntheticNamesProvider.kt | 19 +++++++++-- .../calls/FirSyntheticNamesProvider.kt | 1 + .../implementCollectionThroughKotlin.kt | 1 - .../irrelevantSizeOverrideInJava.kt | 1 - 10 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt create mode 100644 compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.txt diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt b/compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt new file mode 100644 index 00000000000..1d4e92d91fb --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt @@ -0,0 +1,26 @@ +// FILE: PropertyDescriptor.java + +public interface PropertyDescriptor extends DescriptorWithAccessor { + String getSetter(); + boolean isDelegated(); +} + +// FILE: test.kt + +interface DescriptorWithAccessor { + val setter: String + val isDelegated: Boolean +} + +class WrappedPropertyDescriptor : PropertyDescriptor { + override val setter: String get() = "K" + override val isDelegated: Boolean get() = false +} + +fun test() { + val descriptor = WrappedPropertyDescriptor() + val res1 = descriptor.setter + val res2 = descriptor.getSetter() // Should be error + val res3 = descriptor.isDelegated + val res4 = descriptor.isDelegated() // Should be error +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.txt b/compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.txt new file mode 100644 index 00000000000..8af78cf71f5 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.txt @@ -0,0 +1,32 @@ +FILE: test.kt + public abstract interface DescriptorWithAccessor : R|kotlin/Any| { + public abstract val setter: R|kotlin/String| + public get(): R|kotlin/String| + + public abstract val isDelegated: R|kotlin/Boolean| + public get(): R|kotlin/Boolean| + + } + public final class WrappedPropertyDescriptor : R|PropertyDescriptor| { + public constructor(): R|WrappedPropertyDescriptor| { + super() + } + + public final override val setter: R|kotlin/String| + public get(): R|kotlin/String| { + ^ String(K) + } + + public final override val isDelegated: R|kotlin/Boolean| + public get(): R|kotlin/Boolean| { + ^ Boolean(false) + } + + } + public final fun test(): R|kotlin/Unit| { + lval descriptor: R|WrappedPropertyDescriptor| = R|/WrappedPropertyDescriptor.WrappedPropertyDescriptor|() + lval res1: R|kotlin/String| = R|/descriptor|.R|/WrappedPropertyDescriptor.setter| + lval res2: R|ERROR CLASS: Unresolved name: getSetter| = R|/descriptor|.#() + lval res3: R|kotlin/Boolean| = R|/descriptor|.R|/WrappedPropertyDescriptor.isDelegated| + lval res4: R|ERROR CLASS: Unresolved name: isDelegated| = R|/descriptor|.#() + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 4bc91f0ef2a..30804f87cc2 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -1989,6 +1989,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/problems/flexibleTypeVarAgainstNull.kt"); } + @TestMetadata("inaccessibleJavaGetter.kt") + public void testInaccessibleJavaGetter() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt"); + } + @TestMetadata("innerClassHierarchy.kt") public void testInnerClassHierarchy() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/problems/innerClassHierarchy.kt"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 742bb71f633..67e609ec594 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -1989,6 +1989,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/problems/flexibleTypeVarAgainstNull.kt"); } + @TestMetadata("inaccessibleJavaGetter.kt") + public void testInaccessibleJavaGetter() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/problems/inaccessibleJavaGetter.kt"); + } + @TestMetadata("innerClassHierarchy.kt") public void testInnerClassHierarchy() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/problems/innerClassHierarchy.kt"); diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt index fd4667e5950..a4754e51728 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/ConversionUtils.kt @@ -318,7 +318,7 @@ internal fun FirProperty.generateOverriddenAccessorSymbols( scope.processPropertiesByName(name) {} val overriddenSet = mutableSetOf() scope.processDirectlyOverriddenProperties(symbol) { - if (it.fir.visibility == Visibilities.Private) { + if (it is FirAccessorSymbol || it.fir.visibility == Visibilities.Private) { return@processDirectlyOverriddenProperties ProcessorAction.NEXT } val overriddenProperty = declarationStorage.getIrPropertySymbol(it.unwrapSubstitutionOverrides()) as IrPropertySymbol diff --git a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt index b96144c67fc..957dd1f4558 100644 --- a/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt +++ b/compiler/fir/java/src/org/jetbrains/kotlin/fir/java/scopes/JavaClassUseSiteMemberScope.kt @@ -9,11 +9,13 @@ import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.builder.FirSimpleFunctionBuilder import org.jetbrains.kotlin.fir.declarations.builder.FirValueParameterBuilder +import org.jetbrains.kotlin.fir.declarations.synthetic.FirSyntheticProperty import org.jetbrains.kotlin.fir.declarations.synthetic.buildSyntheticProperty import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.java.JavaTypeParameterStack import org.jetbrains.kotlin.fir.java.declarations.* import org.jetbrains.kotlin.fir.resolve.FirJavaSyntheticNamesProvider +import org.jetbrains.kotlin.fir.resolve.calls.syntheticNamesProvider import org.jetbrains.kotlin.fir.scopes.FirScope import org.jetbrains.kotlin.fir.scopes.FirTypeScope import org.jetbrains.kotlin.fir.scopes.getContainingCallableNamesIfPresent @@ -58,7 +60,6 @@ class JavaClassUseSiteMemberScope( private fun generateAccessorSymbol( functionSymbol: FirFunctionSymbol<*>, syntheticPropertyName: Name, - overrideCandidates: MutableSet>, isGetter: Boolean ): FirAccessorSymbol? { if (functionSymbol !is FirNamedFunctionSymbol) { @@ -76,7 +77,6 @@ class JavaClassUseSiteMemberScope( return null } } - overrideCandidates += functionSymbol return buildSyntheticProperty { session = this@JavaClassUseSiteMemberScope.session name = syntheticPropertyName @@ -105,7 +105,7 @@ class JavaClassUseSiteMemberScope( for (getterName in getterNames) { declaredMemberScope.processFunctionsByName(getterName) { functionSymbol -> val accessorSymbol = generateAccessorSymbol( - functionSymbol, propertyName, overrideCandidates, isGetter = true + functionSymbol, propertyName, isGetter = true ) if (accessorSymbol != null) { // NB: accessor should not be processed directly unless we find matching property symbol in supertype @@ -162,6 +162,33 @@ class JavaClassUseSiteMemberScope( return processAccessorFunctionsAndPropertiesByName(name, getterNames, processor) } + override fun processFunctionsByName(name: Name, processor: (FirFunctionSymbol<*>) -> Unit) { + if (symbol.fir !is FirJavaClass) { + return super.processFunctionsByName(name, processor) + } + val potentialPropertyName = session.syntheticNamesProvider.propertyNameByAccessorName(name) + ?: return super.processFunctionsByName(name, processor) + val accessors = mutableListOf() + processAccessorFunctionsAndPropertiesByName(potentialPropertyName, listOf(name)) { + if (it is FirAccessorSymbol) { + accessors += it + } + } + if (accessors.isEmpty()) { + return super.processFunctionsByName(name, processor) + } + super.processFunctionsByName(name) { functionSymbol -> + if (accessors.none { accessorSymbol -> + val syntheticProperty = accessorSymbol.fir as FirSyntheticProperty + syntheticProperty.getter.delegate === functionSymbol.fir || + syntheticProperty.setter?.delegate === functionSymbol.fir + } + ) { + processor(functionSymbol) + } + } + } + companion object { private const val SETTER_PREFIX = "set" } diff --git a/compiler/fir/jvm/src/org/jetbrains/kotlin/fir/resolve/FirJavaSyntheticNamesProvider.kt b/compiler/fir/jvm/src/org/jetbrains/kotlin/fir/resolve/FirJavaSyntheticNamesProvider.kt index ad90cc0e622..2c91d712372 100644 --- a/compiler/fir/jvm/src/org/jetbrains/kotlin/fir/resolve/FirJavaSyntheticNamesProvider.kt +++ b/compiler/fir/jvm/src/org/jetbrains/kotlin/fir/resolve/FirJavaSyntheticNamesProvider.kt @@ -15,6 +15,7 @@ import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeFirstWord @NoMutableState object FirJavaSyntheticNamesProvider : FirSyntheticNamesProvider() { private const val GETTER_PREFIX = "get" + private const val SETTER_PREFIX = "set" private const val IS_PREFIX = "is" override fun possibleGetterNamesByPropertyName(name: Name): List { @@ -34,11 +35,23 @@ object FirJavaSyntheticNamesProvider : FirSyntheticNamesProvider() { override fun setterNameByGetterName(name: Name): Name { val identifier = name.identifier val prefix = when { - identifier.startsWith("get") -> "get" - identifier.startsWith("is") -> "is" + identifier.startsWith(GETTER_PREFIX) -> GETTER_PREFIX + identifier.startsWith(IS_PREFIX) -> IS_PREFIX else -> throw IllegalArgumentException() } - return Name.identifier("set" + identifier.removePrefix(prefix)) + return Name.identifier(SETTER_PREFIX + identifier.removePrefix(prefix)) + } + + override fun propertyNameByAccessorName(name: Name): Name? { + if (name.isSpecial) return null + val identifier = name.identifier + val prefix = when { + identifier.startsWith(GETTER_PREFIX) -> GETTER_PREFIX + identifier.startsWith(IS_PREFIX) -> "" + identifier.startsWith(SETTER_PREFIX) -> SETTER_PREFIX + else -> return null + } + return Name.identifier(identifier.removePrefix(prefix).decapitalize()) } } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirSyntheticNamesProvider.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirSyntheticNamesProvider.kt index 1b1dda43683..acd14225c52 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirSyntheticNamesProvider.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/calls/FirSyntheticNamesProvider.kt @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.name.Name abstract class FirSyntheticNamesProvider : FirSessionComponent { abstract fun possibleGetterNamesByPropertyName(name: Name): List abstract fun setterNameByGetterName(name: Name): Name + abstract fun propertyNameByAccessorName(name: Name): Name? } val FirSession.syntheticNamesProvider: FirSyntheticNamesProvider by FirSession.sessionComponentAccessor() diff --git a/compiler/testData/codegen/box/collections/implementCollectionThroughKotlin.kt b/compiler/testData/codegen/box/collections/implementCollectionThroughKotlin.kt index ce86c6a851c..036eb71f713 100644 --- a/compiler/testData/codegen/box/collections/implementCollectionThroughKotlin.kt +++ b/compiler/testData/codegen/box/collections/implementCollectionThroughKotlin.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // FILE: J.java diff --git a/compiler/testData/codegen/box/collections/irrelevantSizeOverrideInJava.kt b/compiler/testData/codegen/box/collections/irrelevantSizeOverrideInJava.kt index 661d21ad8b7..b3d1023d3c7 100644 --- a/compiler/testData/codegen/box/collections/irrelevantSizeOverrideInJava.kt +++ b/compiler/testData/codegen/box/collections/irrelevantSizeOverrideInJava.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // FILE: J.java