FIR: Fix another inconsistency with FE1.0 in synthetic properties resolution
This commit is contained in:
+6
@@ -29874,6 +29874,12 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nonAsciiSecondChar.kt")
|
||||||
|
public void testNonAsciiSecondChar() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/nonAsciiSecondChar.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("OnlyAscii.kt")
|
@TestMetadata("OnlyAscii.kt")
|
||||||
public void testOnlyAscii() throws Exception {
|
public void testOnlyAscii() throws Exception {
|
||||||
|
|||||||
+6
@@ -29874,6 +29874,12 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nonAsciiSecondChar.kt")
|
||||||
|
public void testNonAsciiSecondChar() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/nonAsciiSecondChar.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("OnlyAscii.kt")
|
@TestMetadata("OnlyAscii.kt")
|
||||||
public void testOnlyAscii() throws Exception {
|
public void testOnlyAscii() throws Exception {
|
||||||
|
|||||||
+6
@@ -29874,6 +29874,12 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nonAsciiSecondChar.kt")
|
||||||
|
public void testNonAsciiSecondChar() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/nonAsciiSecondChar.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("OnlyAscii.kt")
|
@TestMetadata("OnlyAscii.kt")
|
||||||
public void testOnlyAscii() throws Exception {
|
public void testOnlyAscii() throws Exception {
|
||||||
|
|||||||
+6
-90
@@ -7,98 +7,14 @@ package org.jetbrains.kotlin.fir.resolve
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.NoMutableState
|
import org.jetbrains.kotlin.fir.NoMutableState
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticNamesProvider
|
import org.jetbrains.kotlin.fir.resolve.calls.FirSyntheticNamesProvider
|
||||||
|
import org.jetbrains.kotlin.load.java.getPropertyNamesCandidatesByAccessorName
|
||||||
|
import org.jetbrains.kotlin.load.java.possibleGetMethodNames
|
||||||
|
import org.jetbrains.kotlin.load.java.setMethodName
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.capitalizeAsciiOnly
|
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeAsciiOnly
|
|
||||||
import org.jetbrains.kotlin.util.capitalizeDecapitalize.toUpperCaseAsciiOnly
|
|
||||||
|
|
||||||
@NoMutableState
|
@NoMutableState
|
||||||
object FirJavaSyntheticNamesProvider : FirSyntheticNamesProvider() {
|
object FirJavaSyntheticNamesProvider : FirSyntheticNamesProvider() {
|
||||||
private const val GETTER_PREFIX = "get"
|
override fun possibleGetterNamesByPropertyName(name: Name): List<Name> = possibleGetMethodNames(name)
|
||||||
private const val SETTER_PREFIX = "set"
|
override fun setterNameByGetterName(name: Name): Name = setMethodName(getMethodName = name)
|
||||||
private const val IS_PREFIX = "is"
|
override fun possiblePropertyNamesByAccessorName(name: Name): List<Name> = getPropertyNamesCandidatesByAccessorName(name)
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun Char.isAsciiUpperCase() = this in 'A'..'Z'
|
|
||||||
|
|
||||||
@Suppress("NOTHING_TO_INLINE")
|
|
||||||
private inline fun Char.isAsciiLowerCase() = this in 'a'..'z'
|
|
||||||
|
|
||||||
override fun possibleGetterNamesByPropertyName(name: Name): List<Name> {
|
|
||||||
if (name.isSpecial) return emptyList()
|
|
||||||
val identifier = name.identifier
|
|
||||||
if (identifier.isEmpty()) return emptyList()
|
|
||||||
val firstChar = identifier[0]
|
|
||||||
if (!firstChar.isJavaIdentifierStart() || firstChar in 'A'..'Z') return emptyList()
|
|
||||||
val result = ArrayList<Name>(3)
|
|
||||||
val standardName = Name.identifier(GETTER_PREFIX + identifier.capitalizeAsciiOnly())
|
|
||||||
val length = identifier.length
|
|
||||||
if (length == 1) {
|
|
||||||
if (identifier[0].isAsciiLowerCase()) {
|
|
||||||
// 'x' --> 'getX' but not 'X' --> 'getX'
|
|
||||||
result += standardName
|
|
||||||
}
|
|
||||||
} else if (identifier[1].isAsciiLowerCase()) {
|
|
||||||
if (identifier[0].isAsciiLowerCase()) {
|
|
||||||
// 'something' --> 'getSomething' classic case
|
|
||||||
result += standardName
|
|
||||||
}
|
|
||||||
var secondWordStart = 2
|
|
||||||
while (secondWordStart < length && identifier[secondWordStart].isAsciiLowerCase()) {
|
|
||||||
secondWordStart++
|
|
||||||
}
|
|
||||||
val capitalizedFirstWordName = Name.identifier(
|
|
||||||
GETTER_PREFIX + identifier.substring(0, secondWordStart).toUpperCaseAsciiOnly() + identifier.substring(secondWordStart)
|
|
||||||
)
|
|
||||||
if (secondWordStart >= length || identifier[secondWordStart].isAsciiUpperCase()) {
|
|
||||||
// 'xyz' --> 'getXYZ' or 'xyzOfSomething' --> 'getXYZOfSomething'
|
|
||||||
result += capitalizedFirstWordName
|
|
||||||
}
|
|
||||||
} else if (length < 3 || !identifier[2].isAsciiUpperCase()) {
|
|
||||||
// 'xOfSomething' --> 'getXOfSomething' but not 'xYZ' --> 'getXYZ'
|
|
||||||
result += standardName
|
|
||||||
}
|
|
||||||
if (length > IS_PREFIX.length && identifier.startsWith(IS_PREFIX) && !identifier[IS_PREFIX.length].isAsciiLowerCase()) {
|
|
||||||
// 'isSomething' (but not 'is' or 'issomething')
|
|
||||||
result += name
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setterNameByGetterName(name: Name): Name? {
|
|
||||||
val identifier = name.identifier
|
|
||||||
val prefix = when {
|
|
||||||
identifier.startsWith(GETTER_PREFIX) -> GETTER_PREFIX
|
|
||||||
identifier.startsWith(IS_PREFIX) -> IS_PREFIX
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
return Name.identifier(SETTER_PREFIX + identifier.removePrefix(prefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getterNameBySetterName(name: Name): Name? {
|
|
||||||
val identifier = name.identifier
|
|
||||||
val prefix = when {
|
|
||||||
identifier.startsWith(SETTER_PREFIX) -> SETTER_PREFIX
|
|
||||||
else -> return null
|
|
||||||
}
|
|
||||||
return Name.identifier(GETTER_PREFIX + identifier.removePrefix(prefix))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun possiblePropertyNamesByAccessorName(name: Name): List<Name> {
|
|
||||||
if (name.isSpecial) return emptyList()
|
|
||||||
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 emptyList()
|
|
||||||
}
|
|
||||||
val withoutPrefix = identifier.removePrefix(prefix)
|
|
||||||
val withoutPrefixName = Name.identifier(withoutPrefix.decapitalizeAsciiOnly())
|
|
||||||
return if (prefix == SETTER_PREFIX) {
|
|
||||||
listOf(withoutPrefixName, Name.identifier(IS_PREFIX + withoutPrefix))
|
|
||||||
} else {
|
|
||||||
listOf(withoutPrefixName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -11,8 +11,7 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
|
|
||||||
abstract class FirSyntheticNamesProvider : FirSessionComponent {
|
abstract class FirSyntheticNamesProvider : FirSessionComponent {
|
||||||
abstract fun possibleGetterNamesByPropertyName(name: Name): List<Name>
|
abstract fun possibleGetterNamesByPropertyName(name: Name): List<Name>
|
||||||
abstract fun setterNameByGetterName(name: Name): Name?
|
abstract fun setterNameByGetterName(name: Name): Name
|
||||||
abstract fun getterNameBySetterName(name: Name): Name?
|
|
||||||
abstract fun possiblePropertyNamesByAccessorName(name: Name): List<Name>
|
abstract fun possiblePropertyNamesByAccessorName(name: Name): List<Name>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,33 +85,31 @@ class FirSyntheticPropertiesScope(
|
|||||||
var matchingSetter: FirSimpleFunction? = null
|
var matchingSetter: FirSimpleFunction? = null
|
||||||
if (getterReturnType != null) {
|
if (getterReturnType != null) {
|
||||||
val setterName = syntheticNamesProvider.setterNameByGetterName(getterName)
|
val setterName = syntheticNamesProvider.setterNameByGetterName(getterName)
|
||||||
if (setterName != null) {
|
baseScope.processFunctionsByName(setterName, fun(setterSymbol: FirFunctionSymbol<*>) {
|
||||||
baseScope.processFunctionsByName(setterName, fun(setterSymbol: FirFunctionSymbol<*>) {
|
if (matchingSetter != null) return
|
||||||
if (matchingSetter != null) return
|
val setter = setterSymbol.fir as? FirSimpleFunction ?: return
|
||||||
val setter = setterSymbol.fir as? FirSimpleFunction ?: return
|
val parameter = setter.valueParameters.singleOrNull() ?: return
|
||||||
val parameter = setter.valueParameters.singleOrNull() ?: return
|
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
|
||||||
if (setter.typeParameters.isNotEmpty() || setter.isStatic) return
|
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
|
||||||
val parameterType = (parameter.returnTypeRef as? FirResolvedTypeRef)?.type ?: return
|
// TODO: at this moment it works for cases like
|
||||||
// TODO: at this moment it works for cases like
|
// class Base {
|
||||||
// class Base {
|
// void setSomething(Object value) {}
|
||||||
// void setSomething(Object value) {}
|
// }
|
||||||
// }
|
// class Derived extends Base {
|
||||||
// class Derived extends Base {
|
// String getSomething() { return ""; }
|
||||||
// String getSomething() { return ""; }
|
// }
|
||||||
// }
|
// In FE 1.0, we should have also Object getSomething() in class Base for this to work
|
||||||
// In FE 1.0, we should have also Object getSomething() in class Base for this to work
|
// I think details here are worth designing
|
||||||
// I think details here are worth designing
|
if (!AbstractTypeChecker.isSubtypeOf(
|
||||||
if (!AbstractTypeChecker.isSubtypeOf(
|
session.typeContext,
|
||||||
session.typeContext,
|
getterReturnType.withNullability(NOT_NULL, session.typeContext),
|
||||||
getterReturnType.withNullability(NOT_NULL, session.typeContext),
|
parameterType.withNullability(NOT_NULL, session.typeContext)
|
||||||
parameterType.withNullability(NOT_NULL, session.typeContext)
|
)
|
||||||
)
|
) {
|
||||||
) {
|
return
|
||||||
return
|
}
|
||||||
}
|
matchingSetter = setter
|
||||||
matchingSetter = setter
|
})
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val classLookupTag = getterSymbol.originalOrSelf().dispatchReceiverClassOrNull()
|
val classLookupTag = getterSymbol.originalOrSelf().dispatchReceiverClassOrNull()
|
||||||
|
|||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
|
// SKIP_TXT
|
||||||
|
// FILE: A.java
|
||||||
|
public class A {
|
||||||
|
public String getS4ClassRepresentation() { return ""; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: main.kt
|
||||||
|
fun foo(a: A) {
|
||||||
|
a.s4ClassRepresentation.length
|
||||||
|
}
|
||||||
Generated
+6
@@ -29970,6 +29970,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
|||||||
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/KotlinOverridesJava5.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("nonAsciiSecondChar.kt")
|
||||||
|
public void testNonAsciiSecondChar() throws Exception {
|
||||||
|
runTest("compiler/testData/diagnostics/tests/syntheticExtensions/javaProperties/nonAsciiSecondChar.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("OnlyAscii.kt")
|
@TestMetadata("OnlyAscii.kt")
|
||||||
public void testOnlyAscii() throws Exception {
|
public void testOnlyAscii() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user