From 22e631dda32ff17e1ba47eb0027e7dd9df050926 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 10 Jul 2015 18:23:20 +0300 Subject: [PATCH] Naming with "is" supported for synthetic extensions --- .../synthetic/SyntheticExtensionsScope.kt | 30 ++++++++++++------- .../tests/syntheticExtensions/IsNaming.kt | 13 ++++++++ .../tests/syntheticExtensions/IsNaming.txt | 13 ++++++++ .../checkers/JetDiagnosticsTestGenerated.java | 6 ++++ .../org/jetbrains/kotlin/types/TypeUtils.kt | 3 +- .../common/extensions/SyntheticExtensions2.kt | 3 ++ .../callableBuilder/CallableBuilder.kt | 4 +-- 7 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.kt create mode 100644 compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.txt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt index 88c4f6aaba7..000a35d8337 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/synthetic/SyntheticExtensionsScope.kt @@ -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 { + 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()) } diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.kt b/compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.kt new file mode 100644 index 00000000000..c4883eddbf2 --- /dev/null +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.kt @@ -0,0 +1,13 @@ +// FILE: KotlinFile.kt +fun foo(javaClass: JavaClass) { + javaClass.something = !javaClass.something + + javaClass.somethingWrong +} + +// FILE: JavaClass.java +public class JavaClass { + public boolean isSomething() { return true; } + public void setSomething(boolean value) { } + public int isSomethingWrong() { return 1; } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.txt b/compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.txt new file mode 100644 index 00000000000..f80114acd07 --- /dev/null +++ b/compiler/testData/diagnostics/tests/syntheticExtensions/IsNaming.txt @@ -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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java index 7b4f1987491..36c115ecd15 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestGenerated.java @@ -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"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt index 86c5aba7662..64f76e5d310 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt @@ -49,7 +49,8 @@ fun JetType.supertypes(): Set = 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 { val declarationDescriptor = getConstructor().getDeclarationDescriptor() diff --git a/idea/idea-completion/testData/basic/common/extensions/SyntheticExtensions2.kt b/idea/idea-completion/testData/basic/common/extensions/SyntheticExtensions2.kt index 6fdc9c39ef0..a85c4f8780b 100644 --- a/idea/idea-completion/testData/basic/common/extensions/SyntheticExtensions2.kt +++ b/idea/idea-completion/testData/basic/common/extensions/SyntheticExtensions2.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index 3c4d5081922..413b270489d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt @@ -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 }