Naming with "is" supported for synthetic extensions

This commit is contained in:
Valentin Kipyatkov
2015-07-10 18:23:20 +03:00
parent 27b4960001
commit 22e631dda3
7 changed files with 59 additions and 13 deletions
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.utils.addIfNotNull
@@ -53,9 +54,11 @@ interface SyntheticExtensionPropertyDescriptor : PropertyDescriptor {
.firstOrNull { getterOrSetter == it.getMethod || getterOrSetter == it.setMethod }
}
fun propertyNameByGetMethodName(methodName: Name): Name? = propertyNameFromAccessorMethodName(methodName, "get")
fun propertyNameByGetMethodName(methodName: Name): Name?
= propertyNameFromAccessorMethodName(methodName, "get") ?: propertyNameFromAccessorMethodName(methodName, "is")
fun propertyNameBySetMethodName(methodName: Name): Name? = propertyNameFromAccessorMethodName(methodName, "set")
fun propertyNameBySetMethodName(methodName: Name): Name?
= propertyNameFromAccessorMethodName(methodName, "set")
private fun propertyNameFromAccessorMethodName(methodName: Name, prefix: String): Name? {
if (methodName.isSpecial()) return null
@@ -85,19 +88,25 @@ class SyntheticExtensionsScope(storageManager: StorageManager) : JetScope by Jet
if (!firstChar.isJavaIdentifierStart() || firstChar.isUpperCase()) return null
val memberScope = javaClass.getMemberScope(type.getArguments())
val getMethod = memberScope.getFunctions(toGetMethodName(name)).singleOrNull { isGoodGetMethod(it) } ?: return null
val getMethod = possibleGetMethodNames(name)
.asSequence()
.flatMap { memberScope.getFunctions(it).asSequence() }
.singleOrNull { isGoodGetMethod(it) } ?: return null
val propertyType = getMethod.getReturnType() ?: return null
val setMethod = memberScope.getFunctions(toSetMethodName(name)).singleOrNull { isGoodSetMethod(it, propertyType) }
val setMethod = memberScope.getFunctions(possibleSetMethodName(name)).singleOrNull { isGoodSetMethod(it, propertyType) }
return MyPropertyDescriptor(javaClass, getMethod, setMethod, name, propertyType, type)
}
private fun isGoodGetMethod(descriptor: FunctionDescriptor): Boolean {
val returnType = descriptor.getReturnType() ?: return false
if (returnType.isUnit()) return false
if (descriptor.getName().asString().startsWith("is") && !returnType.isBoolean()) return false
return descriptor.getValueParameters().isEmpty()
&& descriptor.getTypeParameters().isEmpty()
&& descriptor.getVisibility() == Visibilities.PUBLIC //TODO: what about protected and package-local?
&& !(descriptor.getReturnType()?.isUnit() ?: true)
}
private fun isGoodSetMethod(descriptor: FunctionDescriptor, propertyType: JetType): Boolean {
@@ -105,7 +114,7 @@ class SyntheticExtensionsScope(storageManager: StorageManager) : JetScope by Jet
return parameter.getType() == propertyType
&& parameter.getVarargElementType() == null
&& descriptor.getTypeParameters().isEmpty()
&& descriptor.getReturnType()?.let { KotlinBuiltIns.isUnit(it) } ?: false
&& descriptor.getReturnType()?.let { it.isUnit() } ?: false
&& descriptor.getVisibility() == Visibilities.PUBLIC
}
@@ -156,14 +165,15 @@ class SyntheticExtensionsScope(storageManager: StorageManager) : JetScope by Jet
return list
}
//TODO: "is"?
//TODO: methods like "getURL"?
//TODO: reuse code with generation?
private fun toGetMethodName(propertyName: Name): Name {
return Name.identifier("get" + propertyName.getIdentifier().capitalize())
private fun possibleGetMethodNames(propertyName: Name): Collection<Name> {
val capitalized = propertyName.getIdentifier().capitalize()
return listOf(Name.identifier("get" + capitalized), Name.identifier("is" + capitalized))
}
private fun toSetMethodName(propertyName: Name): Name {
private fun possibleSetMethodName(propertyName: Name): Name {
return Name.identifier("set" + propertyName.getIdentifier().capitalize())
}
@@ -0,0 +1,13 @@
// FILE: KotlinFile.kt
fun foo(javaClass: JavaClass) {
javaClass.something = !javaClass.something
javaClass.<!UNRESOLVED_REFERENCE!>somethingWrong<!>
}
// FILE: JavaClass.java
public class JavaClass {
public boolean isSomething() { return true; }
public void setSomething(boolean value) { }
public int isSomethingWrong() { return 1; }
}
@@ -0,0 +1,13 @@
package
internal fun foo(/*0*/ javaClass: JavaClass): kotlin.Unit
public open class JavaClass {
public constructor JavaClass()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open fun isSomething(): kotlin.Boolean
public open fun isSomethingWrong(): kotlin.Int
public open fun setSomething(/*0*/ value: kotlin.Boolean): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -14058,6 +14058,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("IsNaming.kt")
public void testIsNaming() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.kt");
doTest(fileName);
}
@TestMetadata("OnlyPublic.kt")
public void testOnlyPublic() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/syntheticExtensions/OnlyPublic.kt");
@@ -49,7 +49,8 @@ fun JetType.supertypes(): Set<JetType> = TypeUtils.getAllSupertypes(this)
fun JetType.isNothing(): Boolean = KotlinBuiltIns.isNothing(this)
fun JetType.isUnit(): Boolean = KotlinBuiltIns.isUnit(this)
fun JetType.isAny(): Boolean = KotlinBuiltIns.isAnyOrNullableAny(this)
fun JetType.isAnyOrNullableAny(): Boolean = KotlinBuiltIns.isAnyOrNullableAny(this)
fun JetType.isBoolean(): Boolean = KotlinBuiltIns.isBoolean(this)
private fun JetType.getContainedTypeParameters(): Collection<TypeParameterDescriptor> {
val declarationDescriptor = getConstructor().getDeclarationDescriptor()
@@ -3,5 +3,8 @@ fun Thread.foo() {
}
// EXIST_JAVA_ONLY: { lookupString: "priority", itemText: "priority", tailText: " for Thread", typeText: "Int" }
// EXIST_JAVA_ONLY: { lookupString: "daemon", itemText: "daemon", tailText: " for Thread", typeText: "Boolean" }
// ABSENT: getPriority
// ABSENT: setPriority
// ABSENT: isDaemon
// ABSENT: setDaemon
@@ -66,7 +66,7 @@ import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.types.typeUtil.isAny
import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
import java.util.*
@@ -316,7 +316,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
CallableKind.FUNCTION ->
returnTypeCandidate?.theType?.isUnit() ?: false
CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR ->
callableInfo.returnTypeInfo == TypeInfo.Empty || returnTypeCandidate?.theType?.isAny() ?: false
callableInfo.returnTypeInfo == TypeInfo.Empty || returnTypeCandidate?.theType?.isAnyOrNullableAny() ?: false
CallableKind.SECONDARY_CONSTRUCTOR -> true
CallableKind.PROPERTY -> containingElement is JetBlockExpression
}