From 95769dc9d3f64a46eadae075f971980ea2eedcb9 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 6 Jul 2017 19:39:31 +0300 Subject: [PATCH] Create from Usage: Make property lateinit where possible #KT-17651 Fixed --- .../callableBuilder/CallableBuilder.kt | 15 +++++++++++++++ .../callableBuilder/CallableInfo.kt | 6 ++++-- .../CreateCallableFromCallActionFactory.kt | 14 +++++++++----- .../property/varOnUserType.kt.after | 3 +-- 4 files changed, 29 insertions(+), 9 deletions(-) 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 a7941ceb295..ea61f781334 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 @@ -31,6 +31,7 @@ import com.intellij.psi.* import com.intellij.psi.codeStyle.CodeStyleManager import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode @@ -41,6 +42,7 @@ import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.imports.importableFqName @@ -68,6 +70,7 @@ import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeProjectionImpl +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny @@ -608,6 +611,16 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { return typeRefsToShorten } + private fun postprocessDeclaration(declaration: KtNamedDeclaration) { + if (callableInfo is PropertyInfo && callableInfo.isLateinitPreferred) { + if (declaration.containingClassOrObject == null) return + val propertyDescriptor = declaration.resolveToDescriptor() as? PropertyDescriptor ?: return + val returnType = propertyDescriptor.returnType ?: return + if (TypeUtils.isNullableType(returnType) || KotlinBuiltIns.isPrimitiveType(returnType)) return + declaration.addModifier(KtTokens.LATEINIT_KEYWORD) + } + } + private fun setupDeclarationBody(func: KtDeclarationWithBody) { val oldBody = func.bodyExpression ?: return val templateKind = when (func) { @@ -910,6 +923,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) { false) ?: return runWriteAction { + postprocessDeclaration(newDeclaration) + // file templates if (newDeclaration is KtNamedFunction || newDeclaration is KtSecondaryConstructor) { setupDeclarationBody(newDeclaration as KtFunction) diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt index 2a161f0e624..4dbce553714 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableInfo.kt @@ -220,7 +220,8 @@ class PropertyInfo(name: String, val writable: Boolean, possibleContainers: List = Collections.emptyList(), typeParameterInfos: List = Collections.emptyList(), - isAbstract: Boolean = false + isAbstract: Boolean = false, + val isLateinitPreferred: Boolean = false ) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) { override val kind: CallableKind get() = CallableKind.PROPERTY override val parameterInfos: List get() = Collections.emptyList() @@ -232,6 +233,7 @@ class PropertyInfo(name: String, writable, possibleContainers, typeParameterInfos, - isAbstract + isAbstract, + isLateinitPreferred ) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt index 3654ca9a0d9..5de2dbc8711 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/createCallable/CreateCallableFromCallActionFactory.kt @@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.analyzer.AnalysisResult +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ConstructorDescriptor @@ -187,11 +188,14 @@ sealed class CreateCallableFromCallActionFactory( ): CallableInfo? { val fullCallExpr = expression.getQualifiedExpressionForSelectorOrThis() val varExpected = fullCallExpr.getAssignmentByLHS() != null - val returnType = TypeInfo( - fullCallExpr.getExpressionForTypeGuess(), - if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE - ) - return PropertyInfo(name, receiverType, returnType, varExpected, possibleContainers) + val expressionForTypeGuess = fullCallExpr.getExpressionForTypeGuess() + val returnTypes = expressionForTypeGuess.guessTypes(analysisResult.bindingContext, analysisResult.moduleDescriptor) + val returnTypeInfo = TypeInfo(expressionForTypeGuess, if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE) + val canBeLateinit = + varExpected + && returnTypes.any { !it.isMarkedNullable && !KotlinBuiltIns.isPrimitiveType(it) } + && fullCallExpr.parents.firstOrNull { it is KtDeclarationWithBody || it is KtClassInitializer } is KtDeclarationWithBody + return PropertyInfo(name, receiverType, returnTypeInfo, varExpected, possibleContainers, isLateinitPreferred = canBeLateinit) } object Default : Property() { diff --git a/idea/testData/quickfix/createFromUsage/createVariable/property/varOnUserType.kt.after b/idea/testData/quickfix/createFromUsage/createVariable/property/varOnUserType.kt.after index 5cd6001f86c..eba2d072858 100644 --- a/idea/testData/quickfix/createFromUsage/createVariable/property/varOnUserType.kt.after +++ b/idea/testData/quickfix/createFromUsage/createVariable/property/varOnUserType.kt.after @@ -1,8 +1,7 @@ // "Create member property 'A.foo'" "true" -// ERROR: Property must be initialized or be abstract class A(val n: T) { - var foo: String + lateinit var foo: String } fun test() {